1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54 package io.netty.handler.codec.http.websocketx;
55
56 import io.netty.buffer.ByteBuf;
57 import io.netty.channel.ChannelHandlerContext;
58 import io.netty.handler.codec.MessageToMessageEncoder;
59 import io.netty.handler.codec.TooLongFrameException;
60 import io.netty.util.internal.logging.InternalLogger;
61 import io.netty.util.internal.logging.InternalLoggerFactory;
62
63 import java.nio.ByteOrder;
64 import java.util.List;
65
66
67
68
69
70
71
72 public class WebSocket08FrameEncoder extends MessageToMessageEncoder<WebSocketFrame> implements WebSocketFrameEncoder {
73
74 private static final InternalLogger logger = InternalLoggerFactory.getInstance(WebSocket08FrameEncoder.class);
75
76 private static final byte OPCODE_CONT = 0x0;
77 private static final byte OPCODE_TEXT = 0x1;
78 private static final byte OPCODE_BINARY = 0x2;
79 private static final byte OPCODE_CLOSE = 0x8;
80 private static final byte OPCODE_PING = 0x9;
81 private static final byte OPCODE_PONG = 0xA;
82
83
84
85
86
87
88
89 private static final int GATHERING_WRITE_THRESHOLD = 1024;
90
91 private final WebSocketFrameMaskGenerator maskGenerator;
92
93
94
95
96
97
98
99
100 public WebSocket08FrameEncoder(boolean maskPayload) {
101 this(maskPayload ? RandomWebSocketFrameMaskGenerator.INSTANCE : null);
102 }
103
104
105
106
107
108
109
110
111 public WebSocket08FrameEncoder(WebSocketFrameMaskGenerator maskGenerator) {
112 this.maskGenerator = maskGenerator;
113 }
114
115 @Override
116 protected void encode(ChannelHandlerContext ctx, WebSocketFrame msg, List<Object> out) throws Exception {
117 final ByteBuf data = msg.content();
118
119 byte opcode = getOpCode(msg);
120
121 int length = data.readableBytes();
122
123 if (logger.isTraceEnabled()) {
124 logger.trace("Encoding WebSocket Frame opCode={} length={}", opcode, length);
125 }
126
127 int b0 = 0;
128 if (msg.isFinalFragment()) {
129 b0 |= 1 << 7;
130 }
131 b0 |= (msg.rsv() & 0x07) << 4;
132 b0 |= opcode & 0x7F;
133
134 if (opcode == OPCODE_PING && length > 125) {
135 throw new TooLongFrameException("invalid payload for PING (payload length must be <= 125, was " + length);
136 }
137
138 boolean release = true;
139 ByteBuf buf = null;
140 try {
141 int maskLength = maskGenerator != null ? 4 : 0;
142 if (length <= 125) {
143 int size = 2 + maskLength + length;
144 buf = ctx.alloc().buffer(size);
145 buf.writeByte(b0);
146 byte b = (byte) (maskGenerator != null ? 0x80 | length : length);
147 buf.writeByte(b);
148 } else if (length <= 0xFFFF) {
149 int size = 4 + maskLength;
150 if (maskGenerator != null || length <= GATHERING_WRITE_THRESHOLD) {
151 size += length;
152 }
153 buf = ctx.alloc().buffer(size);
154 buf.writeByte(b0);
155 buf.writeByte(maskGenerator != null ? 0xFE : 126);
156 buf.writeByte(length >>> 8 & 0xFF);
157 buf.writeByte(length & 0xFF);
158 } else {
159 int size = 10 + maskLength;
160 if (maskGenerator != null) {
161 size += length;
162 }
163 buf = ctx.alloc().buffer(size);
164 buf.writeByte(b0);
165 buf.writeByte(maskGenerator != null ? 0xFF : 127);
166 buf.writeLong(length);
167 }
168
169
170 if (maskGenerator != null) {
171 int mask = maskGenerator.nextMask();
172 buf.writeInt(mask);
173
174
175 if (mask != 0) {
176 if (length > 0) {
177 ByteOrder srcOrder = data.order();
178 ByteOrder dstOrder = buf.order();
179
180 int i = data.readerIndex();
181 int end = data.writerIndex();
182
183 if (srcOrder == dstOrder) {
184
185
186 long longMask = mask & 0xFFFFFFFFL;
187 longMask |= longMask << 32;
188
189
190
191 if (srcOrder == ByteOrder.LITTLE_ENDIAN) {
192 longMask = Long.reverseBytes(longMask);
193 }
194
195 for (int lim = end - 7; i < lim; i += 8) {
196 buf.writeLong(data.getLong(i) ^ longMask);
197 }
198
199 if (i < end - 3) {
200 buf.writeInt(data.getInt(i) ^ (int) longMask);
201 i += 4;
202 }
203 }
204 int maskOffset = 0;
205 for (; i < end; i++) {
206 byte byteData = data.getByte(i);
207 buf.writeByte(byteData ^ WebSocketUtil.byteAtIndex(mask, maskOffset++ & 3));
208 }
209 }
210 out.add(buf);
211 } else {
212 addBuffers(buf, data, out);
213 }
214 } else {
215 addBuffers(buf, data, out);
216 }
217 release = false;
218 } finally {
219 if (release && buf != null) {
220 buf.release();
221 }
222 }
223 }
224
225 private static byte getOpCode(WebSocketFrame msg) {
226 if (msg instanceof TextWebSocketFrame) {
227 return OPCODE_TEXT;
228 }
229 if (msg instanceof BinaryWebSocketFrame) {
230 return OPCODE_BINARY;
231 }
232 if (msg instanceof PingWebSocketFrame) {
233 return OPCODE_PING;
234 }
235 if (msg instanceof PongWebSocketFrame) {
236 return OPCODE_PONG;
237 }
238 if (msg instanceof CloseWebSocketFrame) {
239 return OPCODE_CLOSE;
240 }
241 if (msg instanceof ContinuationWebSocketFrame) {
242 return OPCODE_CONT;
243 }
244 throw new UnsupportedOperationException("Cannot encode frame of type: " + msg.getClass().getName());
245 }
246
247 private static void addBuffers(ByteBuf buf, ByteBuf data, List<Object> out) {
248 int readableBytes = data.readableBytes();
249 if (buf.writableBytes() >= readableBytes) {
250
251 buf.writeBytes(data);
252 out.add(buf);
253 } else {
254 out.add(buf);
255 if (readableBytes > 0) {
256 out.add(data.retain());
257 }
258 }
259 }
260 }