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