summaryrefslogtreecommitdiffhomepage
path: root/src/nxt_http_chunk_parse.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/nxt_http_chunk_parse.c263
1 files changed, 263 insertions, 0 deletions
diff --git a/src/nxt_http_chunk_parse.c b/src/nxt_http_chunk_parse.c
new file mode 100644
index 00000000..c0402605
--- /dev/null
+++ b/src/nxt_http_chunk_parse.c
@@ -0,0 +1,263 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#include <nxt_main.h>
+
+
+#define NXT_HTTP_CHUNK_MIDDLE 0
+#define NXT_HTTP_CHUNK_END_ON_BORDER 1
+#define NXT_HTTP_CHUNK_END 2
+
+
+#define \
+nxt_size_is_sufficient(cs) \
+ (cs < ((__typeof__(cs)) 1 << (sizeof(cs) * 8 - 4)))
+
+
+static nxt_int_t nxt_http_chunk_buffer(nxt_http_chunk_parse_t *hcp,
+ nxt_buf_t ***tail, nxt_buf_t *in);
+
+
+nxt_buf_t *
+nxt_http_chunk_parse(nxt_http_chunk_parse_t *hcp, nxt_buf_t *in)
+{
+ u_char c, ch;
+ nxt_int_t ret;
+ nxt_buf_t *b, *out, *nb, **tail;
+ nxt_thread_t *thr;
+ enum {
+ sw_start = 0,
+ sw_chunk_size,
+ sw_chunk_size_linefeed,
+ sw_chunk_end_newline,
+ sw_chunk_end_linefeed,
+ sw_chunk,
+ } state;
+
+ out = NULL;
+ tail = &out;
+
+ state = hcp->state;
+
+ for (b = in; b != NULL; b = b->next) {
+
+ hcp->pos = b->mem.pos;
+
+ while (hcp->pos < b->mem.free) {
+ /*
+ * The sw_chunk state is tested outside the switch
+ * to preserve hcp->pos and to not touch memory.
+ */
+ if (state == sw_chunk) {
+ ret = nxt_http_chunk_buffer(hcp, &tail, b);
+
+ if (ret == NXT_HTTP_CHUNK_MIDDLE) {
+ goto next;
+ }
+
+ if (nxt_slow_path(ret == NXT_ERROR)) {
+ hcp->error = 1;
+ goto done;
+ }
+
+ state = sw_chunk_end_newline;
+
+ if (ret == NXT_HTTP_CHUNK_END_ON_BORDER) {
+ goto next;
+ }
+
+ /* ret == NXT_HTTP_CHUNK_END_ON_BORDER */
+ }
+
+ ch = *hcp->pos++;
+
+ switch (state) {
+
+ case sw_start:
+ state = sw_chunk_size;
+
+ c = ch - '0';
+
+ if (c <= 9) {
+ hcp->chunk_size = c;
+ continue;
+ }
+
+ c = (ch | 0x20) - 'a';
+
+ if (c <= 5) {
+ hcp->chunk_size = 0x0a + c;
+ continue;
+ }
+
+ goto chunk_error;
+
+ case sw_chunk_size:
+
+ c = ch - '0';
+
+ if (c > 9) {
+ c = (ch | 0x20) - 'a';
+
+ if (nxt_fast_path(c <= 5)) {
+ c += 0x0a;
+
+ } else if (nxt_fast_path(ch == NXT_CR)) {
+ state = sw_chunk_size_linefeed;
+ continue;
+
+ } else {
+ goto chunk_error;
+ }
+ }
+
+ if (nxt_fast_path(nxt_size_is_sufficient(hcp->chunk_size))) {
+ hcp->chunk_size = (hcp->chunk_size << 4) + c;
+ continue;
+ }
+
+ goto chunk_error;
+
+ case sw_chunk_size_linefeed:
+ if (nxt_fast_path(ch == NXT_LF)) {
+
+ if (hcp->chunk_size != 0) {
+ state = sw_chunk;
+ continue;
+ }
+
+ hcp->last = 1;
+ state = sw_chunk_end_newline;
+ continue;
+ }
+
+ goto chunk_error;
+
+ case sw_chunk_end_newline:
+ if (nxt_fast_path(ch == NXT_CR)) {
+ state = sw_chunk_end_linefeed;
+ continue;
+ }
+
+ goto chunk_error;
+
+ case sw_chunk_end_linefeed:
+ if (nxt_fast_path(ch == NXT_LF)) {
+
+ if (!hcp->last) {
+ state = sw_start;
+ continue;
+ }
+
+ goto done;
+ }
+
+ goto chunk_error;
+
+ case sw_chunk:
+ /*
+ * This state is processed before the switch.
+ * It added here just to suppress a warning.
+ */
+ continue;
+ }
+ }
+
+ if (b->retain == 0) {
+ /* No chunk 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;
+ }
+
+ hcp->state = state;
+
+ return out;
+
+chunk_error:
+
+ hcp->chunk_error = 1;
+
+done:
+
+ nb = nxt_buf_sync_alloc(hcp->mem_pool, NXT_BUF_SYNC_LAST);
+
+ if (nxt_fast_path(nb != NULL)) {
+ *tail = nb;
+
+ } else {
+ hcp->error = 1;
+ }
+
+ // STUB: hcp->chunk_error = 1;
+ // STUB: hcp->error = 1;
+
+ return out;
+}
+
+
+static nxt_int_t
+nxt_http_chunk_buffer(nxt_http_chunk_parse_t *hcp, nxt_buf_t ***tail,
+ nxt_buf_t *in)
+{
+ u_char *p;
+ size_t size;
+ nxt_buf_t *b;
+
+ p = hcp->pos;
+ size = in->mem.free - p;
+
+ if (hcp->chunk_size >= size && in->retain == 0) {
+ /*
+ * Use original buffer if the buffer is lesser than or equal
+ * to a chunk size and this is the first chunk in the buffer.
+ */
+ in->mem.pos = p;
+ **tail = in;
+ *tail = &in->next;
+
+ } else {
+ b = nxt_buf_mem_alloc(hcp->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 (hcp->chunk_size < size) {
+ p += hcp->chunk_size;
+ hcp->pos = p;
+
+ b->mem.free = p;
+ b->mem.end = p;
+
+ return NXT_HTTP_CHUNK_END;
+ }
+
+ b->mem.free = in->mem.free;
+ b->mem.end = in->mem.free;
+ }
+
+ hcp->chunk_size -= size;
+
+ if (hcp->chunk_size == 0) {
+ return NXT_HTTP_CHUNK_END_ON_BORDER;
+ }
+
+ return NXT_HTTP_CHUNK_MIDDLE;
+}