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"
7 static inline off_t hexchar2off(int xdigit)
9 if (xdigit >= '0' && xdigit <= '9')
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;
23 include http_common "http_common.rl";
25 chunk_data = (any*) > {
29 if (http->_p.content_len == 0) { /* final chunk */
30 http->_p.chunk_state = MOG_CHUNK_STATE_TRAILER;
33 /* XXX this feels wrong ... */
35 assert(fc == '\n' && "bad chunk end");
36 http->_p.line_end = to_u16(fpc - buf);
41 assert(http->_p.content_len > 0 && "impossible content_len");
43 buf_remain = len - (fpc - buf);
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))
53 http->_p.content_len -= wr_len;
55 assert(p < pe && "buffer overrun");
57 if (http->_p.content_len > 0) {
59 /* let caller handle reading the rest of the body */
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)
72 /* more chunks in this buffer */
73 assert(http->_p.content_len == 0 &&
74 "bad content_len at chunk end");
78 chunk = "\r\n"? # account for trailing CRLF in previous chunk
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) {
86 http->_p.content_len = -1;
91 '\r' '\n' @ { http->_p.chunk_state = MOG_CHUNK_STATE_DATA; }
98 void mog_chunk_init(struct mog_http *http)
103 assert(http->_p.chunked && "not chunked");
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;
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");
125 assert((void *)(pe - p) == (void *)(len - off) &&
126 "pointers aren't same distance");
132 cs = chunk_parser_first_final;
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;