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 org.jboss.netty.channel.Channel;
19 import org.jboss.netty.channel.ChannelDownstreamHandler;
20 import org.jboss.netty.channel.ChannelEvent;
21 import org.jboss.netty.channel.ChannelFuture;
22 import org.jboss.netty.channel.ChannelFutureListener;
23 import org.jboss.netty.channel.ChannelHandlerContext;
24 import org.jboss.netty.channel.ChannelStateEvent;
25 import org.jboss.netty.channel.Channels;
26 import org.jboss.netty.channel.ExceptionEvent;
27 import org.jboss.netty.channel.MessageEvent;
28 import org.jboss.netty.channel.SimpleChannelUpstreamHandler;
29
30 import java.net.SocketAddress;
31 import java.nio.channels.ClosedChannelException;
32 import java.util.concurrent.atomic.AtomicInteger;
33
34 import static org.jboss.netty.handler.codec.spdy.SpdyCodecUtil.*;
35
36
37
38
39 public class SpdySessionHandler extends SimpleChannelUpstreamHandler
40 implements ChannelDownstreamHandler {
41
42 private static final SpdyProtocolException PROTOCOL_EXCEPTION = new SpdyProtocolException();
43
44 private final SpdySession spdySession = new SpdySession();
45 private volatile int lastGoodStreamID;
46
47 private volatile int remoteConcurrentStreams;
48 private volatile int localConcurrentStreams;
49 private volatile int maxConcurrentStreams;
50
51 private static final int DEFAULT_WINDOW_SIZE = 64 * 1024;
52 private volatile int initialSendWindowSize = DEFAULT_WINDOW_SIZE;
53 private volatile int initialReceiveWindowSize = DEFAULT_WINDOW_SIZE;
54
55 private final Object flowControlLock = new Object();
56
57 private final AtomicInteger pings = new AtomicInteger();
58
59 private volatile boolean sentGoAwayFrame;
60 private volatile boolean receivedGoAwayFrame;
61
62 private volatile ChannelFuture closeSessionFuture;
63
64 private final boolean server;
65 private final boolean flowControl;
66
67
68
69
70
71
72
73
74
75 @Deprecated
76 public SpdySessionHandler(boolean server) {
77 this(2, server);
78 }
79
80
81
82
83
84
85
86
87
88
89 public SpdySessionHandler(int version, boolean server) {
90 if (version < SPDY_MIN_VERSION || version > SPDY_MAX_VERSION) {
91 throw new IllegalArgumentException(
92 "unsupported version: " + version);
93 }
94 this.server = server;
95 flowControl = version >= 3;
96 }
97
98 @Override
99 public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
100 throws Exception {
101
102 Object msg = e.getMessage();
103 if (msg instanceof SpdyDataFrame) {
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128 SpdyDataFrame spdyDataFrame = (SpdyDataFrame) msg;
129 int streamID = spdyDataFrame.getStreamId();
130
131
132 if (!spdySession.isActiveStream(streamID)) {
133 if (streamID <= lastGoodStreamID) {
134 issueStreamError(ctx, e.getRemoteAddress(), streamID, SpdyStreamStatus.PROTOCOL_ERROR);
135 } else if (!sentGoAwayFrame) {
136 issueStreamError(ctx, e.getRemoteAddress(), streamID, SpdyStreamStatus.INVALID_STREAM);
137 }
138 return;
139 }
140
141
142 if (spdySession.isRemoteSideClosed(streamID)) {
143 issueStreamError(ctx, e.getRemoteAddress(), streamID, SpdyStreamStatus.STREAM_ALREADY_CLOSED);
144 return;
145 }
146
147
148 if (!isRemoteInitiatedID(streamID) && !spdySession.hasReceivedReply(streamID)) {
149 issueStreamError(ctx, e.getRemoteAddress(), streamID, SpdyStreamStatus.PROTOCOL_ERROR);
150 return;
151 }
152
153
154
155
156
157
158
159 if (flowControl) {
160
161 int deltaWindowSize = -1 * spdyDataFrame.getData().readableBytes();
162 int newWindowSize = spdySession.updateReceiveWindowSize(streamID, deltaWindowSize);
163
164
165
166
167
168
169 if (newWindowSize < spdySession.getReceiveWindowSizeLowerBound(streamID)) {
170 issueStreamError(ctx, e.getRemoteAddress(), streamID, SpdyStreamStatus.FLOW_CONTROL_ERROR);
171 return;
172 }
173
174
175
176 if (newWindowSize < 0) {
177 while (spdyDataFrame.getData().readableBytes() > initialReceiveWindowSize) {
178 SpdyDataFrame partialDataFrame = new DefaultSpdyDataFrame(streamID);
179 partialDataFrame.setData(spdyDataFrame.getData().readSlice(initialReceiveWindowSize));
180 Channels.fireMessageReceived(ctx, partialDataFrame, e.getRemoteAddress());
181 }
182 }
183
184
185 if (newWindowSize <= initialReceiveWindowSize / 2 && !spdyDataFrame.isLast()) {
186 deltaWindowSize = initialReceiveWindowSize - newWindowSize;
187 spdySession.updateReceiveWindowSize(streamID, deltaWindowSize);
188 SpdyWindowUpdateFrame spdyWindowUpdateFrame =
189 new DefaultSpdyWindowUpdateFrame(streamID, deltaWindowSize);
190 Channels.write(
191 ctx, Channels.future(e.getChannel()), spdyWindowUpdateFrame, e.getRemoteAddress());
192 }
193 }
194
195
196 if (spdyDataFrame.isLast()) {
197 halfCloseStream(streamID, true);
198 }
199
200 } else if (msg instanceof SpdySynStreamFrame) {
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216 SpdySynStreamFrame spdySynStreamFrame = (SpdySynStreamFrame) msg;
217 int streamID = spdySynStreamFrame.getStreamId();
218
219
220 if (spdySynStreamFrame.isInvalid() ||
221 !isRemoteInitiatedID(streamID) ||
222 spdySession.isActiveStream(streamID)) {
223 issueStreamError(ctx, e.getRemoteAddress(), streamID, SpdyStreamStatus.PROTOCOL_ERROR);
224 return;
225 }
226
227
228 if (streamID <= lastGoodStreamID) {
229 issueSessionError(ctx, e.getChannel(), e.getRemoteAddress(), SpdySessionStatus.PROTOCOL_ERROR);
230 return;
231 }
232
233
234 byte priority = spdySynStreamFrame.getPriority();
235 boolean remoteSideClosed = spdySynStreamFrame.isLast();
236 boolean localSideClosed = spdySynStreamFrame.isUnidirectional();
237 if (!acceptStream(streamID, priority, remoteSideClosed, localSideClosed)) {
238 issueStreamError(ctx, e.getRemoteAddress(), streamID, SpdyStreamStatus.REFUSED_STREAM);
239 return;
240 }
241
242 } else if (msg instanceof SpdySynReplyFrame) {
243
244
245
246
247
248
249
250
251 SpdySynReplyFrame spdySynReplyFrame = (SpdySynReplyFrame) msg;
252 int streamID = spdySynReplyFrame.getStreamId();
253
254
255 if (spdySynReplyFrame.isInvalid() ||
256 isRemoteInitiatedID(streamID) ||
257 spdySession.isRemoteSideClosed(streamID)) {
258 issueStreamError(ctx, e.getRemoteAddress(), streamID, SpdyStreamStatus.INVALID_STREAM);
259 return;
260 }
261
262
263 if (spdySession.hasReceivedReply(streamID)) {
264 issueStreamError(ctx, e.getRemoteAddress(), streamID, SpdyStreamStatus.STREAM_IN_USE);
265 return;
266 }
267
268 spdySession.receivedReply(streamID);
269
270
271 if (spdySynReplyFrame.isLast()) {
272 halfCloseStream(streamID, true);
273 }
274
275 } else if (msg instanceof SpdyRstStreamFrame) {
276
277
278
279
280
281
282
283
284
285
286 SpdyRstStreamFrame spdyRstStreamFrame = (SpdyRstStreamFrame) msg;
287 removeStream(spdyRstStreamFrame.getStreamId());
288
289 } else if (msg instanceof SpdySettingsFrame) {
290
291 SpdySettingsFrame spdySettingsFrame = (SpdySettingsFrame) msg;
292
293 int newConcurrentStreams =
294 spdySettingsFrame.getValue(SpdySettingsFrame.SETTINGS_MAX_CONCURRENT_STREAMS);
295 if (newConcurrentStreams >= 0) {
296 updateConcurrentStreams(newConcurrentStreams, true);
297 }
298
299
300
301
302 if (spdySettingsFrame.isPersisted(SpdySettingsFrame.SETTINGS_INITIAL_WINDOW_SIZE)) {
303 spdySettingsFrame.removeValue(SpdySettingsFrame.SETTINGS_INITIAL_WINDOW_SIZE);
304 }
305 spdySettingsFrame.setPersistValue(SpdySettingsFrame.SETTINGS_INITIAL_WINDOW_SIZE, false);
306
307 if (flowControl) {
308 int newInitialWindowSize =
309 spdySettingsFrame.getValue(SpdySettingsFrame.SETTINGS_INITIAL_WINDOW_SIZE);
310 if (newInitialWindowSize >= 0) {
311 updateInitialSendWindowSize(newInitialWindowSize);
312 }
313 }
314
315 } else if (msg instanceof SpdyPingFrame) {
316
317
318
319
320
321
322
323
324
325
326 SpdyPingFrame spdyPingFrame = (SpdyPingFrame) msg;
327
328 if (isRemoteInitiatedID(spdyPingFrame.getId())) {
329 Channels.write(ctx, Channels.future(e.getChannel()), spdyPingFrame, e.getRemoteAddress());
330 return;
331 }
332
333
334 if (pings.get() == 0) {
335 return;
336 }
337 pings.getAndDecrement();
338
339 } else if (msg instanceof SpdyGoAwayFrame) {
340
341 receivedGoAwayFrame = true;
342
343 } else if (msg instanceof SpdyHeadersFrame) {
344
345 SpdyHeadersFrame spdyHeadersFrame = (SpdyHeadersFrame) msg;
346 int streamID = spdyHeadersFrame.getStreamId();
347
348
349 if (spdyHeadersFrame.isInvalid()) {
350 issueStreamError(ctx, e.getRemoteAddress(), streamID, SpdyStreamStatus.PROTOCOL_ERROR);
351 return;
352 }
353
354 if (spdySession.isRemoteSideClosed(streamID)) {
355 issueStreamError(ctx, e.getRemoteAddress(), streamID, SpdyStreamStatus.INVALID_STREAM);
356 return;
357 }
358
359
360 if (spdyHeadersFrame.isLast()) {
361 halfCloseStream(streamID, true);
362 }
363
364 } else if (msg instanceof SpdyWindowUpdateFrame) {
365
366
367
368
369
370
371
372
373
374
375
376 if (flowControl) {
377 SpdyWindowUpdateFrame spdyWindowUpdateFrame = (SpdyWindowUpdateFrame) msg;
378 int streamID = spdyWindowUpdateFrame.getStreamId();
379 int deltaWindowSize = spdyWindowUpdateFrame.getDeltaWindowSize();
380
381
382 if (spdySession.isLocalSideClosed(streamID)) {
383 return;
384 }
385
386
387 if (spdySession.getSendWindowSize(streamID) > Integer.MAX_VALUE - deltaWindowSize) {
388 issueStreamError(ctx, e.getRemoteAddress(), streamID, SpdyStreamStatus.FLOW_CONTROL_ERROR);
389 return;
390 }
391
392 updateSendWindowSize(ctx, streamID, deltaWindowSize);
393 }
394 return;
395 }
396
397 super.messageReceived(ctx, e);
398 }
399
400 @Override
401 public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e)
402 throws Exception {
403
404 Throwable cause = e.getCause();
405 if (cause instanceof SpdyProtocolException) {
406 issueSessionError(ctx, e.getChannel(), null, SpdySessionStatus.PROTOCOL_ERROR);
407 }
408
409 super.exceptionCaught(ctx, e);
410 }
411
412 public void handleDownstream(ChannelHandlerContext ctx, ChannelEvent evt)
413 throws Exception {
414 if (evt instanceof ChannelStateEvent) {
415 ChannelStateEvent e = (ChannelStateEvent) evt;
416 switch (e.getState()) {
417 case OPEN:
418 case CONNECTED:
419 case BOUND:
420
421
422
423
424
425
426
427 if (Boolean.FALSE.equals(e.getValue()) || e.getValue() == null) {
428 sendGoAwayFrame(ctx, e);
429 return;
430 }
431 }
432 }
433 if (!(evt instanceof MessageEvent)) {
434 ctx.sendDownstream(evt);
435 return;
436 }
437
438 MessageEvent e = (MessageEvent) evt;
439 Object msg = e.getMessage();
440
441 if (msg instanceof SpdyDataFrame) {
442
443 SpdyDataFrame spdyDataFrame = (SpdyDataFrame) msg;
444 final int streamID = spdyDataFrame.getStreamId();
445
446
447 if (spdySession.isLocalSideClosed(streamID)) {
448 e.getFuture().setFailure(PROTOCOL_EXCEPTION);
449 return;
450 }
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465 if (flowControl) {
466 synchronized (flowControlLock) {
467 int dataLength = spdyDataFrame.getData().readableBytes();
468 int sendWindowSize = spdySession.getSendWindowSize(streamID);
469
470 if (sendWindowSize >= dataLength) {
471
472 spdySession.updateSendWindowSize(streamID, -1 * dataLength);
473
474
475
476 final SocketAddress remoteAddress = e.getRemoteAddress();
477 final ChannelHandlerContext context = ctx;
478 e.getFuture().addListener(new ChannelFutureListener() {
479 public void operationComplete(ChannelFuture future) throws Exception {
480 if (!future.isSuccess()) {
481 issueStreamError(
482 context, remoteAddress, streamID, SpdyStreamStatus.INTERNAL_ERROR);
483 }
484 }
485 });
486
487 } else if (sendWindowSize > 0) {
488
489 spdySession.updateSendWindowSize(streamID, -1 * sendWindowSize);
490
491
492 SpdyDataFrame partialDataFrame = new DefaultSpdyDataFrame(streamID);
493 partialDataFrame.setData(spdyDataFrame.getData().readSlice(sendWindowSize));
494
495
496 spdySession.putPendingWrite(streamID, e);
497
498 ChannelFuture writeFuture = Channels.future(e.getChannel());
499
500
501
502 final SocketAddress remoteAddress = e.getRemoteAddress();
503 final ChannelHandlerContext context = ctx;
504 e.getFuture().addListener(new ChannelFutureListener() {
505 public void operationComplete(ChannelFuture future) throws Exception {
506 if (!future.isSuccess()) {
507 issueStreamError(
508 context, remoteAddress, streamID, SpdyStreamStatus.INTERNAL_ERROR);
509 }
510 }
511 });
512
513 Channels.write(ctx, writeFuture, partialDataFrame, remoteAddress);
514 return;
515 } else {
516
517 spdySession.putPendingWrite(streamID, e);
518 return;
519 }
520 }
521 }
522
523
524 if (spdyDataFrame.isLast()) {
525 halfCloseStream(streamID, false);
526 }
527
528 } else if (msg instanceof SpdySynStreamFrame) {
529
530 SpdySynStreamFrame spdySynStreamFrame = (SpdySynStreamFrame) msg;
531 int streamID = spdySynStreamFrame.getStreamId();
532
533 if (isRemoteInitiatedID(streamID)) {
534 e.getFuture().setFailure(PROTOCOL_EXCEPTION);
535 return;
536 }
537
538 byte priority = spdySynStreamFrame.getPriority();
539 boolean remoteSideClosed = spdySynStreamFrame.isUnidirectional();
540 boolean localSideClosed = spdySynStreamFrame.isLast();
541 if (!acceptStream(streamID, priority, remoteSideClosed, localSideClosed)) {
542 e.getFuture().setFailure(PROTOCOL_EXCEPTION);
543 return;
544 }
545
546 } else if (msg instanceof SpdySynReplyFrame) {
547
548 SpdySynReplyFrame spdySynReplyFrame = (SpdySynReplyFrame) msg;
549 int streamID = spdySynReplyFrame.getStreamId();
550
551
552 if (!isRemoteInitiatedID(streamID) || spdySession.isLocalSideClosed(streamID)) {
553 e.getFuture().setFailure(PROTOCOL_EXCEPTION);
554 return;
555 }
556
557
558 if (spdySynReplyFrame.isLast()) {
559 halfCloseStream(streamID, false);
560 }
561
562 } else if (msg instanceof SpdyRstStreamFrame) {
563
564 SpdyRstStreamFrame spdyRstStreamFrame = (SpdyRstStreamFrame) msg;
565 removeStream(spdyRstStreamFrame.getStreamId());
566
567 } else if (msg instanceof SpdySettingsFrame) {
568
569 SpdySettingsFrame spdySettingsFrame = (SpdySettingsFrame) msg;
570
571 int newConcurrentStreams =
572 spdySettingsFrame.getValue(SpdySettingsFrame.SETTINGS_MAX_CONCURRENT_STREAMS);
573 if (newConcurrentStreams >= 0) {
574 updateConcurrentStreams(newConcurrentStreams, false);
575 }
576
577
578
579
580 if (spdySettingsFrame.isPersisted(SpdySettingsFrame.SETTINGS_INITIAL_WINDOW_SIZE)) {
581 spdySettingsFrame.removeValue(SpdySettingsFrame.SETTINGS_INITIAL_WINDOW_SIZE);
582 }
583 spdySettingsFrame.setPersistValue(SpdySettingsFrame.SETTINGS_INITIAL_WINDOW_SIZE, false);
584
585 if (flowControl) {
586 int newInitialWindowSize =
587 spdySettingsFrame.getValue(SpdySettingsFrame.SETTINGS_INITIAL_WINDOW_SIZE);
588 if (newInitialWindowSize >= 0) {
589 updateInitialReceiveWindowSize(newInitialWindowSize);
590 }
591 }
592
593 } else if (msg instanceof SpdyPingFrame) {
594
595 SpdyPingFrame spdyPingFrame = (SpdyPingFrame) msg;
596 if (isRemoteInitiatedID(spdyPingFrame.getId())) {
597 e.getFuture().setFailure(new IllegalArgumentException(
598 "invalid PING ID: " + spdyPingFrame.getId()));
599 return;
600 }
601 pings.getAndIncrement();
602
603 } else if (msg instanceof SpdyGoAwayFrame) {
604
605
606
607 e.getFuture().setFailure(PROTOCOL_EXCEPTION);
608 return;
609
610 } else if (msg instanceof SpdyHeadersFrame) {
611
612 SpdyHeadersFrame spdyHeadersFrame = (SpdyHeadersFrame) msg;
613 int streamID = spdyHeadersFrame.getStreamId();
614
615
616 if (spdySession.isLocalSideClosed(streamID)) {
617 e.getFuture().setFailure(PROTOCOL_EXCEPTION);
618 return;
619 }
620
621
622 if (spdyHeadersFrame.isLast()) {
623 halfCloseStream(streamID, false);
624 }
625
626 } else if (msg instanceof SpdyWindowUpdateFrame) {
627
628
629 e.getFuture().setFailure(PROTOCOL_EXCEPTION);
630 return;
631 }
632
633 ctx.sendDownstream(evt);
634 }
635
636
637
638
639
640
641
642
643
644
645 private void issueSessionError(
646 ChannelHandlerContext ctx, Channel channel, SocketAddress remoteAddress, SpdySessionStatus status) {
647
648 ChannelFuture future = sendGoAwayFrame(ctx, channel, remoteAddress, status);
649 future.addListener(ChannelFutureListener.CLOSE);
650 }
651
652
653
654
655
656
657
658
659
660
661
662
663 private void issueStreamError(
664 ChannelHandlerContext ctx, SocketAddress remoteAddress, int streamID, SpdyStreamStatus status) {
665
666 boolean fireMessageReceived = !spdySession.isRemoteSideClosed(streamID);
667 removeStream(streamID);
668
669 SpdyRstStreamFrame spdyRstStreamFrame = new DefaultSpdyRstStreamFrame(streamID, status);
670 Channels.write(ctx, Channels.future(ctx.getChannel()), spdyRstStreamFrame, remoteAddress);
671 if (fireMessageReceived) {
672 Channels.fireMessageReceived(ctx, spdyRstStreamFrame, remoteAddress);
673 }
674 }
675
676
677
678
679
680 private boolean isRemoteInitiatedID(int id) {
681 boolean serverID = isServerId(id);
682 return server && !serverID || !server && serverID;
683 }
684
685 private void updateConcurrentStreams(int newConcurrentStreams, boolean remote) {
686 if (remote) {
687 remoteConcurrentStreams = newConcurrentStreams;
688 } else {
689 localConcurrentStreams = newConcurrentStreams;
690 }
691 if (localConcurrentStreams == remoteConcurrentStreams) {
692 maxConcurrentStreams = localConcurrentStreams;
693 return;
694 }
695 if (localConcurrentStreams == 0) {
696 maxConcurrentStreams = remoteConcurrentStreams;
697 return;
698 }
699 if (remoteConcurrentStreams == 0) {
700 maxConcurrentStreams = localConcurrentStreams;
701 return;
702 }
703 if (localConcurrentStreams > remoteConcurrentStreams) {
704 maxConcurrentStreams = remoteConcurrentStreams;
705 } else {
706 maxConcurrentStreams = localConcurrentStreams;
707 }
708 }
709
710
711 private synchronized void updateInitialSendWindowSize(int newInitialWindowSize) {
712 int deltaWindowSize = newInitialWindowSize - initialSendWindowSize;
713 initialSendWindowSize = newInitialWindowSize;
714 for (Integer StreamID: spdySession.getActiveStreams()) {
715 spdySession.updateSendWindowSize(StreamID.intValue(), deltaWindowSize);
716 }
717 }
718
719
720 private synchronized void updateInitialReceiveWindowSize(int newInitialWindowSize) {
721 int deltaWindowSize = newInitialWindowSize - initialReceiveWindowSize;
722 initialReceiveWindowSize = newInitialWindowSize;
723 spdySession.updateAllReceiveWindowSizes(deltaWindowSize);
724 }
725
726
727 private synchronized boolean acceptStream(
728 int streamID, byte priority, boolean remoteSideClosed, boolean localSideClosed) {
729
730 if (receivedGoAwayFrame || sentGoAwayFrame) {
731 return false;
732 }
733
734 int maxConcurrentStreams = this.maxConcurrentStreams;
735 if (maxConcurrentStreams != 0 &&
736 spdySession.numActiveStreams() >= maxConcurrentStreams) {
737 return false;
738 }
739 spdySession.acceptStream(
740 streamID, priority, remoteSideClosed, localSideClosed,
741 initialSendWindowSize, initialReceiveWindowSize);
742 if (isRemoteInitiatedID(streamID)) {
743 lastGoodStreamID = streamID;
744 }
745 return true;
746 }
747
748 private void halfCloseStream(int streamID, boolean remote) {
749 if (remote) {
750 spdySession.closeRemoteSide(streamID);
751 } else {
752 spdySession.closeLocalSide(streamID);
753 }
754 if (closeSessionFuture != null && spdySession.noActiveStreams()) {
755 closeSessionFuture.setSuccess();
756 }
757 }
758
759 private void removeStream(int streamID) {
760 spdySession.removeStream(streamID);
761 if (closeSessionFuture != null && spdySession.noActiveStreams()) {
762 closeSessionFuture.setSuccess();
763 }
764 }
765
766 private void updateSendWindowSize(ChannelHandlerContext ctx, final int streamID, int deltaWindowSize) {
767 synchronized (flowControlLock) {
768 int newWindowSize = spdySession.updateSendWindowSize(streamID, deltaWindowSize);
769
770 while (newWindowSize > 0) {
771
772 MessageEvent e = spdySession.getPendingWrite(streamID);
773 if (e == null) {
774 break;
775 }
776
777 SpdyDataFrame spdyDataFrame = (SpdyDataFrame) e.getMessage();
778 int dataFrameSize = spdyDataFrame.getData().readableBytes();
779
780 if (newWindowSize >= dataFrameSize) {
781
782 spdySession.removePendingWrite(streamID);
783 newWindowSize = spdySession.updateSendWindowSize(streamID, -1 * dataFrameSize);
784
785
786
787 final SocketAddress remoteAddress = e.getRemoteAddress();
788 final ChannelHandlerContext context = ctx;
789 e.getFuture().addListener(new ChannelFutureListener() {
790 public void operationComplete(ChannelFuture future) throws Exception {
791 if (!future.isSuccess()) {
792 issueStreamError(context, remoteAddress, streamID, SpdyStreamStatus.INTERNAL_ERROR);
793 }
794 }
795 });
796
797
798 if (spdyDataFrame.isLast()) {
799 halfCloseStream(streamID, false);
800 }
801
802 Channels.write(ctx, e.getFuture(), spdyDataFrame, e.getRemoteAddress());
803 } else {
804
805 spdySession.updateSendWindowSize(streamID, -1 * newWindowSize);
806
807
808 SpdyDataFrame partialDataFrame = new DefaultSpdyDataFrame(streamID);
809 partialDataFrame.setData(spdyDataFrame.getData().readSlice(newWindowSize));
810
811 ChannelFuture writeFuture = Channels.future(e.getChannel());
812
813
814
815 final SocketAddress remoteAddress = e.getRemoteAddress();
816 final ChannelHandlerContext context = ctx;
817 e.getFuture().addListener(new ChannelFutureListener() {
818 public void operationComplete(ChannelFuture future) throws Exception {
819 if (!future.isSuccess()) {
820 issueStreamError(context, remoteAddress, streamID, SpdyStreamStatus.INTERNAL_ERROR);
821 }
822 }
823 });
824
825 Channels.write(ctx, writeFuture, partialDataFrame, remoteAddress);
826
827 newWindowSize = 0;
828 }
829 }
830 }
831 }
832
833 private void sendGoAwayFrame(ChannelHandlerContext ctx, ChannelStateEvent e) {
834
835 if (!e.getChannel().isConnected()) {
836 ctx.sendDownstream(e);
837 return;
838 }
839
840 ChannelFuture future = sendGoAwayFrame(ctx, e.getChannel(), null, SpdySessionStatus.OK);
841 if (spdySession.noActiveStreams()) {
842 future.addListener(new ClosingChannelFutureListener(ctx, e));
843 } else {
844 closeSessionFuture = Channels.future(e.getChannel());
845 closeSessionFuture.addListener(new ClosingChannelFutureListener(ctx, e));
846 }
847 }
848
849 private synchronized ChannelFuture sendGoAwayFrame(
850 ChannelHandlerContext ctx, Channel channel, SocketAddress remoteAddress, SpdySessionStatus status) {
851 if (!sentGoAwayFrame) {
852 sentGoAwayFrame = true;
853 SpdyGoAwayFrame spdyGoAwayFrame = new DefaultSpdyGoAwayFrame(lastGoodStreamID, status);
854 ChannelFuture future = Channels.future(channel);
855 Channels.write(ctx, future, spdyGoAwayFrame, remoteAddress);
856 return future;
857 }
858 return Channels.succeededFuture(channel);
859 }
860
861 private static final class ClosingChannelFutureListener implements ChannelFutureListener {
862 private final ChannelHandlerContext ctx;
863 private final ChannelStateEvent e;
864
865 ClosingChannelFutureListener(ChannelHandlerContext ctx, ChannelStateEvent e) {
866 this.ctx = ctx;
867 this.e = e;
868 }
869
870 public void operationComplete(ChannelFuture sentGoAwayFuture) throws Exception {
871 if (!(sentGoAwayFuture.getCause() instanceof ClosedChannelException)) {
872 Channels.close(ctx, e.getFuture());
873 } else {
874 e.getFuture().setSuccess();
875 }
876 }
877 }
878 }