1 module hunt.http.codec.CommonDecoder; 2 3 import hunt.io.ByteBuffer; 4 import hunt.Exceptions; 5 import hunt.logging; 6 7 import hunt.http.HttpConnection; 8 import hunt.http.HttpConnection; 9 import hunt.http.HttpConnection; 10 import hunt.net.codec.Decoder; 11 import hunt.net.Connection; 12 import hunt.net.Exceptions; 13 import hunt.net.secure.SecureSession; 14 import hunt.util.DateTime; 15 16 /** 17 * 18 */ 19 class CommonDecoder : DecoderChain { 20 21 this(DecoderChain next) { 22 super(next); 23 } 24 25 override void decode(ByteBuffer buf, Connection session) { 26 version(HUNT_METRIC) { 27 import core.time; 28 MonoTime startTime = MonoTime.currTime; 29 debug infof("start decoding ..."); 30 } 31 32 version(WITH_HUNT_SECURITY) { 33 try { 34 decodeSecureSession(buf, session); 35 } catch(SSLHandshakeException ex) { 36 warning(ex.msg); 37 session.write(ErrorResponseMessage); 38 // session.close(); 39 } catch(Exception ex) { 40 warning(ex.msg); 41 version(HUNT_HTTP_DEBUG) warning(ex); 42 version(HUNT_DEBUG) { 43 session.write(ex.msg); 44 } else { 45 session.write(ex.msg); 46 } 47 session.close(); 48 } 49 } else { 50 decodePlaintextSession(buf, session); 51 } 52 53 version(HUNT_METRIC) { 54 Duration timeElapsed = MonoTime.currTime - startTime; 55 warningf("decoding done for session %d in: %d microseconds", 56 session.getId, timeElapsed.total!(TimeUnit.Microsecond)()); 57 } 58 } 59 60 private void decodePlaintextSession(ByteBuffer buf, Connection session) { 61 62 ConnectionState connState; 63 do { 64 connState = session.getState(); 65 version(HUNT_HTTP_DEBUG) { 66 if(connState == ConnectionState.Opening) 67 warning("Waiting for a http session..."); 68 } 69 } while(connState == ConnectionState.Opening); 70 71 version(HUNT_HTTP_DEBUG) { 72 infof("ConnectionState: %s", connState); 73 } 74 75 DecoderChain next = getNext(); 76 if (next !is null) { 77 next.decode(buf, session); 78 } else { 79 warning("The next decoder is null."); 80 } 81 } 82 83 private void decodeSecureSession(ByteBuffer buf, Connection session) { 84 ConnectionState connState = session.getState(); 85 86 version(HUNT_HTTP_DEBUG) { 87 infof("ConnectionState: %s", connState); 88 } 89 90 if(connState == ConnectionState.Secured) { 91 DecoderChain next = getNext(); 92 SecureSession secureSession = cast(SecureSession) session.getAttribute(SecureSession.NAME); 93 assert(secureSession !is null, "secureSession is null"); 94 95 // version(HUNT_HTTP_DEBUG) tracef("Raw buffer: %s", buf.toString()); 96 ByteBuffer plaintext = secureSession.read(buf); // httpConnection.decrypt(buf); 97 98 if (plaintext !is null && plaintext.hasRemaining() && next !is null) { 99 version(HUNT_HTTP_DEBUG) { 100 infof("decrypted buffer: %s", plaintext.toString()); 101 } 102 version(HUNT_HTTP_DEBUG_MORE) { 103 // int r = plaintext.remaining(); 104 // if(r < 64) { 105 // tracef("%(%02X %)", plaintext.getRemaining()); 106 // } 107 string msg = cast(string)plaintext.getRemaining(); 108 trace(msg); 109 // tracef("%(%02X %)", plaintext.getRemaining()); 110 } 111 112 next.decode(plaintext, session); 113 } else { 114 version(HUNT_HTTP_DEBUG) warning("No data decrypted!"); 115 } 116 } else if(connState == ConnectionState.Securing) { 117 version(HUNT_DEBUG) { 118 info("TLS handshaking..."); 119 } 120 // TLS handshake 121 enum int MaxTimes = 5; 122 SecureSession secureSession = waitForSecureSession(MaxTimes, session); 123 124 if(secureSession is null) { 125 version(HUNT_DEBUG) warning("Running handshake in another thread."); 126 import std.parallelism; 127 // auto handshakeTask = task(&handleTlsHandshake, buf, session, secureSession, next); 128 auto handshakeTask = task(() { 129 // 130 // FIXME: Needing refactor or cleanup -@zxp at 8/8/2019, 4:29:49 PM 131 // Maybe the buf needs be copied. 132 SecureSession s = waitForSecureSession(0, session); 133 if(s is null) { 134 warning("No SecureSession created"); 135 } else { 136 handleTlsHandshake(buf, session, s); 137 } 138 }); 139 taskPool.put(handshakeTask); 140 } else { 141 handleTlsHandshake(buf, session, secureSession); 142 } 143 144 } else { 145 decodePlaintextSession(buf, session); 146 } 147 } 148 149 private SecureSession waitForSecureSession(int maxTimes, Connection session) { 150 SecureSession secureSession; 151 152 int count = 0; 153 if(maxTimes>0) { 154 do { 155 secureSession = cast(SecureSession) session.getAttribute(SecureSession.NAME); 156 count++; 157 version(HUNT_HTTP_DEBUG) { 158 if(secureSession is null) 159 tracef("Waiting for a secure session...%d", count); 160 } 161 } while(count < maxTimes && secureSession is null); 162 } else { 163 // Waiting until the SecureSession is avaliable. 164 do { 165 version(HUNT_HTTP_DEBUG_MORE) { 166 if(secureSession is null) 167 trace("Waiting for a secure session..."); 168 } 169 secureSession = cast(SecureSession) session.getAttribute(SecureSession.NAME); 170 } while(secureSession is null && session.getState() != ConnectionState.Error); 171 } 172 173 return secureSession; 174 } 175 176 private void handleTlsHandshake(ByteBuffer buf, Connection session, 177 SecureSession secureSession) { 178 179 DecoderChain next = getNext(); 180 ByteBuffer plaintext = secureSession.read(buf); 181 182 if (plaintext !is null && plaintext.hasRemaining()) { 183 version(HUNT_DEBUG) { 184 tracef("The session %s handshake finished and received cleartext size %s", 185 session.getId(), plaintext.remaining()); 186 } 187 188 AbstractHttpConnection httpConnection = cast(AbstractHttpConnection) session.getAttribute(HttpConnection.NAME); 189 version(HUNT_HTTP_DEBUG) { 190 tracef("http connection: %s", httpConnection is null ? "null" : typeid(httpConnection).name); 191 } 192 193 if (httpConnection !is null) { 194 if (next !is null) 195 next.decode(plaintext, session); 196 else 197 warning("The next decoder is null."); 198 } else { 199 warningf("httpConnection is null"); 200 throw new IllegalStateException("the http connection has not been created"); 201 } 202 } else { 203 version(HUNT_DEBUG) { 204 if (secureSession.isHandshakeFinished()) { 205 tracef("The ssl session %s need more data", session.getId()); 206 } else { 207 tracef("The ssl session %s is shaking hand", session.getId()); 208 } 209 } 210 } 211 } 212 } 213 214 215 // TODO: Tasks pending completion -@zhangxueping at 2019-12-23T19:11:48+08:00 216 // 217 enum ErrorResponseMessage = "HTTP/1.1 400 Bad Request\n" ~ 218 "Server: nginx/1.17.6\n" ~ 219 "Date: Mon, 23 Dec 2019 10:54:33 GMT\n" ~ 220 "Content-Type: text/html\n" ~ 221 "Connection: close\n" ~ 222 223 "<html>\n" ~ 224 "<head><title>400 No required SSL certificate was sent</title></head>\n" ~ 225 "<body>\n" ~ 226 "<center><h1>400 Bad Request</h1></center>\n" ~ 227 "<hr><center>Hunt</center>\n" ~ 228 "</body>\n" ~ 229 "</html>\n";