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                     
77                     // protocol = "h2"; // test
78                     string p = sslSession.getApplicationProtocol();
79                     if(p.empty)
80                         warningf("The selected application protocol is empty. Now using the default: %s", protocol);
81                     else
82                         protocol = p;
83 
84                     version(HUNT_HTTP_DEBUG) infof("Client connection %s SSL handshake finished. The app protocol is %s", 
85                         connection.getId(), protocol);
86 
87                     infof("connection state: %s", connection.getState());
88                     connection.setState(ConnectionState.Secured);
89 
90                     switch (protocol) {
91                         case "http/1.1":
92                             initializeHttp1ClientConnection(connection, context);
93                             break;
94                         case "h2":
95                             initializeHttp2ClientConnection(connection, context);
96                             break;
97                         default:
98                             throw new IllegalStateException("SSL application protocol negotiates failure. The protocol " 
99                                 ~ protocol ~ " is not supported");
100                     }
101                 };
102 
103                 SecureSession secureSession;
104                 if(_options.isCertificateAuth()) {
105                     secureSession = SecureUtils.createClientSession(connection, handshakeListener, _options.getKeyCertOptions());
106                 } else {
107                     secureSession = SecureUtils.createClientSession(connection, handshakeListener);
108                 }
109 
110                 // connection.attachObject(cast(Object)secureSession);
111 
112                 connection.setAttribute(SecureSession.NAME, cast(Object)secureSession);
113             } else {
114                 assert(false, "To support SSL, please read the Readme.md in project hunt-net.");
115             }
116         } else {
117             version(HUNT_HTTP_DEBUG) {
118                 info("initilizing a connection");
119             }
120 
121             if (_options.getProtocol().empty) {
122                 initializeHttp1ClientConnection(connection, context);
123             } else {
124                 HttpVersion httpVersion = HttpVersion.fromString(_options.getProtocol());
125                 if (httpVersion == HttpVersion.Null) {
126                     throw new IllegalArgumentException("the protocol " ~ _options.getProtocol() ~ " is not support.");
127                 }
128                 if(httpVersion == HttpVersion.HTTP_1_1) {
129                         initializeHttp1ClientConnection(connection, context);
130                 } else if(httpVersion == HttpVersion.HTTP_2) {
131                         initializeHttp2ClientConnection(connection, context);
132                 } else {
133                         throw new IllegalArgumentException("the protocol " ~ _options.getProtocol() ~ " is not support.");
134                 }
135             }
136 
137         }
138     }
139 
140     private void initializeHttp1ClientConnection(Connection connection, HttpClientContext context) {
141         Promise!(HttpClientConnection) promise  = context.getPromise();
142         assert(promise !is null);
143         
144         try {
145             Http1ClientConnection http1ClientConnection = new Http1ClientConnection(_options, connection);
146             connection.setAttribute(HttpConnection.NAME, http1ClientConnection);
147             promise.succeeded(http1ClientConnection);
148 
149         } catch (Exception t) {
150             warning(t);
151             promise.failed(t);
152         } finally {
153             // _httpClientContext.remove(connection.getId());
154         }
155     }
156 
157     private void initializeHttp2ClientConnection(Connection connection, HttpClientContext context) {
158         try {
159             Http2ClientConnection conn = new Http2ClientConnection(_options, connection, context.getListener());
160             // connection.attachObject(conn);
161             connection.setAttribute(HttpConnection.NAME, conn);
162             context.getListener().setConnection(conn);            
163             // connection.initialize(_options, cast(Promise!(Http2ClientConnection))context.getPromise(), context.getListener());
164             conn.initialize(_options, context.getPromise(), context.getListener());
165         } finally {
166             // _httpClientContext.remove(connection.getId());
167         }
168     }
169 
170     override
171     void connectionClosed(Connection connection) {
172         try {
173             super.connectionClosed(connection);
174         } catch(Exception ex) {
175             warning(ex.msg);
176             version(HUNT_DEBUG) {
177                 warning(ex);
178             }
179         } finally {
180             // _httpClientContext.remove(connection.getId());
181         }
182     }
183 
184     override
185     void failedOpeningConnection(int sessionId, Throwable t) {
186 
187         auto c = _httpClientContext; //.remove(sessionId);
188         if(c !is null)
189         {
190             auto promise = c.getPromise();
191             if(promise !is null)
192                 promise.failed(cast(Exception)t);
193         }
194         
195         // Optional.ofNullable(_httpClientContext.remove(sessionId))
196         //         .map(HttpClientContext::getPromise)
197         //         .ifPresent(promise => promise.failed(t));
198     }
199 
200     override
201     void exceptionCaught(Connection connection, Throwable t) {
202         try {
203             super.exceptionCaught(connection, t);
204         } finally {
205             // _httpClientContext; //.remove(connection.getId());
206         }
207     }
208 
209 }