1
/* Copyright (C) agentzh */
3
#define EXTENDED_DEBUG 1
8
#include "chunked_parser.h"
9
#include "ngx_http_chunkin_util.h"
11
#define ngx_chunkin_min(x, y) ((x) < (y) ? (x) : (y))
23
ngx_http_chunkin_init_chunked_parser(ngx_http_request_t *r,
24
ngx_http_chunkin_ctx_t *ctx)
31
ctx->next_chunk = NULL;
34
ctx->chunk_size_order = 0;
35
ctx->chunk_bytes_read = 0;
37
ctx->chunks_total_size = 0;
39
ctx->parser_state = cs;
46
ngx_http_chunkin_run_chunked_parser(ngx_http_request_t *r,
47
ngx_http_chunkin_ctx_t *ctx, u_char **pos_addr, u_char *last, char *caller_info)
49
int cs = ctx->parser_state;
50
ngx_connection_t *c = r->connection;
51
char *pos = (char *) *pos_addr;
52
char *p = (char *) *pos_addr;
53
char *pe = (char *) last;
59
ngx_str_t user_agent = ngx_null_string;
63
#alphtype unsigned char;
70
ctx->chunk_bytes_read < ctx->chunk_size
73
action read_data_byte {
74
/* optimization for buffered chunk data */
76
rest = ngx_chunkin_min(
77
(ssize_t)ctx->chunk_size - (ssize_t)ctx->chunk_bytes_read,
80
dd("moving %d, chunk size %d, read %d, rest %d",
83
(int)ctx->chunk_bytes_read,
86
ctx->chunk_bytes_read += rest;
88
ctx->chunk->buf->last = (u_char *)p + 1;
89
ctx->chunks_total_size += rest;
91
/* dd("bytes read: %d (char '%c', bytes read %d, chunk size %d)", ctx->chunk->buf->last - ctx->chunk->buf->pos, *p, ctx->chunk_bytes_read, ctx->chunk_size); */
92
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
93
"chunkin: data bytes read: %uz (char: \"%c\")\n",
94
ctx->chunk_bytes_read, *p);
97
action start_reading_size {
98
ctx->chunk_bytes_read = 0;
100
ctx->chunk_size_order = 0;
104
ctx->chunk_size <<= 4;
105
ctx->chunk_size_order++;
106
if (*p >= 'A' && *p <= 'F') {
107
ctx->chunk_size |= 10 + *p - 'A';
108
} else if (*p >= 'a' && *p <= 'f') {
109
ctx->chunk_size |= 10 + *p - 'a';
111
ctx->chunk_size |= *p - '0';
114
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
115
"chunkin: chunk size: %uz\n", ctx->chunk_size);
118
action start_reading_data {
119
ctx->chunk = ngx_http_chunkin_get_buf(r->pool, ctx);
124
*ctx->next_chunk = ctx->chunk;
126
ctx->chunks = ctx->chunk;
129
ctx->next_chunk = &ctx->chunk->next;
133
b->last = b->pos = (u_char *) p;
138
if (ctx->chunk_bytes_read != ctx->chunk_size) {
139
ngx_log_error(NGX_LOG_ERR, c->log, 0,
140
"ERROR: chunk size not met: "
141
"%uz != %uz\n", ctx->chunk_bytes_read,
143
*pos_addr = (u_char*) p;
144
ctx->parser_state = chunked_error;
148
if (ctx->chunk_size == 0) {
149
/* remove the current chunk */
150
ctx->chunk->next = ctx->free_bufs;
151
ctx->free_bufs = ctx->chunk;
152
ctx->chunk = ctx->last_complete_chunk;
153
if (ctx->last_complete_chunk) {
154
ctx->last_complete_chunk->next = NULL;
159
ctx->last_complete_chunk = ctx->chunk;
167
CRLF = CR LF $err{ err_ctx = "CRLF"; };
169
chunk_size = (xdigit+ - "0"+) >start_reading_size $read_size;
171
chunk_data_octet = any when test_len;
173
chunk_data = (chunk_data_octet+)
176
$err{ err_ctx = "chunk_data"; }
179
chunk_data_terminator = CR when ! test_len LF
180
$err{ err_ctx = "chunk_data_terminator"; }
186
LWS = CRLF ? ( SP | HT )+;
188
separator = "(" | ")" | "<" | ">" | "@"
189
| "," | ";" | ":" | "\\" | ["]
190
| "/" | "[" | "]" | "?" | "="
191
| "{" | "}" | SP | HT
196
token = (any -- (CTL | separator))+;
198
chunk_ext_name = token;
200
TEXT = (any -- CTL) | LWS;
202
qdtext = TEXT -- ["];
206
quoted_pair = "\\" CHAR;
208
quoted_string = ["] ( qdtext | quoted_pair )* ["];
210
chunk_ext_val = token | quoted_string;
212
chunk_extension = ( ";" LWS* chunk_ext_name LWS*
213
("=" LWS* chunk_ext_val LWS*) ? )*
216
chunk = chunk_size (LWS* -- CRLF)
217
$err{ err_ctx = "chunk_size"; }
218
(chunk_extension -- CRLF) ?
220
$err{ err_ctx = "chunk_ext"; }
221
chunk_data chunk_data_terminator
224
last_chunk = "0"+ (LWS* -- CRLF)
225
(chunk_extension -- CRLF) ?
226
CRLF $err{ err_ctx = "last_chunk"; }
229
parser = chunk* last_chunk CRLF
230
$err{ err_ctx = "parser"; }
233
main := parser @finalize;
239
ctx->parser_state = cs;
241
*pos_addr = (u_char *) p;
244
dd("ASSERTION FAILED: p != pe");
251
if (cs == chunked_error) {
255
ngx_str_t headers_buf, preread_buf;
259
for (post.data = (u_char *) p, post.len = 0;
260
post.data + post.len != (u_char *) pe; post.len++)
262
if (post.len >= POST_TEXT_LEN) {
267
for (pre.data = (u_char *) p, pre.len = 0;
268
pre.data != (u_char *) pos; pre.data--, pre.len++)
270
if (pre.len >= PRE_TEXT_LEN) {
275
if (r->headers_in.user_agent) {
276
user_agent = r->headers_in.user_agent->value;
281
headers_buf.data = r->header_in->start;
282
headers_buf.len = ctx->saved_header_in_pos - r->header_in->start;
284
if (strcmp(caller_info, "preread") == 0) {
285
preread_buf.data = (u_char *) pos;
286
preread_buf.len = pe - pos;
289
preread_buf.data = ctx->saved_header_in_pos;
290
preread_buf.len = r->header_in->pos - ctx->saved_header_in_pos;
295
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
296
"bad chunked body (buf size %O, buf offset %O, "
297
"total decoded %uz, chunks count %d, "
298
"chunk size %uz, chunk data read %uz, "
299
"total to disk %uz, "
300
"raw body size %O, caller \"%s\", "
308
"keepalive %d, err ctx \"%s\", "
309
"ctx ref count %ud, user agent \"%V\", "
313
"headers \"%V\", preread \"%V\", "
317
"at char '%c' (%d), "
318
"near \"%V <-- HERE %V\", marked by \" <-- HERE \").\n",
319
(off_t) (pe - pos), (off_t) (p - pos),
320
ctx->chunks_total_size, ctx->chunks_count,
321
ctx->chunk_size, ctx->chunk_bytes_read,
322
ctx->chunks_written_size,
323
(off_t) ctx->raw_body_size, caller_info,
328
r->connection->ssl ? 1 : 0,
332
(int) r->keepalive, err_ctx,
333
ctx->count, &user_agent,
337
&headers_buf, &preread_buf,