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(future -> {
557 if (!future.isSuccess()) {
558 issueSessionError(context, SpdySessionStatus.INTERNAL_ERROR);
559 }
560 });
561 return;
562 } else {
563
564 spdySession.updateSendWindowSize(streamId, -1 * dataLength);
565 spdySession.updateSendWindowSize(SPDY_SESSION_STREAM_ID, -1 * dataLength);
566
567
568
569 final ChannelHandlerContext context = ctx;
570 promise.addListener(future -> {
571 if (!future.isSuccess()) {
572 issueSessionError(context, SpdySessionStatus.INTERNAL_ERROR);
573 }
574 });
575 }
576
577
578 if (spdyDataFrame.isLast()) {
579 halfCloseStream(streamId, false, promise);
580 }
581
582 } else if (msg instanceof SpdySynStreamFrame) {
583
584 SpdySynStreamFrame spdySynStreamFrame = (SpdySynStreamFrame) msg;
585 int streamId = spdySynStreamFrame.streamId();
586
587 if (isRemoteInitiatedId(streamId)) {
588 promise.setFailure(PROTOCOL_EXCEPTION);
589 return;
590 }
591
592 byte priority = spdySynStreamFrame.priority();
593 boolean remoteSideClosed = spdySynStreamFrame.isUnidirectional();
594 boolean localSideClosed = spdySynStreamFrame.isLast();
595 if (!acceptStream(streamId, priority, remoteSideClosed, localSideClosed)) {
596 promise.setFailure(PROTOCOL_EXCEPTION);
597 return;
598 }
599
600 } else if (msg instanceof SpdySynReplyFrame) {
601
602 SpdySynReplyFrame spdySynReplyFrame = (SpdySynReplyFrame) msg;
603 int streamId = spdySynReplyFrame.streamId();
604
605
606 if (!isRemoteInitiatedId(streamId) || spdySession.isLocalSideClosed(streamId)) {
607 promise.setFailure(PROTOCOL_EXCEPTION);
608 return;
609 }
610
611
612 if (spdySynReplyFrame.isLast()) {
613 halfCloseStream(streamId, false, promise);
614 }
615
616 } else if (msg instanceof SpdyRstStreamFrame) {
617
618 SpdyRstStreamFrame spdyRstStreamFrame = (SpdyRstStreamFrame) msg;
619 removeStream(spdyRstStreamFrame.streamId(), promise);
620
621 } else if (msg instanceof SpdySettingsFrame) {
622
623 SpdySettingsFrame spdySettingsFrame = (SpdySettingsFrame) msg;
624
625 int settingsMinorVersion = spdySettingsFrame.getValue(SpdySettingsFrame.SETTINGS_MINOR_VERSION);
626 if (settingsMinorVersion >= 0 && settingsMinorVersion != minorVersion) {
627
628 promise.setFailure(PROTOCOL_EXCEPTION);
629 return;
630 }
631
632 int newConcurrentStreams =
633 spdySettingsFrame.getValue(SpdySettingsFrame.SETTINGS_MAX_CONCURRENT_STREAMS);
634 if (newConcurrentStreams >= 0) {
635 localConcurrentStreams = newConcurrentStreams;
636 }
637
638
639
640
641 if (spdySettingsFrame.isPersisted(SpdySettingsFrame.SETTINGS_INITIAL_WINDOW_SIZE)) {
642 spdySettingsFrame.removeValue(SpdySettingsFrame.SETTINGS_INITIAL_WINDOW_SIZE);
643 }
644 spdySettingsFrame.setPersistValue(SpdySettingsFrame.SETTINGS_INITIAL_WINDOW_SIZE, false);
645
646 int newInitialWindowSize =
647 spdySettingsFrame.getValue(SpdySettingsFrame.SETTINGS_INITIAL_WINDOW_SIZE);
648 if (newInitialWindowSize >= 0) {
649 updateInitialReceiveWindowSize(newInitialWindowSize);
650 }
651
652 } else if (msg instanceof SpdyPingFrame) {
653
654 SpdyPingFrame spdyPingFrame = (SpdyPingFrame) msg;
655 if (isRemoteInitiatedId(spdyPingFrame.id())) {
656 ctx.fireExceptionCaught(new IllegalArgumentException(
657 "invalid PING ID: " + spdyPingFrame.id()));
658 return;
659 }
660 pings.getAndIncrement();
661
662 } else if (msg instanceof SpdyGoAwayFrame) {
663
664
665
666 promise.setFailure(PROTOCOL_EXCEPTION);
667 return;
668
669 } else if (msg instanceof SpdyHeadersFrame) {
670
671 SpdyHeadersFrame spdyHeadersFrame = (SpdyHeadersFrame) msg;
672 int streamId = spdyHeadersFrame.streamId();
673
674
675 if (spdySession.isLocalSideClosed(streamId)) {
676 promise.setFailure(PROTOCOL_EXCEPTION);
677 return;
678 }
679
680
681 if (spdyHeadersFrame.isLast()) {
682 halfCloseStream(streamId, false, promise);
683 }
684
685 } else if (msg instanceof SpdyWindowUpdateFrame) {
686
687
688 promise.setFailure(PROTOCOL_EXCEPTION);
689 return;
690 }
691
692 ctx.write(msg, promise);
693 }
694
695
696
697
698
699
700
701
702
703
704 private void issueSessionError(
705 ChannelHandlerContext ctx, SpdySessionStatus status) {
706
707 sendGoAwayFrame(ctx, status).addListener(new ClosingChannelFutureListener(ctx, ctx.newPromise()));
708 }
709
710
711
712
713
714
715
716
717
718
719
720
721 private void issueStreamError(ChannelHandlerContext ctx, int streamId, SpdyStreamStatus status) {
722 boolean fireChannelRead = !spdySession.isRemoteSideClosed(streamId);
723 ChannelPromise promise = ctx.newPromise();
724 removeStream(streamId, promise);
725
726 SpdyRstStreamFrame spdyRstStreamFrame = new DefaultSpdyRstStreamFrame(streamId, status);
727 ctx.writeAndFlush(spdyRstStreamFrame, promise);
728 if (fireChannelRead) {
729 ctx.fireChannelRead(spdyRstStreamFrame);
730 }
731 }
732
733
734
735
736
737 private boolean isRemoteInitiatedId(int id) {
738 boolean serverId = isServerId(id);
739 return server && !serverId || !server && serverId;
740 }
741
742
743 private void updateInitialSendWindowSize(int newInitialWindowSize) {
744 int deltaWindowSize = newInitialWindowSize - initialSendWindowSize;
745 initialSendWindowSize = newInitialWindowSize;
746 spdySession.updateAllSendWindowSizes(deltaWindowSize);
747 }
748
749
750 private void updateInitialReceiveWindowSize(int newInitialWindowSize) {
751 int deltaWindowSize = newInitialWindowSize - initialReceiveWindowSize;
752 initialReceiveWindowSize = newInitialWindowSize;
753 spdySession.updateAllReceiveWindowSizes(deltaWindowSize);
754 }
755
756
757 private boolean acceptStream(
758 int streamId, byte priority, boolean remoteSideClosed, boolean localSideClosed) {
759
760 if (receivedGoAwayFrame || sentGoAwayFrame) {
761 return false;
762 }
763
764 boolean remote = isRemoteInitiatedId(streamId);
765 int maxConcurrentStreams = remote ? localConcurrentStreams : remoteConcurrentStreams;
766 if (spdySession.numActiveStreams(remote) >= maxConcurrentStreams) {
767 return false;
768 }
769 spdySession.acceptStream(
770 streamId, priority, remoteSideClosed, localSideClosed,
771 initialSendWindowSize, initialReceiveWindowSize, remote);
772 if (remote) {
773 lastGoodStreamId = streamId;
774 }
775 return true;
776 }
777
778 private void halfCloseStream(int streamId, boolean remote, ChannelFuture future) {
779 if (remote) {
780 spdySession.closeRemoteSide(streamId, isRemoteInitiatedId(streamId));
781 } else {
782 spdySession.closeLocalSide(streamId, isRemoteInitiatedId(streamId));
783 }
784 if (closeSessionFutureListener != null && spdySession.noActiveStreams()) {
785 future.addListener(closeSessionFutureListener);
786 }
787 }
788
789 private void removeStream(int streamId, ChannelFuture future) {
790 spdySession.removeStream(streamId, STREAM_CLOSED, isRemoteInitiatedId(streamId));
791
792 if (closeSessionFutureListener != null && spdySession.noActiveStreams()) {
793 future.addListener(closeSessionFutureListener);
794 }
795 }
796
797 private void updateSendWindowSize(final ChannelHandlerContext ctx, int streamId, int deltaWindowSize) {
798 spdySession.updateSendWindowSize(streamId, deltaWindowSize);
799
800 while (true) {
801
802 SpdySession.PendingWrite pendingWrite = spdySession.getPendingWrite(streamId);
803 if (pendingWrite == null) {
804 return;
805 }
806
807 SpdyDataFrame spdyDataFrame = pendingWrite.spdyDataFrame;
808 int dataFrameSize = spdyDataFrame.content().readableBytes();
809 int writeStreamId = spdyDataFrame.streamId();
810 int sendWindowSize = spdySession.getSendWindowSize(writeStreamId);
811 int sessionSendWindowSize = spdySession.getSendWindowSize(SPDY_SESSION_STREAM_ID);
812 sendWindowSize = Math.min(sendWindowSize, sessionSendWindowSize);
813
814 if (sendWindowSize <= 0) {
815 return;
816 } else if (sendWindowSize < dataFrameSize) {
817
818 spdySession.updateSendWindowSize(writeStreamId, -1 * sendWindowSize);
819 spdySession.updateSendWindowSize(SPDY_SESSION_STREAM_ID, -1 * sendWindowSize);
820
821
822 SpdyDataFrame partialDataFrame = new DefaultSpdyDataFrame(
823 writeStreamId, spdyDataFrame.content().readRetainedSlice(sendWindowSize));
824
825
826 ctx.writeAndFlush(partialDataFrame).addListener(future -> {
827 if (!future.isSuccess()) {
828 issueSessionError(ctx, SpdySessionStatus.INTERNAL_ERROR);
829 }
830 });
831 } else {
832
833 spdySession.removePendingWrite(writeStreamId);
834 spdySession.updateSendWindowSize(writeStreamId, -1 * dataFrameSize);
835 spdySession.updateSendWindowSize(SPDY_SESSION_STREAM_ID, -1 * dataFrameSize);
836
837
838 if (spdyDataFrame.isLast()) {
839 halfCloseStream(writeStreamId, false, pendingWrite.promise);
840 }
841
842
843
844 ctx.writeAndFlush(spdyDataFrame, pendingWrite.promise).addListener(future -> {
845 if (!future.isSuccess()) {
846 issueSessionError(ctx, SpdySessionStatus.INTERNAL_ERROR);
847 }
848 });
849 }
850 }
851 }
852
853 private void sendGoAwayFrame(ChannelHandlerContext ctx, ChannelPromise future) {
854
855 if (!ctx.channel().isActive()) {
856 ctx.close(future);
857 return;
858 }
859
860 ChannelFuture f = sendGoAwayFrame(ctx, SpdySessionStatus.OK);
861 if (spdySession.noActiveStreams()) {
862 f.addListener(new ClosingChannelFutureListener(ctx, future));
863 } else {
864 closeSessionFutureListener = new ClosingChannelFutureListener(ctx, future);
865 }
866
867 }
868
869 private ChannelFuture sendGoAwayFrame(
870 ChannelHandlerContext ctx, SpdySessionStatus status) {
871 if (!sentGoAwayFrame) {
872 sentGoAwayFrame = true;
873 SpdyGoAwayFrame spdyGoAwayFrame = new DefaultSpdyGoAwayFrame(lastGoodStreamId, status);
874 return ctx.writeAndFlush(spdyGoAwayFrame);
875 } else {
876 return ctx.newSucceededFuture();
877 }
878 }
879
880 private static final class ClosingChannelFutureListener implements ChannelFutureListener {
881 private final ChannelHandlerContext ctx;
882 private final ChannelPromise promise;
883
884 ClosingChannelFutureListener(ChannelHandlerContext ctx, ChannelPromise promise) {
885 this.ctx = ctx;
886 this.promise = promise;
887 }
888
889 @Override
890 public void operationComplete(ChannelFuture sentGoAwayFuture) throws Exception {
891 ctx.close(promise);
892 }
893 }
894 }