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 }