diff options
author | Igor Sysoev <igor@sysoev.ru> | 2017-01-17 20:00:00 +0300 |
---|---|---|
committer | Igor Sysoev <igor@sysoev.ru> | 2017-01-17 20:00:00 +0300 |
commit | 16cbf3c076a0aca6d47adaf3f719493674cf2363 (patch) | |
tree | e6530480020f62a2bdbf249988ec3e2a751d3927 /src/nxt_fastcgi_record_parse.c | |
download | unit-16cbf3c076a0aca6d47adaf3f719493674cf2363.tar.gz unit-16cbf3c076a0aca6d47adaf3f719493674cf2363.tar.bz2 |
Initial version.
Diffstat (limited to 'src/nxt_fastcgi_record_parse.c')
-rw-r--r-- | src/nxt_fastcgi_record_parse.c | 307 |
1 files changed, 307 insertions, 0 deletions
diff --git a/src/nxt_fastcgi_record_parse.c b/src/nxt_fastcgi_record_parse.c new file mode 100644 index 00000000..66b9d4c3 --- /dev/null +++ b/src/nxt_fastcgi_record_parse.c @@ -0,0 +1,307 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) NGINX, Inc. + */ + +#include <nxt_main.h> + + +#define NXT_FASTCGI_DATA_MIDDLE 0 +#define NXT_FASTCGI_DATA_END_ON_BORDER 1 +#define NXT_FASTCGI_DATA_END 2 + + +static nxt_int_t nxt_fastcgi_buffer(nxt_fastcgi_parse_t *fp, nxt_buf_t ***tail, + nxt_buf_t *in); + + +void +nxt_fastcgi_record_parse(nxt_fastcgi_parse_t *fp, nxt_buf_t *in) +{ + u_char ch; + nxt_int_t ret, stream; + nxt_buf_t *b, *nb, **tail[2]; + const char *msg; + nxt_thread_t *thr; + enum { + sw_fastcgi_version = 0, + sw_fastcgi_type, + sw_fastcgi_request_id_high, + sw_fastcgi_request_id_low, + sw_fastcgi_content_length_high, + sw_fastcgi_content_length_low, + sw_fastcgi_padding_length, + sw_fastcgi_reserved, + sw_fastcgi_data, + sw_fastcgi_padding, + sw_fastcgi_end_request, + } state; + + fp->out[0] = NULL; + fp->out[1] = NULL; + + tail[0] = &fp->out[0]; + tail[1] = &fp->out[1]; + + state = fp->state; + + for (b = in; b != NULL; b = b->next) { + + if (nxt_buf_is_sync(b)) { + **tail = b; + *tail = &b->next; + continue; + } + + fp->pos = b->mem.pos; + + while (fp->pos < b->mem.free) { + /* + * The sw_fastcgi_data state is tested outside the + * switch to preserve fp->pos and to not touch memory. + */ + if (state == sw_fastcgi_data) { + + /* + * fp->type here can be only NXT_FASTCGI_STDOUT + * or NXT_FASTCGI_STDERR. NXT_FASTCGI_END_REQUEST + * is tested in sw_fastcgi_reserved. + */ + stream = fp->type - NXT_FASTCGI_STDOUT; + + ret = nxt_fastcgi_buffer(fp, &tail[stream], b); + + if (ret == NXT_FASTCGI_DATA_MIDDLE) { + goto next; + } + + if (nxt_slow_path(ret == NXT_ERROR)) { + fp->error = 1; + goto done; + } + + if (fp->padding == 0) { + state = sw_fastcgi_version; + + } else { + state = sw_fastcgi_padding; + } + + if (ret == NXT_FASTCGI_DATA_END_ON_BORDER) { + goto next; + } + + /* ret == NXT_FASTCGI_DATA_END */ + } + + ch = *fp->pos++; + + nxt_thread_log_debug("fastcgi record byte: %02Xd", ch); + + switch (state) { + + case sw_fastcgi_version: + if (nxt_fast_path(ch == 1)) { + state = sw_fastcgi_type; + continue; + } + + msg = "unsupported FastCGI protocol version"; + goto fastcgi_error; + + case sw_fastcgi_type: + switch (ch) { + case NXT_FASTCGI_STDOUT: + case NXT_FASTCGI_STDERR: + case NXT_FASTCGI_END_REQUEST: + fp->type = ch; + state = sw_fastcgi_request_id_high; + continue; + default: + msg = "invalid FastCGI record type"; + goto fastcgi_error; + } + + case sw_fastcgi_request_id_high: + /* FastCGI multiplexing is not supported. */ + if (nxt_fast_path(ch == 0)) { + state = sw_fastcgi_request_id_low; + continue; + } + + msg = "unexpected FastCGI request ID high byte"; + goto fastcgi_error; + + case sw_fastcgi_request_id_low: + if (nxt_fast_path(ch == 1)) { + state = sw_fastcgi_content_length_high; + continue; + } + + msg = "unexpected FastCGI request ID low byte"; + goto fastcgi_error; + + case sw_fastcgi_content_length_high: + fp->length = ch << 8; + state = sw_fastcgi_content_length_low; + continue; + + case sw_fastcgi_content_length_low: + fp->length |= ch; + state = sw_fastcgi_padding_length; + continue; + + case sw_fastcgi_padding_length: + fp->padding = ch; + state = sw_fastcgi_reserved; + continue; + + case sw_fastcgi_reserved: + nxt_thread_log_debug("fastcgi record type:%d " + "length:%uz padding:%d", + fp->type, fp->length, fp->padding); + + if (nxt_fast_path(fp->type != NXT_FASTCGI_END_REQUEST)) { + state = sw_fastcgi_data; + continue; + } + + state = sw_fastcgi_end_request; + continue; + + case sw_fastcgi_data: + /* + * This state is processed before the switch. + * It added here just to suppress a warning. + */ + continue; + + case sw_fastcgi_padding: + /* + * No special fast processing of padding + * because it usually takes just 1-7 bytes. + */ + fp->padding--; + + if (fp->padding == 0) { + nxt_thread_log_debug("fastcgi record end"); + state = sw_fastcgi_version; + } + continue; + + case sw_fastcgi_end_request: + /* Just skip 8 bytes of END_REQUEST. */ + fp->length--; + + if (fp->length != 0) { + continue; + } + + fp->done = 1; + + nxt_thread_log_debug("fastcgi end request"); + + goto done; + } + } + + if (b->retain == 0) { + /* No record data was found in a buffer. */ + thr = nxt_thread(); + nxt_thread_current_work_queue_add(thr, b->completion_handler, + b, b->parent, thr->log); + } + + next: + + continue; + } + + fp->state = state; + + return; + +fastcgi_error: + + nxt_thread_log_error(NXT_LOG_ERR, "upstream sent %s: %d", msg, ch); + + fp->fastcgi_error = 1; + +done: + + nb = fp->last_buf(fp); + + if (nxt_fast_path(nb != NULL)) { + *tail[0] = nb; + + } else { + fp->error = 1; + } + + // STUB: fp->fastcgi_error = 1; + // STUB: fp->error = 1; + + return; +} + + +static nxt_int_t +nxt_fastcgi_buffer(nxt_fastcgi_parse_t *fp, nxt_buf_t ***tail, nxt_buf_t *in) +{ + u_char *p; + size_t size; + nxt_buf_t *b; + + if (fp->length == 0) { + return NXT_FASTCGI_DATA_END; + } + + p = fp->pos; + size = in->mem.free - p; + + if (fp->length >= size && in->retain == 0) { + /* + * Use original buffer if the buffer is lesser than or equal to + * FastCGI record size and this is the first record in the buffer. + */ + in->mem.pos = p; + **tail = in; + *tail = &in->next; + + } else { + b = nxt_buf_mem_alloc(fp->mem_pool, 0, 0); + if (nxt_slow_path(b == NULL)) { + return NXT_ERROR; + } + + **tail = b; + *tail = &b->next; + + b->parent = in; + in->retain++; + b->mem.pos = p; + b->mem.start = p; + + if (fp->length < size) { + p += fp->length; + fp->pos = p; + + b->mem.free = p; + b->mem.end = p; + + return NXT_FASTCGI_DATA_END; + } + + b->mem.free = in->mem.free; + b->mem.end = in->mem.free; + } + + fp->length -= size; + + if (fp->length == 0) { + return NXT_FASTCGI_DATA_END_ON_BORDER; + } + + return NXT_FASTCGI_DATA_MIDDLE; +} |