cmogstored 1.8.1 - use default system stack size
[cmogstored.git] / chunk_parser.rl
blob5869f139e5d5ac9ccd52bcd17ae5b0cc7b667e59
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"
7 static inline off_t hexchar2off(int xdigit)
9         if (xdigit >= '0' && xdigit <= '9')
10                 return xdigit - '0';
11         if (xdigit >= 'a' && xdigit <= 'f')
12                 return xdigit - 'a' + 10;
13         if (xdigit >= 'A' && xdigit <= 'F')
14                 return xdigit - 'A' + 10;
16         /* Ragel already does runtime range checking for us  */
17         assert(0 && "invalid digit character");
18         return (off_t)LLONG_MIN;
21 %%{
22         machine chunk_parser;
23         include http_common "http_common.rl";
25         chunk_data = (any*) > {
26                 off_t buf_remain;
27                 size_t wr_len;
29                 if (http->_p.content_len == 0) { /* final chunk */
30                         http->_p.chunk_state = MOG_CHUNK_STATE_TRAILER;
31                         fhold;
33                         /* XXX this feels wrong ... */
34                         if (fpc >= buf) {
35                                 assert(fc == '\n' && "bad chunk end");
36                                 http->_p.line_end = to_u16(fpc - buf);
37                         }
38                         fgoto more_trailers;
39                 }
41                 assert(http->_p.content_len > 0 && "impossible content_len");
43                 buf_remain = len - (fpc - buf);
44                 if (buf_remain == 0)
45                         fbreak;
47                 assert(buf_remain > 0 && "impossible buf_remain");
48                 wr_len = MIN((size_t)http->_p.content_len, (size_t)buf_remain);
49                 assert(wr_len != 0 && "invalid wr_len");
50                 if (! mog_http_write_full(http->forward, fpc, wr_len))
51                         fbreak;
53                 http->_p.content_len -= wr_len;
54                 p += wr_len - 1;
55                 assert(p < pe && "buffer overrun");
57                 if (http->_p.content_len > 0) {
58                         really_done = 1;
59                         /* let caller handle reading the rest of the body */
60                         fbreak;
61                 }
63                 /* next chunk header */
64                 http->_p.chunk_state = MOG_CHUNK_STATE_SIZE;
65                 if (wr_len == buf_remain) {
66                         if (http->_p.content_len == 0)
67                                 fgoto main;
68                         really_done = 1;
69                         fbreak;
70                 }
72                 /* more chunks in this buffer */
73                 assert(http->_p.content_len == 0 &&
74                        "bad content_len at chunk end");
76                 fgoto main;
77         };
78         chunk = "\r\n"? # account for trailing CRLF in previous chunk
79                 (xdigit+) $ {
80                         off_t prev = http->_p.content_len;
82                         http->_p.content_len *= 16;
83                         http->_p.content_len += hexchar2off(fc);
84                         if (http->_p.content_len < prev) {
85                                 errno = ERANGE;
86                                 http->_p.content_len = -1;
87                                 fbreak;
88                         }
89                 }
90                 (any -- [\r\n])*
91                 '\r' '\n' @ { http->_p.chunk_state = MOG_CHUNK_STATE_DATA; }
92                 chunk_data;
93         main := chunk+;
94 }%%
96 %% write data;
98 void mog_chunk_init(struct mog_http *http)
100         int cs;
102         %% write init;
103         assert(http->_p.chunked && "not chunked");
104         http->cs = cs;
105         http->_p.line_end = 0;
106         http->_p.content_len = 0;
107         http->_p.buf_off = 0;
108         http->_p.chunk_state = MOG_CHUNK_STATE_SIZE;
111 enum mog_parser_state
112 mog_chunk_parse(struct mog_http *http, char *buf, size_t len)
114         char *p, *pe, *eof = NULL;
115         int cs = http->cs;
116         int really_done = 0;
117         size_t off = http->_p.buf_off;
119         assert(http->wbuf == NULL && "unwritten data in buffer");
120         assert(off <= len && "http offset past end of buffer");
122         p = buf + off;
123         pe = buf + len;
125         assert((void *)(pe - p) == (void *)(len - off) &&
126                "pointers aren't same distance");
128         errno = 0;
129         %% write exec;
131         if (really_done)
132                 cs = chunk_parser_first_final;
134         http->cs = cs;
135         http->_p.buf_off = p - buf;
137         if (cs == chunk_parser_error || errno)
138                 return MOG_PARSER_ERROR;
140         assert(p <= pe && "buffer overflow after chunk parse");
141         assert(http->_p.buf_off <= len && "offset longer than len");
143         if (http->cs == chunk_parser_first_final) return MOG_PARSER_DONE;
144         return MOG_PARSER_CONTINUE;