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.ChannelFuture;
20 import io.netty.channel.ChannelPromise;
21 import io.netty.handler.codec.http2.Http2Stream.State;
22 import io.netty.util.collection.IntObjectHashMap;
23 import io.netty.util.collection.IntObjectMap;
24 import io.netty.util.collection.IntObjectMap.PrimitiveEntry;
25 import io.netty.util.concurrent.Future;
26 import io.netty.util.concurrent.Promise;
27 import io.netty.util.concurrent.PromiseNotifier;
28 import io.netty.util.internal.EmptyArrays;
29 import io.netty.util.internal.logging.InternalLogger;
30 import io.netty.util.internal.logging.InternalLoggerFactory;
31
32 import java.util.ArrayDeque;
33 import java.util.ArrayList;
34 import java.util.Arrays;
35 import java.util.Iterator;
36 import java.util.LinkedHashSet;
37 import java.util.List;
38 import java.util.Queue;
39 import java.util.Set;
40
41 import static io.netty.handler.codec.http2.Http2CodecUtil.CONNECTION_STREAM_ID;
42 import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_MAX_RESERVED_STREAMS;
43 import static io.netty.handler.codec.http2.Http2Error.INTERNAL_ERROR;
44 import static io.netty.handler.codec.http2.Http2Error.PROTOCOL_ERROR;
45 import static io.netty.handler.codec.http2.Http2Error.REFUSED_STREAM;
46 import static io.netty.handler.codec.http2.Http2Exception.closedStreamError;
47 import static io.netty.handler.codec.http2.Http2Exception.connectionError;
48 import static io.netty.handler.codec.http2.Http2Exception.streamError;
49 import static io.netty.handler.codec.http2.Http2Stream.State.CLOSED;
50 import static io.netty.handler.codec.http2.Http2Stream.State.HALF_CLOSED_LOCAL;
51 import static io.netty.handler.codec.http2.Http2Stream.State.HALF_CLOSED_REMOTE;
52 import static io.netty.handler.codec.http2.Http2Stream.State.IDLE;
53 import static io.netty.handler.codec.http2.Http2Stream.State.OPEN;
54 import static io.netty.handler.codec.http2.Http2Stream.State.RESERVED_LOCAL;
55 import static io.netty.handler.codec.http2.Http2Stream.State.RESERVED_REMOTE;
56 import static io.netty.util.internal.ObjectUtil.checkNotNull;
57 import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero;
58 import static java.lang.Integer.MAX_VALUE;
59
60
61
62
63 public class DefaultHttp2Connection implements Http2Connection {
64 private static final InternalLogger logger = InternalLoggerFactory.getInstance(DefaultHttp2Connection.class);
65
66 final IntObjectMap<Http2Stream> streamMap = new IntObjectHashMap<Http2Stream>();
67 final PropertyKeyRegistry propertyKeyRegistry = new PropertyKeyRegistry();
68 final ConnectionStream connectionStream = new ConnectionStream();
69 final DefaultEndpoint<Http2LocalFlowController> localEndpoint;
70 final DefaultEndpoint<Http2RemoteFlowController> remoteEndpoint;
71
72
73
74
75
76
77
78
79
80 final List<Listener> listeners = new ArrayList<Listener>(4);
81 final ActiveStreams activeStreams;
82 Promise<Void> closePromise;
83
84
85
86
87
88 public DefaultHttp2Connection(boolean server) {
89 this(server, DEFAULT_MAX_RESERVED_STREAMS);
90 }
91
92
93
94
95
96
97 public DefaultHttp2Connection(boolean server, int maxReservedStreams) {
98 activeStreams = new ActiveStreams(listeners);
99
100
101
102
103
104 localEndpoint = new DefaultEndpoint<Http2LocalFlowController>(server, server ? MAX_VALUE : maxReservedStreams);
105 remoteEndpoint = new DefaultEndpoint<Http2RemoteFlowController>(!server, maxReservedStreams);
106
107
108 streamMap.put(connectionStream.id(), connectionStream);
109 }
110
111
112
113
114 final boolean isClosed() {
115 return closePromise != null;
116 }
117
118 @Override
119 public Future<Void> close(final Promise<Void> promise) {
120 checkNotNull(promise, "promise");
121
122
123 if (closePromise != null) {
124 if (closePromise == promise) {
125
126 } else if (promise instanceof ChannelPromise && ((ChannelFuture) closePromise).isVoid()) {
127 closePromise = promise;
128 } else {
129 PromiseNotifier.cascade(closePromise, promise);
130 }
131 } else {
132 closePromise = promise;
133 }
134 if (isStreamMapEmpty()) {
135 promise.trySuccess(null);
136 return promise;
137 }
138
139 Iterator<PrimitiveEntry<Http2Stream>> itr = streamMap.entries().iterator();
140
141
142 if (activeStreams.allowModifications()) {
143 activeStreams.incrementPendingIterations();
144 try {
145 while (itr.hasNext()) {
146 DefaultStream stream = (DefaultStream) itr.next().value();
147 if (stream.id() != CONNECTION_STREAM_ID) {
148
149
150
151 stream.close(itr);
152 }
153 }
154 } finally {
155 activeStreams.decrementPendingIterations();
156 }
157 } else {
158 while (itr.hasNext()) {
159 Http2Stream stream = itr.next().value();
160 if (stream.id() != CONNECTION_STREAM_ID) {
161
162
163 stream.close();
164 }
165 }
166 }
167 return closePromise;
168 }
169
170 @Override
171 public void addListener(Listener listener) {
172 listeners.add(listener);
173 }
174
175 @Override
176 public void removeListener(Listener listener) {
177 listeners.remove(listener);
178 }
179
180 @Override
181 public boolean isServer() {
182 return localEndpoint.isServer();
183 }
184
185 @Override
186 public Http2Stream connectionStream() {
187 return connectionStream;
188 }
189
190 @Override
191 public Http2Stream stream(int streamId) {
192 return streamMap.get(streamId);
193 }
194
195 @Override
196 public boolean streamMayHaveExisted(int streamId) {
197 return remoteEndpoint.mayHaveCreatedStream(streamId) || localEndpoint.mayHaveCreatedStream(streamId);
198 }
199
200 @Override
201 public int numActiveStreams() {
202 return activeStreams.size();
203 }
204
205 @Override
206 public Http2Stream forEachActiveStream(Http2StreamVisitor visitor) throws Http2Exception {
207 return activeStreams.forEachActiveStream(visitor);
208 }
209
210 @Override
211 public Endpoint<Http2LocalFlowController> local() {
212 return localEndpoint;
213 }
214
215 @Override
216 public Endpoint<Http2RemoteFlowController> remote() {
217 return remoteEndpoint;
218 }
219
220 @Override
221 public boolean goAwayReceived() {
222 return localEndpoint.lastStreamKnownByPeer >= 0;
223 }
224
225 @Override
226 public void goAwayReceived(final int lastKnownStream, long errorCode, ByteBuf debugData) throws Http2Exception {
227 if (localEndpoint.lastStreamKnownByPeer() >= 0 && localEndpoint.lastStreamKnownByPeer() < lastKnownStream) {
228 throw connectionError(PROTOCOL_ERROR, "lastStreamId MUST NOT increase. Current value: %d new value: %d",
229 localEndpoint.lastStreamKnownByPeer(), lastKnownStream);
230 }
231
232 localEndpoint.lastStreamKnownByPeer(lastKnownStream);
233 for (int i = 0; i < listeners.size(); ++i) {
234 try {
235 listeners.get(i).onGoAwayReceived(lastKnownStream, errorCode, debugData);
236 } catch (Throwable cause) {
237 logger.error("Caught Throwable from listener onGoAwayReceived.", cause);
238 }
239 }
240
241 closeStreamsGreaterThanLastKnownStreamId(lastKnownStream, localEndpoint);
242 }
243
244 @Override
245 public boolean goAwaySent() {
246 return remoteEndpoint.lastStreamKnownByPeer >= 0;
247 }
248
249 @Override
250 public boolean goAwaySent(final int lastKnownStream, long errorCode, ByteBuf debugData) throws Http2Exception {
251 if (remoteEndpoint.lastStreamKnownByPeer() >= 0) {
252
253
254 if (lastKnownStream == remoteEndpoint.lastStreamKnownByPeer()) {
255 return false;
256 }
257 if (lastKnownStream > remoteEndpoint.lastStreamKnownByPeer()) {
258 throw connectionError(PROTOCOL_ERROR, "Last stream identifier must not increase between " +
259 "sending multiple GOAWAY frames (was '%d', is '%d').",
260 remoteEndpoint.lastStreamKnownByPeer(), lastKnownStream);
261 }
262 }
263
264 remoteEndpoint.lastStreamKnownByPeer(lastKnownStream);
265 for (int i = 0; i < listeners.size(); ++i) {
266 try {
267 listeners.get(i).onGoAwaySent(lastKnownStream, errorCode, debugData);
268 } catch (Throwable cause) {
269 logger.error("Caught Throwable from listener onGoAwaySent.", cause);
270 }
271 }
272
273 closeStreamsGreaterThanLastKnownStreamId(lastKnownStream, remoteEndpoint);
274 return true;
275 }
276
277 private void closeStreamsGreaterThanLastKnownStreamId(final int lastKnownStream,
278 final DefaultEndpoint<?> endpoint) throws Http2Exception {
279 forEachActiveStream(new Http2StreamVisitor() {
280 @Override
281 public boolean visit(Http2Stream stream) {
282 if (stream.id() > lastKnownStream && endpoint.isValidStreamId(stream.id())) {
283 stream.close();
284 }
285 return true;
286 }
287 });
288 }
289
290
291
292
293 private boolean isStreamMapEmpty() {
294 return streamMap.size() == 1;
295 }
296
297
298
299
300
301
302
303 void removeStream(DefaultStream stream, Iterator<?> itr) {
304 final boolean removed;
305 if (itr == null) {
306 removed = streamMap.remove(stream.id()) != null;
307 } else {
308 itr.remove();
309 removed = true;
310 }
311
312 if (removed) {
313 for (int i = 0; i < listeners.size(); i++) {
314 try {
315 listeners.get(i).onStreamRemoved(stream);
316 } catch (Throwable cause) {
317 logger.error("Caught Throwable from listener onStreamRemoved.", cause);
318 }
319 }
320
321 if (closePromise != null && isStreamMapEmpty()) {
322 closePromise.trySuccess(null);
323 }
324 }
325 }
326
327 static State activeState(int streamId, State initialState, boolean isLocal, boolean halfClosed)
328 throws Http2Exception {
329 switch (initialState) {
330 case IDLE:
331 return halfClosed ? isLocal ? HALF_CLOSED_LOCAL : HALF_CLOSED_REMOTE : OPEN;
332 case RESERVED_LOCAL:
333 return HALF_CLOSED_REMOTE;
334 case RESERVED_REMOTE:
335 return HALF_CLOSED_LOCAL;
336 default:
337 throw streamError(streamId, PROTOCOL_ERROR, "Attempting to open a stream in an invalid state: "
338 + initialState);
339 }
340 }
341
342 void notifyHalfClosed(Http2Stream stream) {
343 for (int i = 0; i < listeners.size(); i++) {
344 try {
345 listeners.get(i).onStreamHalfClosed(stream);
346 } catch (Throwable cause) {
347 logger.error("Caught Throwable from listener onStreamHalfClosed.", cause);
348 }
349 }
350 }
351
352 void notifyClosed(Http2Stream stream) {
353 for (int i = 0; i < listeners.size(); i++) {
354 try {
355 listeners.get(i).onStreamClosed(stream);
356 } catch (Throwable cause) {
357 logger.error("Caught Throwable from listener onStreamClosed.", cause);
358 }
359 }
360 }
361
362 @Override
363 public PropertyKey newKey() {
364 return propertyKeyRegistry.newKey();
365 }
366
367
368
369
370
371
372 final DefaultPropertyKey verifyKey(DefaultPropertyKey key) {
373 return key.verifyConnection(this);
374 }
375
376
377
378
379 private class DefaultStream implements Http2Stream {
380 private static final byte META_STATE_SENT_RST = 1;
381 private static final byte META_STATE_SENT_HEADERS = 1 << 1;
382 private static final byte META_STATE_SENT_TRAILERS = 1 << 2;
383 private static final byte META_STATE_SENT_PUSHPROMISE = 1 << 3;
384 private static final byte META_STATE_RECV_HEADERS = 1 << 4;
385 private static final byte META_STATE_RECV_TRAILERS = 1 << 5;
386 private final int id;
387 private final long identity;
388 private final PropertyMap properties = new PropertyMap();
389 private State state;
390 private byte metaState;
391
392 DefaultStream(long identity, int id, State state) {
393 this.identity = identity;
394 this.id = id;
395 this.state = state;
396 }
397
398 @Override
399 public final int id() {
400 return id;
401 }
402
403 @Override
404 public final State state() {
405 return state;
406 }
407
408 @Override
409 public boolean isResetSent() {
410 return (metaState & META_STATE_SENT_RST) != 0;
411 }
412
413 @Override
414 public Http2Stream resetSent() {
415 metaState |= META_STATE_SENT_RST;
416 return this;
417 }
418
419 @Override
420 public Http2Stream headersSent(boolean isInformational) {
421 if (!isInformational) {
422 metaState |= isHeadersSent() ? META_STATE_SENT_TRAILERS : META_STATE_SENT_HEADERS;
423 }
424 return this;
425 }
426
427 @Override
428 public boolean isHeadersSent() {
429 return (metaState & META_STATE_SENT_HEADERS) != 0;
430 }
431
432 @Override
433 public boolean isTrailersSent() {
434 return (metaState & META_STATE_SENT_TRAILERS) != 0;
435 }
436
437 @Override
438 public Http2Stream headersReceived(boolean isInformational) {
439 if (!isInformational) {
440 metaState |= isHeadersReceived() ? META_STATE_RECV_TRAILERS : META_STATE_RECV_HEADERS;
441 }
442 return this;
443 }
444
445 @Override
446 public boolean isHeadersReceived() {
447 return (metaState & META_STATE_RECV_HEADERS) != 0;
448 }
449
450 @Override
451 public boolean isTrailersReceived() {
452 return (metaState & META_STATE_RECV_TRAILERS) != 0;
453 }
454
455 @Override
456 public Http2Stream pushPromiseSent() {
457 metaState |= META_STATE_SENT_PUSHPROMISE;
458 return this;
459 }
460
461 @Override
462 public boolean isPushPromiseSent() {
463 return (metaState & META_STATE_SENT_PUSHPROMISE) != 0;
464 }
465
466 @Override
467 public final <V> V setProperty(PropertyKey key, V value) {
468 checkNotNull(key, "key");
469 if (key instanceof DefaultPropertyKey) {
470 return properties.add(verifyKey((DefaultPropertyKey) key), value);
471 }
472 if (key instanceof Http2ConnectionPropertyKeys.GlobalPropertyKey) {
473 return properties.addGlobal((Http2ConnectionPropertyKeys.GlobalPropertyKey) key, value);
474 }
475 throw new IllegalArgumentException("Unsupported key: " + key);
476 }
477
478 @Override
479 public final <V> V getProperty(PropertyKey key) {
480 checkNotNull(key, "key");
481 if (key instanceof DefaultPropertyKey) {
482 return properties.get(verifyKey((DefaultPropertyKey) key));
483 }
484 if (key instanceof Http2ConnectionPropertyKeys.GlobalPropertyKey) {
485 return properties.getGlobal((Http2ConnectionPropertyKeys.GlobalPropertyKey) key);
486 }
487 throw new IllegalArgumentException("Unsupported key: " + key);
488 }
489
490 @Override
491 public final <V> V removeProperty(PropertyKey key) {
492 checkNotNull(key, "key");
493 if (key instanceof DefaultPropertyKey) {
494 return properties.remove(verifyKey((DefaultPropertyKey) key));
495 }
496 if (key instanceof Http2ConnectionPropertyKeys.GlobalPropertyKey) {
497 return properties.removeGlobal((Http2ConnectionPropertyKeys.GlobalPropertyKey) key);
498 }
499 throw new IllegalArgumentException("Unsupported key: " + key);
500 }
501
502 @Override
503 public Http2Stream open(boolean halfClosed) throws Http2Exception {
504 state = activeState(id, state, isLocal(), halfClosed);
505 final DefaultEndpoint<? extends Http2FlowController> endpoint = createdBy();
506 if (!endpoint.canOpenStream()) {
507 throw connectionError(PROTOCOL_ERROR, "Maximum active streams violated for this endpoint: " +
508 endpoint.maxActiveStreams());
509 }
510
511 activate();
512 return this;
513 }
514
515 void activate() {
516
517
518 if (state == HALF_CLOSED_LOCAL) {
519 headersSent( false);
520 } else if (state == HALF_CLOSED_REMOTE) {
521 headersReceived( false);
522 }
523 activeStreams.activate(this);
524 }
525
526 Http2Stream close(Iterator<?> itr) {
527 if (state == CLOSED) {
528 return this;
529 }
530
531 state = CLOSED;
532
533 --createdBy().numStreams;
534 activeStreams.deactivate(this, itr);
535 return this;
536 }
537
538 @Override
539 public Http2Stream close() {
540 return close(null);
541 }
542
543 @Override
544 public Http2Stream closeLocalSide() {
545 switch (state) {
546 case OPEN:
547 state = HALF_CLOSED_LOCAL;
548 notifyHalfClosed(this);
549 break;
550 case HALF_CLOSED_LOCAL:
551 break;
552 default:
553 close();
554 break;
555 }
556 return this;
557 }
558
559 @Override
560 public Http2Stream closeRemoteSide() {
561 switch (state) {
562 case OPEN:
563 state = HALF_CLOSED_REMOTE;
564 notifyHalfClosed(this);
565 break;
566 case HALF_CLOSED_REMOTE:
567 break;
568 default:
569 close();
570 break;
571 }
572 return this;
573 }
574
575 DefaultEndpoint<? extends Http2FlowController> createdBy() {
576 return localEndpoint.isValidStreamId(id) ? localEndpoint : remoteEndpoint;
577 }
578
579 final boolean isLocal() {
580 return localEndpoint.isValidStreamId(id);
581 }
582
583
584
585
586 private class PropertyMap {
587 Object[] values = EmptyArrays.EMPTY_OBJECTS;
588
589
590
591
592
593 Object[] globalValues = EmptyArrays.EMPTY_OBJECTS;
594
595 <V> V add(DefaultPropertyKey key, V value) {
596 resizeIfNecessary(key.index);
597 @SuppressWarnings("unchecked")
598 V prevValue = (V) values[key.index];
599 values[key.index] = value;
600 return prevValue;
601 }
602
603 @SuppressWarnings("unchecked")
604 <V> V get(DefaultPropertyKey key) {
605 if (key.index >= values.length) {
606 return null;
607 }
608 return (V) values[key.index];
609 }
610
611 @SuppressWarnings("unchecked")
612 <V> V remove(DefaultPropertyKey key) {
613 V prevValue = null;
614 if (key.index < values.length) {
615 prevValue = (V) values[key.index];
616 values[key.index] = null;
617 }
618 return prevValue;
619 }
620
621 void resizeIfNecessary(int index) {
622 if (index >= values.length) {
623 values = Arrays.copyOf(values, propertyKeyRegistry.size());
624 }
625 }
626
627 <V> V addGlobal(Http2ConnectionPropertyKeys.GlobalPropertyKey key, V value) {
628 resizeGlobalIfNecessary(key.index);
629 @SuppressWarnings("unchecked")
630 V prevValue = (V) globalValues[key.index];
631 globalValues[key.index] = value;
632 return prevValue;
633 }
634
635 @SuppressWarnings("unchecked")
636 <V> V getGlobal(Http2ConnectionPropertyKeys.GlobalPropertyKey key) {
637 if (key.index >= globalValues.length) {
638 return null;
639 }
640 return (V) globalValues[key.index];
641 }
642
643 @SuppressWarnings("unchecked")
644 <V> V removeGlobal(Http2ConnectionPropertyKeys.GlobalPropertyKey key) {
645 V prevValue = null;
646 if (key.index < globalValues.length) {
647 prevValue = (V) globalValues[key.index];
648 globalValues[key.index] = null;
649 }
650 return prevValue;
651 }
652
653 void resizeGlobalIfNecessary(int index) {
654 if (index >= globalValues.length) {
655 globalValues = Arrays.copyOf(globalValues, Http2ConnectionPropertyKeys.keyCount());
656 }
657 }
658 }
659
660 @Override
661 public boolean equals(final Object obj) {
662 return super.equals(obj);
663 }
664
665 @Override
666 public int hashCode() {
667 long value = identity;
668 if (value == 0) {
669 return System.identityHashCode(this);
670 }
671 return (int) (value ^ (value >>> 32));
672 }
673
674 @Override
675 public String toString() {
676 return getClass().getSimpleName() +
677 "{id=" + id +
678 ", state=" + state +
679 ", metaState=" + metaState +
680 '}';
681 }
682 }
683
684
685
686
687 private final class ConnectionStream extends DefaultStream {
688 ConnectionStream() {
689 super(0, CONNECTION_STREAM_ID, IDLE);
690 }
691
692 @Override
693 public boolean isResetSent() {
694 return false;
695 }
696
697 @Override
698 DefaultEndpoint<? extends Http2FlowController> createdBy() {
699 return null;
700 }
701
702 @Override
703 public Http2Stream resetSent() {
704 throw new UnsupportedOperationException();
705 }
706
707 @Override
708 public Http2Stream open(boolean halfClosed) {
709 throw new UnsupportedOperationException();
710 }
711
712 @Override
713 public Http2Stream close() {
714 throw new UnsupportedOperationException();
715 }
716
717 @Override
718 public Http2Stream closeLocalSide() {
719 throw new UnsupportedOperationException();
720 }
721
722 @Override
723 public Http2Stream closeRemoteSide() {
724 throw new UnsupportedOperationException();
725 }
726
727 @Override
728 public Http2Stream headersSent(boolean isInformational) {
729 throw new UnsupportedOperationException();
730 }
731
732 @Override
733 public boolean isHeadersSent() {
734 throw new UnsupportedOperationException();
735 }
736
737 @Override
738 public Http2Stream pushPromiseSent() {
739 throw new UnsupportedOperationException();
740 }
741
742 @Override
743 public boolean isPushPromiseSent() {
744 throw new UnsupportedOperationException();
745 }
746 }
747
748
749
750
751 private final class DefaultEndpoint<F extends Http2FlowController> implements Endpoint<F> {
752 private final boolean server;
753
754
755
756 private long lastCreatedStreamIdentity;
757
758
759
760
761
762 private int nextStreamIdToCreate;
763
764
765
766
767
768
769 private int nextReservationStreamId;
770 private int lastStreamKnownByPeer = -1;
771 private boolean pushToAllowed;
772 private F flowController;
773 private int maxStreams;
774 private int maxActiveStreams;
775 private final int maxReservedStreams;
776
777 int numActiveStreams;
778 int numStreams;
779
780 DefaultEndpoint(boolean server, int maxReservedStreams) {
781 this.lastCreatedStreamIdentity = 0;
782 this.server = server;
783
784
785
786
787
788 if (server) {
789 nextStreamIdToCreate = 2;
790 nextReservationStreamId = 0;
791 } else {
792 nextStreamIdToCreate = 1;
793
794 nextReservationStreamId = 1;
795 }
796
797
798 pushToAllowed = !server;
799 maxActiveStreams = MAX_VALUE;
800 this.maxReservedStreams = checkPositiveOrZero(maxReservedStreams, "maxReservedStreams");
801 updateMaxStreams();
802 }
803
804 @Override
805 public int incrementAndGetNextStreamId() {
806 return nextReservationStreamId >= 0 ? nextReservationStreamId += 2 : nextReservationStreamId;
807 }
808
809 private void incrementExpectedStreamId(int streamId) {
810 if (streamId > nextReservationStreamId && nextReservationStreamId >= 0) {
811 nextReservationStreamId = streamId;
812 }
813 nextStreamIdToCreate = streamId + 2;
814 ++numStreams;
815 }
816
817 @Override
818 public boolean isValidStreamId(int streamId) {
819 return streamId > 0 && server == ((streamId & 1) == 0);
820 }
821
822 @Override
823 public boolean mayHaveCreatedStream(int streamId) {
824 return isValidStreamId(streamId) && streamId <= lastStreamCreated();
825 }
826
827 @Override
828 public boolean canOpenStream() {
829 return numActiveStreams < maxActiveStreams;
830 }
831
832 @Override
833 public DefaultStream createStream(int streamId, boolean halfClosed) throws Http2Exception {
834 State state = activeState(streamId, IDLE, isLocal(), halfClosed);
835
836 checkNewStreamAllowed(streamId, state);
837
838 lastCreatedStreamIdentity++;
839
840
841 DefaultStream stream = new DefaultStream(lastCreatedStreamIdentity, streamId, state);
842
843 incrementExpectedStreamId(streamId);
844
845 addStream(stream);
846
847 stream.activate();
848 return stream;
849 }
850
851 @Override
852 public boolean created(Http2Stream stream) {
853 return stream instanceof DefaultStream && ((DefaultStream) stream).createdBy() == this;
854 }
855
856 @Override
857 public boolean isServer() {
858 return server;
859 }
860
861 @Override
862 public DefaultStream reservePushStream(int streamId, Http2Stream parent) throws Http2Exception {
863 if (parent == null) {
864 throw connectionError(PROTOCOL_ERROR, "Parent stream missing");
865 }
866 if (isLocal() ? !parent.state().localSideOpen() : !parent.state().remoteSideOpen()) {
867 throw connectionError(PROTOCOL_ERROR, "Stream %d is not open for sending push promise", parent.id());
868 }
869 if (!opposite().allowPushTo()) {
870 throw connectionError(PROTOCOL_ERROR, "Server push not allowed to opposite endpoint");
871 }
872 State state = isLocal() ? RESERVED_LOCAL : RESERVED_REMOTE;
873 checkNewStreamAllowed(streamId, state);
874
875 lastCreatedStreamIdentity++;
876
877
878 DefaultStream stream = new DefaultStream(lastCreatedStreamIdentity, streamId, state);
879
880 incrementExpectedStreamId(streamId);
881
882
883 addStream(stream);
884 return stream;
885 }
886
887 private void addStream(DefaultStream stream) {
888
889 streamMap.put(stream.id(), stream);
890
891
892 for (int i = 0; i < listeners.size(); i++) {
893 try {
894 listeners.get(i).onStreamAdded(stream);
895 } catch (Throwable cause) {
896 logger.error("Caught Throwable from listener onStreamAdded.", cause);
897 }
898 }
899 }
900
901 @Override
902 public void allowPushTo(boolean allow) {
903 if (allow && server) {
904 throw new IllegalArgumentException("Servers do not allow push");
905 }
906 pushToAllowed = allow;
907 }
908
909 @Override
910 public boolean allowPushTo() {
911 return pushToAllowed;
912 }
913
914 @Override
915 public int numActiveStreams() {
916 return numActiveStreams;
917 }
918
919 @Override
920 public int maxActiveStreams() {
921 return maxActiveStreams;
922 }
923
924 @Override
925 public void maxActiveStreams(int maxActiveStreams) {
926 this.maxActiveStreams = maxActiveStreams;
927 updateMaxStreams();
928 }
929
930 @Override
931 public int lastStreamCreated() {
932
933
934
935
936 return Math.max(0, nextStreamIdToCreate - 2);
937 }
938
939 @Override
940 public int lastStreamKnownByPeer() {
941 return lastStreamKnownByPeer;
942 }
943
944 private void lastStreamKnownByPeer(int lastKnownStream) {
945 lastStreamKnownByPeer = lastKnownStream;
946 }
947
948 @Override
949 public F flowController() {
950 return flowController;
951 }
952
953 @Override
954 public void flowController(F flowController) {
955 this.flowController = checkNotNull(flowController, "flowController");
956 }
957
958 @Override
959 public Endpoint<? extends Http2FlowController> opposite() {
960 return isLocal() ? remoteEndpoint : localEndpoint;
961 }
962
963 private void updateMaxStreams() {
964 maxStreams = (int) Math.min(MAX_VALUE, (long) maxActiveStreams + maxReservedStreams);
965 }
966
967 private void checkNewStreamAllowed(int streamId, State state) throws Http2Exception {
968 assert state != IDLE;
969 if (lastStreamKnownByPeer >= 0 && streamId > lastStreamKnownByPeer) {
970 throw streamError(streamId, REFUSED_STREAM,
971 "Cannot create stream %d greater than Last-Stream-ID %d from GOAWAY.",
972 streamId, lastStreamKnownByPeer);
973 }
974 if (!isValidStreamId(streamId)) {
975 if (streamId < 0) {
976 throw new Http2NoMoreStreamIdsException();
977 }
978 throw connectionError(PROTOCOL_ERROR, "Request stream %d is not correct for %s connection", streamId,
979 server ? "server" : "client");
980 }
981
982
983 if (streamId < nextStreamIdToCreate) {
984 throw closedStreamError(PROTOCOL_ERROR, "Request stream %d is behind the next expected stream %d",
985 streamId, nextStreamIdToCreate);
986 }
987 if (nextStreamIdToCreate <= 0) {
988
989
990 throw new Http2Exception(REFUSED_STREAM, "Stream IDs are exhausted for this endpoint.",
991 Http2Exception.ShutdownHint.GRACEFUL_SHUTDOWN);
992 }
993 boolean isReserved = state == RESERVED_LOCAL || state == RESERVED_REMOTE;
994 if (!isReserved && !canOpenStream() || isReserved && numStreams >= maxStreams) {
995 throw streamError(streamId, REFUSED_STREAM, "Maximum active streams violated for this endpoint: " +
996 (isReserved ? maxStreams : maxActiveStreams));
997 }
998 if (isClosed()) {
999 throw connectionError(INTERNAL_ERROR, "Attempted to create stream id %d after connection was closed",
1000 streamId);
1001 }
1002 }
1003
1004 private boolean isLocal() {
1005 return this == localEndpoint;
1006 }
1007 }
1008
1009
1010
1011
1012
1013 interface Event {
1014
1015
1016
1017
1018
1019
1020 void process();
1021 }
1022
1023
1024
1025
1026
1027 private final class ActiveStreams {
1028 private final List<Listener> listeners;
1029 private final Queue<Event> pendingEvents = new ArrayDeque<Event>(4);
1030 private final Set<Http2Stream> streams = new LinkedHashSet<Http2Stream>();
1031 private int pendingIterations;
1032
1033 ActiveStreams(List<Listener> listeners) {
1034 this.listeners = listeners;
1035 }
1036
1037 public int size() {
1038 return streams.size();
1039 }
1040
1041 public void activate(final DefaultStream stream) {
1042 if (allowModifications()) {
1043 addToActiveStreams(stream);
1044 } else {
1045 pendingEvents.add(new Event() {
1046 @Override
1047 public void process() {
1048 addToActiveStreams(stream);
1049 }
1050 });
1051 }
1052 }
1053
1054 public void deactivate(final DefaultStream stream, final Iterator<?> itr) {
1055 if (allowModifications() || itr != null) {
1056 removeFromActiveStreams(stream, itr);
1057 } else {
1058 pendingEvents.add(new Event() {
1059 @Override
1060 public void process() {
1061 removeFromActiveStreams(stream, itr);
1062 }
1063 });
1064 }
1065 }
1066
1067 public Http2Stream forEachActiveStream(Http2StreamVisitor visitor) throws Http2Exception {
1068 incrementPendingIterations();
1069 try {
1070 for (Http2Stream stream : streams) {
1071 if (!visitor.visit(stream)) {
1072 return stream;
1073 }
1074 }
1075 return null;
1076 } finally {
1077 decrementPendingIterations();
1078 }
1079 }
1080
1081 void addToActiveStreams(DefaultStream stream) {
1082 if (streams.add(stream)) {
1083
1084 stream.createdBy().numActiveStreams++;
1085
1086 for (int i = 0; i < listeners.size(); i++) {
1087 try {
1088 listeners.get(i).onStreamActive(stream);
1089 } catch (Throwable cause) {
1090 logger.error("Caught Throwable from listener onStreamActive.", cause);
1091 }
1092 }
1093 }
1094 }
1095
1096 void removeFromActiveStreams(DefaultStream stream, Iterator<?> itr) {
1097 if (streams.remove(stream)) {
1098
1099 stream.createdBy().numActiveStreams--;
1100 notifyClosed(stream);
1101 }
1102 removeStream(stream, itr);
1103 }
1104
1105 boolean allowModifications() {
1106 return pendingIterations == 0;
1107 }
1108
1109 void incrementPendingIterations() {
1110 ++pendingIterations;
1111 }
1112
1113 void decrementPendingIterations() {
1114 --pendingIterations;
1115 if (allowModifications()) {
1116 for (;;) {
1117 Event event = pendingEvents.poll();
1118 if (event == null) {
1119 break;
1120 }
1121 try {
1122 event.process();
1123 } catch (Throwable cause) {
1124 logger.error("Caught Throwable while processing pending ActiveStreams$Event.", cause);
1125 }
1126 }
1127 }
1128 }
1129 }
1130
1131
1132
1133
1134 final class DefaultPropertyKey implements PropertyKey {
1135 final int index;
1136
1137 DefaultPropertyKey(int index) {
1138 this.index = index;
1139 }
1140
1141 DefaultPropertyKey verifyConnection(Http2Connection connection) {
1142 if (connection != DefaultHttp2Connection.this) {
1143 throw new IllegalArgumentException("Using a key that was not created by this connection");
1144 }
1145 return this;
1146 }
1147 }
1148
1149
1150
1151
1152 private final class PropertyKeyRegistry {
1153
1154
1155
1156
1157
1158 final List<DefaultPropertyKey> keys = new ArrayList<DefaultPropertyKey>(4);
1159
1160
1161
1162
1163 DefaultPropertyKey newKey() {
1164 DefaultPropertyKey key = new DefaultPropertyKey(keys.size());
1165 keys.add(key);
1166 return key;
1167 }
1168
1169 int size() {
1170 return keys.size();
1171 }
1172 }
1173 }