1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.jetty.http2;
20
21 import java.io.IOException;
22 import java.nio.channels.ClosedChannelException;
23 import java.nio.charset.StandardCharsets;
24 import java.util.ArrayList;
25 import java.util.Collection;
26 import java.util.Collections;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.concurrent.ConcurrentHashMap;
30 import java.util.concurrent.ConcurrentMap;
31 import java.util.concurrent.TimeUnit;
32 import java.util.concurrent.TimeoutException;
33 import java.util.concurrent.atomic.AtomicInteger;
34 import java.util.concurrent.atomic.AtomicReference;
35
36 import org.eclipse.jetty.http2.api.Session;
37 import org.eclipse.jetty.http2.api.Stream;
38 import org.eclipse.jetty.http2.frames.DataFrame;
39 import org.eclipse.jetty.http2.frames.DisconnectFrame;
40 import org.eclipse.jetty.http2.frames.Frame;
41 import org.eclipse.jetty.http2.frames.FrameType;
42 import org.eclipse.jetty.http2.frames.GoAwayFrame;
43 import org.eclipse.jetty.http2.frames.HeadersFrame;
44 import org.eclipse.jetty.http2.frames.PingFrame;
45 import org.eclipse.jetty.http2.frames.PriorityFrame;
46 import org.eclipse.jetty.http2.frames.PushPromiseFrame;
47 import org.eclipse.jetty.http2.frames.ResetFrame;
48 import org.eclipse.jetty.http2.frames.SettingsFrame;
49 import org.eclipse.jetty.http2.frames.WindowUpdateFrame;
50 import org.eclipse.jetty.http2.generator.Generator;
51 import org.eclipse.jetty.http2.parser.Parser;
52 import org.eclipse.jetty.io.ByteBufferPool;
53 import org.eclipse.jetty.io.EndPoint;
54 import org.eclipse.jetty.util.Atomics;
55 import org.eclipse.jetty.util.Callback;
56 import org.eclipse.jetty.util.CountingCallback;
57 import org.eclipse.jetty.util.Promise;
58 import org.eclipse.jetty.util.annotation.ManagedAttribute;
59 import org.eclipse.jetty.util.annotation.ManagedObject;
60 import org.eclipse.jetty.util.component.ContainerLifeCycle;
61 import org.eclipse.jetty.util.log.Log;
62 import org.eclipse.jetty.util.log.Logger;
63 import org.eclipse.jetty.util.thread.Scheduler;
64
65 @ManagedObject
66 public abstract class HTTP2Session extends ContainerLifeCycle implements ISession, Parser.Listener
67 {
68 private static final Logger LOG = Log.getLogger(HTTP2Session.class);
69
70 private final ConcurrentMap<Integer, IStream> streams = new ConcurrentHashMap<>();
71 private final AtomicInteger streamIds = new AtomicInteger();
72 private final AtomicInteger lastStreamId = new AtomicInteger();
73 private final AtomicInteger localStreamCount = new AtomicInteger();
74 private final AtomicInteger remoteStreamCount = new AtomicInteger();
75 private final AtomicInteger sendWindow = new AtomicInteger();
76 private final AtomicInteger recvWindow = new AtomicInteger();
77 private final AtomicReference<CloseState> closed = new AtomicReference<>(CloseState.NOT_CLOSED);
78 private final Scheduler scheduler;
79 private final EndPoint endPoint;
80 private final Generator generator;
81 private final Session.Listener listener;
82 private final FlowControlStrategy flowControl;
83 private final HTTP2Flusher flusher;
84 private int maxLocalStreams;
85 private int maxRemoteStreams;
86 private long streamIdleTimeout;
87 private int initialSessionRecvWindow;
88 private boolean pushEnabled;
89 private long idleTime;
90
91 public HTTP2Session(Scheduler scheduler, EndPoint endPoint, Generator generator, Session.Listener listener, FlowControlStrategy flowControl, int initialStreamId)
92 {
93 this.scheduler = scheduler;
94 this.endPoint = endPoint;
95 this.generator = generator;
96 this.listener = listener;
97 this.flowControl = flowControl;
98 this.flusher = new HTTP2Flusher(this);
99 this.maxLocalStreams = -1;
100 this.maxRemoteStreams = -1;
101 this.streamIds.set(initialStreamId);
102 this.streamIdleTimeout = endPoint.getIdleTimeout();
103 this.sendWindow.set(FlowControlStrategy.DEFAULT_WINDOW_SIZE);
104 this.recvWindow.set(FlowControlStrategy.DEFAULT_WINDOW_SIZE);
105 this.pushEnabled = true;
106 this.idleTime = System.nanoTime();
107 }
108
109 @Override
110 protected void doStart() throws Exception
111 {
112 addBean(flowControl);
113 super.doStart();
114 }
115
116 @ManagedAttribute(value = "The flow control strategy", readonly = true)
117 public FlowControlStrategy getFlowControlStrategy()
118 {
119 return flowControl;
120 }
121
122 public int getMaxLocalStreams()
123 {
124 return maxLocalStreams;
125 }
126
127 public void setMaxLocalStreams(int maxLocalStreams)
128 {
129 this.maxLocalStreams = maxLocalStreams;
130 }
131
132 public int getMaxRemoteStreams()
133 {
134 return maxRemoteStreams;
135 }
136
137 public void setMaxRemoteStreams(int maxRemoteStreams)
138 {
139 this.maxRemoteStreams = maxRemoteStreams;
140 }
141
142 @ManagedAttribute("The stream's idle timeout")
143 public long getStreamIdleTimeout()
144 {
145 return streamIdleTimeout;
146 }
147
148 public void setStreamIdleTimeout(long streamIdleTimeout)
149 {
150 this.streamIdleTimeout = streamIdleTimeout;
151 }
152
153 @ManagedAttribute("The initial size of session's flow control receive window")
154 public int getInitialSessionRecvWindow()
155 {
156 return initialSessionRecvWindow;
157 }
158
159 public void setInitialSessionRecvWindow(int initialSessionRecvWindow)
160 {
161 this.initialSessionRecvWindow = initialSessionRecvWindow;
162 }
163
164 public EndPoint getEndPoint()
165 {
166 return endPoint;
167 }
168
169 public Generator getGenerator()
170 {
171 return generator;
172 }
173
174 @Override
175 public void onData(final DataFrame frame)
176 {
177 if (LOG.isDebugEnabled())
178 LOG.debug("Received {}", frame);
179
180 int streamId = frame.getStreamId();
181 final IStream stream = getStream(streamId);
182
183
184
185 final int flowControlLength = frame.remaining() + frame.padding();
186 flowControl.onDataReceived(this, stream, flowControlLength);
187
188 if (stream != null)
189 {
190 if (getRecvWindow() < 0)
191 {
192 close(ErrorCode.FLOW_CONTROL_ERROR.code, "session_window_exceeded", Callback.NOOP);
193 }
194 else
195 {
196 stream.process(frame, new Callback()
197 {
198 @Override
199 public void succeeded()
200 {
201 complete();
202 }
203
204 @Override
205 public void failed(Throwable x)
206 {
207
208
209 complete();
210 }
211
212 private void complete()
213 {
214 notIdle();
215 stream.notIdle();
216 flowControl.onDataConsumed(HTTP2Session.this, stream, flowControlLength);
217 }
218 });
219 }
220 }
221 else
222 {
223 if (LOG.isDebugEnabled())
224 LOG.debug("Ignoring {}, stream #{} not found", frame, streamId);
225
226
227 flowControl.onDataConsumed(this, null, flowControlLength);
228 }
229 }
230
231 @Override
232 public abstract void onHeaders(HeadersFrame frame);
233
234 @Override
235 public void onPriority(PriorityFrame frame)
236 {
237 if (LOG.isDebugEnabled())
238 LOG.debug("Received {}", frame);
239 }
240
241 @Override
242 public void onReset(ResetFrame frame)
243 {
244 if (LOG.isDebugEnabled())
245 LOG.debug("Received {}", frame);
246
247 IStream stream = getStream(frame.getStreamId());
248 if (stream != null)
249 stream.process(frame, Callback.NOOP);
250 else
251 notifyReset(this, frame);
252 }
253
254 @Override
255 public void onSettings(SettingsFrame frame)
256 {
257
258 onSettings(frame, true);
259 }
260
261 public void onSettings(SettingsFrame frame, boolean reply)
262 {
263 if (LOG.isDebugEnabled())
264 LOG.debug("Received {}", frame);
265
266 if (frame.isReply())
267 return;
268
269
270 for (Map.Entry<Integer, Integer> entry : frame.getSettings().entrySet())
271 {
272 int key = entry.getKey();
273 int value = entry.getValue();
274 switch (key)
275 {
276 case SettingsFrame.HEADER_TABLE_SIZE:
277 {
278 if (LOG.isDebugEnabled())
279 LOG.debug("Update HPACK header table size to {}", value);
280 generator.setHeaderTableSize(value);
281 break;
282 }
283 case SettingsFrame.ENABLE_PUSH:
284 {
285
286 if (value != 0 && value != 1)
287 {
288 onConnectionFailure(ErrorCode.PROTOCOL_ERROR.code, "invalid_settings_enable_push");
289 return;
290 }
291 pushEnabled = value == 1;
292 break;
293 }
294 case SettingsFrame.MAX_CONCURRENT_STREAMS:
295 {
296 maxLocalStreams = value;
297 if (LOG.isDebugEnabled())
298 LOG.debug("Update max local concurrent streams to {}", maxLocalStreams);
299 break;
300 }
301 case SettingsFrame.INITIAL_WINDOW_SIZE:
302 {
303 if (LOG.isDebugEnabled())
304 LOG.debug("Update initial window size to {}", value);
305 flowControl.updateInitialStreamWindow(this, value, false);
306 break;
307 }
308 case SettingsFrame.MAX_FRAME_SIZE:
309 {
310 if (LOG.isDebugEnabled())
311 LOG.debug("Update max frame size to {}", value);
312
313 if (value < Frame.DEFAULT_MAX_LENGTH || value > Frame.MAX_MAX_LENGTH)
314 {
315 onConnectionFailure(ErrorCode.PROTOCOL_ERROR.code, "invalid_settings_max_frame_size");
316 return;
317 }
318 generator.setMaxFrameSize(value);
319 break;
320 }
321 case SettingsFrame.MAX_HEADER_LIST_SIZE:
322 {
323
324 LOG.warn("NOT IMPLEMENTED max header list size to {}", value);
325 break;
326 }
327 default:
328 {
329 LOG.debug("Unknown setting {}:{}", key, value);
330 break;
331 }
332 }
333 }
334 notifySettings(this, frame);
335
336 if (reply)
337 {
338 SettingsFrame replyFrame = new SettingsFrame(Collections.<Integer, Integer>emptyMap(), true);
339 settings(replyFrame, Callback.NOOP);
340 }
341 }
342
343 @Override
344 public void onPing(PingFrame frame)
345 {
346 if (LOG.isDebugEnabled())
347 LOG.debug("Received {}", frame);
348
349 if (frame.isReply())
350 {
351 notifyPing(this, frame);
352 }
353 else
354 {
355 PingFrame reply = new PingFrame(frame.getPayload(), true);
356 control(null, Callback.NOOP, reply);
357 }
358 }
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377 @Override
378 public void onGoAway(final GoAwayFrame frame)
379 {
380 if (LOG.isDebugEnabled())
381 LOG.debug("Received {}", frame);
382
383 while (true)
384 {
385 CloseState current = closed.get();
386 switch (current)
387 {
388 case NOT_CLOSED:
389 {
390 if (closed.compareAndSet(current, CloseState.REMOTELY_CLOSED))
391 {
392
393
394 control(null, new Callback()
395 {
396 @Override
397 public void succeeded()
398 {
399 notifyClose(HTTP2Session.this, frame);
400 }
401
402 @Override
403 public void failed(Throwable x)
404 {
405 notifyClose(HTTP2Session.this, frame);
406 }
407 }, new DisconnectFrame());
408 return;
409 }
410 break;
411 }
412 default:
413 {
414 if (LOG.isDebugEnabled())
415 LOG.debug("Ignored {}, already closed", frame);
416 return;
417 }
418 }
419 }
420 }
421
422 @Override
423 public void onWindowUpdate(WindowUpdateFrame frame)
424 {
425 if (LOG.isDebugEnabled())
426 LOG.debug("Received {}", frame);
427
428 int streamId = frame.getStreamId();
429 if (streamId > 0)
430 {
431 IStream stream = getStream(streamId);
432 if (stream != null)
433 {
434 stream.process(frame, Callback.NOOP);
435 onWindowUpdate(stream, frame);
436 }
437 }
438 else
439 {
440 onWindowUpdate(null, frame);
441 }
442 }
443
444 @Override
445 public void onConnectionFailure(int error, String reason)
446 {
447 close(error, reason, Callback.NOOP);
448 notifyFailure(this, new IOException(String.format("%d/%s", error, reason)));
449 }
450
451 @Override
452 public void newStream(HeadersFrame frame, Promise<Stream> promise, Stream.Listener listener)
453 {
454
455
456 boolean queued;
457 synchronized (this)
458 {
459 int streamId = frame.getStreamId();
460 if (streamId <= 0)
461 {
462 streamId = streamIds.getAndAdd(2);
463 PriorityFrame priority = frame.getPriority();
464 priority = priority == null ? null : new PriorityFrame(streamId, priority.getParentStreamId(),
465 priority.getWeight(), priority.isExclusive());
466 frame = new HeadersFrame(streamId, frame.getMetaData(), priority, frame.isEndStream());
467 }
468 final IStream stream = createLocalStream(streamId, promise);
469 if (stream == null)
470 return;
471 stream.setListener(listener);
472
473 ControlEntry entry = new ControlEntry(frame, stream, new PromiseCallback<>(promise, stream));
474 queued = flusher.append(entry);
475 }
476
477 if (queued)
478 flusher.iterate();
479 }
480
481 @Override
482 public int priority(PriorityFrame frame, Callback callback)
483 {
484 int streamId = frame.getStreamId();
485 IStream stream = streams.get(streamId);
486 if (stream == null)
487 {
488 streamId = streamIds.getAndAdd(2);
489 frame = new PriorityFrame(streamId, frame.getParentStreamId(),
490 frame.getWeight(), frame.isExclusive());
491 }
492 control(stream, callback, frame);
493 return streamId;
494 }
495
496 @Override
497 public void push(IStream stream, Promise<Stream> promise, PushPromiseFrame frame, Stream.Listener listener)
498 {
499
500
501 boolean queued;
502 synchronized (this)
503 {
504 int streamId = streamIds.getAndAdd(2);
505 frame = new PushPromiseFrame(frame.getStreamId(), streamId, frame.getMetaData());
506
507 final IStream pushStream = createLocalStream(streamId, promise);
508 if (pushStream == null)
509 return;
510 pushStream.setListener(listener);
511
512 ControlEntry entry = new ControlEntry(frame, pushStream, new PromiseCallback<>(promise, pushStream));
513 queued = flusher.append(entry);
514 }
515
516 if (queued)
517 flusher.iterate();
518 }
519
520
521 @Override
522 public void settings(SettingsFrame frame, Callback callback)
523 {
524 control(null, callback, frame);
525 }
526
527 @Override
528 public void ping(PingFrame frame, Callback callback)
529 {
530 if (frame.isReply())
531 callback.failed(new IllegalArgumentException());
532 else
533 control(null, callback, frame);
534 }
535
536 protected void reset(ResetFrame frame, Callback callback)
537 {
538 control(getStream(frame.getStreamId()), callback, frame);
539 }
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564 @Override
565 public boolean close(int error, String reason, Callback callback)
566 {
567 while (true)
568 {
569 CloseState current = closed.get();
570 switch (current)
571 {
572 case NOT_CLOSED:
573 {
574 if (closed.compareAndSet(current, CloseState.LOCALLY_CLOSED))
575 {
576 byte[] payload = null;
577 if (reason != null)
578 {
579
580 reason = reason.substring(0, Math.min(reason.length(), 32));
581 payload = reason.getBytes(StandardCharsets.UTF_8);
582 }
583 GoAwayFrame frame = new GoAwayFrame(lastStreamId.get(), error, payload);
584 control(null, callback, frame);
585 return true;
586 }
587 break;
588 }
589 default:
590 {
591 if (LOG.isDebugEnabled())
592 LOG.debug("Ignoring close {}/{}, already closed", error, reason);
593 callback.succeeded();
594 return false;
595 }
596 }
597 }
598 }
599
600 @Override
601 public boolean isClosed()
602 {
603 return closed.get() != CloseState.NOT_CLOSED;
604 }
605
606 private void control(IStream stream, Callback callback, Frame frame)
607 {
608 frames(stream, callback, frame, Frame.EMPTY_ARRAY);
609 }
610
611 @Override
612 public void frames(IStream stream, Callback callback, Frame frame, Frame... frames)
613 {
614
615
616
617
618
619 int length = frames.length;
620 if (length == 0)
621 {
622 frame(new ControlEntry(frame, stream, callback), true);
623 }
624 else
625 {
626 callback = new CountingCallback(callback, 1 + length);
627 frame(new ControlEntry(frame, stream, callback), false);
628 for (int i = 1; i <= length; ++i)
629 frame(new ControlEntry(frames[i - 1], stream, callback), i == length);
630 }
631 }
632
633 @Override
634 public void data(IStream stream, Callback callback, DataFrame frame)
635 {
636
637 frame(new DataEntry(frame, stream, callback), true);
638 }
639
640 private void frame(HTTP2Flusher.Entry entry, boolean flush)
641 {
642 if (LOG.isDebugEnabled())
643 LOG.debug("{} {}", flush ? "Sending" : "Queueing", entry.frame);
644
645 boolean queued = entry.frame.getType() == FrameType.PING ? flusher.prepend(entry) : flusher.append(entry);
646 if (queued && flush)
647 {
648 if (entry.stream != null)
649 entry.stream.notIdle();
650 flusher.iterate();
651 }
652 }
653
654 protected IStream createLocalStream(int streamId, Promise<Stream> promise)
655 {
656 while (true)
657 {
658 int localCount = localStreamCount.get();
659 int maxCount = maxLocalStreams;
660 if (maxCount >= 0 && localCount >= maxCount)
661 {
662 promise.failed(new IllegalStateException("Max local stream count " + maxCount + " exceeded"));
663 return null;
664 }
665 if (localStreamCount.compareAndSet(localCount, localCount + 1))
666 break;
667 }
668
669 IStream stream = newStream(streamId, true);
670 if (streams.putIfAbsent(streamId, stream) == null)
671 {
672 stream.setIdleTimeout(getStreamIdleTimeout());
673 flowControl.onStreamCreated(stream);
674 if (LOG.isDebugEnabled())
675 LOG.debug("Created local {}", stream);
676 return stream;
677 }
678 else
679 {
680 promise.failed(new IllegalStateException("Duplicate stream " + streamId));
681 return null;
682 }
683 }
684
685 protected IStream createRemoteStream(int streamId)
686 {
687
688 while (true)
689 {
690 int remoteCount = remoteStreamCount.get();
691 int maxCount = getMaxRemoteStreams();
692 if (maxCount >= 0 && remoteCount >= maxCount)
693 {
694 reset(new ResetFrame(streamId, ErrorCode.REFUSED_STREAM_ERROR.code), Callback.NOOP);
695 return null;
696 }
697 if (remoteStreamCount.compareAndSet(remoteCount, remoteCount + 1))
698 break;
699 }
700
701 IStream stream = newStream(streamId, false);
702
703
704 if (streams.putIfAbsent(streamId, stream) == null)
705 {
706 updateLastStreamId(streamId);
707 stream.setIdleTimeout(getStreamIdleTimeout());
708 flowControl.onStreamCreated(stream);
709 if (LOG.isDebugEnabled())
710 LOG.debug("Created remote {}", stream);
711 return stream;
712 }
713 else
714 {
715 close(ErrorCode.PROTOCOL_ERROR.code, "duplicate_stream", Callback.NOOP);
716 return null;
717 }
718 }
719
720 protected IStream newStream(int streamId, boolean local)
721 {
722 return new HTTP2Stream(scheduler, this, streamId, local);
723 }
724
725 @Override
726 public void removeStream(IStream stream)
727 {
728 IStream removed = streams.remove(stream.getId());
729 if (removed != null)
730 {
731 assert removed == stream;
732
733 boolean local = stream.isLocal();
734 if (local)
735 localStreamCount.decrementAndGet();
736 else
737 remoteStreamCount.decrementAndGet();
738
739 flowControl.onStreamDestroyed(stream);
740
741 if (LOG.isDebugEnabled())
742 LOG.debug("Removed {} {}", local ? "local" : "remote", stream);
743 }
744 }
745
746 @Override
747 public Collection<Stream> getStreams()
748 {
749 List<Stream> result = new ArrayList<>();
750 result.addAll(streams.values());
751 return result;
752 }
753
754 @ManagedAttribute("The number of active streams")
755 public int getStreamCount()
756 {
757 return streams.size();
758 }
759
760 @Override
761 public IStream getStream(int streamId)
762 {
763 return streams.get(streamId);
764 }
765
766 @ManagedAttribute(value = "The flow control send window", readonly = true)
767 public int getSendWindow()
768 {
769 return sendWindow.get();
770 }
771
772 @ManagedAttribute(value = "The flow control receive window", readonly = true)
773 public int getRecvWindow()
774 {
775 return recvWindow.get();
776 }
777
778 @Override
779 public int updateSendWindow(int delta)
780 {
781 return sendWindow.getAndAdd(delta);
782 }
783
784 @Override
785 public int updateRecvWindow(int delta)
786 {
787 return recvWindow.getAndAdd(delta);
788 }
789
790 @Override
791 public void onWindowUpdate(IStream stream, WindowUpdateFrame frame)
792 {
793
794
795
796
797
798
799
800
801
802 flusher.window(stream, frame);
803 }
804
805 @Override
806 @ManagedAttribute(value = "Whether HTTP/2 push is enabled", readonly = true)
807 public boolean isPushEnabled()
808 {
809 return pushEnabled;
810 }
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834 @Override
835 public void onShutdown()
836 {
837 if (LOG.isDebugEnabled())
838 LOG.debug("Shutting down {}", this);
839
840 switch (closed.get())
841 {
842 case NOT_CLOSED:
843 {
844
845 if (LOG.isDebugEnabled())
846 LOG.debug("Abrupt close for {}", this);
847 abort(new ClosedChannelException());
848 break;
849 }
850 case LOCALLY_CLOSED:
851 {
852
853
854 control(null, Callback.NOOP, new DisconnectFrame());
855 break;
856 }
857 case REMOTELY_CLOSED:
858 {
859
860
861 break;
862 }
863 default:
864 {
865 break;
866 }
867 }
868 }
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891 @Override
892 public boolean onIdleTimeout()
893 {
894 switch (closed.get())
895 {
896 case NOT_CLOSED:
897 {
898 long elapsed = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - idleTime);
899 if (elapsed < endPoint.getIdleTimeout())
900 return false;
901 return notifyIdleTimeout(this);
902 }
903 case LOCALLY_CLOSED:
904 case REMOTELY_CLOSED:
905 {
906 abort(new TimeoutException("Idle timeout " + endPoint.getIdleTimeout() + " ms"));
907 return false;
908 }
909 default:
910 {
911 return false;
912 }
913 }
914 }
915
916 private void notIdle()
917 {
918 idleTime = System.nanoTime();
919 }
920
921 @Override
922 public void onFrame(Frame frame)
923 {
924 onConnectionFailure(ErrorCode.PROTOCOL_ERROR.code, "upgrade");
925 }
926
927 public void disconnect()
928 {
929 if (LOG.isDebugEnabled())
930 LOG.debug("Disconnecting {}", this);
931 endPoint.close();
932 }
933
934 private void terminate()
935 {
936 while (true)
937 {
938 CloseState current = closed.get();
939 switch (current)
940 {
941 case NOT_CLOSED:
942 case LOCALLY_CLOSED:
943 case REMOTELY_CLOSED:
944 {
945 if (closed.compareAndSet(current, CloseState.CLOSED))
946 {
947 flusher.terminate();
948 for (IStream stream : streams.values())
949 stream.close();
950 streams.clear();
951 disconnect();
952 return;
953 }
954 break;
955 }
956 default:
957 {
958 return;
959 }
960 }
961 }
962 }
963
964 protected void abort(Throwable failure)
965 {
966 terminate();
967 notifyFailure(this, failure);
968 }
969
970 public boolean isDisconnected()
971 {
972 return !endPoint.isOpen();
973 }
974
975 private void updateLastStreamId(int streamId)
976 {
977 Atomics.updateMax(lastStreamId, streamId);
978 }
979
980 protected Stream.Listener notifyNewStream(Stream stream, HeadersFrame frame)
981 {
982 try
983 {
984 return listener.onNewStream(stream, frame);
985 }
986 catch (Throwable x)
987 {
988 LOG.info("Failure while notifying listener " + listener, x);
989 return null;
990 }
991 }
992
993 protected void notifySettings(Session session, SettingsFrame frame)
994 {
995 try
996 {
997 listener.onSettings(session, frame);
998 }
999 catch (Throwable x)
1000 {
1001 LOG.info("Failure while notifying listener " + listener, x);
1002 }
1003 }
1004
1005 protected void notifyPing(Session session, PingFrame frame)
1006 {
1007 try
1008 {
1009 listener.onPing(session, frame);
1010 }
1011 catch (Throwable x)
1012 {
1013 LOG.info("Failure while notifying listener " + listener, x);
1014 }
1015 }
1016
1017 protected void notifyReset(Session session, ResetFrame frame)
1018 {
1019 try
1020 {
1021 listener.onReset(session, frame);
1022 }
1023 catch (Throwable x)
1024 {
1025 LOG.info("Failure while notifying listener " + listener, x);
1026 }
1027 }
1028
1029 protected void notifyClose(Session session, GoAwayFrame frame)
1030 {
1031 try
1032 {
1033 listener.onClose(session, frame);
1034 }
1035 catch (Throwable x)
1036 {
1037 LOG.info("Failure while notifying listener " + listener, x);
1038 }
1039 }
1040
1041 protected boolean notifyIdleTimeout(Session session)
1042 {
1043 try
1044 {
1045 return listener.onIdleTimeout(session);
1046 }
1047 catch (Throwable x)
1048 {
1049 LOG.info("Failure while notifying listener " + listener, x);
1050 return true;
1051 }
1052 }
1053
1054 protected void notifyFailure(Session session, Throwable failure)
1055 {
1056 try
1057 {
1058 listener.onFailure(session, failure);
1059 }
1060 catch (Throwable x)
1061 {
1062 LOG.info("Failure while notifying listener " + listener, x);
1063 }
1064 }
1065
1066 @Override
1067 public String toString()
1068 {
1069 return String.format("%s@%x{l:%s <-> r:%s,queueSize=%d,sendWindow=%s,recvWindow=%s,streams=%d,%s}",
1070 getClass().getSimpleName(),
1071 hashCode(),
1072 getEndPoint().getLocalAddress(),
1073 getEndPoint().getRemoteAddress(),
1074 flusher.getQueueSize(),
1075 sendWindow,
1076 recvWindow,
1077 streams.size(),
1078 closed);
1079 }
1080
1081 private class ControlEntry extends HTTP2Flusher.Entry
1082 {
1083 private ControlEntry(Frame frame, IStream stream, Callback callback)
1084 {
1085 super(frame, stream, callback);
1086 }
1087
1088 public Throwable generate(ByteBufferPool.Lease lease)
1089 {
1090 try
1091 {
1092 generator.control(lease, frame);
1093 if (LOG.isDebugEnabled())
1094 LOG.debug("Generated {}", frame);
1095 prepare();
1096 return null;
1097 }
1098 catch (Throwable x)
1099 {
1100 if (LOG.isDebugEnabled())
1101 LOG.debug("Failure generating frame " + frame, x);
1102 return x;
1103 }
1104 }
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119 private void prepare()
1120 {
1121 switch (frame.getType())
1122 {
1123 case SETTINGS:
1124 {
1125 SettingsFrame settingsFrame = (SettingsFrame)frame;
1126 Integer initialWindow = settingsFrame.getSettings().get(SettingsFrame.INITIAL_WINDOW_SIZE);
1127 if (initialWindow != null)
1128 flowControl.updateInitialStreamWindow(HTTP2Session.this, initialWindow, true);
1129 break;
1130 }
1131 default:
1132 {
1133 break;
1134 }
1135 }
1136 }
1137
1138 @Override
1139 public void succeeded()
1140 {
1141 switch (frame.getType())
1142 {
1143 case HEADERS:
1144 {
1145 HeadersFrame headersFrame = (HeadersFrame)frame;
1146 if (stream.updateClose(headersFrame.isEndStream(), true))
1147 removeStream(stream);
1148 break;
1149 }
1150 case RST_STREAM:
1151 {
1152 if (stream != null)
1153 {
1154 stream.close();
1155 removeStream(stream);
1156 }
1157 break;
1158 }
1159 case PUSH_PROMISE:
1160 {
1161
1162
1163 stream.updateClose(true, false);
1164 break;
1165 }
1166 case GO_AWAY:
1167 {
1168
1169
1170 getEndPoint().shutdownOutput();
1171 break;
1172 }
1173 case WINDOW_UPDATE:
1174 {
1175 flowControl.windowUpdate(HTTP2Session.this, stream, (WindowUpdateFrame)frame);
1176 break;
1177 }
1178 case DISCONNECT:
1179 {
1180 terminate();
1181 break;
1182 }
1183 default:
1184 {
1185 break;
1186 }
1187 }
1188 super.succeeded();
1189 }
1190 }
1191
1192 private class DataEntry extends HTTP2Flusher.Entry
1193 {
1194 private int length;
1195
1196 private DataEntry(DataFrame frame, IStream stream, Callback callback)
1197 {
1198 super(frame, stream, callback);
1199 }
1200
1201 @Override
1202 public int dataRemaining()
1203 {
1204
1205
1206
1207
1208
1209 return ((DataFrame)frame).remaining();
1210 }
1211
1212 public Throwable generate(ByteBufferPool.Lease lease)
1213 {
1214 try
1215 {
1216 int flowControlLength = dataRemaining();
1217
1218 int sessionSendWindow = getSendWindow();
1219 if (sessionSendWindow < 0)
1220 throw new IllegalStateException();
1221
1222 int streamSendWindow = stream.updateSendWindow(0);
1223 if (streamSendWindow < 0)
1224 throw new IllegalStateException();
1225
1226 int window = Math.min(streamSendWindow, sessionSendWindow);
1227
1228 int length = this.length = Math.min(flowControlLength, window);
1229 if (LOG.isDebugEnabled())
1230 LOG.debug("Generated {}, length/window={}/{}", frame, length, window);
1231
1232 generator.data(lease, (DataFrame)frame, length);
1233 flowControl.onDataSending(stream, length);
1234 return null;
1235 }
1236 catch (Throwable x)
1237 {
1238 if (LOG.isDebugEnabled())
1239 LOG.debug("Failure generating frame " + frame, x);
1240 return x;
1241 }
1242 }
1243
1244 @Override
1245 public void succeeded()
1246 {
1247 flowControl.onDataSent(stream, length);
1248
1249 DataFrame dataFrame = (DataFrame)frame;
1250 if (dataFrame.remaining() > 0)
1251 {
1252
1253
1254
1255 flusher.prepend(this);
1256 }
1257 else
1258 {
1259
1260
1261 if (stream.updateClose(dataFrame.isEndStream(), true))
1262 removeStream(stream);
1263 super.succeeded();
1264 }
1265 }
1266 }
1267
1268 private static class PromiseCallback<C> implements Callback
1269 {
1270 private final Promise<C> promise;
1271 private final C value;
1272
1273 private PromiseCallback(Promise<C> promise, C value)
1274 {
1275 this.promise = promise;
1276 this.value = value;
1277 }
1278
1279 @Override
1280 public void succeeded()
1281 {
1282 promise.succeeded(value);
1283 }
1284
1285 @Override
1286 public void failed(Throwable x)
1287 {
1288 promise.failed(x);
1289 }
1290 }
1291 }