1 module hunt.http.client.HttpClientHandler;
2 
3 import hunt.http.client.HttpClientContext;
4 import hunt.http.client.HttpClientConnection;
5 import hunt.http.client.HttpClientOptions;
6 import hunt.http.client.Http1ClientConnection;
7 import hunt.http.client.Http2ClientConnection;
8 import hunt.http.HttpVersion;
9 import hunt.http.HttpConnection;
10 import hunt.http.HttpOptions;
11 
12 import hunt.net.Connection;
13 import hunt.net.KeyCertOptions;
14 import hunt.net.secure.SecureSession;
15 
16 
17 // dfmt off
18 version(WITH_HUNT_SECURITY) {
19     import hunt.net.secure.SecureSessionFactory;
20 }
21 // dfmt on
22 
23 import hunt.concurrency.Promise;
24 import hunt.Exceptions;
25 import hunt.text.Common;
26 
27 import hunt.collection.Map;
28 
29 import hunt.logging;
30 import std.array;
31 
32 /**
33  * 
34  */
35 class HttpClientHandler : HttpConnectionHandler {
36 
37     // private Map!(int, HttpClientContext) _httpClientContext;
38     private HttpClientContext _httpClientContext;
39     private HttpClientOptions _options;
40 
41     this(HttpClientOptions options, HttpClientContext httpClientContext) {
42         // super(_options);
43         _options = options;
44         _httpClientContext = httpClientContext;
45     }
46 
47     override
48     void connectionOpened(Connection connection) {
49         HttpClientContext context = _httpClientContext; //.get(connection.getId());
50         
51         version(HUNT_HTTP_DEBUG) {
52             infof("HTTP connection %d opened", connection.getId());
53         }
54         connection.setState(ConnectionState.Opened);
55 
56         if (context is null) {
57             errorf("http2 client can not get the client context of connection %s", connection.getId());
58             connection.close();
59             return;
60         }
61 
62         if (_options.isSecureConnectionEnabled()) {
63             version(HUNT_DEBUG) {
64                 infof("initilizing a secure connection %d: %s", connection.getId(), connection.getState());
65             }
66 
67             version(WITH_HUNT_SECURITY) {
68                 import hunt.net.secure.SecureUtils;
69                 connection.setState(ConnectionState.Securing);
70 
71                 SecureSessionHandshakeListener handshakeListener = (SecureSession sslSession) {
72 
73                     // connection.setAttribute(SecureSession.NAME, cast(Object)sslSession);
74 
75                     string protocol = "http/1.1";
76                     string p = sslSession.getApplicationProtocol();
77                     if(p.empty)
78                         warningf("The selected application protocol is empty. Now using the default: %s", protocol);
79                     else
80                         protocol = p;
81 
82                     version(HUNT_HTTP_DEBUG) infof("Client connection %s SSL handshake finished. The app protocol is %s", 
83                         connection.getId(), protocol);
84 
85                     infof("connection state: %s", connection.getState());
86                     connection.setState(ConnectionState.Secured);
87 
88                     switch (protocol) {
89                         case "http/1.1":
90                             initializeHttp1ClientConnection(connection, context);
91                             break;
92                         case "h2":
93                             initializeHttp2ClientConnection(connection, context);
94                             break;
95                         default:
96                             throw new IllegalStateException("SSL application protocol negotiates failure. The protocol " 
97                                 ~ protocol ~ " is not supported");
98                     }
99                 };
100 
101                 SecureSession secureSession;
102                 if(_options.isCertificateAuth()) {
103                     secureSession = SecureUtils.createClientSession(connection, handshakeListener, _options.getKeyCertOptions());
104                 } else {
105                     secureSession = SecureUtils.createClientSession(connection, handshakeListener);
106                 }
107 
108                 // connection.attachObject(cast(Object)secureSession);
109 
110                 connection.setAttribute(SecureSession.NAME, cast(Object)secureSession);
111             } else {
112                 assert(false, "To support SSL, please read the Readme.md in project hunt-net.");
113             }
114         } else {
115             version(HUNT_HTTP_DEBUG) {
116                 info("initilizing a connection");
117             }
118 
119             if (_options.getProtocol().empty) {
120                 initializeHttp1ClientConnection(connection, context);
121             } else {
122                 HttpVersion httpVersion = HttpVersion.fromString(_options.getProtocol());
123                 if (httpVersion == HttpVersion.Null) {
124                     throw new IllegalArgumentException("the protocol " ~ _options.getProtocol() ~ " is not support.");
125                 }
126                 if(httpVersion == HttpVersion.HTTP_1_1) {
127                         initializeHttp1ClientConnection(connection, context);
128                 } else if(httpVersion == HttpVersion.HTTP_2) {
129                         initializeHttp2ClientConnection(connection, context);
130                 } else {
131                         throw new IllegalArgumentException("the protocol " ~ _options.getProtocol() ~ " is not support.");
132                 }
133             }
134 
135         }
136     }
137 
138     private void initializeHttp1ClientConnection(Connection connection, HttpClientContext context) {
139         Promise!(HttpClientConnection) promise  = context.getPromise();
140         assert(promise !is null);
141         
142         try {
143             Http1ClientConnection http1ClientConnection = new Http1ClientConnection(_options, connection);
144             connection.setAttribute(HttpConnection.NAME, http1ClientConnection);
145             promise.succeeded(http1ClientConnection);
146 
147         } catch (Exception t) {
148             warning(t);
149             promise.failed(t);
150         } finally {
151             // _httpClientContext.remove(connection.getId());
152         }
153     }
154 
155     private void initializeHttp2ClientConnection(Connection connection, HttpClientContext context) {
156         try {
157             Http2ClientConnection conn = new Http2ClientConnection(_options, connection, context.getListener());
158             // connection.attachObject(conn);
159             connection.setAttribute(HttpConnection.NAME, conn);
160             context.getListener().setConnection(conn);            
161             // connection.initialize(_options, cast(Promise!(Http2ClientConnection))context.getPromise(), context.getListener());
162             conn.initialize(_options, context.getPromise(), context.getListener());
163         } finally {
164             // _httpClientContext.remove(connection.getId());
165         }
166     }
167 
168     override
169     void connectionClosed(Connection connection) {
170         try {
171             super.connectionClosed(connection);
172         } catch(Exception ex) {
173             warning(ex.msg);
174             version(HUNT_DEBUG) {
175                 warning(ex);
176             }
177         } finally {
178             // _httpClientContext.remove(connection.getId());
179         }
180     }
181 
182     override
183     void failedOpeningConnection(int sessionId, Throwable t) {
184 
185         auto c = _httpClientContext; //.remove(sessionId);
186         if(c !is null)
187         {
188             auto promise = c.getPromise();
189             if(promise !is null)
190                 promise.failed(cast(Exception)t);
191         }
192         
193         // Optional.ofNullable(_httpClientContext.remove(sessionId))
194         //         .map(HttpClientContext::getPromise)
195         //         .ifPresent(promise => promise.failed(t));
196     }
197 
198     override
199     void exceptionCaught(Connection connection, Throwable t) {
200         try {
201             super.exceptionCaught(connection, t);
202         } finally {
203             // _httpClientContext; //.remove(connection.getId());
204         }
205     }
206 
207 }