1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.handler.codec.spdy;
17
18 import io.netty.buffer.ByteBuf;
19 import io.netty.channel.ChannelHandler;
20 import io.netty.channel.ChannelHandlerContext;
21 import io.netty.channel.ChannelOutboundHandler;
22 import io.netty.channel.ChannelPromise;
23 import io.netty.handler.codec.ByteToMessageDecoder;
24 import io.netty.handler.codec.UnsupportedMessageTypeException;
25
26 import java.net.SocketAddress;
27 import java.util.List;
28
29
30
31
32 public class SpdyFrameCodec extends ByteToMessageDecoder
33 implements SpdyFrameDecoderDelegate, ChannelOutboundHandler {
34
35 protected static final SpdyProtocolException INVALID_FRAME =
36 new SpdyProtocolException("Received invalid frame");
37
38 protected final SpdyFrameDecoder spdyFrameDecoder;
39 protected final SpdyFrameEncoder spdyFrameEncoder;
40 private final SpdyHeaderBlockDecoder spdyHeaderBlockDecoder;
41 private final SpdyHeaderBlockEncoder spdyHeaderBlockEncoder;
42
43 private SpdyHeadersFrame spdyHeadersFrame;
44 private SpdySettingsFrame spdySettingsFrame;
45
46 private ChannelHandlerContext ctx;
47 private boolean read;
48 private final boolean validateHeaders;
49 private final boolean supportsUnknownFrames;
50
51
52
53
54
55
56
57
58
59 public SpdyFrameCodec(SpdyVersion version) {
60 this(version, true);
61 }
62
63
64
65
66
67
68
69
70
71 public SpdyFrameCodec(SpdyVersion version, boolean validateHeaders) {
72 this(version, 8192, 16384, 6, 15, 8, validateHeaders);
73 }
74
75
76
77
78
79 public SpdyFrameCodec(
80 SpdyVersion version, int maxChunkSize, int maxHeaderSize,
81 int compressionLevel, int windowBits, int memLevel) {
82 this(version, maxChunkSize, maxHeaderSize, compressionLevel, windowBits, memLevel, true);
83 }
84
85
86
87
88
89 public SpdyFrameCodec(
90 SpdyVersion version, int maxChunkSize, int maxHeaderSize,
91 int compressionLevel, int windowBits, int memLevel, boolean validateHeaders) {
92 this(version, maxChunkSize,
93 SpdyHeaderBlockDecoder.newInstance(version, maxHeaderSize),
94 SpdyHeaderBlockEncoder.newInstance(version, compressionLevel, windowBits, memLevel),
95 validateHeaders, false);
96 }
97
98
99
100
101
102 public SpdyFrameCodec(
103 SpdyVersion version, int maxChunkSize, int maxHeaderSize,
104 int compressionLevel, int windowBits, int memLevel, boolean validateHeaders,
105 boolean supportsUnknownFrames) {
106 this(version, maxChunkSize,
107 SpdyHeaderBlockDecoder.newInstance(version, maxHeaderSize),
108 SpdyHeaderBlockEncoder.newInstance(version, compressionLevel, windowBits, memLevel),
109 validateHeaders, supportsUnknownFrames);
110 }
111
112 protected SpdyFrameCodec(SpdyVersion version, int maxChunkSize,
113 SpdyHeaderBlockDecoder spdyHeaderBlockDecoder,
114 SpdyHeaderBlockEncoder spdyHeaderBlockEncoder,
115 boolean validateHeaders) {
116 this(version, maxChunkSize, spdyHeaderBlockDecoder, spdyHeaderBlockEncoder, validateHeaders, false);
117 }
118
119 protected SpdyFrameCodec(SpdyVersion version, int maxChunkSize,
120 SpdyHeaderBlockDecoder spdyHeaderBlockDecoder, SpdyHeaderBlockEncoder spdyHeaderBlockEncoder,
121 boolean validateHeaders, boolean supportsUnknownFrames) {
122 this.supportsUnknownFrames = supportsUnknownFrames;
123 spdyFrameDecoder = createDecoder(version, this, maxChunkSize);
124 spdyFrameEncoder = createEncoder(version);
125 this.spdyHeaderBlockDecoder = spdyHeaderBlockDecoder;
126 this.spdyHeaderBlockEncoder = spdyHeaderBlockEncoder;
127 this.validateHeaders = validateHeaders;
128 }
129
130 protected SpdyFrameDecoder createDecoder(SpdyVersion version, SpdyFrameDecoderDelegate delegate, int maxChunkSize) {
131 return new SpdyFrameDecoder(version, delegate, maxChunkSize) {
132 @Override
133 protected boolean isValidUnknownFrameHeader(int streamId, int type, byte flags, int length) {
134 if (supportsUnknownFrames) {
135 return SpdyFrameCodec.this.isValidUnknownFrameHeader(streamId, type, flags, length);
136 }
137 return super.isValidUnknownFrameHeader(streamId, type, flags, length);
138 }
139 };
140 }
141
142 protected SpdyFrameEncoder createEncoder(SpdyVersion version) {
143 return new SpdyFrameEncoder(version);
144 }
145
146 @Override
147 public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
148 super.handlerAdded(ctx);
149 this.ctx = ctx;
150 ctx.channel().closeFuture().addListener(future -> {
151 spdyHeaderBlockDecoder.end();
152 spdyHeaderBlockEncoder.end();
153 });
154 }
155
156 @Override
157 protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
158 spdyFrameDecoder.decode(in);
159 }
160
161 @Override
162 public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
163 if (!read) {
164 if (!ctx.channel().config().isAutoRead()) {
165 ctx.read();
166 }
167 }
168 read = false;
169 super.channelReadComplete(ctx);
170 }
171
172 @Override
173 public void bind(ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise) throws Exception {
174 ctx.bind(localAddress, promise);
175 }
176
177 @Override
178 public void connect(ChannelHandlerContext ctx, SocketAddress remoteAddress, SocketAddress localAddress,
179 ChannelPromise promise) throws Exception {
180 ctx.connect(remoteAddress, localAddress, promise);
181 }
182
183 @Override
184 public void disconnect(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
185 ctx.disconnect(promise);
186 }
187
188 @Override
189 public void close(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
190 ctx.close(promise);
191 }
192
193 @Override
194 public void deregister(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
195 ctx.deregister(promise);
196 }
197
198 @Override
199 public void read(ChannelHandlerContext ctx) throws Exception {
200 ctx.read();
201 }
202
203 @Override
204 public void flush(ChannelHandlerContext ctx) throws Exception {
205 ctx.flush();
206 }
207
208 @Override
209 public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
210 ByteBuf frame;
211
212 if (msg instanceof SpdyDataFrame) {
213
214 SpdyDataFrame spdyDataFrame = (SpdyDataFrame) msg;
215 try {
216 frame = spdyFrameEncoder.encodeDataFrame(
217 ctx.alloc(),
218 spdyDataFrame.streamId(),
219 spdyDataFrame.isLast(),
220 spdyDataFrame.content()
221 );
222 ctx.write(frame, promise);
223 } finally {
224 spdyDataFrame.release();
225 }
226
227 } else if (msg instanceof SpdySynStreamFrame) {
228
229 SpdySynStreamFrame spdySynStreamFrame = (SpdySynStreamFrame) msg;
230 ByteBuf headerBlock = spdyHeaderBlockEncoder.encode(ctx.alloc(), spdySynStreamFrame);
231 try {
232 frame = spdyFrameEncoder.encodeSynStreamFrame(
233 ctx.alloc(),
234 spdySynStreamFrame.streamId(),
235 spdySynStreamFrame.associatedStreamId(),
236 spdySynStreamFrame.priority(),
237 spdySynStreamFrame.isLast(),
238 spdySynStreamFrame.isUnidirectional(),
239 headerBlock
240 );
241 } finally {
242 headerBlock.release();
243 }
244 ctx.write(frame, promise);
245
246 } else if (msg instanceof SpdySynReplyFrame) {
247
248 SpdySynReplyFrame spdySynReplyFrame = (SpdySynReplyFrame) msg;
249 ByteBuf headerBlock = spdyHeaderBlockEncoder.encode(ctx.alloc(), spdySynReplyFrame);
250 try {
251 frame = spdyFrameEncoder.encodeSynReplyFrame(
252 ctx.alloc(),
253 spdySynReplyFrame.streamId(),
254 spdySynReplyFrame.isLast(),
255 headerBlock
256 );
257 } finally {
258 headerBlock.release();
259 }
260 ctx.write(frame, promise);
261
262 } else if (msg instanceof SpdyRstStreamFrame) {
263
264 SpdyRstStreamFrame spdyRstStreamFrame = (SpdyRstStreamFrame) msg;
265 frame = spdyFrameEncoder.encodeRstStreamFrame(
266 ctx.alloc(),
267 spdyRstStreamFrame.streamId(),
268 spdyRstStreamFrame.status().code()
269 );
270 ctx.write(frame, promise);
271
272 } else if (msg instanceof SpdySettingsFrame) {
273
274 SpdySettingsFrame spdySettingsFrame = (SpdySettingsFrame) msg;
275 frame = spdyFrameEncoder.encodeSettingsFrame(
276 ctx.alloc(),
277 spdySettingsFrame
278 );
279 ctx.write(frame, promise);
280
281 } else if (msg instanceof SpdyPingFrame) {
282
283 SpdyPingFrame spdyPingFrame = (SpdyPingFrame) msg;
284 frame = spdyFrameEncoder.encodePingFrame(
285 ctx.alloc(),
286 spdyPingFrame.id()
287 );
288 ctx.write(frame, promise);
289
290 } else if (msg instanceof SpdyGoAwayFrame) {
291
292 SpdyGoAwayFrame spdyGoAwayFrame = (SpdyGoAwayFrame) msg;
293 frame = spdyFrameEncoder.encodeGoAwayFrame(
294 ctx.alloc(),
295 spdyGoAwayFrame.lastGoodStreamId(),
296 spdyGoAwayFrame.status().code()
297 );
298 ctx.write(frame, promise);
299
300 } else if (msg instanceof SpdyHeadersFrame) {
301
302 SpdyHeadersFrame spdyHeadersFrame = (SpdyHeadersFrame) msg;
303 ByteBuf headerBlock = spdyHeaderBlockEncoder.encode(ctx.alloc(), spdyHeadersFrame);
304 try {
305 frame = spdyFrameEncoder.encodeHeadersFrame(
306 ctx.alloc(),
307 spdyHeadersFrame.streamId(),
308 spdyHeadersFrame.isLast(),
309 headerBlock
310 );
311 } finally {
312 headerBlock.release();
313 }
314 ctx.write(frame, promise);
315
316 } else if (msg instanceof SpdyWindowUpdateFrame) {
317
318 SpdyWindowUpdateFrame spdyWindowUpdateFrame = (SpdyWindowUpdateFrame) msg;
319 frame = spdyFrameEncoder.encodeWindowUpdateFrame(
320 ctx.alloc(),
321 spdyWindowUpdateFrame.streamId(),
322 spdyWindowUpdateFrame.deltaWindowSize()
323 );
324 ctx.write(frame, promise);
325 } else if (msg instanceof SpdyUnknownFrame) {
326 SpdyUnknownFrame spdyUnknownFrame = (SpdyUnknownFrame) msg;
327 try {
328 frame = spdyFrameEncoder.encodeUnknownFrame(
329 ctx.alloc(),
330 spdyUnknownFrame.frameType(),
331 spdyUnknownFrame.flags(),
332 spdyUnknownFrame.content());
333 ctx.write(frame, promise);
334 } finally {
335 spdyUnknownFrame.release();
336 }
337 } else {
338 throw new UnsupportedMessageTypeException(msg);
339 }
340 }
341
342 @Override
343 public void readDataFrame(int streamId, boolean last, ByteBuf data) {
344 read = true;
345
346 SpdyDataFrame spdyDataFrame = new DefaultSpdyDataFrame(streamId, data);
347 spdyDataFrame.setLast(last);
348 ctx.fireChannelRead(spdyDataFrame);
349 }
350
351 @Override
352 public void readSynStreamFrame(
353 int streamId, int associatedToStreamId, byte priority, boolean last, boolean unidirectional) {
354 SpdySynStreamFrame spdySynStreamFrame =
355 new DefaultSpdySynStreamFrame(streamId, associatedToStreamId, priority, validateHeaders);
356 spdySynStreamFrame.setLast(last);
357 spdySynStreamFrame.setUnidirectional(unidirectional);
358 spdyHeadersFrame = spdySynStreamFrame;
359 }
360
361 @Override
362 public void readSynReplyFrame(int streamId, boolean last) {
363 SpdySynReplyFrame spdySynReplyFrame = new DefaultSpdySynReplyFrame(streamId, validateHeaders);
364 spdySynReplyFrame.setLast(last);
365 spdyHeadersFrame = spdySynReplyFrame;
366 }
367
368 @Override
369 public void readRstStreamFrame(int streamId, int statusCode) {
370 read = true;
371
372 SpdyRstStreamFrame spdyRstStreamFrame = new DefaultSpdyRstStreamFrame(streamId, statusCode);
373 ctx.fireChannelRead(spdyRstStreamFrame);
374 }
375
376 @Override
377 public void readSettingsFrame(boolean clearPersisted) {
378 read = true;
379
380 spdySettingsFrame = new DefaultSpdySettingsFrame();
381 spdySettingsFrame.setClearPreviouslyPersistedSettings(clearPersisted);
382 }
383
384 @Override
385 public void readSetting(int id, int value, boolean persistValue, boolean persisted) {
386 spdySettingsFrame.setValue(id, value, persistValue, persisted);
387 }
388
389 @Override
390 public void readSettingsEnd() {
391 read = true;
392
393 Object frame = spdySettingsFrame;
394 spdySettingsFrame = null;
395 ctx.fireChannelRead(frame);
396 }
397
398 @Override
399 public void readPingFrame(int id) {
400 read = true;
401
402 SpdyPingFrame spdyPingFrame = new DefaultSpdyPingFrame(id);
403 ctx.fireChannelRead(spdyPingFrame);
404 }
405
406 @Override
407 public void readGoAwayFrame(int lastGoodStreamId, int statusCode) {
408 read = true;
409
410 SpdyGoAwayFrame spdyGoAwayFrame = new DefaultSpdyGoAwayFrame(lastGoodStreamId, statusCode);
411 ctx.fireChannelRead(spdyGoAwayFrame);
412 }
413
414 @Override
415 public void readHeadersFrame(int streamId, boolean last) {
416 spdyHeadersFrame = new DefaultSpdyHeadersFrame(streamId, validateHeaders);
417 spdyHeadersFrame.setLast(last);
418 }
419
420 @Override
421 public void readWindowUpdateFrame(int streamId, int deltaWindowSize) {
422 read = true;
423
424 SpdyWindowUpdateFrame spdyWindowUpdateFrame = new DefaultSpdyWindowUpdateFrame(streamId, deltaWindowSize);
425 ctx.fireChannelRead(spdyWindowUpdateFrame);
426 }
427
428 @Override
429 public void readHeaderBlock(ByteBuf headerBlock) {
430 try {
431 spdyHeaderBlockDecoder.decode(ctx.alloc(), headerBlock, spdyHeadersFrame);
432 } catch (Exception e) {
433 ctx.fireExceptionCaught(e);
434 } finally {
435 headerBlock.release();
436 }
437 }
438
439 @Override
440 public void readHeaderBlockEnd() {
441 Object frame = null;
442 try {
443 spdyHeaderBlockDecoder.endHeaderBlock(spdyHeadersFrame);
444 frame = spdyHeadersFrame;
445 spdyHeadersFrame = null;
446 } catch (Exception e) {
447 ctx.fireExceptionCaught(e);
448 }
449 if (frame != null) {
450 read = true;
451
452 ctx.fireChannelRead(frame);
453 }
454 }
455
456 @Override
457 public void readUnknownFrame(int frameType, byte flags, ByteBuf payload) {
458 read = true;
459 ctx.fireChannelRead(newSpdyUnknownFrame(frameType, flags, payload));
460 }
461
462
463
464
465 protected SpdyFrame newSpdyUnknownFrame(int frameType, byte flags, ByteBuf payload) {
466 return new DefaultSpdyUnknownFrame(frameType, flags, payload);
467 }
468
469
470
471
472
473
474
475 protected boolean isValidUnknownFrameHeader(@SuppressWarnings("unused") int streamId,
476 @SuppressWarnings("unused") int type,
477 @SuppressWarnings("unused") byte flags,
478 @SuppressWarnings("unused") int length) {
479 return false;
480 }
481
482 @Override
483 public void readFrameError(String message) {
484 ctx.fireExceptionCaught(INVALID_FRAME);
485 }
486 }