1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.handler.codec.http2;
17
18 import io.netty.buffer.ByteBuf;
19 import io.netty.channel.Channel;
20 import io.netty.channel.ChannelFuture;
21 import io.netty.channel.ChannelFutureListener;
22 import io.netty.channel.ChannelHandlerContext;
23 import io.netty.channel.ChannelInboundHandler;
24 import io.netty.channel.ChannelPromise;
25 import io.netty.handler.codec.UnsupportedMessageTypeException;
26 import io.netty.handler.codec.http.HttpServerUpgradeHandler.UpgradeEvent;
27 import io.netty.handler.codec.http2.Http2Connection.PropertyKey;
28 import io.netty.handler.codec.http2.Http2Stream.State;
29 import io.netty.handler.codec.http2.StreamBufferingEncoder.Http2ChannelClosedException;
30 import io.netty.handler.codec.http2.StreamBufferingEncoder.Http2GoAwayException;
31 import io.netty.util.ReferenceCountUtil;
32 import io.netty.util.ReferenceCounted;
33 import io.netty.util.collection.IntObjectHashMap;
34 import io.netty.util.collection.IntObjectMap;
35 import io.netty.util.internal.logging.InternalLogger;
36 import io.netty.util.internal.logging.InternalLoggerFactory;
37
38 import static io.netty.buffer.ByteBufUtil.writeAscii;
39 import static io.netty.handler.codec.http2.Http2CodecUtil.HTTP_UPGRADE_STREAM_ID;
40 import static io.netty.handler.codec.http2.Http2CodecUtil.isStreamIdValid;
41 import static io.netty.handler.codec.http2.Http2Error.NO_ERROR;
42 import static io.netty.util.internal.logging.InternalLogLevel.DEBUG;
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147 public class Http2FrameCodec extends Http2ConnectionHandler {
148
149 private static final InternalLogger LOG = InternalLoggerFactory.getInstance(Http2FrameCodec.class);
150
151 private static final Class<?>[] SUPPORTED_MESSAGES = new Class[] {
152 Http2DataFrame.class, Http2HeadersFrame.class, Http2WindowUpdateFrame.class, Http2ResetFrame.class,
153 Http2PingFrame.class, Http2SettingsFrame.class, Http2SettingsAckFrame.class, Http2GoAwayFrame.class,
154 Http2PushPromiseFrame.class, Http2PriorityFrame.class, Http2UnknownFrame.class };
155
156 protected final PropertyKey streamKey;
157 private final PropertyKey upgradeKey;
158
159 private final Integer initialFlowControlWindowSize;
160
161 ChannelHandlerContext ctx;
162
163
164
165
166 private int numBufferedStreams;
167 private final IntObjectMap<DefaultHttp2FrameStream> frameStreamToInitializeMap =
168 new IntObjectHashMap<DefaultHttp2FrameStream>(8);
169
170 protected Http2FrameCodec(Http2ConnectionEncoder encoder, Http2ConnectionDecoder decoder,
171 Http2Settings initialSettings, boolean decoupleCloseAndGoAway, boolean flushPreface) {
172 super(decoder, encoder, initialSettings, decoupleCloseAndGoAway, flushPreface);
173
174 decoder.frameListener(new FrameListener());
175 connection().addListener(new ConnectionListener());
176 connection().remote().flowController().listener(new Http2RemoteFlowControllerListener());
177 streamKey = connection().newKey();
178 upgradeKey = connection().newKey();
179 initialFlowControlWindowSize = initialSettings.initialWindowSize();
180 }
181
182
183
184
185 DefaultHttp2FrameStream newStream() {
186 return new DefaultHttp2FrameStream();
187 }
188
189
190
191
192
193
194 final void forEachActiveStream(final Http2FrameStreamVisitor streamVisitor) throws Http2Exception {
195 assert ctx.executor().inEventLoop();
196 if (connection().numActiveStreams() > 0) {
197 connection().forEachActiveStream(new Http2StreamVisitor() {
198 @Override
199 public boolean visit(Http2Stream stream) {
200 try {
201 return streamVisitor.visit((Http2FrameStream) stream.getProperty(streamKey));
202 } catch (Throwable cause) {
203 onError(ctx, false, cause);
204 return false;
205 }
206 }
207 });
208 }
209 }
210
211
212
213
214
215
216 int numInitializingStreams() {
217 return frameStreamToInitializeMap.size();
218 }
219
220 @Override
221 public final void handlerAdded(ChannelHandlerContext ctx) throws Exception {
222 this.ctx = ctx;
223 super.handlerAdded(ctx);
224 handlerAdded0(ctx);
225
226
227 Http2Connection connection = connection();
228 if (connection.isServer()) {
229 tryExpandConnectionFlowControlWindow(connection);
230 }
231 }
232
233 private void tryExpandConnectionFlowControlWindow(Http2Connection connection) throws Http2Exception {
234 if (initialFlowControlWindowSize != null) {
235
236
237 Http2Stream connectionStream = connection.connectionStream();
238 Http2LocalFlowController localFlowController = connection.local().flowController();
239 final int delta = initialFlowControlWindowSize - localFlowController.initialWindowSize(connectionStream);
240
241 if (delta > 0) {
242
243 localFlowController.incrementWindowSize(connectionStream, Math.max(delta << 1, delta));
244 flush(ctx);
245 }
246 }
247 }
248
249 void handlerAdded0(@SuppressWarnings("unsed") ChannelHandlerContext ctx) throws Exception {
250
251 }
252
253
254
255
256
257 @Override
258 public final void userEventTriggered(final ChannelHandlerContext ctx, final Object evt) throws Exception {
259 if (evt == Http2ConnectionPrefaceAndSettingsFrameWrittenEvent.INSTANCE) {
260
261 tryExpandConnectionFlowControlWindow(connection());
262
263
264
265
266 ctx.executor().execute(new Runnable() {
267 @Override
268 public void run() {
269 ctx.fireUserEventTriggered(evt);
270 }
271 });
272 } else if (evt instanceof UpgradeEvent) {
273 UpgradeEvent upgrade = (UpgradeEvent) evt;
274 try {
275 onUpgradeEvent(ctx, upgrade.retain());
276 Http2Stream stream = connection().stream(HTTP_UPGRADE_STREAM_ID);
277 if (stream.getProperty(streamKey) == null) {
278
279
280
281 onStreamActive0(stream);
282 }
283 upgrade.upgradeRequest().headers().setInt(
284 HttpConversionUtil.ExtensionHeaderNames.STREAM_ID.text(), HTTP_UPGRADE_STREAM_ID);
285 stream.setProperty(upgradeKey, true);
286 InboundHttpToHttp2Adapter.handle(
287 ctx, connection(), decoder().frameListener(), upgrade.upgradeRequest().retain());
288 } finally {
289 upgrade.release();
290 }
291 } else {
292 onUserEventTriggered(ctx, evt);
293 ctx.fireUserEventTriggered(evt);
294 }
295 }
296
297 void onUserEventTriggered(final ChannelHandlerContext ctx, final Object evt) throws Exception {
298
299 }
300
301
302
303
304
305 @Override
306 public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) {
307 if (msg instanceof Http2DataFrame) {
308 Http2DataFrame dataFrame = (Http2DataFrame) msg;
309 encoder().writeData(ctx, dataFrame.stream().id(), dataFrame.content(),
310 dataFrame.padding(), dataFrame.isEndStream(), promise);
311 } else if (msg instanceof Http2HeadersFrame) {
312 writeHeadersFrame(ctx, (Http2HeadersFrame) msg, promise);
313 } else if (msg instanceof Http2WindowUpdateFrame) {
314 Http2WindowUpdateFrame frame = (Http2WindowUpdateFrame) msg;
315 Http2FrameStream frameStream = frame.stream();
316
317
318 try {
319 if (frameStream == null) {
320 increaseInitialConnectionWindow(frame.windowSizeIncrement());
321 } else {
322 consumeBytes(frameStream.id(), frame.windowSizeIncrement());
323 }
324 promise.setSuccess();
325 } catch (Throwable t) {
326 promise.setFailure(t);
327 }
328 } else if (msg instanceof Http2ResetFrame) {
329 Http2ResetFrame rstFrame = (Http2ResetFrame) msg;
330 int id = rstFrame.stream().id();
331
332
333 if (connection().streamMayHaveExisted(id)) {
334 encoder().writeRstStream(ctx, rstFrame.stream().id(), rstFrame.errorCode(), promise);
335 } else {
336 ReferenceCountUtil.release(rstFrame);
337 promise.setFailure(Http2Exception.streamError(
338 rstFrame.stream().id(), Http2Error.PROTOCOL_ERROR, "Stream never existed"));
339 }
340 } else if (msg instanceof Http2PingFrame) {
341 Http2PingFrame frame = (Http2PingFrame) msg;
342 encoder().writePing(ctx, frame.ack(), frame.content(), promise);
343 } else if (msg instanceof Http2SettingsFrame) {
344 encoder().writeSettings(ctx, ((Http2SettingsFrame) msg).settings(), promise);
345 } else if (msg instanceof Http2SettingsAckFrame) {
346
347
348 encoder().writeSettingsAck(ctx, promise);
349 } else if (msg instanceof Http2GoAwayFrame) {
350 writeGoAwayFrame(ctx, (Http2GoAwayFrame) msg, promise);
351 } else if (msg instanceof Http2PushPromiseFrame) {
352 Http2PushPromiseFrame pushPromiseFrame = (Http2PushPromiseFrame) msg;
353 writePushPromise(ctx, pushPromiseFrame, promise);
354 } else if (msg instanceof Http2PriorityFrame) {
355 Http2PriorityFrame priorityFrame = (Http2PriorityFrame) msg;
356 encoder().writePriority(ctx, priorityFrame.stream().id(), priorityFrame.streamDependency(),
357 priorityFrame.weight(), priorityFrame.exclusive(), promise);
358 } else if (msg instanceof Http2UnknownFrame) {
359 Http2UnknownFrame unknownFrame = (Http2UnknownFrame) msg;
360 encoder().writeFrame(ctx, unknownFrame.frameType(), unknownFrame.stream().id(),
361 unknownFrame.flags(), unknownFrame.content(), promise);
362 } else if (!(msg instanceof Http2Frame)) {
363 ctx.write(msg, promise);
364 } else {
365 ReferenceCountUtil.release(msg);
366 throw new UnsupportedMessageTypeException(msg, SUPPORTED_MESSAGES);
367 }
368 }
369
370 private void increaseInitialConnectionWindow(int deltaBytes) throws Http2Exception {
371
372 connection().local().flowController().incrementWindowSize(connection().connectionStream(), deltaBytes);
373 }
374
375 final boolean consumeBytes(int streamId, int bytes) throws Http2Exception {
376 Http2Stream stream = connection().stream(streamId);
377
378
379 if (stream != null && streamId == Http2CodecUtil.HTTP_UPGRADE_STREAM_ID) {
380 Boolean upgraded = stream.getProperty(upgradeKey);
381 if (Boolean.TRUE.equals(upgraded)) {
382 return false;
383 }
384 }
385
386 return connection().local().flowController().consumeBytes(stream, bytes);
387 }
388
389 private void writeGoAwayFrame(ChannelHandlerContext ctx, Http2GoAwayFrame frame, ChannelPromise promise) {
390 if (frame.lastStreamId() > -1) {
391 frame.release();
392 throw new IllegalArgumentException("Last stream id must not be set on GOAWAY frame");
393 }
394
395 int lastStreamCreated = connection().remote().lastStreamCreated();
396 long lastStreamId = lastStreamCreated + ((long) frame.extraStreamIds()) * 2;
397
398 if (lastStreamId > Integer.MAX_VALUE) {
399 lastStreamId = Integer.MAX_VALUE;
400 }
401 goAway(ctx, (int) lastStreamId, frame.errorCode(), frame.content(), promise);
402 }
403
404 private void writeHeadersFrame(final ChannelHandlerContext ctx, Http2HeadersFrame headersFrame,
405 ChannelPromise promise) {
406
407 if (isStreamIdValid(headersFrame.stream().id())) {
408 encoder().writeHeaders(ctx, headersFrame.stream().id(), headersFrame.headers(), headersFrame.padding(),
409 headersFrame.isEndStream(), promise);
410 } else if (initializeNewStream(ctx, (DefaultHttp2FrameStream) headersFrame.stream(), promise)) {
411 promise = promise.unvoid();
412
413 final int streamId = headersFrame.stream().id();
414
415 encoder().writeHeaders(ctx, streamId, headersFrame.headers(), headersFrame.padding(),
416 headersFrame.isEndStream(), promise);
417
418 if (!promise.isDone()) {
419 numBufferedStreams++;
420
421
422 promise.addListener(new ChannelFutureListener() {
423 @Override
424 public void operationComplete(ChannelFuture channelFuture) {
425 numBufferedStreams--;
426 handleHeaderFuture(channelFuture, streamId);
427 }
428 });
429 } else {
430 handleHeaderFuture(promise, streamId);
431 }
432 }
433 }
434
435 private void writePushPromise(final ChannelHandlerContext ctx, Http2PushPromiseFrame pushPromiseFrame,
436 final ChannelPromise promise) {
437 if (isStreamIdValid(pushPromiseFrame.pushStream().id())) {
438 encoder().writePushPromise(ctx, pushPromiseFrame.stream().id(), pushPromiseFrame.pushStream().id(),
439 pushPromiseFrame.http2Headers(), pushPromiseFrame.padding(), promise);
440 } else if (initializeNewStream(ctx, (DefaultHttp2FrameStream) pushPromiseFrame.pushStream(), promise)) {
441 final int streamId = pushPromiseFrame.stream().id();
442 encoder().writePushPromise(ctx, streamId, pushPromiseFrame.pushStream().id(),
443 pushPromiseFrame.http2Headers(), pushPromiseFrame.padding(), promise);
444
445 if (promise.isDone()) {
446 handleHeaderFuture(promise, streamId);
447 } else {
448 numBufferedStreams++;
449
450
451 promise.addListener(new ChannelFutureListener() {
452 @Override
453 public void operationComplete(ChannelFuture channelFuture) {
454 numBufferedStreams--;
455 handleHeaderFuture(channelFuture, streamId);
456 }
457 });
458 }
459 }
460 }
461
462 private boolean initializeNewStream(ChannelHandlerContext ctx, DefaultHttp2FrameStream http2FrameStream,
463 ChannelPromise promise) {
464 final Http2Connection connection = connection();
465 final int streamId = connection.local().incrementAndGetNextStreamId();
466 if (streamId < 0) {
467 promise.setFailure(new Http2NoMoreStreamIdsException());
468
469
470
471 onHttp2Frame(ctx, new DefaultHttp2GoAwayFrame(connection.isServer() ? Integer.MAX_VALUE :
472 Integer.MAX_VALUE - 1, NO_ERROR.code(),
473 writeAscii(ctx.alloc(), "Stream IDs exhausted on local stream creation")));
474
475 return false;
476 }
477 http2FrameStream.id = streamId;
478
479
480
481
482
483
484 Object old = frameStreamToInitializeMap.put(streamId, http2FrameStream);
485
486
487 assert old == null;
488 return true;
489 }
490
491 private void handleHeaderFuture(ChannelFuture channelFuture, int streamId) {
492 if (!channelFuture.isSuccess()) {
493 frameStreamToInitializeMap.remove(streamId);
494 }
495 }
496
497 private void onStreamActive0(Http2Stream stream) {
498 if (stream.id() != Http2CodecUtil.HTTP_UPGRADE_STREAM_ID &&
499 connection().local().isValidStreamId(stream.id())) {
500 return;
501 }
502
503 DefaultHttp2FrameStream stream2 = newStream().setStreamAndProperty(streamKey, stream);
504 onHttp2StreamStateChanged(ctx, stream2);
505 }
506
507 private final class ConnectionListener extends Http2ConnectionAdapter {
508 @Override
509 public void onStreamAdded(Http2Stream stream) {
510 DefaultHttp2FrameStream frameStream = frameStreamToInitializeMap.remove(stream.id());
511
512 if (frameStream != null) {
513 frameStream.setStreamAndProperty(streamKey, stream);
514 }
515 }
516
517 @Override
518 public void onStreamActive(Http2Stream stream) {
519 onStreamActive0(stream);
520 }
521
522 @Override
523 public void onStreamClosed(Http2Stream stream) {
524 onHttp2StreamStateChanged0(stream);
525 }
526
527 @Override
528 public void onStreamHalfClosed(Http2Stream stream) {
529 onHttp2StreamStateChanged0(stream);
530 }
531
532 private void onHttp2StreamStateChanged0(Http2Stream stream) {
533 DefaultHttp2FrameStream stream2 = stream.getProperty(streamKey);
534 if (stream2 != null) {
535 onHttp2StreamStateChanged(ctx, stream2);
536 }
537 }
538 }
539
540 @Override
541 protected void onConnectionError(
542 ChannelHandlerContext ctx, boolean outbound, Throwable cause, Http2Exception http2Ex) {
543 if (!outbound) {
544
545
546
547
548 ctx.fireExceptionCaught(cause);
549 }
550 super.onConnectionError(ctx, outbound, cause, http2Ex);
551 }
552
553
554
555
556
557 @Override
558 protected final void onStreamError(ChannelHandlerContext ctx, boolean outbound, Throwable cause,
559 Http2Exception.StreamException streamException) {
560 int streamId = streamException.streamId();
561 Http2Stream connectionStream = connection().stream(streamId);
562 if (connectionStream == null) {
563 onHttp2UnknownStreamError(ctx, cause, streamException);
564
565 super.onStreamError(ctx, outbound, cause, streamException);
566 return;
567 }
568
569 Http2FrameStream stream = connectionStream.getProperty(streamKey);
570 if (stream == null) {
571 LOG.warn("{} Stream exception thrown without stream object attached.", ctx.channel(), cause);
572
573 super.onStreamError(ctx, outbound, cause, streamException);
574 return;
575 }
576
577 if (!outbound) {
578
579 onHttp2FrameStreamException(ctx, new Http2FrameStreamException(stream, streamException.error(), cause));
580 }
581 }
582
583 private static void onHttp2UnknownStreamError(@SuppressWarnings("unused") ChannelHandlerContext ctx,
584 Throwable cause, Http2Exception.StreamException streamException) {
585
586
587
588
589
590 LOG.log(DEBUG, "{} Stream exception thrown for unknown stream {}.",
591 ctx.channel(), streamException.streamId(), cause);
592 }
593
594 @Override
595 protected final boolean isGracefulShutdownComplete() {
596 return super.isGracefulShutdownComplete() && numBufferedStreams == 0;
597 }
598
599 private final class FrameListener implements Http2FrameListener {
600
601 @Override
602 public void onUnknownFrame(
603 ChannelHandlerContext ctx, byte frameType, int streamId, Http2Flags flags, ByteBuf payload) {
604 if (streamId == 0) {
605
606 return;
607 }
608 Http2FrameStream stream = requireStream(streamId);
609 onHttp2Frame(ctx, newHttp2UnknownFrame(frameType, streamId, flags, payload.retain()).stream(stream));
610 }
611
612 @Override
613 public void onSettingsRead(ChannelHandlerContext ctx, Http2Settings settings) {
614 onHttp2Frame(ctx, new DefaultHttp2SettingsFrame(settings));
615 }
616
617 @Override
618 public void onPingRead(ChannelHandlerContext ctx, long data) {
619 onHttp2Frame(ctx, new DefaultHttp2PingFrame(data, false));
620 }
621
622 @Override
623 public void onPingAckRead(ChannelHandlerContext ctx, long data) {
624 onHttp2Frame(ctx, new DefaultHttp2PingFrame(data, true));
625 }
626
627 @Override
628 public void onRstStreamRead(ChannelHandlerContext ctx, int streamId, long errorCode) {
629 Http2FrameStream stream = requireStream(streamId);
630 onHttp2Frame(ctx, new DefaultHttp2ResetFrame(errorCode).stream(stream));
631 }
632
633 @Override
634 public void onWindowUpdateRead(ChannelHandlerContext ctx, int streamId, int windowSizeIncrement) {
635 if (streamId == 0) {
636
637 return;
638 }
639 Http2FrameStream stream = requireStream(streamId);
640 onHttp2Frame(ctx, new DefaultHttp2WindowUpdateFrame(windowSizeIncrement).stream(stream));
641 }
642
643 @Override
644 public void onHeadersRead(ChannelHandlerContext ctx, int streamId,
645 Http2Headers headers, int streamDependency, short weight, boolean
646 exclusive, int padding, boolean endStream) {
647 onHeadersRead(ctx, streamId, headers, padding, endStream);
648 }
649
650 @Override
651 public void onHeadersRead(ChannelHandlerContext ctx, int streamId, Http2Headers headers,
652 int padding, boolean endOfStream) {
653 Http2FrameStream stream = requireStream(streamId);
654 onHttp2Frame(ctx, new DefaultHttp2HeadersFrame(headers, endOfStream, padding).stream(stream));
655 }
656
657 @Override
658 public int onDataRead(ChannelHandlerContext ctx, int streamId, ByteBuf data, int padding,
659 boolean endOfStream) {
660 Http2FrameStream stream = requireStream(streamId);
661 final Http2DataFrame dataframe;
662 try {
663 dataframe = new DefaultHttp2DataFrame(data.retain(), endOfStream, padding);
664 } catch (IllegalArgumentException e) {
665
666 data.release();
667 throw e;
668 }
669 dataframe.stream(stream);
670 onHttp2Frame(ctx, dataframe);
671
672 return 0;
673 }
674
675 @Override
676 public void onGoAwayRead(ChannelHandlerContext ctx, int lastStreamId, long errorCode, ByteBuf debugData) {
677 onHttp2Frame(ctx, new DefaultHttp2GoAwayFrame(lastStreamId, errorCode, debugData.retain()));
678 }
679
680 @Override
681 public void onPriorityRead(ChannelHandlerContext ctx, int streamId, int streamDependency,
682 short weight, boolean exclusive) {
683
684 Http2Stream stream = connection().stream(streamId);
685 if (stream == null) {
686
687 return;
688 }
689 Http2FrameStream frameStream = requireStream(streamId);
690 onHttp2Frame(ctx, new DefaultHttp2PriorityFrame(streamDependency, weight, exclusive)
691 .stream(frameStream));
692 }
693
694 @Override
695 public void onSettingsAckRead(ChannelHandlerContext ctx) {
696 onHttp2Frame(ctx, Http2SettingsAckFrame.INSTANCE);
697 }
698
699 @Override
700 public void onPushPromiseRead(ChannelHandlerContext ctx, int streamId, int promisedStreamId,
701 Http2Headers headers, int padding) {
702 Http2FrameStream stream = requireStream(streamId);
703 onHttp2Frame(ctx, new DefaultHttp2PushPromiseFrame(headers, padding, promisedStreamId)
704 .pushStream(new DefaultHttp2FrameStream()
705 .setStreamAndProperty(streamKey, connection().stream(promisedStreamId)))
706 .stream(stream));
707 }
708
709 private Http2FrameStream requireStream(int streamId) {
710 Http2FrameStream stream = connection().stream(streamId).getProperty(streamKey);
711 if (stream == null) {
712 throw new IllegalStateException("Stream object required for identifier: " + streamId);
713 }
714 return stream;
715 }
716 }
717
718 private void onUpgradeEvent(ChannelHandlerContext ctx, UpgradeEvent evt) {
719 ctx.fireUserEventTriggered(evt);
720 }
721
722 private void onHttp2StreamWritabilityChanged(ChannelHandlerContext ctx, DefaultHttp2FrameStream stream,
723 @SuppressWarnings("unused") boolean writable) {
724 ctx.fireUserEventTriggered(stream.writabilityChanged);
725 }
726
727 void onHttp2StreamStateChanged(ChannelHandlerContext ctx, DefaultHttp2FrameStream stream) {
728 ctx.fireUserEventTriggered(stream.stateChanged);
729 }
730
731 void onHttp2Frame(ChannelHandlerContext ctx, Http2Frame frame) {
732 ctx.fireChannelRead(frame);
733 }
734
735
736
737
738 protected Http2StreamFrame newHttp2UnknownFrame(byte frameType, int streamId, Http2Flags flags, ByteBuf payload) {
739 return new DefaultHttp2UnknownFrame(frameType, flags, payload);
740 }
741
742 void onHttp2FrameStreamException(ChannelHandlerContext ctx, Http2FrameStreamException cause) {
743 ctx.fireExceptionCaught(cause);
744 }
745
746 private final class Http2RemoteFlowControllerListener implements Http2RemoteFlowController.Listener {
747 @Override
748 public void writabilityChanged(Http2Stream stream) {
749 DefaultHttp2FrameStream frameStream = stream.getProperty(streamKey);
750 if (frameStream == null) {
751 return;
752 }
753 onHttp2StreamWritabilityChanged(
754 ctx, frameStream, connection().remote().flowController().isWritable(stream));
755 }
756 }
757
758
759
760
761
762 static class DefaultHttp2FrameStream implements Http2FrameStream {
763
764 private volatile int id = -1;
765 private volatile Http2Stream stream;
766
767 final Http2FrameStreamEvent stateChanged = Http2FrameStreamEvent.stateChanged(this);
768 final Http2FrameStreamEvent writabilityChanged = Http2FrameStreamEvent.writabilityChanged(this);
769
770 Channel attachment;
771
772 DefaultHttp2FrameStream setStreamAndProperty(PropertyKey streamKey, Http2Stream stream) {
773 assert id == -1 || stream.id() == id;
774 this.stream = stream;
775 this.id = stream.id();
776 stream.setProperty(streamKey, this);
777 return this;
778 }
779
780 @Override
781 public int id() {
782 Http2Stream stream = this.stream;
783 return stream == null ? id : stream.id();
784 }
785
786 @Override
787 public State state() {
788 Http2Stream stream = this.stream;
789 return stream == null ? State.IDLE : stream.state();
790 }
791
792 @Override
793 public String toString() {
794 return String.valueOf(id());
795 }
796 }
797 }