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 super(WebSocketFrame.class);
113 this.maskGenerator = maskGenerator;
114 }
115
116 @Override
117 protected void encode(ChannelHandlerContext ctx, WebSocketFrame msg, List<Object> out) throws Exception {
118 final ByteBuf data = msg.content();
119
120 byte opcode = getOpCode(msg);
121
122 int length = data.readableBytes();
123
124 if (logger.isTraceEnabled()) {
125 logger.trace("Encoding WebSocket Frame opCode={} length={}", opcode, length);
126 }
127
128 int b0 = 0;
129 if (msg.isFinalFragment()) {
130 b0 |= 1 << 7;
131 }
132 b0 |= (msg.rsv() & 0x07) << 4;
133 b0 |= opcode & 0x7F;
134
135 if (opcode == OPCODE_PING && length > 125) {
136 throw new TooLongFrameException("invalid payload for PING (payload length must be <= 125, was " + length);
137 }
138
139 boolean release = true;
140 ByteBuf buf = null;
141 try {
142 int maskLength = maskGenerator != null ? 4 : 0;
143 if (length <= 125) {
144 int size = 2 + maskLength + length;
145 buf = ctx.alloc().buffer(size);
146 buf.writeByte(b0);
147 byte b = (byte) (maskGenerator != null ? 0x80 | length : length);
148 buf.writeByte(b);
149 } else if (length <= 0xFFFF) {
150 int size = 4 + maskLength;
151 if (maskGenerator != null || length <= GATHERING_WRITE_THRESHOLD) {
152 size += length;
153 }
154 buf = ctx.alloc().buffer(size);
155 buf.writeByte(b0);
156 buf.writeByte(maskGenerator != null ? 0xFE : 126);
157 buf.writeByte(length >>> 8 & 0xFF);
158 buf.writeByte(length & 0xFF);
159 } else {
160 int size = 10 + maskLength;
161 if (maskGenerator != null) {
162 size += length;
163 }
164 buf = ctx.alloc().buffer(size);
165 buf.writeByte(b0);
166 buf.writeByte(maskGenerator != null ? 0xFF : 127);
167 buf.writeLong(length);
168 }
169
170
171 if (maskGenerator != null) {
172 int mask = maskGenerator.nextMask();
173 buf.writeInt(mask);
174
175
176 if (mask != 0) {
177 if (length > 0) {
178 ByteOrder srcOrder = data.order();
179 ByteOrder dstOrder = buf.order();
180
181 int i = data.readerIndex();
182 int end = data.writerIndex();
183
184 if (srcOrder == dstOrder) {
185
186
187 long longMask = mask & 0xFFFFFFFFL;
188 longMask |= longMask << 32;
189
190
191
192 if (srcOrder == ByteOrder.LITTLE_ENDIAN) {
193 longMask = Long.reverseBytes(longMask);
194 }
195
196 for (int lim = end - 7; i < lim; i += 8) {
197 buf.writeLong(data.getLong(i) ^ longMask);
198 }
199
200 if (i < end - 3) {
201 buf.writeInt(data.getInt(i) ^ (int) longMask);
202 i += 4;
203 }
204 }
205 int maskOffset = 0;
206 for (; i < end; i++) {
207 byte byteData = data.getByte(i);
208 buf.writeByte(byteData ^ WebSocketUtil.byteAtIndex(mask, maskOffset++ & 3));
209 }
210 }
211 out.add(buf);
212 } else {
213 addBuffers(buf, data, out);
214 }
215 } else {
216 addBuffers(buf, data, out);
217 }
218 release = false;
219 } finally {
220 if (release && buf != null) {
221 buf.release();
222 }
223 }
224 }
225
226 private static byte getOpCode(WebSocketFrame msg) {
227 if (msg instanceof TextWebSocketFrame) {
228 return OPCODE_TEXT;
229 }
230 if (msg instanceof BinaryWebSocketFrame) {
231 return OPCODE_BINARY;
232 }
233 if (msg instanceof PingWebSocketFrame) {
234 return OPCODE_PING;
235 }
236 if (msg instanceof PongWebSocketFrame) {
237 return OPCODE_PONG;
238 }
239 if (msg instanceof CloseWebSocketFrame) {
240 return OPCODE_CLOSE;
241 }
242 if (msg instanceof ContinuationWebSocketFrame) {
243 return OPCODE_CONT;
244 }
245 throw new UnsupportedOperationException("Cannot encode frame of type: " + msg.getClass().getName());
246 }
247
248 private static void addBuffers(ByteBuf buf, ByteBuf data, List<Object> out) {
249 int readableBytes = data.readableBytes();
250 if (buf.writableBytes() >= readableBytes) {
251
252 buf.writeBytes(data);
253 out.add(buf);
254 } else {
255 out.add(buf);
256 if (readableBytes > 0) {
257 out.add(data.retain());
258 }
259 }
260 }
261 }