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.server;
20  
21  import java.io.IOException;
22  import java.util.HashMap;
23  import java.util.Map;
24  import java.util.concurrent.TimeoutException;
25  
26  import org.eclipse.jetty.http2.ErrorCode;
27  import org.eclipse.jetty.http2.HTTP2Cipher;
28  import org.eclipse.jetty.http2.IStream;
29  import org.eclipse.jetty.http2.api.Session;
30  import org.eclipse.jetty.http2.api.Stream;
31  import org.eclipse.jetty.http2.api.server.ServerSessionListener;
32  import org.eclipse.jetty.http2.frames.DataFrame;
33  import org.eclipse.jetty.http2.frames.GoAwayFrame;
34  import org.eclipse.jetty.http2.frames.HeadersFrame;
35  import org.eclipse.jetty.http2.frames.PushPromiseFrame;
36  import org.eclipse.jetty.http2.frames.ResetFrame;
37  import org.eclipse.jetty.http2.frames.SettingsFrame;
38  import org.eclipse.jetty.io.EndPoint;
39  import org.eclipse.jetty.server.Connector;
40  import org.eclipse.jetty.server.HttpConfiguration;
41  import org.eclipse.jetty.server.NegotiatingServerConnection.CipherDiscriminator;
42  import org.eclipse.jetty.util.Callback;
43  import org.eclipse.jetty.util.annotation.Name;
44  import org.eclipse.jetty.util.log.Log;
45  import org.eclipse.jetty.util.log.Logger;
46  
47  public class HTTP2ServerConnectionFactory extends AbstractHTTP2ServerConnectionFactory implements CipherDiscriminator
48  {
49      private static final Logger LOG = Log.getLogger(HTTP2ServerConnectionFactory.class);
50  
51      public HTTP2ServerConnectionFactory(@Name("config") HttpConfiguration httpConfiguration)
52      {
53          super(httpConfiguration);
54      }
55  
56      protected HTTP2ServerConnectionFactory(@Name("config") HttpConfiguration httpConfiguration,String... protocols)
57      {
58          super(httpConfiguration,protocols);
59      }
60  
61      @Override
62      protected ServerSessionListener newSessionListener(Connector connector, EndPoint endPoint)
63      {
64          return new HTTPServerSessionListener(connector, endPoint);
65      }
66  
67      @Override
68      public boolean isAcceptable(String protocol, String tlsProtocol, String tlsCipher)
69      {
70          // TODO remove this draft 14 protection
71          // Implement 9.2.2
72          boolean acceptable = "h2-14".equals(protocol) || !(HTTP2Cipher.isBlackListProtocol(tlsProtocol) && HTTP2Cipher.isBlackListCipher(tlsCipher));
73          if (LOG.isDebugEnabled())
74              LOG.debug("proto={} tls={} cipher={} 9.2.2-acceptable={}",protocol,tlsProtocol,tlsCipher,acceptable);
75          return acceptable;
76      }
77  
78      protected class HTTPServerSessionListener extends ServerSessionListener.Adapter implements Stream.Listener
79      {
80          private final Connector connector;
81          private final EndPoint endPoint;
82  
83          public HTTPServerSessionListener(Connector connector, EndPoint endPoint)
84          {
85              this.connector = connector;
86              this.endPoint = endPoint;
87          }
88  
89          protected HTTP2ServerConnection getConnection()
90          {
91              return (HTTP2ServerConnection)endPoint.getConnection();
92          }
93  
94          @Override
95          public Map<Integer, Integer> onPreface(Session session)
96          {
97              Map<Integer, Integer> settings = new HashMap<>();
98              settings.put(SettingsFrame.HEADER_TABLE_SIZE, getMaxDynamicTableSize());
99              settings.put(SettingsFrame.INITIAL_WINDOW_SIZE, getInitialStreamRecvWindow());
100             int maxConcurrentStreams = getMaxConcurrentStreams();
101             if (maxConcurrentStreams >= 0)
102                 settings.put(SettingsFrame.MAX_CONCURRENT_STREAMS, maxConcurrentStreams);
103             settings.put(SettingsFrame.MAX_HEADER_LIST_SIZE, getHttpConfiguration().getRequestHeaderSize());
104             return settings;
105         }
106 
107         @Override
108         public Stream.Listener onNewStream(Stream stream, HeadersFrame frame)
109         {
110             getConnection().onNewStream(connector, (IStream)stream, frame);
111             return this;
112         }
113 
114         @Override
115         public boolean onIdleTimeout(Session session)
116         {
117             boolean close = super.onIdleTimeout(session);
118             if (!close)
119                 return false;
120 
121             long idleTimeout = getConnection().getEndPoint().getIdleTimeout();
122             return getConnection().onSessionTimeout(new TimeoutException("Session idle timeout " + idleTimeout + " ms"));
123         }
124 
125         @Override
126         public void onClose(Session session, GoAwayFrame frame)
127         {
128             ErrorCode error = ErrorCode.from(frame.getError());
129             if (error == null)
130                 error = ErrorCode.STREAM_CLOSED_ERROR;
131             String reason = frame.tryConvertPayload();
132             if (reason != null && !reason.isEmpty())
133                 reason = " (" + reason + ")";
134             getConnection().onSessionFailure(new IOException("HTTP/2 " + error + reason));
135         }
136 
137         @Override
138         public void onFailure(Session session, Throwable failure)
139         {
140             getConnection().onSessionFailure(failure);
141         }
142 
143         @Override
144         public void onHeaders(Stream stream, HeadersFrame frame)
145         {
146             // Servers do not receive responses.
147             close(stream, "response_headers");
148         }
149 
150         @Override
151         public Stream.Listener onPush(Stream stream, PushPromiseFrame frame)
152         {
153             // Servers do not receive pushes.
154             close(stream, "push_promise");
155             return null;
156         }
157 
158         @Override
159         public void onData(Stream stream, DataFrame frame, Callback callback)
160         {
161             getConnection().onData((IStream)stream, frame, callback);
162         }
163 
164         @Override
165         public void onReset(Stream stream, ResetFrame frame)
166         {
167             ErrorCode error = ErrorCode.from(frame.getError());
168             if (error == null)
169                 error = ErrorCode.CANCEL_STREAM_ERROR;
170             getConnection().onStreamFailure((IStream)stream, new IOException("HTTP/2 " + error));
171         }
172 
173         @Override
174         public boolean onIdleTimeout(Stream stream, Throwable x)
175         {
176             return getConnection().onStreamTimeout((IStream)stream, x);
177         }
178 
179         private void close(Stream stream, String reason)
180         {
181             stream.getSession().close(ErrorCode.PROTOCOL_ERROR.code, reason, Callback.NOOP);
182         }
183     }
184 }