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.client.http;
20  
21  import java.io.IOException;
22  import java.net.InetSocketAddress;
23  import java.util.HashMap;
24  import java.util.Map;
25  
26  import org.eclipse.jetty.alpn.client.ALPNClientConnectionFactory;
27  import org.eclipse.jetty.client.HttpClient;
28  import org.eclipse.jetty.client.HttpClientTransport;
29  import org.eclipse.jetty.client.HttpDestination;
30  import org.eclipse.jetty.client.Origin;
31  import org.eclipse.jetty.client.api.Connection;
32  import org.eclipse.jetty.http.HttpScheme;
33  import org.eclipse.jetty.http2.api.Session;
34  import org.eclipse.jetty.http2.client.HTTP2Client;
35  import org.eclipse.jetty.http2.client.HTTP2ClientConnectionFactory;
36  import org.eclipse.jetty.http2.frames.GoAwayFrame;
37  import org.eclipse.jetty.http2.frames.SettingsFrame;
38  import org.eclipse.jetty.io.ClientConnectionFactory;
39  import org.eclipse.jetty.io.EndPoint;
40  import org.eclipse.jetty.io.ssl.SslClientConnectionFactory;
41  import org.eclipse.jetty.util.Promise;
42  import org.eclipse.jetty.util.annotation.ManagedAttribute;
43  import org.eclipse.jetty.util.annotation.ManagedObject;
44  import org.eclipse.jetty.util.component.ContainerLifeCycle;
45  import org.eclipse.jetty.util.ssl.SslContextFactory;
46  
47  @ManagedObject("The HTTP/2 client transport")
48  public class HttpClientTransportOverHTTP2 extends ContainerLifeCycle implements HttpClientTransport
49  {
50      private final HTTP2Client client;
51      private ClientConnectionFactory connectionFactory;
52      private HttpClient httpClient;
53  
54      public HttpClientTransportOverHTTP2(HTTP2Client client)
55      {
56          this.client = client;
57      }
58  
59      @ManagedAttribute(value = "The number of selectors", readonly = true)
60      public int getSelectors()
61      {
62          return client.getSelectors();
63      }
64  
65      @Override
66      protected void doStart() throws Exception
67      {
68          if (!client.isStarted())
69          {
70              client.setExecutor(httpClient.getExecutor());
71              client.setScheduler(httpClient.getScheduler());
72              client.setByteBufferPool(httpClient.getByteBufferPool());
73              client.setConnectTimeout(httpClient.getConnectTimeout());
74              client.setIdleTimeout(httpClient.getIdleTimeout());
75              client.setInputBufferSize(httpClient.getResponseBufferSize());
76          }
77          addBean(client);
78          super.doStart();
79  
80          this.connectionFactory = new HTTP2ClientConnectionFactory();
81          client.setClientConnectionFactory((endPoint, context) ->
82          {
83              HttpDestination destination = (HttpDestination)context.get(HTTP_DESTINATION_CONTEXT_KEY);
84              return destination.getClientConnectionFactory().newConnection(endPoint, context);
85          });
86      }
87  
88      @Override
89      protected void doStop() throws Exception
90      {
91          super.doStop();
92          removeBean(client);
93      }
94  
95      @Override
96      public void setHttpClient(HttpClient client)
97      {
98          httpClient = client;
99      }
100 
101     @Override
102     public HttpDestination newHttpDestination(Origin origin)
103     {
104         return new HttpDestinationOverHTTP2(httpClient, origin);
105     }
106 
107     @Override
108     public void connect(InetSocketAddress address, Map<String, Object> context)
109     {
110         client.setConnectTimeout(httpClient.getConnectTimeout());
111 
112         HttpDestinationOverHTTP2 destination = (HttpDestinationOverHTTP2)context.get(HTTP_DESTINATION_CONTEXT_KEY);
113         @SuppressWarnings("unchecked")
114         Promise<Connection> connectionPromise = (Promise<Connection>)context.get(HTTP_CONNECTION_PROMISE_CONTEXT_KEY);
115 
116         SessionListenerPromise listenerPromise = new SessionListenerPromise(destination, connectionPromise);
117 
118         SslContextFactory sslContextFactory = null;
119         if (HttpScheme.HTTPS.is(destination.getScheme()))
120             sslContextFactory = httpClient.getSslContextFactory();
121 
122         client.connect(sslContextFactory, address, listenerPromise, listenerPromise, context);
123     }
124 
125     @Override
126     public org.eclipse.jetty.io.Connection newConnection(EndPoint endPoint, Map<String, Object> context) throws IOException
127     {
128         ClientConnectionFactory factory = connectionFactory;
129         SslContextFactory sslContextFactory = (SslContextFactory)context.get(SslClientConnectionFactory.SSL_CONTEXT_FACTORY_CONTEXT_KEY);
130         if (sslContextFactory != null)
131             factory = new ALPNClientConnectionFactory(client.getExecutor(), factory, client.getProtocols());
132         return factory.newConnection(endPoint, context);
133     }
134 
135     protected HttpConnectionOverHTTP2 newHttpConnection(HttpDestination destination, Session session)
136     {
137         return new HttpConnectionOverHTTP2(destination, session);
138     }
139 
140     private class SessionListenerPromise extends Session.Listener.Adapter implements Promise<Session>
141     {
142         private final HttpDestinationOverHTTP2 destination;
143         private final Promise<Connection> promise;
144         private HttpConnectionOverHTTP2 connection;
145 
146         public SessionListenerPromise(HttpDestinationOverHTTP2 destination, Promise<Connection> promise)
147         {
148             this.destination = destination;
149             this.promise = promise;
150         }
151 
152         @Override
153         public void succeeded(Session session)
154         {
155             connection = newHttpConnection(destination, session);
156             promise.succeeded(connection);
157         }
158 
159         @Override
160         public void failed(Throwable failure)
161         {
162             promise.failed(failure);
163         }
164 
165         @Override
166         public Map<Integer, Integer> onPreface(Session session)
167         {
168             Map<Integer, Integer> settings = new HashMap<>();
169             settings.put(SettingsFrame.INITIAL_WINDOW_SIZE, client.getInitialStreamRecvWindow());
170             return settings;
171         }
172 
173         @Override
174         public void onSettings(Session session, SettingsFrame frame)
175         {
176             Map<Integer, Integer> settings = frame.getSettings();
177             if (settings.containsKey(SettingsFrame.MAX_CONCURRENT_STREAMS))
178                 destination.setMaxRequestsPerConnection(settings.get(SettingsFrame.MAX_CONCURRENT_STREAMS));
179         }
180 
181         @Override
182         public void onClose(Session session, GoAwayFrame frame)
183         {
184             connection.close();
185         }
186 
187         @Override
188         public boolean onIdleTimeout(Session session)
189         {
190             return connection.onIdleTimeout();
191         }
192 
193         @Override
194         public void onFailure(Session session, Throwable failure)
195         {
196             connection.close(failure);
197         }
198     }
199 }