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 }