summaryrefslogtreecommitdiff
path: root/src/libtipidee/tipidee_headers_parse.c
diff options
context:
space:
mode:
authorLaurent Bercot <ska-skaware@skarnet.org>2023-08-05 11:51:25 +0000
committerLaurent Bercot <ska@appnovation.com>2023-08-05 11:51:25 +0000
commit17c382d1c9d7236c101418060758d2296cc5e17e (patch)
treefd00e58df0d9d3c70ddd1accfec9e819249c672a /src/libtipidee/tipidee_headers_parse.c
downloadtipidee-17c382d1c9d7236c101418060758d2296cc5e17e.tar.xz
Initial commit
Signed-off-by: Laurent Bercot <ska@appnovation.com>
Diffstat (limited to 'src/libtipidee/tipidee_headers_parse.c')
-rw-r--r--src/libtipidee/tipidee_headers_parse.c210
1 files changed, 210 insertions, 0 deletions
diff --git a/src/libtipidee/tipidee_headers_parse.c b/src/libtipidee/tipidee_headers_parse.c
new file mode 100644
index 0000000..57108bb
--- /dev/null
+++ b/src/libtipidee/tipidee_headers_parse.c
@@ -0,0 +1,210 @@
+/* ISC license. */
+
+#include <stdint.h>
+#include <string.h>
+#include <strings.h>
+#include <errno.h>
+
+#include <skalibs/bytestr.h>
+#include <skalibs/buffer.h>
+#include <skalibs/error.h>
+#include <skalibs/avltreen.h>
+#include <skalibs/unix-timed.h>
+// #include <skalibs/lolstdio.h>
+
+#include <tipidee/headers.h>
+
+/*
+
+Reads header lines, separates into \0-terminated keys+values.
+key is at hdr->buf + hdr->list[i].left
+value is at hdr->buf + hdr->list[i].right
+Compresses linear whitespace.
+Does not unquote strings/comments in values.
+
+
+st\ev 0 1 2 3 4 5 6 7
+ CTL CR LF LWS : special normal 8bit
+
+00 kp
+START X END? END X X X K X
+
+01
+END? X X END X X X X X
+
+02 zv p
+K X X X X V1 X K X
+
+03 p p p p
+V1 X V1?? V1? V1 V V V V
+
+04
+V1?? X X V1? X X X X X
+
+05 zn zn znkp
+V1? X END? END V1 X X K X
+
+06 s s s p p p p
+V X V2?? V2? V2 V V V V
+
+07 p p p p
+V2 X V2?? V2? V2 V V V V
+
+08
+V2?? X X V2? X X X X X
+
+09 mzn mzn mznkp
+V2? X END? END V2 X X K X
+
+END = 0a, X = 0b
+
+0x4000 s: write space
+0x2000 m: go back one char
+0x1000 z: write \0
+0x0800 n: cut key/value pair, prepare next
+0x0400 k: start of key
+0x0200 v: start of value
+0x0100 p: write current char
+
+states: 4 bits, actions: 7 bits
+
+*/
+
+
+struct tainp_s
+{
+ tain const *deadline ;
+ tain *stamp ;
+} ;
+
+typedef int get1_func (buffer *, char *, struct tainp_s *) ;
+typedef get1_func *get1_func_ref ;
+
+static int get1_timed (buffer *b, char *c, struct tainp_s *d)
+{
+ return buffer_timed_get(b, c, 1, d->deadline, d->stamp) ;
+}
+
+static int get1_notimed (buffer *b, char *c, struct tainp_s *data)
+{
+ (void)data ;
+ return buffer_get(b, c, 1) == 1 ;
+}
+
+static uint8_t cclass (char c)
+{
+ static uint8_t const ctable[128] = "00000000032001000000000000000000365666665566566566666666664565655666666666666666666666666665556666666666666666666666666666656560" ;
+ return c & 0x80 ? 7 : ctable[(uint8_t)c] - '0' ;
+}
+
+static int needs_processing (char const *s)
+{
+ if (!strcasecmp(s, "Set-Cookie")) return 0 ;
+ if (str_start(s, "X-")) return 0 ;
+ return 1 ;
+}
+
+static int tipidee_headers_parse_with (buffer *b, tipidee_headers *hdr, get1_func_ref next, struct tainp_s *data, disize *header, uint32_t *state)
+{
+ static uint16_t const table[10][8] =
+ {
+ { 0x000b, 0x0001, 0x000a, 0x000b, 0x000b, 0x000b, 0x0502, 0x000b },
+ { 0x000b, 0x000b, 0x000a, 0x000b, 0x000b, 0x000b, 0x000b, 0x000b },
+ { 0x000b, 0x000b, 0x000b, 0x000b, 0x1203, 0x000b, 0x0102, 0x000b },
+ { 0x000b, 0x0004, 0x0005, 0x0003, 0x0106, 0x0106, 0x0106, 0x0106 },
+ { 0x000b, 0x000b, 0x0005, 0x000b, 0x000b, 0x000b, 0x000b, 0x000b },
+ { 0x000b, 0x1801, 0x180a, 0x0003, 0x000b, 0x000b, 0x1d02, 0x000b },
+ { 0x000b, 0x4008, 0x4009, 0x4007, 0x0106, 0x0106, 0x0106, 0x0106 },
+ { 0x000b, 0x0008, 0x0009, 0x0007, 0x0106, 0x0106, 0x0106, 0x0106 },
+ { 0x000b, 0x000b, 0x0009, 0x000b, 0x000b, 0x000b, 0x000b, 0x000b },
+ { 0x000b, 0x3801, 0x380a, 0x0007, 0x000b, 0x000b, 0x3d02, 0x000b },
+ } ;
+ while (*state < 0x0a)
+ {
+ uint16_t c ;
+ char cur ;
+ if (!(*next)(b, &cur, data))
+ return errno == ETIMEDOUT ? 408 : error_isagain(errno) ? -2 : -1 ;
+ c = table[*state][cclass(cur)] ;
+/*
+ {
+
+ char s[2] = { cur, 0 } ;
+ LOLDEBUG("tipidee_headers_parse_with: state %hhu, event %s, newstate %hhu, actions %s%s%s%s%s%s%s",
+ *state,
+ cur == '\r' ? "\\r" : cur == '\n' ? "\\n" : s,
+ c & 0x0f,
+ c & 0x4000 ? "s" : "",
+ c & 0x2000 ? "m" : "",
+ c & 0x1000 ? "z" : "",
+ c & 0x0800 ? "n" : "",
+ c & 0x0400 ? "k" : "",
+ c & 0x0200 ? "v" : "",
+ c & 0x0100 ? "p" : ""
+ ) ;
+ }
+*/
+ *state = c & 0x0f ;
+ if (c & 0x4000) { if (hdr->len >= hdr->max) return 413 ; hdr->buf[hdr->len++] = ' ' ; }
+ if (c & 0x2000) hdr->len-- ;
+ if (c & 0x1000) { if (hdr->len >= hdr->max) return 413 ; hdr->buf[hdr->len++] = 0 ; }
+ if (c & 0x0800)
+ {
+ uint32_t prev ;
+ if (hdr->n >= TIPIDEE_HEADERS_MAX) return 413 ;
+ hdr->list[hdr->n] = *header ;
+ if (needs_processing(hdr->buf + header->left))
+ {
+// LOLDEBUG("tipidee_headers_parse_with: n: adding header %u - key %zu (%s), value %zu (%s)", hdr->n, header->left, hdr->buf + header->left, header->right, hdr->buf + header->right) ;
+ if (avltreen_search(&hdr->map, hdr->buf + header->left, &prev))
+ {
+ size_t start = hdr->list[prev+1].left ;
+ if (prev+1 == hdr->n)
+ {
+ hdr->buf[start - 1] = ',' ;
+ hdr->buf[start] = ' ' ;
+ memcpy(hdr->buf + start + 1, hdr->buf + header->right, hdr->len - header->right) ;
+ }
+ else
+ {
+ size_t len = header->left - start ;
+ size_t offset = hdr->len - header->right + 1 ;
+ char tmp[len] ;
+ memcpy(tmp, hdr->buf + start, len) ;
+ hdr->buf[start - 1] = ',' ;
+ hdr->buf[start] = ' ' ;
+ memcpy(hdr->buf + start + 1, hdr->buf + header->right, hdr->len - header->right) ;
+ memcpy(hdr->buf + start + offset, tmp, len) ;
+ for (uint32_t i = prev + 1 ; i < hdr->n ; i++)
+ {
+ hdr->list[i].left += offset ;
+ hdr->list[i].right += offset ;
+ }
+ }
+ hdr->len -= header->right - header->left - 1 ;
+ hdr->n-- ;
+ }
+ else if (!avltreen_insert(&hdr->map, hdr->n)) return 500 ;
+ }
+ hdr->n++ ;
+ }
+ if (c & 0x0400) header->left = hdr->len ;
+ if (c & 0x0200) header->right = hdr->len ;
+ if (c & 0x0100) { if (hdr->len >= hdr->max) return 413 ; hdr->buf[hdr->len++] = cur ; }
+ }
+ if (*state > 0x0a) return 400 ;
+ return 0 ;
+}
+
+int tipidee_headers_timed_parse (buffer *b, tipidee_headers *hdr, tain const *deadline, tain *stamp)
+{
+ struct tainp_s d = { .deadline = deadline, .stamp = stamp } ;
+ disize header = DISIZE_ZERO ;
+ uint32_t state = 0 ;
+ return tipidee_headers_parse_with(b, hdr, &get1_timed, &d, &header, &state) ;
+}
+
+int tipidee_headers_parse_nb (buffer *b, tipidee_headers *hdr, disize *header, uint32_t *state)
+{
+ return tipidee_headers_parse_with(b, hdr, &get1_notimed, 0, header, state) ;
+}