1 module hio.http.http_parser;
2 
3 import std.experimental.logger;
4 import std.bitmanip;
5 
6 enum http_parser_type
7 {
8     HTTP_REQUEST,
9     HTTP_RESPONSE,
10     HTTP_BOTH
11 }
12 
13 struct http_parser_settings
14 {
15     http_cb      on_message_begin;
16     http_data_cb on_url;
17     http_data_cb on_status;
18     http_data_cb on_header_field;
19     http_data_cb on_header_value;
20     http_cb      on_headers_complete;
21     http_data_cb on_body;
22     http_cb      on_message_complete;
23     /* When on_chunk_header is called, the current chunk length is stored
24     * in parser->content_length.
25     */
26     http_cb      on_chunk_header;
27     http_cb      on_chunk_complete;
28 }
29 
30 struct http_parser
31 {
32     mixin(bitfields!(
33         uint, "type", 2,         /* enum http_parser_type */
34         uint, "flags", 8,        /* F_* values from 'flags' enum; semi-public */
35         uint, "state", 7,        /* enum state from http_parser.c */
36         uint, "header_state", 7, /* enum header_state from http_parser.c */
37         uint, "index", 5,        /* index into current matcher */
38         uint, "extra_flags", 2,
39         uint, "lenient_http_headers", 1
40     ));
41 
42     uint    nread;
43     ulong   content_length;
44 
45     ushort http_major;
46     ushort http_minor;
47     mixin(bitfields!(
48         uint, "status_code", 16,
49         uint, "method", 8,
50         uint, "http_errno", 7,
51         uint, "upgrade", 1
52     ));
53     // uint status_code : 16; /* responses only */
54     // uint method : 8;       /* requests only */
55     // uint http_errno : 7;
56 
57     /* 1 = Upgrade header was present and the parser has exited because of that.
58     * 0 = No upgrade header present.
59     * Should be checked when http_parser_execute() returns in addition to
60     * error checking.
61     */
62     //uint upgrade : 1;
63 
64     /** PUBLIC **/
65     void *data; /* A pointer to get hook to the "connection" or "socket" object */
66 }
67 enum http_parser_url_fields {
68     UF_SCHEMA           = 0
69   , UF_HOST             = 1
70   , UF_PORT             = 2
71   , UF_PATH             = 3
72   , UF_QUERY            = 4
73   , UF_FRAGMENT         = 5
74   , UF_USERINFO         = 6
75   , UF_MAX              = 7
76   }
77 
78 package struct _field_data {
79     ushort off;               /* Offset into buffer in which field starts */
80     ushort len;               /* Length of run in buffer */
81 }
82 package struct http_parser_url {
83     ushort  field_set;           /* Bitmask of (1 << UF_*) values */
84     ushort  port;                /* Converted UF_PORT string */
85 
86     _field_data[http_parser_url_fields.UF_MAX]
87             field_data;
88 }
89 
90 package extern(C)
91 {
92     alias http_data_cb = int function(http_parser*, const char *at, size_t length);
93     alias http_cb = int function(http_parser*);
94     ///
95     void http_parser_init(http_parser *parser, http_parser_type type) @trusted;
96     size_t http_parser_execute(http_parser *parser,
97                             const http_parser_settings *settings,
98                             const char *data,
99                             size_t len);
100     /* Initialize all http_parser_url members to 0 */
101     void http_parser_url_init(http_parser_url *u) @trusted;
102 
103     /* Parse a URL; return nonzero on failure */
104     int http_parser_parse_url(const char *buf, size_t buflen,
105                             int is_connect,
106                             http_parser_url *u) @trusted;
107 }
108 
109 unittest
110 {
111     info("Test http_parser basics");
112     http_parser parser;
113     http_parser_settings settings;
114     http_parser_init(&parser, http_parser_type.HTTP_REQUEST);
115     auto data0 = "GET / HTTP/1.0\nConnect";
116     auto data1 = "ion: close\n\n";
117     auto r = http_parser_execute(&parser, &settings, data0.ptr, data0.length);
118     assert(r == data0.length);
119     r = http_parser_execute(&parser, &settings, data1.ptr, data1.length);
120     assert(r == data1.length);
121     assert(parser.http_errno == 0);
122     assert(parser.http_major == 1);
123     assert(parser.http_minor == 0);
124 }
125 
126 unittest
127 {
128     import std.stdio;
129     info("Test http_parser callbacks");
130     static bool
131         message_begin,
132         message_complete,
133         headers_complete;
134     http_cb on_message_begin = (http_parser* parser)
135     {
136         message_begin = true;
137         return 0;
138     };
139     http_cb on_message_complete = (http_parser* parser)
140     {
141         message_complete = true;
142         return 0;
143     };
144     http_cb on_headers_complete = (http_parser* parser)
145     {
146         headers_complete = true;
147         writeln("headers complete");
148         return 0;
149     };
150     http_data_cb on_header_field = (http_parser* parser, const char* at, size_t length)
151     {
152         writefln("HeaderField: <%s>", at[0..length]);
153         return 0;
154     };
155     http_data_cb on_header_value = (http_parser* parser, const char* at, size_t length)
156     {
157         writefln("HeaderValue: <%s>", at[0..length]);
158         return 0;
159     };
160     http_parser parser;
161     http_parser_settings settings;
162     settings.on_message_begin = on_message_begin;
163     settings.on_message_complete = on_message_complete;
164     settings.on_headers_complete = on_headers_complete;
165     settings.on_header_field = on_header_field;
166     settings.on_header_value = on_header_value;
167     http_parser_init(&parser, http_parser_type.HTTP_REQUEST);
168     auto data0 = "GET / HTTP/1.0\nConnecti";
169     auto data1 = "on: close\nX: Y\n Z\n\n";
170     auto r = http_parser_execute(&parser, &settings, data0.ptr, data0.length);
171     r = http_parser_execute(&parser, &settings, data1.ptr, data1.length);
172     http_parser_execute(&parser, &settings, null, 0);
173     assert(message_begin);
174     assert(headers_complete);
175     assert(message_complete);
176 }