cmogstored.git  about / heads / tags
alternative mogstored implementation for MogileFS
blob 5869f139e5d5ac9ccd52bcd17ae5b0cc7b667e59 3419 bytes (raw)
$ git show HEAD:chunk_parser.rl	# shows this blob on the CLI

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
 
/*
 * Copyright (C) 2012-2020 all contributors <cmogstored-public@yhbt.net>
 * License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
 */
#include "cmogstored.h"
#include "http_util.h"
static inline off_t hexchar2off(int xdigit)
{
	if (xdigit >= '0' && xdigit <= '9')
		return xdigit - '0';
	if (xdigit >= 'a' && xdigit <= 'f')
		return xdigit - 'a' + 10;
	if (xdigit >= 'A' && xdigit <= 'F')
		return xdigit - 'A' + 10;

	/* Ragel already does runtime range checking for us  */
	assert(0 && "invalid digit character");
	return (off_t)LLONG_MIN;
}

%%{
	machine chunk_parser;
	include http_common "http_common.rl";

	chunk_data = (any*) > {
		off_t buf_remain;
		size_t wr_len;

		if (http->_p.content_len == 0) { /* final chunk */
			http->_p.chunk_state = MOG_CHUNK_STATE_TRAILER;
			fhold;

			/* XXX this feels wrong ... */
			if (fpc >= buf) {
				assert(fc == '\n' && "bad chunk end");
				http->_p.line_end = to_u16(fpc - buf);
			}
			fgoto more_trailers;
		}

		assert(http->_p.content_len > 0 && "impossible content_len");

		buf_remain = len - (fpc - buf);
		if (buf_remain == 0)
			fbreak;

		assert(buf_remain > 0 && "impossible buf_remain");
		wr_len = MIN((size_t)http->_p.content_len, (size_t)buf_remain);
		assert(wr_len != 0 && "invalid wr_len");
		if (! mog_http_write_full(http->forward, fpc, wr_len))
			fbreak;

		http->_p.content_len -= wr_len;
		p += wr_len - 1;
		assert(p < pe && "buffer overrun");

		if (http->_p.content_len > 0) {
			really_done = 1;
			/* let caller handle reading the rest of the body */
			fbreak;
		}

		/* next chunk header */
		http->_p.chunk_state = MOG_CHUNK_STATE_SIZE;
		if (wr_len == buf_remain) {
			if (http->_p.content_len == 0)
				fgoto main;
			really_done = 1;
			fbreak;
		}

		/* more chunks in this buffer */
		assert(http->_p.content_len == 0 &&
		       "bad content_len at chunk end");

		fgoto main;
	};
	chunk = "\r\n"? # account for trailing CRLF in previous chunk
		(xdigit+) $ {
			off_t prev = http->_p.content_len;

			http->_p.content_len *= 16;
			http->_p.content_len += hexchar2off(fc);
			if (http->_p.content_len < prev) {
				errno = ERANGE;
				http->_p.content_len = -1;
				fbreak;
			}
		}
		(any -- [\r\n])*
		'\r' '\n' @ { http->_p.chunk_state = MOG_CHUNK_STATE_DATA; }
		chunk_data;
	main := chunk+;
}%%

%% write data;

void mog_chunk_init(struct mog_http *http)
{
	int cs;

	%% write init;
	assert(http->_p.chunked && "not chunked");
	http->cs = cs;
	http->_p.line_end = 0;
	http->_p.content_len = 0;
	http->_p.buf_off = 0;
	http->_p.chunk_state = MOG_CHUNK_STATE_SIZE;
}

enum mog_parser_state
mog_chunk_parse(struct mog_http *http, char *buf, size_t len)
{
	char *p, *pe, *eof = NULL;
	int cs = http->cs;
	int really_done = 0;
	size_t off = http->_p.buf_off;

	assert(http->wbuf == NULL && "unwritten data in buffer");
	assert(off <= len && "http offset past end of buffer");

	p = buf + off;
	pe = buf + len;

	assert((void *)(pe - p) == (void *)(len - off) &&
	       "pointers aren't same distance");

	errno = 0;
	%% write exec;

	if (really_done)
		cs = chunk_parser_first_final;

	http->cs = cs;
	http->_p.buf_off = p - buf;

	if (cs == chunk_parser_error || errno)
		return MOG_PARSER_ERROR;

	assert(p <= pe && "buffer overflow after chunk parse");
	assert(http->_p.buf_off <= len && "offset longer than len");

	if (http->cs == chunk_parser_first_final) return MOG_PARSER_DONE;
	return MOG_PARSER_CONTINUE;
}

git clone https://yhbt.net/cmogstored.git