cmogstored 1.8.1 - use default system stack size
[cmogstored.git] / http_parser.rl
blob835ba655dd5056872e4dda6b9b759c5b02e3aa64
1 /*
2  * Copyright (C) 2012-2020 all contributors <cmogstored-public@yhbt.net>
3  * License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
4  */
5 #include "cmogstored.h"
6 #include "http_util.h"
8 static bool length_incr(off_t *len, unsigned c)
10         off_t prev = *len;
12         *len *= 10;
13         *len += c - '0';
15         if (*len >= prev)
16                 return true;
18         errno = ERANGE;
19         *len = -1;
21         return false;
24 static char *skip_header(struct mog_http *http, char *buf, const char *pe)
26         char *p;
28         assert(http->_p.line_end > 0 && "no previous request/header line");
29         assert(buf[http->_p.line_end] == '\n' && "bad http->_p.line_end");
30         p = buf + http->_p.line_end + 1;
31         assert(p <= pe && "overflow");
33         return p;
36 %%{
37         machine http_parser;
38         include http_common "http_common.rl";
39         include path_parser "path_parser.rl";
41         ignored_header := header_name ':' sep header_value eor @ {
42                 fgoto more_headers;
43         };
45         GET = "GET "> { http->_p.http_method = MOG_HTTP_METHOD_GET; };
46         HEAD = "HEAD "> { http->_p.http_method = MOG_HTTP_METHOD_HEAD; };
47         PUT = "PUT "> { http->_p.http_method = MOG_HTTP_METHOD_PUT; };
48         DELETE = "DELETE "> { http->_p.http_method = MOG_HTTP_METHOD_DELETE; };
49         MKCOL = "MKCOL "> { http->_p.http_method = MOG_HTTP_METHOD_MKCOL; };
51         mog_path_start = '/' > { http->_p.path_tip = to_u8(fpc - buf); };
52                 # TODO: maybe folks use query string/fragments for logging...
53         mog_path_end = (" HTTP/1.") > { http->_p.path_end = to_u8(fpc - buf); };
54         usage_path = ("usage HTTP/1.") @ { http->_p.usage_txt = 1; };
56         # no HTTP/0.9 for now, sorry (not :P)
57         req_line = (HEAD|GET|PUT|DELETE|MKCOL)
58                 ("http://" [^/]+)?
59                 '/'* mog_path_start devid? (usage_path |
60                                             (mog_path_rest mog_path_end) )
61                 ('0'|'1'> { http->_p.persistent = 1; }) '\r'LF;
63         content_length = "Content-Length:"i sep
64                 (digit+) $ {
65                         /*
66                          * RFC 7230 3.3.2, 3.3.3,:
67                          * favor Transfer-Encoding over Content-Length
68                          */
69                         if (!http->_p.chunked &&
70                                         !length_incr(&http->_p.content_len, fc))
71                                 fbreak;
72                 }
73                 $! { errno = EINVAL; fbreak; }
74                 eor;
75         content_range = "Content-Range:"i sep "bytes"LWS+
76                 (digit+) $ {
77                         if (!length_incr(&http->_p.range_beg, fc))
78                                 fbreak;
79                 }
80                 $! { errno = EINVAL; fbreak; }
81                 "-"
82                 (digit+) $ {
83                         if (!length_incr(&http->_p.range_end, fc))
84                                 fbreak;
85                 }
86                 $! { errno = EINVAL; fbreak; }
87                 "/*"
88                 eor > { http->_p.has_content_range = 1; };
89         range = "Range:"i sep (
90                         "bytes=" > {
91                                 http->_p.range_beg = http->_p.range_end = -1;
92                         }
93                         (
94                                 (digit*) $ {
95                                         if (http->_p.range_beg < 0)
96                                                 http->_p.range_beg = 0;
97                                         if (!length_incr(&http->_p.range_beg,
98                                                          fc))
99                                                 fbreak;
100                                 }
101                                 '-'
102                                 (digit*) $ {
103                                         if (http->_p.range_end < 0)
104                                                 http->_p.range_end = 0;
105                                         if (!length_incr(&http->_p.range_end,
106                                                          fc))
107                                                 fbreak;
108                                 }
109                         ) $! {
110                                 http->_p.bad_range = 1;
111                                 p = skip_header(http, buf, pe);
112                                 fgoto ignored_header;
113                         }
114                 ) $! {
115                         p = skip_header(http, buf, pe);
116                         fgoto ignored_header;
117                 }
118                 eor @ { http->_p.has_range = 1; };
119         transfer_encoding_chunked = "Transfer-Encoding:"i sep
120                 # XXX we don't know how to deal with "gzip", "deflate", or
121                 # "compress" as described in RFC 7230, so reject them, here.
122                 "chunked"i
123                 $! { errno = EINVAL; fbreak; }
124                 eor @ {
125                         http->_p.chunked = 1;
126                         /* RFC 7230 3.3.2, 3.3.3,: ignore length if chunked */
127                         http->_p.content_len = 0;
128                 };
129         trailer = "Trailer:"i sep
130                 (("Content-MD5"i @ { http->_p.has_md5 = 1; })
131                  | header_name | ',')
132                 eor;
133         connection = "Connection:"i sep
134                 (("close"i @ { http->_p.persistent = 0; }) |
135                  ("keep-alive"i @ { http->_p.persistent = 1; })) eor;
136         header_line =
137                 ( content_length |
138                   transfer_encoding_chunked |
139                   trailer |
140                   range |
141                   content_range |
142                   content_md5 |
143                   connection ) $!
144                 {
145                         p = skip_header(http, buf, pe);
146                         fgoto ignored_header;
147                 };
148         headers = header_line* '\r''\n' > { really_done = 1; fbreak; };
149         more_headers := headers;
150         main := req_line headers;
153 %% write data;
155 void mog_http_reset_parser(struct mog_http *http)
157         int cs;
159         %% write init;
160         http->cs = cs;
161         memset(&http->_p, 0, sizeof(http->_p));
163         if (http->rbuf) /* already pipelined */
164                 http->_p.persist_client_at_start = 1;
165         else
166                 /*
167                  * we need to know persist_client when we start reading
168                  * the request because we do not want to break pipelined
169                  * requests
170                  */
171                 http->_p.persist_client_at_start = http->svc->persist_client;
173         /* these should probably be in mog_http_init */
174         http->forward = NULL;
175         http->wbuf = NULL;
178 void mog_http_init(struct mog_http *http, struct mog_svc *svc)
180         http->svc = svc;
181         http->rbuf = NULL;
182         mog_http_reset_parser(http);
185 enum mog_parser_state
186 mog_http_parse(struct mog_http *http, char *buf, size_t len)
188         char *p, *pe, *eof = NULL;
189         int cs = http->cs;
190         int really_done = 0;
191         size_t off = http->_p.buf_off;
192         uint32_t *mog_devid = &http->_p.mog_devid;
194         assert(http->wbuf == NULL && "unwritten data in buffer");
195         assert(off <= len && "http offset past end of buffer");
197         p = buf + off;
198         pe = buf + len;
200         assert((void *)(pe - p) == (void *)(len - off) &&
201                "pointers aren't same distance");
203         errno = 0;
204         %% write exec;
206         if (really_done)
207                 cs = http_parser_first_final;
209         http->cs = cs;
210         http->_p.buf_off = p - buf;
212         if (cs == http_parser_error || errno)
213                 return MOG_PARSER_ERROR;
215         assert(p <= pe && "buffer overflow after http parse");
216         assert(http->_p.buf_off <= len && "offset longer than len");
218         if (http->cs == http_parser_first_final) {
219                 http->_p.persistent &= http->_p.persist_client_at_start;
220                 return MOG_PARSER_DONE;
221         }
222         return MOG_PARSER_CONTINUE;