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.ByteBuffer;
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_TRESHOLD = 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 byte[] mask;
109
110 byte opcode;
111 if (msg instanceof TextWebSocketFrame) {
112 opcode = OPCODE_TEXT;
113 } else if (msg instanceof PingWebSocketFrame) {
114 opcode = OPCODE_PING;
115 } else if (msg instanceof PongWebSocketFrame) {
116 opcode = OPCODE_PONG;
117 } else if (msg instanceof CloseWebSocketFrame) {
118 opcode = OPCODE_CLOSE;
119 } else if (msg instanceof BinaryWebSocketFrame) {
120 opcode = OPCODE_BINARY;
121 } else if (msg instanceof ContinuationWebSocketFrame) {
122 opcode = OPCODE_CONT;
123 } else {
124 throw new UnsupportedOperationException("Cannot encode frame of type: " + msg.getClass().getName());
125 }
126
127 int length = data.readableBytes();
128
129 if (logger.isDebugEnabled()) {
130 logger.debug("Encoding WebSocket Frame opCode=" + opcode + " length=" + length);
131 }
132
133 int b0 = 0;
134 if (msg.isFinalFragment()) {
135 b0 |= 1 << 7;
136 }
137 b0 |= msg.rsv() % 8 << 4;
138 b0 |= opcode % 128;
139
140 if (opcode == OPCODE_PING && length > 125) {
141 throw new TooLongFrameException("invalid payload for PING (payload length must be <= 125, was "
142 + length);
143 }
144
145 boolean release = true;
146 ByteBuf buf = null;
147 try {
148 int maskLength = maskPayload ? 4 : 0;
149 if (length <= 125) {
150 int size = 2 + maskLength;
151 if (maskPayload || length <= GATHERING_WRITE_TRESHOLD) {
152 size += length;
153 }
154 buf = ctx.alloc().buffer(size);
155 buf.writeByte(b0);
156 byte b = (byte) (maskPayload ? 0x80 | (byte) length : (byte) length);
157 buf.writeByte(b);
158 } else if (length <= 0xFFFF) {
159 int size = 4 + maskLength;
160 if (maskPayload || length <= GATHERING_WRITE_TRESHOLD) {
161 size += length;
162 }
163 buf = ctx.alloc().buffer(size);
164 buf.writeByte(b0);
165 buf.writeByte(maskPayload ? 0xFE : 126);
166 buf.writeByte(length >>> 8 & 0xFF);
167 buf.writeByte(length & 0xFF);
168 } else {
169 int size = 10 + maskLength;
170 if (maskPayload || length <= GATHERING_WRITE_TRESHOLD) {
171 size += length;
172 }
173 buf = ctx.alloc().buffer(size);
174 buf.writeByte(b0);
175 buf.writeByte(maskPayload ? 0xFF : 127);
176 buf.writeLong(length);
177 }
178
179
180 if (maskPayload) {
181 int random = (int) (Math.random() * Integer.MAX_VALUE);
182 mask = ByteBuffer.allocate(4).putInt(random).array();
183 buf.writeBytes(mask);
184
185 ByteOrder srcOrder = data.order();
186 ByteOrder dstOrder = buf.order();
187
188 int counter = 0;
189 int i = data.readerIndex();
190 int end = data.writerIndex();
191
192 if (srcOrder == dstOrder) {
193
194
195
196 int intMask = ((mask[0] & 0xFF) << 24)
197 | ((mask[1] & 0xFF) << 16)
198 | ((mask[2] & 0xFF) << 8)
199 | (mask[3] & 0xFF);
200
201
202
203 if (srcOrder == ByteOrder.LITTLE_ENDIAN) {
204 intMask = Integer.reverseBytes(intMask);
205 }
206
207 for (; i + 3 < end; i += 4) {
208 int intData = data.getInt(i);
209 buf.writeInt(intData ^ intMask);
210 }
211 }
212 for (; i < end; i++) {
213 byte byteData = data.getByte(i);
214 buf.writeByte(byteData ^ mask[counter++ % 4]);
215 }
216 out.add(buf);
217 } else {
218 if (buf.writableBytes() >= data.readableBytes()) {
219
220 buf.writeBytes(data);
221 out.add(buf);
222 } else {
223 out.add(buf);
224 out.add(data.retain());
225 }
226 }
227 release = false;
228 } finally {
229 if (release && buf != null) {
230 buf.release();
231 }
232 }
233 }
234 }