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.PlatformDependent;
61 import io.netty.util.internal.logging.InternalLogger;
62 import io.netty.util.internal.logging.InternalLoggerFactory;
63
64 import java.nio.ByteOrder;
65 import java.util.List;
66
67
68
69
70
71
72
73 public class WebSocket08FrameEncoder extends MessageToMessageEncoder<WebSocketFrame> implements WebSocketFrameEncoder {
74
75 private static final InternalLogger logger = InternalLoggerFactory.getInstance(WebSocket08FrameEncoder.class);
76
77 private static final byte OPCODE_CONT = 0x0;
78 private static final byte OPCODE_TEXT = 0x1;
79 private static final byte OPCODE_BINARY = 0x2;
80 private static final byte OPCODE_CLOSE = 0x8;
81 private static final byte OPCODE_PING = 0x9;
82 private static final byte OPCODE_PONG = 0xA;
83
84
85
86
87
88
89
90 private static final int GATHERING_WRITE_THRESHOLD = 1024;
91
92 private final boolean maskPayload;
93
94
95
96
97
98
99
100
101 public WebSocket08FrameEncoder(boolean maskPayload) {
102 this.maskPayload = maskPayload;
103 }
104
105 @Override
106 protected void encode(ChannelHandlerContext ctx, WebSocketFrame msg, List<Object> out) throws Exception {
107 final ByteBuf data = msg.content();
108
109 byte opcode;
110 if (msg instanceof TextWebSocketFrame) {
111 opcode = OPCODE_TEXT;
112 } else if (msg instanceof PingWebSocketFrame) {
113 opcode = OPCODE_PING;
114 } else if (msg instanceof PongWebSocketFrame) {
115 opcode = OPCODE_PONG;
116 } else if (msg instanceof CloseWebSocketFrame) {
117 opcode = OPCODE_CLOSE;
118 } else if (msg instanceof BinaryWebSocketFrame) {
119 opcode = OPCODE_BINARY;
120 } else if (msg instanceof ContinuationWebSocketFrame) {
121 opcode = OPCODE_CONT;
122 } else {
123 throw new UnsupportedOperationException("Cannot encode frame of type: " + msg.getClass().getName());
124 }
125
126 int length = data.readableBytes();
127
128 if (logger.isTraceEnabled()) {
129 logger.trace("Encoding WebSocket Frame opCode={} length={}", opcode, length);
130 }
131
132 int b0 = 0;
133 if (msg.isFinalFragment()) {
134 b0 |= 1 << 7;
135 }
136 b0 |= msg.rsv() % 8 << 4;
137 b0 |= opcode % 128;
138
139 if (opcode == OPCODE_PING && length > 125) {
140 throw new TooLongFrameException("invalid payload for PING (payload length must be <= 125, was " + length);
141 }
142
143 boolean release = true;
144 ByteBuf buf = null;
145 try {
146 int maskLength = maskPayload ? 4 : 0;
147 if (length <= 125) {
148 int size = 2 + maskLength + length;
149 buf = ctx.alloc().buffer(size);
150 buf.writeByte(b0);
151 byte b = (byte) (maskPayload ? 0x80 | (byte) length : (byte) length);
152 buf.writeByte(b);
153 } else if (length <= 0xFFFF) {
154 int size = 4 + maskLength;
155 if (maskPayload || length <= GATHERING_WRITE_THRESHOLD) {
156 size += length;
157 }
158 buf = ctx.alloc().buffer(size);
159 buf.writeByte(b0);
160 buf.writeByte(maskPayload ? 0xFE : 126);
161 buf.writeByte(length >>> 8 & 0xFF);
162 buf.writeByte(length & 0xFF);
163 } else {
164 int size = 10 + maskLength;
165 if (maskPayload) {
166 size += length;
167 }
168 buf = ctx.alloc().buffer(size);
169 buf.writeByte(b0);
170 buf.writeByte(maskPayload ? 0xFF : 127);
171 buf.writeLong(length);
172 }
173
174
175 if (maskPayload) {
176 int mask = PlatformDependent.threadLocalRandom().nextInt(Integer.MAX_VALUE);
177 buf.writeInt(mask);
178
179 if (data.isReadable()) {
180
181 ByteOrder srcOrder = data.order();
182 ByteOrder dstOrder = buf.order();
183
184 int i = data.readerIndex();
185 int end = data.writerIndex();
186
187 if (srcOrder == dstOrder) {
188
189
190 long longMask = mask & 0xFFFFFFFFL;
191 longMask |= longMask << 32;
192
193
194
195 if (srcOrder == ByteOrder.LITTLE_ENDIAN) {
196 longMask = Long.reverseBytes(longMask);
197 }
198
199 for (int lim = end - 7; i < lim; i += 8) {
200 buf.writeLong(data.getLong(i) ^ longMask);
201 }
202
203 if (i < end - 3) {
204 buf.writeInt(data.getInt(i) ^ (int) longMask);
205 i += 4;
206 }
207 }
208 int maskOffset = 0;
209 for (; i < end; i++) {
210 byte byteData = data.getByte(i);
211 buf.writeByte(byteData ^ WebSocketUtil.byteAtIndex(mask, maskOffset++ & 3));
212 }
213 }
214 out.add(buf);
215 } else {
216 if (buf.writableBytes() >= data.readableBytes()) {
217
218 buf.writeBytes(data);
219 out.add(buf);
220 } else {
221 out.add(buf);
222 out.add(data.retain());
223 }
224 }
225 release = false;
226 } finally {
227 if (release && buf != null) {
228 buf.release();
229 }
230 }
231 }
232 }