summaryrefslogtreecommitdiffhomepage
path: root/src/nxt_fastcgi_record_parse.c
diff options
context:
space:
mode:
authorIgor Sysoev <igor@sysoev.ru>2017-01-17 20:00:00 +0300
committerIgor Sysoev <igor@sysoev.ru>2017-01-17 20:00:00 +0300
commit16cbf3c076a0aca6d47adaf3f719493674cf2363 (patch)
treee6530480020f62a2bdbf249988ec3e2a751d3927 /src/nxt_fastcgi_record_parse.c
downloadunit-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.c307
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;
+}