View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
4   //  ------------------------------------------------------------------------
5   //  All rights reserved. This program and the accompanying materials
6   //  are made available under the terms of the Eclipse Public License v1.0
7   //  and Apache License v2.0 which accompanies this distribution.
8   //
9   //      The Eclipse Public License is available at
10  //      http://www.eclipse.org/legal/epl-v10.html
11  //
12  //      The Apache License v2.0 is available at
13  //      http://www.opensource.org/licenses/apache2.0.php
14  //
15  //  You may elect to redistribute this code under either of these licenses.
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; // SPEC: by default, push is enabled.
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         // SPEC: the session window must be updated even if the stream is null.
184         // The flow control length includes the padding bytes.
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                         // Consume also in case of failures, to free the
208                         // session flow control window for other streams.
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             // We must enlarge the session flow control window,
226             // otherwise other requests will be stalled.
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         // SPEC: SETTINGS frame MUST be replied.
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         // Iterate over all settings
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                     // SPEC: check the value is sane.
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                     // SPEC: check the max frame size is sane.
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                     // TODO implement
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      * This method is called when receiving a GO_AWAY from the other peer.
362      * We check the close state to act appropriately:
363      *
364      * * NOT_CLOSED: we move to REMOTELY_CLOSED and queue a disconnect, so
365      *   that the content of the queue is written, and then the connection
366      *   closed. We notify the application after being terminated.
367      *   See <code>HTTP2Session.ControlEntry#succeeded()</code>
368      *
369      * * In all other cases, we do nothing since other methods are already
370      *   performing their actions.
371      *
372      * @param frame the GO_AWAY frame that has been received.
373      * @see #close(int, String, Callback)
374      * @see #onShutdown()
375      * @see #onIdleTimeout()
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                         // We received a GO_AWAY, so try to write
393                         // what's in the queue and then disconnect.
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         // Synchronization is necessary to atomically create
455         // the stream id and enqueue the frame to be sent.
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         // Iterate outside the synchronized block.
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         // Synchronization is necessary to atomically create
500         // the stream id and enqueue the frame to be sent.
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         // Iterate outside the synchronized block.
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      * Invoked internally and by applications to send a GO_AWAY frame to the
543      * other peer. We check the close state to act appropriately:
544      *
545      * * NOT_CLOSED: we move to LOCALLY_CLOSED and queue a GO_AWAY. When the
546      *   GO_AWAY has been written, it will only cause the output to be shut
547      *   down (not the connection closed), so that the application can still
548      *   read frames arriving from the other peer.
549      *   Ideally the other peer will notice the GO_AWAY and close the connection.
550      *   When that happen, we close the connection from {@link #onShutdown()}.
551      *   Otherwise, the idle timeout mechanism will close the connection, see
552      *   {@link #onIdleTimeout()}.
553      *
554      * * In all other cases, we do nothing since other methods are already
555      *   performing their actions.
556      *
557      * @param error the error code
558      * @param reason the reason
559      * @param callback the callback to invoke when the operation is complete
560      * @see #onGoAway(GoAwayFrame)
561      * @see #onShutdown()
562      * @see #onIdleTimeout()
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                             // Trim the reason to avoid attack vectors.
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         // We want to generate as late as possible to allow re-prioritization;
615         // generation will happen while processing the entries.
616 
617         // The callback needs to be notified only when the last frame completes.
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         // We want to generate as late as possible to allow re-prioritization.
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         // Ping frames are prepended to process them as soon as possible.
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         // SPEC: exceeding max concurrent streams is treated as stream error.
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         // SPEC: duplicate stream is treated as connection error.
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         // WindowUpdateFrames arrive concurrently with writes.
794         // Increasing (or reducing) the window size concurrently
795         // with writes requires coordination with the flusher, that
796         // decides how many frames to write depending on the available
797         // window sizes. If the window sizes vary concurrently, the
798         // flusher may take non-optimal or wrong decisions.
799         // Here, we "queue" window updates to the flusher, so it will
800         // be the only component responsible for window updates, for
801         // both increments and reductions.
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      * A typical close by a remote peer involves a GO_AWAY frame followed by TCP FIN.
814      * This method is invoked when the TCP FIN is received, or when an exception is
815      * thrown while reading, and we check the close state to act appropriately:
816      *
817      * * NOT_CLOSED: means that the remote peer did not send a GO_AWAY (abrupt close)
818      *   or there was an exception while reading, and therefore we terminate.
819      *
820      * * LOCALLY_CLOSED: we have sent the GO_AWAY to the remote peer, which received
821      *   it and closed the connection; we queue a disconnect to close the connection
822      *   on the local side.
823      *   The GO_AWAY just shutdown the output, so we need this step to make sure the
824      *   connection is closed. See {@link #close(int, String, Callback)}.
825      *
826      * * REMOTELY_CLOSED: we received the GO_AWAY, and the TCP FIN afterwards, so we
827      *   do nothing since the handling of the GO_AWAY will take care of closing the
828      *   connection. See {@link #onGoAway(GoAwayFrame)}.
829      *
830      * @see #onGoAway(GoAwayFrame)
831      * @see #close(int, String, Callback)
832      * @see #onIdleTimeout()
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                 // The other peer did not send a GO_AWAY, no need to be gentle.
845                 if (LOG.isDebugEnabled())
846                     LOG.debug("Abrupt close for {}", this);
847                 abort(new ClosedChannelException());
848                 break;
849             }
850             case LOCALLY_CLOSED:
851             {
852                 // We have closed locally, and only shutdown
853                 // the output; now queue a disconnect.
854                 control(null, Callback.NOOP, new DisconnectFrame());
855                 break;
856             }
857             case REMOTELY_CLOSED:
858             {
859                 // Nothing to do, the GO_AWAY frame we
860                 // received will close the connection.
861                 break;
862             }
863             default:
864             {
865                 break;
866             }
867         }
868     }
869 
870     /**
871      * This method is invoked when the idle timeout triggers. We check the close state
872      * to act appropriately:
873      *
874      * * NOT_CLOSED: it's a real idle timeout, we just initiate a close, see
875      *   {@link #close(int, String, Callback)}.
876      *
877      * * LOCALLY_CLOSED: we have sent a GO_AWAY and only shutdown the output, but the
878      *   other peer did not close the connection so we never received the TCP FIN, and
879      *   therefore we terminate.
880      *
881      * * REMOTELY_CLOSED: the other peer sent us a GO_AWAY, we should have queued a
882      *   disconnect, but for some reason it was not processed (for example, queue was
883      *   stuck because of TCP congestion), therefore we terminate.
884      *   See {@link #onGoAway(GoAwayFrame)}.
885      *
886      * @return true if the session should be closed, false otherwise
887      * @see #onGoAway(GoAwayFrame)
888      * @see #close(int, String, Callback)
889      * @see #onShutdown()
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          * <p>Performs actions just before writing the frame to the network.</p>
1108          * <p>Some frame, when sent over the network, causes the receiver
1109          * to react and send back frames that may be processed by the original
1110          * sender *before* {@link #succeeded()} is called.
1111          * <p>If the action to perform updates some state, this update may
1112          * not be seen by the received frames and cause errors.</p>
1113          * <p>For example, suppose the action updates the stream window to a
1114          * larger value; the sender sends the frame; the receiver is now entitled
1115          * to send back larger data; when the data is received by the original
1116          * sender, the action may have not been performed yet, causing the larger
1117          * data to be rejected, when it should have been accepted.</p>
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                     // Pushed streams are implicitly remotely closed.
1162                     // They are closed when sending an end-stream DATA frame.
1163                     stream.updateClose(true, false);
1164                     break;
1165                 }
1166                 case GO_AWAY:
1167                 {
1168                     // We just sent a GO_AWAY, only shutdown the
1169                     // output without closing yet, to allow reads.
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             // We don't do any padding, so the flow control length is
1205             // always the data remaining. This simplifies the handling
1206             // of data frames that cannot be completely written due to
1207             // the flow control window exhausting, since in that case
1208             // we would have to count the padding only once.
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             // Do we have more to send ?
1249             DataFrame dataFrame = (DataFrame)frame;
1250             if (dataFrame.remaining() > 0)
1251             {
1252                 // We have written part of the frame, but there is more to write.
1253                 // We need to keep the correct ordering of frames, to avoid that other
1254                 // frames for the same stream are written before this one is finished.
1255                 flusher.prepend(this);
1256             }
1257             else
1258             {
1259                 // Only now we can update the close state
1260                 // and eventually remove the stream.
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 }