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";