1 // Copyright (c) 2013 Heapsource.com and Contributors - http://www.heapsource.com
2 //
3 // Permission is hereby granted, free of charge, to any person obtaining a copy
4 // of this software and associated documentation files (the "Software"), to deal
5 // in the Software without restriction, including without limitation the rights
6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 // copies of the Software, and to permit persons to whom the Software is
8 // furnished to do so, subject to the following conditions:
9 //
10 // The above copyright notice and this permission notice shall be included in
11 // all copies or substantial portions of the Software.
12 //
13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 // THE SOFTWARE.
20 //
21 
22 
23 module http.parser.core;
24 import http.parser.c;
25 import std.stdio;
26 import std.conv;
27 import std.string : toStringz, CaseSensitive, indexOf;
28 import std.stdint : uint16_t;
29 
30 enum HttpParserType {
31   REQUEST,
32   RESPONSE,
33   BOTH
34 };
35 
36 enum HttpBodyTransmissionMode {
37     None,
38     ContentLength,
39     Chunked
40 };
41 
42 @property bool shouldRead(HttpBodyTransmissionMode mode) pure nothrow {
43     return mode > HttpBodyTransmissionMode.None;
44 }
45 
46 public struct HttpVersion {
47     private:
48         ushort _minor;
49         ushort _major;
50         string _str;
51     public:
52         this(ushort major, ushort minor) {
53             _major = major;
54             _minor = minor;
55             _str = std..string.format("%d.%d", _major, _minor);
56         }
57 
58         @property {
59             ushort major() pure nothrow {
60                 return _major;
61             }
62             ushort minor() pure nothrow {
63                 return _minor;
64             }
65         }
66 
67         string toString() {
68             return _str;
69         }
70 }
71 public struct HttpHeader {
72   package string _name, _value;
73 
74   public:
75     @property string name() {
76       return _name;
77     }
78     @property void name(string name) {
79       _name = name;
80     }
81     @property string value() {
82       return _value;
83     }
84     @property void value(string value) {
85       _value = value;
86     }
87 
88     @property bool hasValue() {
89       return _value !is null;
90     }
91 
92     @property bool hasName() {
93       return _name !is null;
94     }
95 
96     @property bool isEmpty() {
97       return !hasName() && !hasValue();
98     }
99 }
100 
101 public class HttpParserException : Exception {
102   private string _name;
103   public this(string message, string name, string filename = __FILE__, size_t line = __LINE__, Throwable next = null) {
104     super(message, filename, line, next);
105   }
106   public @property string name() {
107     return _name;
108   }
109   public @property void name(string name) {
110     _name = name;
111   }
112 }
113 
114 public struct HttpBodyChunk {
115     private:
116         ubyte[] _buffer;
117         bool _isFinal;
118 
119     public:
120         this(ubyte[] buffer, bool isFinal)
121         {
122             _buffer = buffer;
123             _isFinal = isFinal;
124         }
125 
126         @property {
127 
128             bool isFinal() pure nothrow {
129                 return _isFinal;
130             }
131 
132             ubyte[] buffer() pure nothrow {
133                 return _buffer;
134             }
135 
136         }
137 }
138 
139 public alias void delegate(HttpParser) HttpParserDelegate;
140 public alias void delegate(HttpParser, HttpBodyChunk) HttpParserBodyChunkDelegate;
141 public alias void delegate(HttpParser, string data) HttpParserStringDelegate;
142 public alias void delegate(HttpParser, HttpHeader header) HttpParserHeaderDelegate;
143 
144 public class HttpParser {
145   private {
146 
147     extern(C) {
148       mixin(http_parser_cb!("on_message_begin"));
149       mixin(http_parser_data_cb!("on_url"));
150       mixin(http_parser_data_cb!("on_status_complete"));
151       mixin(http_parser_data_cb!("on_header_value"));
152       mixin(http_parser_data_cb!("on_header_field"));
153       mixin(http_parser_cb!("on_headers_complete"));
154       mixin(http_parser_data_cb!("on_body"));
155       mixin(http_parser_cb!("on_message_complete"));
156     }
157 
158     http_parser* _parser;
159     http_parser_settings _settings;
160     HttpParserType _type;
161 
162     // delegates
163     HttpParserDelegate _messageBegin, _messageComplete, _headersComplete;
164     HttpParserBodyChunkDelegate _onBody;
165     HttpParserStringDelegate _onUrl, _statusComplete;
166     HttpParserHeaderDelegate _onHeader;
167     Throwable _lastException;
168     HttpBodyTransmissionMode _transmissionMode;
169     bool _transferEncodingPresent = false;
170 
171     __gshared const int CB_OK = 0;
172     __gshared const int CB_ERR = 1;
173 
174     /** Begin Counters
175       Countes are reset every time a new message is received. Check _resetCounters.
176       */
177     int _headerFields;
178     int _headerValues;
179     uint _statusCode;
180     HttpHeader _currentHeader;
181 
182     void _resetCounters() {
183       _transferEncodingPresent = false;
184       _headerFields = 0;
185       _headerValues = 0;
186       _resetCurrentHeader();
187     }
188 
189     void _resetCurrentHeader() {
190       destroy(_currentHeader);
191     }
192     /** End Counters **/
193   }
194 
195 
196   public {
197 
198     this() {
199       this(HttpParserType.REQUEST);
200     }
201 
202     this(HttpParserType type) {
203       _type = type;
204       _parser = duv_alloc_http_parser();
205       duv_set_http_parser_data(_parser, cast(void*)this);
206       http_parser_init(_parser, cast(http_parser_type)type);
207       _settings.on_message_begin = &duv_http_parser_on_message_begin;
208       _settings.on_message_complete = &duv_http_parser_on_message_complete;
209       _settings.on_status_complete = &duv_http_parser_on_status_complete;
210       _settings.on_header_field = &duv_http_parser_on_header_field;
211       _settings.on_header_value = &duv_http_parser_on_header_value;
212       _settings.on_headers_complete = &duv_http_parser_on_headers_complete;
213       _settings.on_body = &duv_http_parser_on_body;
214       _settings.on_url = &duv_http_parser_on_url;
215     }
216 
217     size_t execute(string text) {
218       return execute(cast(ubyte[])text);
219     }
220 
221     size_t execute(ubyte[] data) {
222       _lastException = null;
223       size_t inputLength = data.length;
224       size_t ret = http_parser_execute(_parser, &_settings, cast(ubyte*)data, inputLength);
225       auto error = duv_http_parser_get_errno(_parser);
226       //writefln("Parsed %d of %d", ret, inputLength);
227       if(_lastException || error || ret != inputLength) {
228         if(_lastException) {
229           throw _lastException;
230         }
231         const(char)* errName = duv_http_errno_name(_parser);
232         const(char)* errDescription = duv_http_errno_description(_parser);
233         string errNameStr = to!string(errName);
234         string errDescStr = to!string(errDescription);
235         throw new HttpParserException(errDescStr, errNameStr);
236       }
237       return ret;
238     }
239 
240     @property HttpVersion protocolVersion() {
241         return HttpVersion(duv_http_major(_parser), duv_http_minor(_parser));
242     }
243 
244     @property HttpParserType type() {
245       return _type;
246     }
247 
248     @property HttpParserDelegate onMessageBegin() {
249       return _messageBegin;
250     }
251     @property void onMessageBegin(HttpParserDelegate callback) {
252       _messageBegin = callback;
253     }
254 
255     @property HttpParserDelegate onMessageComplete() {
256       return _messageComplete;
257     }
258     @property void onMessageComplete(HttpParserDelegate callback) {
259       _messageComplete = callback;
260     }
261 
262     @property HttpParserStringDelegate onStatusComplete() {
263       return _statusComplete;
264     }
265     @property void onStatusComplete(HttpParserStringDelegate callback) {
266       _statusComplete = callback;
267     }
268 
269     @property HttpParserDelegate onHeadersComplete() {
270       return _headersComplete;
271     }
272     @property void onHeadersComplete(HttpParserDelegate callback) {
273       _headersComplete = callback;
274     }
275 
276     @property HttpParserBodyChunkDelegate onBody() {
277       return _onBody;
278     }
279     @property void onBody(HttpParserBodyChunkDelegate callback) {
280       _onBody = callback;
281     }
282 
283     @property HttpParserStringDelegate onUrl() {
284       return _onUrl;
285     }
286     @property void onUrl(HttpParserStringDelegate callback) {
287       _onUrl = callback;
288     }
289 
290     @property HttpParserHeaderDelegate onHeader() {
291       return _onHeader;
292     }
293     @property void onHeader(HttpParserHeaderDelegate callback) {
294       _onHeader = callback;
295     }
296 
297     @property string method() {
298         return std.conv.to!string(duv_http_method_str(_parser));
299     }
300 
301     @property ulong contentLength() {
302         return http_parser_get_content_length(_parser);
303     }
304     @property HttpBodyTransmissionMode transmissionMode() {
305         return _transmissionMode;
306     }
307 
308     /*
309        Available on statusComplete
310      */
311     @property uint statusCode() {
312         return _statusCode;
313     }
314   }
315 
316   package {
317     int _on_message_begin() {
318       _resetCounters();
319       if(this._messageBegin) {
320         try {
321           _messageBegin(this);
322         } catch(Throwable ex) {
323           _lastException = ex;
324           return CB_ERR;
325         }
326       }
327       return CB_OK;
328     }
329 
330     int _on_message_complete() {
331       if(this._messageComplete) {
332         try {
333           _messageComplete(this);
334         } catch(Throwable ex) {
335           _lastException = ex;
336           return CB_ERR;
337         }
338       }
339       return CB_OK;
340     }
341 
342 
343     int _on_status_complete(ubyte[] data) {
344       if(_type == HttpParserType.RESPONSE) {
345         _statusCode = duv_http_status_code(_parser);
346       }
347       if(this._statusComplete) {
348         try {
349           _statusComplete(this, cast(string)data);
350         } catch(Throwable ex) {
351           _lastException = ex;
352           return CB_ERR;
353         }
354       }
355       return CB_OK;
356     }
357 
358     int _on_url(ubyte[] data) {
359       if(this._onUrl) {
360         try {
361           _onUrl(this, cast(string)data);
362         } catch(Throwable ex) {
363           _lastException = ex;
364           return CB_ERR;
365         }
366       }
367       return CB_OK;
368     }
369 
370     int _on_header_field(ubyte[] data) {
371       if(_currentHeader.hasValue) {
372         int res = _safePublishHeader();
373         _resetCurrentHeader();
374         if(res != CB_OK) {
375           return res;
376         }
377       }
378       string text = cast(string)data;
379       _currentHeader._name ~= text;
380       return CB_OK;
381     }
382 
383     int _on_header_value(ubyte[] data) {
384       string text = cast(string)data;
385       _currentHeader._value ~= text;
386       return CB_OK;
387     }
388 
389     int _safePublishHeader() {
390       try {
391         _publishHeader();
392       } catch(Throwable ex) {
393         _lastException = ex;
394         return CB_ERR;
395       }
396       return CB_OK;
397     }
398     void _publishHeader() {
399       if(_currentHeader.isEmpty) return;
400       if(_currentHeader.name.indexOf("Transfer-Encoding", CaseSensitive.no) != -1) {
401         _transferEncodingPresent = true;
402       }
403       if(this._onHeader) {
404         this._onHeader(this, _currentHeader);
405       }
406     }
407 
408     void _determinateTransmissionMode() {
409         // Follow the order in the RFC http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.4
410 
411         // use transfer encoding if present
412         if(_transferEncodingPresent) {
413             _transmissionMode = HttpBodyTransmissionMode.Chunked;
414         }
415         // otherwise check if there is contentLength (ContentLength: 0 must be interpreted as non-existent)
416         else if(this.contentLength > 0) {
417             _transmissionMode = HttpBodyTransmissionMode.ContentLength;
418         } else {
419             // No HTTP Entity Body
420             _transmissionMode = HttpBodyTransmissionMode.None;
421         }
422     }
423 
424     int _on_headers_complete() {
425       try {
426         _publishHeader();
427         _determinateTransmissionMode();
428         if(this._headersComplete) {
429           _headersComplete(this);
430         }
431       } catch(Throwable ex) {
432         _lastException = ex;
433         return CB_ERR;
434       }
435       return CB_OK;
436     }
437 
438     int _on_body(ubyte[] data) {
439       if(this._onBody) {
440         try {
441             bool isFinal = http_body_is_final(_parser) != 0;
442             auto chunk = HttpBodyChunk(data, isFinal);
443           _onBody(this, chunk);
444         } catch(Throwable ex) {
445           _lastException = ex;
446           return CB_ERR;
447         }
448       }
449       return CB_OK;
450     }
451   }
452 
453   ~this() {
454     if(_parser) {
455       duv_free_http_parser(_parser);
456       _parser = null;
457     }
458   }
459 }
460 
461 struct Uri {
462     private:
463         string _schema, _host, _path, _query, _fragment, _userInfo;
464         ushort _port;
465         string _absoluteUri;
466 
467         void buildAbsoluteUri() {
468             string absolutePort = _port == 0 ? "" : ":" ~ _port.to!string;
469             string absoluteQuery = _query.length == 0 ? "" : "?" ~ _query;
470             string absoluteUserInfo = _userInfo.length == 0 ? "" :  _userInfo ~ "@";
471             _absoluteUri = this.schema ~ "://" ~ absoluteUserInfo ~ _host ~ absolutePort ~ _path ~ absoluteQuery;
472         }
473 
474     public:
475         this(in string rawUri, bool isConnect = false) {
476             http_parser_url * url = alloc_http_parser_url();
477             scope (exit) free_http_parser_url(url);
478             immutable(char) * buff = rawUri.toStringz;
479             int res = http_parser_parse_url(buff, rawUri.length, isConnect ? 1 : 0, url);
480             if(res != 0) {
481                 throw new Exception("Failed to parse rawUri " ~ rawUri);
482             }
483 
484             auto port = http_parser_get_port(url);
485             auto schema = http_parser_get_field_string(url, rawUri, http_parser_url_fields.UF_SCHEMA);
486             auto host = http_parser_get_field_string(url, rawUri, http_parser_url_fields.UF_HOST);
487             auto path = http_parser_get_field_string(url, rawUri, http_parser_url_fields.UF_PATH);
488             auto query = http_parser_get_field_string(url, rawUri, http_parser_url_fields.UF_QUERY);
489             auto fragment = http_parser_get_field_string(url, rawUri, http_parser_url_fields.UF_FRAGMENT);
490             auto userInfo = http_parser_get_field_string(url, rawUri, http_parser_url_fields.UF_USERINFO);
491 
492             this(schema, host, port, path, query, fragment, userInfo);
493         }
494 
495         this(in string schema, in string host, in ushort port, in string path, in string query, in string fragment, in string userInfo) {
496             _schema = schema;
497             _host = host;
498             _port = port;
499             _path = path;
500             _query = query;
501             _fragment = fragment;
502             _userInfo = userInfo;
503             this.buildAbsoluteUri();
504         }
505 
506     @property {
507 
508         string schema() pure nothrow {
509             return _schema;
510         }
511 
512         string host() pure nothrow {
513             return _host;
514         }
515 
516         ushort port() pure nothrow {
517             return _port;
518         }
519 
520         string path() pure nothrow {
521             return _path;
522         }
523 
524         string query() pure nothrow {
525             return _query;
526         }
527 
528         string fragment() pure nothrow {
529             return _fragment;
530         }
531 
532         string userInfo() pure nothrow {
533             return _userInfo;
534         }
535         string absoluteUri() pure nothrow {
536             return _absoluteUri;
537         }
538     }
539     string toString() {
540         return this.absoluteUri;
541     }
542 }