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