1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.handler.codec.redis;
17
18 import io.netty.buffer.ByteBuf;
19 import io.netty.buffer.ByteBufAllocator;
20 import io.netty.buffer.ByteBufUtil;
21 import io.netty.channel.ChannelHandlerContext;
22 import io.netty.handler.codec.CodecException;
23 import io.netty.handler.codec.MessageToMessageEncoder;
24 import io.netty.util.internal.ObjectUtil;
25 import io.netty.util.internal.UnstableApi;
26
27 import java.util.List;
28
29
30
31
32
33 @UnstableApi
34 public class RedisEncoder extends MessageToMessageEncoder<RedisMessage> {
35
36 private final RedisMessagePool messagePool;
37
38
39
40
41 public RedisEncoder() {
42 this(FixedRedisMessagePool.INSTANCE);
43 }
44
45
46
47
48
49 public RedisEncoder(RedisMessagePool messagePool) {
50 this.messagePool = ObjectUtil.checkNotNull(messagePool, "messagePool");
51 }
52
53 @Override
54 protected void encode(ChannelHandlerContext ctx, RedisMessage msg, List<Object> out) throws Exception {
55 try {
56 writeRedisMessage(ctx.alloc(), msg, out);
57 } catch (CodecException e) {
58 throw e;
59 } catch (Exception e) {
60 throw new CodecException(e);
61 }
62 }
63
64 private void writeRedisMessage(ByteBufAllocator allocator, RedisMessage msg, List<Object> out) {
65 if (msg instanceof InlineCommandRedisMessage) {
66 writeInlineCommandMessage(allocator, (InlineCommandRedisMessage) msg, out);
67 } else if (msg instanceof SimpleStringRedisMessage) {
68 writeSimpleStringMessage(allocator, (SimpleStringRedisMessage) msg, out);
69 } else if (msg instanceof ErrorRedisMessage) {
70 writeErrorMessage(allocator, (ErrorRedisMessage) msg, out);
71 } else if (msg instanceof IntegerRedisMessage) {
72 writeIntegerMessage(allocator, (IntegerRedisMessage) msg, out);
73 } else if (msg instanceof FullBulkStringRedisMessage) {
74 writeFullBulkStringMessage(allocator, (FullBulkStringRedisMessage) msg, out);
75 } else if (msg instanceof BulkStringRedisContent) {
76 writeBulkStringContent(allocator, (BulkStringRedisContent) msg, out);
77 } else if (msg instanceof BulkStringHeaderRedisMessage) {
78 writeBulkStringHeader(allocator, (BulkStringHeaderRedisMessage) msg, out);
79 } else if (msg instanceof ArrayHeaderRedisMessage) {
80 writeArrayHeader(allocator, (ArrayHeaderRedisMessage) msg, out);
81 } else if (msg instanceof ArrayRedisMessage) {
82 writeArrayMessage(allocator, (ArrayRedisMessage) msg, out);
83 } else {
84 throw new CodecException("unknown message type: " + msg);
85 }
86 }
87
88 private static void writeInlineCommandMessage(ByteBufAllocator allocator, InlineCommandRedisMessage msg,
89 List<Object> out) {
90 writeString(allocator, RedisMessageType.INLINE_COMMAND, msg.content(), out);
91 }
92
93 private static void writeSimpleStringMessage(ByteBufAllocator allocator, SimpleStringRedisMessage msg,
94 List<Object> out) {
95 writeString(allocator, RedisMessageType.SIMPLE_STRING, msg.content(), out);
96 }
97
98 private static void writeErrorMessage(ByteBufAllocator allocator, ErrorRedisMessage msg, List<Object> out) {
99 writeString(allocator, RedisMessageType.ERROR, msg.content(), out);
100 }
101
102 private static void writeString(ByteBufAllocator allocator, RedisMessageType type, String content,
103 List<Object> out) {
104 if (type.isInline()) {
105
106 if (content.indexOf('\r') != -1 || content.indexOf('\n') != -1) {
107 throw new CodecException("Line breaks are not permitted in 'simple' messages");
108 }
109 }
110 ByteBuf buf = allocator.ioBuffer(type.length() + ByteBufUtil.utf8MaxBytes(content) +
111 RedisConstants.EOL_LENGTH);
112 type.writeTo(buf);
113 ByteBufUtil.writeUtf8(buf, content);
114 buf.writeShort(RedisConstants.EOL_SHORT);
115 out.add(buf);
116 }
117
118 private void writeIntegerMessage(ByteBufAllocator allocator, IntegerRedisMessage msg, List<Object> out) {
119 ByteBuf buf = allocator.ioBuffer(RedisConstants.TYPE_LENGTH + RedisConstants.LONG_MAX_LENGTH +
120 RedisConstants.EOL_LENGTH);
121 RedisMessageType.INTEGER.writeTo(buf);
122 buf.writeBytes(numberToBytes(msg.value()));
123 buf.writeShort(RedisConstants.EOL_SHORT);
124 out.add(buf);
125 }
126
127 private void writeBulkStringHeader(ByteBufAllocator allocator, BulkStringHeaderRedisMessage msg, List<Object> out) {
128 final ByteBuf buf = allocator.ioBuffer(RedisConstants.TYPE_LENGTH +
129 (msg.isNull() ? RedisConstants.NULL_LENGTH :
130 RedisConstants.LONG_MAX_LENGTH + RedisConstants.EOL_LENGTH));
131 RedisMessageType.BULK_STRING.writeTo(buf);
132 if (msg.isNull()) {
133 buf.writeShort(RedisConstants.NULL_SHORT);
134 } else {
135 buf.writeBytes(numberToBytes(msg.bulkStringLength()));
136 buf.writeShort(RedisConstants.EOL_SHORT);
137 }
138 out.add(buf);
139 }
140
141 private static void writeBulkStringContent(ByteBufAllocator allocator, BulkStringRedisContent msg,
142 List<Object> out) {
143 out.add(msg.content().retain());
144 if (msg instanceof LastBulkStringRedisContent) {
145 out.add(allocator.ioBuffer(RedisConstants.EOL_LENGTH).writeShort(RedisConstants.EOL_SHORT));
146 }
147 }
148
149 private void writeFullBulkStringMessage(ByteBufAllocator allocator, FullBulkStringRedisMessage msg,
150 List<Object> out) {
151 if (msg.isNull()) {
152 ByteBuf buf = allocator.ioBuffer(RedisConstants.TYPE_LENGTH + RedisConstants.NULL_LENGTH +
153 RedisConstants.EOL_LENGTH);
154 RedisMessageType.BULK_STRING.writeTo(buf);
155 buf.writeShort(RedisConstants.NULL_SHORT);
156 buf.writeShort(RedisConstants.EOL_SHORT);
157 out.add(buf);
158 } else {
159 ByteBuf headerBuf = allocator.ioBuffer(RedisConstants.TYPE_LENGTH + RedisConstants.LONG_MAX_LENGTH +
160 RedisConstants.EOL_LENGTH);
161 RedisMessageType.BULK_STRING.writeTo(headerBuf);
162 headerBuf.writeBytes(numberToBytes(msg.content().readableBytes()));
163 headerBuf.writeShort(RedisConstants.EOL_SHORT);
164 out.add(headerBuf);
165 out.add(msg.content().retain());
166 out.add(allocator.ioBuffer(RedisConstants.EOL_LENGTH).writeShort(RedisConstants.EOL_SHORT));
167 }
168 }
169
170
171
172
173 private void writeArrayHeader(ByteBufAllocator allocator, ArrayHeaderRedisMessage msg, List<Object> out) {
174 writeArrayHeader(allocator, msg.isNull(), msg.length(), out);
175 }
176
177
178
179
180 private void writeArrayMessage(ByteBufAllocator allocator, ArrayRedisMessage msg, List<Object> out) {
181 if (msg.isNull()) {
182 writeArrayHeader(allocator, msg.isNull(), RedisConstants.NULL_VALUE, out);
183 } else {
184 writeArrayHeader(allocator, msg.isNull(), msg.children().size(), out);
185 for (RedisMessage child : msg.children()) {
186 writeRedisMessage(allocator, child, out);
187 }
188 }
189 }
190
191 private void writeArrayHeader(ByteBufAllocator allocator, boolean isNull, long length, List<Object> out) {
192 if (isNull) {
193 final ByteBuf buf = allocator.ioBuffer(RedisConstants.TYPE_LENGTH + RedisConstants.NULL_LENGTH +
194 RedisConstants.EOL_LENGTH);
195 RedisMessageType.ARRAY_HEADER.writeTo(buf);
196 buf.writeShort(RedisConstants.NULL_SHORT);
197 buf.writeShort(RedisConstants.EOL_SHORT);
198 out.add(buf);
199 } else {
200 final ByteBuf buf = allocator.ioBuffer(RedisConstants.TYPE_LENGTH + RedisConstants.LONG_MAX_LENGTH +
201 RedisConstants.EOL_LENGTH);
202 RedisMessageType.ARRAY_HEADER.writeTo(buf);
203 buf.writeBytes(numberToBytes(length));
204 buf.writeShort(RedisConstants.EOL_SHORT);
205 out.add(buf);
206 }
207 }
208
209 private byte[] numberToBytes(long value) {
210 byte[] bytes = messagePool.getByteBufOfInteger(value);
211 return bytes != null ? bytes : RedisCodecUtil.longToAsciiBytes(value);
212 }
213 }