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 org.jboss.netty.handler.codec.http.websocketx;
55
56 import java.nio.ByteBuffer;
57
58 import org.jboss.netty.buffer.ChannelBuffer;
59 import org.jboss.netty.buffer.ChannelBuffers;
60 import org.jboss.netty.channel.Channel;
61 import org.jboss.netty.channel.ChannelHandlerContext;
62 import org.jboss.netty.handler.codec.frame.TooLongFrameException;
63 import org.jboss.netty.handler.codec.oneone.OneToOneEncoder;
64 import org.jboss.netty.logging.InternalLogger;
65 import org.jboss.netty.logging.InternalLoggerFactory;
66
67
68
69
70
71
72
73 public class WebSocket08FrameEncoder extends OneToOneEncoder {
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 private final boolean maskPayload;
85
86
87
88
89
90
91
92
93 public WebSocket08FrameEncoder(boolean maskPayload) {
94 this.maskPayload = maskPayload;
95 }
96
97 @Override
98 protected Object encode(ChannelHandlerContext ctx, Channel channel, Object msg) throws Exception {
99
100 byte[] mask;
101
102 if (msg instanceof WebSocketFrame) {
103 WebSocketFrame frame = (WebSocketFrame) msg;
104 ChannelBuffer data = frame.getBinaryData();
105 if (data == null) {
106 data = ChannelBuffers.EMPTY_BUFFER;
107 }
108
109 byte opcode;
110 if (frame instanceof TextWebSocketFrame) {
111 opcode = OPCODE_TEXT;
112 } else if (frame instanceof PingWebSocketFrame) {
113 opcode = OPCODE_PING;
114 } else if (frame instanceof PongWebSocketFrame) {
115 opcode = OPCODE_PONG;
116 } else if (frame instanceof CloseWebSocketFrame) {
117 opcode = OPCODE_CLOSE;
118 } else if (frame instanceof BinaryWebSocketFrame) {
119 opcode = OPCODE_BINARY;
120 } else if (frame instanceof ContinuationWebSocketFrame) {
121 opcode = OPCODE_CONT;
122 } else {
123 throw new UnsupportedOperationException("Cannot encode frame of type: " + frame.getClass().getName());
124 }
125
126 int length = data.readableBytes();
127
128 if (logger.isDebugEnabled()) {
129 logger.debug("Encoding WebSocket Frame opCode=" + opcode + " length=" + length);
130 }
131
132 int b0 = 0;
133 if (frame.isFinalFragment()) {
134 b0 |= 1 << 7;
135 }
136 b0 |= frame.getRsv() % 8 << 4;
137 b0 |= opcode % 128;
138
139 ChannelBuffer header;
140 ChannelBuffer body;
141
142 if (opcode == OPCODE_PING && length > 125) {
143 throw new TooLongFrameException("invalid payload for PING (payload length must be <= 125, was "
144 + length);
145 }
146
147 int maskLength = maskPayload ? 4 : 0;
148 if (length <= 125) {
149 header = ChannelBuffers.buffer(2 + maskLength);
150 header.writeByte(b0);
151 byte b = (byte) (maskPayload ? 0x80 | (byte) length : (byte) length);
152 header.writeByte(b);
153 } else if (length <= 0xFFFF) {
154 header = ChannelBuffers.buffer(4 + maskLength);
155 header.writeByte(b0);
156 header.writeByte(maskPayload ? 0xFE : 126);
157 header.writeByte(length >>> 8 & 0xFF);
158 header.writeByte(length & 0xFF);
159 } else {
160 header = ChannelBuffers.buffer(10 + maskLength);
161 header.writeByte(b0);
162 header.writeByte(maskPayload ? 0xFF : 127);
163 header.writeLong(length);
164 }
165
166
167 if (maskPayload) {
168 Integer random = (int) (Math.random() * Integer.MAX_VALUE);
169 mask = ByteBuffer.allocate(4).putInt(random).array();
170 header.writeBytes(mask);
171
172 body = ChannelBuffers.buffer(length);
173 int counter = 0;
174 while (data.readableBytes() > 0) {
175 byte byteData = data.readByte();
176 body.writeByte(byteData ^ mask[+counter++ % 4]);
177 }
178 } else {
179 body = data;
180 }
181 return ChannelBuffers.wrappedBuffer(header, body);
182 }
183
184
185 return msg;
186 }
187
188 }