1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.jboss.netty.handler.codec.spdy;
17
18 import static org.jboss.netty.handler.codec.spdy.SpdyCodecUtil.*;
19
20 import java.nio.ByteOrder;
21 import java.util.Set;
22
23 import org.jboss.netty.buffer.ChannelBuffer;
24 import org.jboss.netty.buffer.ChannelBuffers;
25 import org.jboss.netty.channel.ChannelDownstreamHandler;
26 import org.jboss.netty.channel.ChannelEvent;
27 import org.jboss.netty.channel.ChannelHandlerContext;
28 import org.jboss.netty.channel.ChannelStateEvent;
29 import org.jboss.netty.channel.Channels;
30 import org.jboss.netty.channel.MessageEvent;
31
32
33
34
35 public class SpdyFrameEncoder implements ChannelDownstreamHandler {
36
37 private final int version;
38 private volatile boolean finished;
39 private final SpdyHeaderBlockCompressor headerBlockCompressor;
40
41
42
43
44
45
46 @Deprecated
47 public SpdyFrameEncoder() {
48 this(2, 6, 15, 8);
49 }
50
51
52
53
54
55
56 public SpdyFrameEncoder(int version) {
57 this(version, 6, 15, 8);
58 }
59
60
61
62
63
64 @Deprecated
65 public SpdyFrameEncoder(int compressionLevel, int windowBits, int memLevel) {
66 this(2, compressionLevel, windowBits, memLevel);
67 }
68
69
70
71
72 public SpdyFrameEncoder(int version, int compressionLevel, int windowBits, int memLevel) {
73 if (version < SPDY_MIN_VERSION || version > SPDY_MAX_VERSION) {
74 throw new IllegalArgumentException(
75 "unknown version: " + version);
76 }
77 this.version = version;
78 headerBlockCompressor = SpdyHeaderBlockCompressor.newInstance(
79 version, compressionLevel, windowBits, memLevel);
80 }
81
82 public void handleDownstream(
83 final ChannelHandlerContext ctx, ChannelEvent evt) throws Exception {
84 if (evt instanceof ChannelStateEvent) {
85 ChannelStateEvent e = (ChannelStateEvent) evt;
86 switch (e.getState()) {
87 case OPEN:
88 case CONNECTED:
89 case BOUND:
90 if (Boolean.FALSE.equals(e.getValue()) || e.getValue() == null) {
91 synchronized (headerBlockCompressor) {
92 finished = true;
93 headerBlockCompressor.end();
94 }
95 }
96 }
97 }
98
99 if (!(evt instanceof MessageEvent)) {
100 ctx.sendDownstream(evt);
101 return;
102 }
103
104 final MessageEvent e = (MessageEvent) evt;
105 Object msg = e.getMessage();
106
107 if (msg instanceof SpdyDataFrame) {
108
109 SpdyDataFrame spdyDataFrame = (SpdyDataFrame) msg;
110 ChannelBuffer data = spdyDataFrame.getData();
111 byte flags = spdyDataFrame.isLast() ? SPDY_DATA_FLAG_FIN : 0;
112 ChannelBuffer header = ChannelBuffers.buffer(
113 ByteOrder.BIG_ENDIAN, SPDY_HEADER_SIZE);
114 header.writeInt(spdyDataFrame.getStreamId() & 0x7FFFFFFF);
115 header.writeByte(flags);
116 header.writeMedium(data.readableBytes());
117 ChannelBuffer frame = ChannelBuffers.wrappedBuffer(header, data);
118 Channels.write(ctx, e.getFuture(), frame, e.getRemoteAddress());
119 return;
120
121 } else if (msg instanceof SpdySynStreamFrame) {
122
123 synchronized (headerBlockCompressor) {
124 SpdySynStreamFrame spdySynStreamFrame = (SpdySynStreamFrame) msg;
125 ChannelBuffer data = compressHeaderBlock(
126 encodeHeaderBlock(version, spdySynStreamFrame));
127 byte flags = spdySynStreamFrame.isLast() ? SPDY_FLAG_FIN : 0;
128 if (spdySynStreamFrame.isUnidirectional()) {
129 flags |= SPDY_FLAG_UNIDIRECTIONAL;
130 }
131 int headerBlockLength = data.readableBytes();
132 int length;
133 if (version < 3) {
134 length = headerBlockLength == 0 ? 12 : 10 + headerBlockLength;
135 } else {
136 length = 10 + headerBlockLength;
137 }
138 ChannelBuffer frame = ChannelBuffers.buffer(
139 ByteOrder.BIG_ENDIAN, SPDY_HEADER_SIZE + 12);
140 frame.writeShort(version | 0x8000);
141 frame.writeShort(SPDY_SYN_STREAM_FRAME);
142 frame.writeByte(flags);
143 frame.writeMedium(length);
144 frame.writeInt(spdySynStreamFrame.getStreamId());
145 frame.writeInt(spdySynStreamFrame.getAssociatedToStreamId());
146 if (version < 3) {
147
148 byte priority = spdySynStreamFrame.getPriority();
149 if (priority > 3) {
150 priority = 3;
151 }
152 frame.writeShort((priority & 0xFF) << 14);
153 } else {
154 frame.writeShort((spdySynStreamFrame.getPriority() & 0xFF) << 13);
155 }
156 if (version < 3 && data.readableBytes() == 0) {
157 frame.writeShort(0);
158 }
159
160 final ChannelBuffer buffer = ChannelBuffers.wrappedBuffer(frame, data);
161 Channels.write(ctx, e.getFuture(), buffer, e.getRemoteAddress());
162 }
163 return;
164
165 } else if (msg instanceof SpdySynReplyFrame) {
166
167 synchronized (headerBlockCompressor) {
168 SpdySynReplyFrame spdySynReplyFrame = (SpdySynReplyFrame) msg;
169 ChannelBuffer data = compressHeaderBlock(
170 encodeHeaderBlock(version, spdySynReplyFrame));
171 byte flags = spdySynReplyFrame.isLast() ? SPDY_FLAG_FIN : 0;
172 int headerBlockLength = data.readableBytes();
173 int length;
174 if (version < 3) {
175 length = headerBlockLength == 0 ? 8 : 6 + headerBlockLength;
176 } else {
177 length = 4 + headerBlockLength;
178 }
179 ChannelBuffer frame = ChannelBuffers.buffer(
180 ByteOrder.BIG_ENDIAN, SPDY_HEADER_SIZE + 8);
181 frame.writeShort(version | 0x8000);
182 frame.writeShort(SPDY_SYN_REPLY_FRAME);
183 frame.writeByte(flags);
184 frame.writeMedium(length);
185 frame.writeInt(spdySynReplyFrame.getStreamId());
186 if (version < 3) {
187 if (data.readableBytes() == 0) {
188 frame.writeInt(0);
189 } else {
190 frame.writeShort(0);
191 }
192 }
193
194 final ChannelBuffer buffer = ChannelBuffers.wrappedBuffer(frame, data);
195 Channels.write(ctx, e.getFuture(), buffer, e.getRemoteAddress());
196 }
197 return;
198
199 } else if (msg instanceof SpdyRstStreamFrame) {
200
201 SpdyRstStreamFrame spdyRstStreamFrame = (SpdyRstStreamFrame) msg;
202 ChannelBuffer frame = ChannelBuffers.buffer(
203 ByteOrder.BIG_ENDIAN, SPDY_HEADER_SIZE + 8);
204 frame.writeShort(version | 0x8000);
205 frame.writeShort(SPDY_RST_STREAM_FRAME);
206 frame.writeInt(8);
207 frame.writeInt(spdyRstStreamFrame.getStreamId());
208 frame.writeInt(spdyRstStreamFrame.getStatus().getCode());
209 Channels.write(ctx, e.getFuture(), frame, e.getRemoteAddress());
210 return;
211
212 } else if (msg instanceof SpdySettingsFrame) {
213
214 SpdySettingsFrame spdySettingsFrame = (SpdySettingsFrame) msg;
215 byte flags = spdySettingsFrame.clearPreviouslyPersistedSettings() ?
216 SPDY_SETTINGS_CLEAR : 0;
217 Set<Integer> IDs = spdySettingsFrame.getIds();
218 int numEntries = IDs.size();
219 int length = 4 + numEntries * 8;
220 ChannelBuffer frame = ChannelBuffers.buffer(
221 ByteOrder.BIG_ENDIAN, SPDY_HEADER_SIZE + length);
222 frame.writeShort(version | 0x8000);
223 frame.writeShort(SPDY_SETTINGS_FRAME);
224 frame.writeByte(flags);
225 frame.writeMedium(length);
226 frame.writeInt(numEntries);
227 for (Integer ID: IDs) {
228 int id = ID.intValue();
229 byte ID_flags = (byte) 0;
230 if (spdySettingsFrame.isPersistValue(id)) {
231 ID_flags |= SPDY_SETTINGS_PERSIST_VALUE;
232 }
233 if (spdySettingsFrame.isPersisted(id)) {
234 ID_flags |= SPDY_SETTINGS_PERSISTED;
235 }
236 if (version < 3) {
237
238
239
240 frame.writeByte(id & 0xFF);
241 frame.writeByte(id >> 8 & 0xFF);
242 frame.writeByte(id >> 16 & 0xFF);
243 frame.writeByte(ID_flags);
244 } else {
245 frame.writeByte(ID_flags);
246 frame.writeMedium(id);
247 }
248 frame.writeInt(spdySettingsFrame.getValue(id));
249 }
250 Channels.write(ctx, e.getFuture(), frame, e.getRemoteAddress());
251 return;
252
253 } else if (msg instanceof SpdyNoOpFrame) {
254
255 ChannelBuffer frame = ChannelBuffers.buffer(
256 ByteOrder.BIG_ENDIAN, SPDY_HEADER_SIZE);
257 frame.writeShort(version | 0x8000);
258 frame.writeShort(SPDY_NOOP_FRAME);
259 frame.writeInt(0);
260 Channels.write(ctx, e.getFuture(), frame, e.getRemoteAddress());
261 return;
262
263 } else if (msg instanceof SpdyPingFrame) {
264
265 SpdyPingFrame spdyPingFrame = (SpdyPingFrame) msg;
266 ChannelBuffer frame = ChannelBuffers.buffer(
267 ByteOrder.BIG_ENDIAN, SPDY_HEADER_SIZE + 4);
268 frame.writeShort(version | 0x8000);
269 frame.writeShort(SPDY_PING_FRAME);
270 frame.writeInt(4);
271 frame.writeInt(spdyPingFrame.getId());
272 Channels.write(ctx, e.getFuture(), frame, e.getRemoteAddress());
273 return;
274
275 } else if (msg instanceof SpdyGoAwayFrame) {
276
277 SpdyGoAwayFrame spdyGoAwayFrame = (SpdyGoAwayFrame) msg;
278 int length = version < 3 ? 4 : 8;
279 ChannelBuffer frame = ChannelBuffers.buffer(
280 ByteOrder.BIG_ENDIAN, SPDY_HEADER_SIZE + length);
281 frame.writeShort(version | 0x8000);
282 frame.writeShort(SPDY_GOAWAY_FRAME);
283 frame.writeInt(length);
284 frame.writeInt(spdyGoAwayFrame.getLastGoodStreamId());
285 if (version >= 3) {
286 frame.writeInt(spdyGoAwayFrame.getStatus().getCode());
287 }
288 Channels.write(ctx, e.getFuture(), frame, e.getRemoteAddress());
289 return;
290
291 } else if (msg instanceof SpdyHeadersFrame) {
292
293 synchronized (headerBlockCompressor) {
294 SpdyHeadersFrame spdyHeadersFrame = (SpdyHeadersFrame) msg;
295 ChannelBuffer data = compressHeaderBlock(
296 encodeHeaderBlock(version, spdyHeadersFrame));
297 byte flags = spdyHeadersFrame.isLast() ? SPDY_FLAG_FIN : 0;
298 int headerBlockLength = data.readableBytes();
299 int length;
300 if (version < 3) {
301 length = headerBlockLength == 0 ? 4 : 6 + headerBlockLength;
302 } else {
303 length = 4 + headerBlockLength;
304 }
305 ChannelBuffer frame = ChannelBuffers.buffer(
306 ByteOrder.BIG_ENDIAN, SPDY_HEADER_SIZE + length);
307 frame.writeShort(version | 0x8000);
308 frame.writeShort(SPDY_HEADERS_FRAME);
309 frame.writeByte(flags);
310 frame.writeMedium(length);
311 frame.writeInt(spdyHeadersFrame.getStreamId());
312 if (version < 3 && data.readableBytes() != 0) {
313 frame.writeShort(0);
314 }
315
316 final ChannelBuffer buffer = ChannelBuffers.wrappedBuffer(frame, data);
317 Channels.write(ctx, e.getFuture(), buffer, e.getRemoteAddress());
318 }
319 return;
320
321 } else if (msg instanceof SpdyWindowUpdateFrame) {
322
323 SpdyWindowUpdateFrame spdyWindowUpdateFrame = (SpdyWindowUpdateFrame) msg;
324 ChannelBuffer frame = ChannelBuffers.buffer(
325 ByteOrder.BIG_ENDIAN, SPDY_HEADER_SIZE + 8);
326 frame.writeShort(version | 0x8000);
327 frame.writeShort(SPDY_WINDOW_UPDATE_FRAME);
328 frame.writeInt(8);
329 frame.writeInt(spdyWindowUpdateFrame.getStreamId());
330 frame.writeInt(spdyWindowUpdateFrame.getDeltaWindowSize());
331 Channels.write(ctx, e.getFuture(), frame, e.getRemoteAddress());
332 return;
333 }
334
335
336 ctx.sendDownstream(evt);
337 }
338
339 private static void writeLengthField(int version, ChannelBuffer buffer, int length) {
340 if (version < 3) {
341 buffer.writeShort(length);
342 } else {
343 buffer.writeInt(length);
344 }
345 }
346
347 private static void setLengthField(int version, ChannelBuffer buffer, int writerIndex, int length) {
348 if (version < 3) {
349 buffer.setShort(writerIndex, length);
350 } else {
351 buffer.setInt(writerIndex, length);
352 }
353 }
354
355 private static ChannelBuffer encodeHeaderBlock(int version, SpdyHeaderBlock headerFrame)
356 throws Exception {
357 Set<String> names = headerFrame.getHeaderNames();
358 int numHeaders = names.size();
359 if (numHeaders == 0) {
360 return ChannelBuffers.EMPTY_BUFFER;
361 }
362 if (numHeaders > SPDY_MAX_NV_LENGTH) {
363 throw new IllegalArgumentException(
364 "header block contains too many headers");
365 }
366 ChannelBuffer headerBlock = ChannelBuffers.dynamicBuffer(
367 ByteOrder.BIG_ENDIAN, 256);
368 writeLengthField(version, headerBlock, numHeaders);
369 for (String name: names) {
370 byte[] nameBytes = name.getBytes("UTF-8");
371 writeLengthField(version, headerBlock, nameBytes.length);
372 headerBlock.writeBytes(nameBytes);
373 int savedIndex = headerBlock.writerIndex();
374 int valueLength = 0;
375 writeLengthField(version, headerBlock, valueLength);
376 for (String value: headerFrame.getHeaders(name)) {
377 byte[] valueBytes = value.getBytes("UTF-8");
378 if (valueBytes.length > 0) {
379 headerBlock.writeBytes(valueBytes);
380 headerBlock.writeByte(0);
381 valueLength += valueBytes.length + 1;
382 }
383 }
384 if (valueLength == 0) {
385 if (version < 3) {
386 throw new IllegalArgumentException(
387 "header value cannot be empty: " + name);
388 }
389 } else {
390 valueLength --;
391 }
392 if (valueLength > SPDY_MAX_NV_LENGTH) {
393 throw new IllegalArgumentException(
394 "header exceeds allowable length: " + name);
395 }
396 if (valueLength > 0) {
397 setLengthField(version, headerBlock, savedIndex, valueLength);
398 headerBlock.writerIndex(headerBlock.writerIndex() - 1);
399 }
400 }
401 return headerBlock;
402 }
403
404
405 private ChannelBuffer compressHeaderBlock(ChannelBuffer uncompressed)
406 throws Exception {
407 if (uncompressed.readableBytes() == 0) {
408 return ChannelBuffers.EMPTY_BUFFER;
409 }
410 ChannelBuffer compressed = ChannelBuffers.dynamicBuffer();
411 if (!finished) {
412 headerBlockCompressor.setInput(uncompressed);
413 headerBlockCompressor.encode(compressed);
414 }
415 return compressed;
416 }
417 }