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>
5 #include "cmogstored.h"
8 static bool length_incr(off_t *len, unsigned c)
24 static char *skip_header(struct mog_http *http, char *buf, const char *pe)
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");
38 include http_common "http_common.rl";
39 include path_parser "path_parser.rl";
41 ignored_header := header_name ':' sep header_value eor @ {
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)
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
66 * RFC 7230 3.3.2, 3.3.3,:
67 * favor Transfer-Encoding over Content-Length
69 if (!http->_p.chunked &&
70 !length_incr(&http->_p.content_len, fc))
73 $! { errno = EINVAL; fbreak; }
75 content_range = "Content-Range:"i sep "bytes"LWS+
77 if (!length_incr(&http->_p.range_beg, fc))
80 $! { errno = EINVAL; fbreak; }
83 if (!length_incr(&http->_p.range_end, fc))
86 $! { errno = EINVAL; fbreak; }
88 eor > { http->_p.has_content_range = 1; };
89 range = "Range:"i sep (
91 http->_p.range_beg = http->_p.range_end = -1;
95 if (http->_p.range_beg < 0)
96 http->_p.range_beg = 0;
97 if (!length_incr(&http->_p.range_beg,
103 if (http->_p.range_end < 0)
104 http->_p.range_end = 0;
105 if (!length_incr(&http->_p.range_end,
110 http->_p.bad_range = 1;
111 p = skip_header(http, buf, pe);
112 fgoto ignored_header;
115 p = skip_header(http, buf, pe);
116 fgoto ignored_header;
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.
123 $! { errno = EINVAL; fbreak; }
125 http->_p.chunked = 1;
126 /* RFC 7230 3.3.2, 3.3.3,: ignore length if chunked */
127 http->_p.content_len = 0;
129 trailer = "Trailer:"i sep
130 (("Content-MD5"i @ { http->_p.has_md5 = 1; })
133 connection = "Connection:"i sep
134 (("close"i @ { http->_p.persistent = 0; }) |
135 ("keep-alive"i @ { http->_p.persistent = 1; })) eor;
138 transfer_encoding_chunked |
145 p = skip_header(http, buf, pe);
146 fgoto ignored_header;
148 headers = header_line* '\r''\n' > { really_done = 1; fbreak; };
149 more_headers := headers;
150 main := req_line headers;
155 void mog_http_reset_parser(struct mog_http *http)
161 memset(&http->_p, 0, sizeof(http->_p));
163 if (http->rbuf) /* already pipelined */
164 http->_p.persist_client_at_start = 1;
167 * we need to know persist_client when we start reading
168 * the request because we do not want to break pipelined
171 http->_p.persist_client_at_start = http->svc->persist_client;
173 /* these should probably be in mog_http_init */
174 http->forward = NULL;
178 void mog_http_init(struct mog_http *http, struct mog_svc *svc)
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;
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");
200 assert((void *)(pe - p) == (void *)(len - off) &&
201 "pointers aren't same distance");
207 cs = http_parser_first_final;
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;
222 return MOG_PARSER_CONTINUE;