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