dpward / rpms / sssd

Forked from rpms/sssd 3 years ago
Clone

Blame SOURCES/0009-NOUPSTREAM-Bundle-http-parser.patch

b2d430
From bd5daccde22322d216d450f40a50c1a3d7c5a32c Mon Sep 17 00:00:00 2001
b2d430
From: Jakub Hrozek <jhrozek@redhat.com>
b2d430
Date: Tue, 12 Jul 2016 13:36:32 +0200
b2d430
Subject: [PATCH 09/18] NOUPSTREAM: Bundle http-parser
b2d430
b2d430
---
b2d430
 Makefile.am                            |   15 +
b2d430
 src/external/libhttp_parser.m4         |    7 +-
b2d430
 src/responder/secrets/http_parser.c    | 2469 ++++++++++++++++++++++++++++++++
b2d430
 src/responder/secrets/http_parser.h    |  362 +++++
b2d430
 src/responder/secrets/secsrv_private.h |    2 +-
b2d430
 5 files changed, 2850 insertions(+), 5 deletions(-)
b2d430
 create mode 100644 src/responder/secrets/http_parser.c
b2d430
 create mode 100644 src/responder/secrets/http_parser.h
b2d430
b2d430
diff --git a/Makefile.am b/Makefile.am
b2d430
index 706b60d6a065e0a983f5a1cfbc26a78331c67d58..d05919705910fa565ff954224ce40feb5d7ff39f 100644
b2d430
--- a/Makefile.am
b2d430
+++ b/Makefile.am
b2d430
@@ -1370,6 +1370,9 @@ sssd_secrets_SOURCES = \
b2d430
     $(SSSD_RESPONDER_OBJ) \
b2d430
     $(SSSD_RESOLV_OBJ) \
b2d430
     $(NULL)
b2d430
+sssd_secrets_CFLAGS = \
b2d430
+    $(AM_CFLAGS) \
b2d430
+    $(NULL)
b2d430
 sssd_secrets_LDADD = \
b2d430
     $(HTTP_PARSER_LIBS) \
b2d430
     $(JANSSON_LIBS) \
b2d430
@@ -1379,6 +1382,18 @@ sssd_secrets_LDADD = \
b2d430
     $(CARES_LIBS) \
b2d430
     $(SSSD_INTERNAL_LTLIBS) \
b2d430
     $(NULL)
b2d430
+
b2d430
+if BUNDLE_HTTP_PARSER
b2d430
+sssd_secrets_CFLAGS += \
b2d430
+    -DHTTP_PARSER_STRICT=1 \
b2d430
+    $(NULL)
b2d430
+
b2d430
+sssd_secrets_SOURCES += \
b2d430
+    src/responder/secrets/http_parser.c \
b2d430
+    src/responder/secrets/http_parser.h \
b2d430
+    $(NULL)
b2d430
+endif
b2d430
+
b2d430
 endif
b2d430
 
b2d430
 sssd_be_SOURCES = \
b2d430
diff --git a/src/external/libhttp_parser.m4 b/src/external/libhttp_parser.m4
b2d430
index 504bdf0f66c95b3d224c677a205a46e6f8b44726..c4c5b0dad29da281295529be047d30e502f5de70 100644
b2d430
--- a/src/external/libhttp_parser.m4
b2d430
+++ b/src/external/libhttp_parser.m4
b2d430
@@ -16,7 +16,6 @@ AS_IF([test x"$found_http_parser" != xyes],
b2d430
                                     [-L$sss_extra_libdir -lhttp_parser])
b2d430
                       ],
b2d430
                       [-L$sss_extra_libdir -lhttp_parser_strict])],
b2d430
-        [AC_MSG_ERROR([
b2d430
-You must have the header file http_parse.h installed to build sssd
b2d430
-with secrets responder. If you want to build sssd without secret responder
b2d430
-then specify --without-secrets when running configure.])])])
b2d430
+        [AC_MSG_WARN([Will use bundled http-parser])])])
b2d430
+
b2d430
+AM_CONDITIONAL([BUNDLE_HTTP_PARSER], [test x"$HTTP_PARSER_LIBS" = x])
b2d430
diff --git a/src/responder/secrets/http_parser.c b/src/responder/secrets/http_parser.c
b2d430
new file mode 100644
b2d430
index 0000000000000000000000000000000000000000..3c896ffadcc0dbf13942457d32c77d15bfec33e7
b2d430
--- /dev/null
b2d430
+++ b/src/responder/secrets/http_parser.c
b2d430
@@ -0,0 +1,2469 @@
b2d430
+/* Based on src/http/ngx_http_parse.c from NGINX copyright Igor Sysoev
b2d430
+ *
b2d430
+ * Additional changes are licensed under the same terms as NGINX and
b2d430
+ * copyright Joyent, Inc. and other Node contributors. All rights reserved.
b2d430
+ *
b2d430
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
b2d430
+ * of this software and associated documentation files (the "Software"), to
b2d430
+ * deal in the Software without restriction, including without limitation the
b2d430
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
b2d430
+ * sell copies of the Software, and to permit persons to whom the Software is
b2d430
+ * furnished to do so, subject to the following conditions:
b2d430
+ *
b2d430
+ * The above copyright notice and this permission notice shall be included in
b2d430
+ * all copies or substantial portions of the Software.
b2d430
+ *
b2d430
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
b2d430
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
b2d430
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
b2d430
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
b2d430
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
b2d430
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
b2d430
+ * IN THE SOFTWARE.
b2d430
+ */
b2d430
+#include "http_parser.h"
b2d430
+#include <assert.h>
b2d430
+#include <stddef.h>
b2d430
+#include <ctype.h>
b2d430
+#include <stdlib.h>
b2d430
+#include <string.h>
b2d430
+#include <limits.h>
b2d430
+
b2d430
+#ifndef ULLONG_MAX
b2d430
+# define ULLONG_MAX ((uint64_t) -1) /* 2^64-1 */
b2d430
+#endif
b2d430
+
b2d430
+#ifndef MIN
b2d430
+# define MIN(a,b) ((a) < (b) ? (a) : (b))
b2d430
+#endif
b2d430
+
b2d430
+#ifndef ARRAY_SIZE
b2d430
+# define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
b2d430
+#endif
b2d430
+
b2d430
+#ifndef BIT_AT
b2d430
+# define BIT_AT(a, i)                                                \
b2d430
+  (!!((unsigned int) (a)[(unsigned int) (i) >> 3] &                  \
b2d430
+   (1 << ((unsigned int) (i) & 7))))
b2d430
+#endif
b2d430
+
b2d430
+#ifndef ELEM_AT
b2d430
+# define ELEM_AT(a, i, v) ((unsigned int) (i) < ARRAY_SIZE(a) ? (a)[(i)] : (v))
b2d430
+#endif
b2d430
+
b2d430
+#define SET_ERRNO(e)                                                 \
b2d430
+do {                                                                 \
b2d430
+  parser->http_errno = (e);                                          \
b2d430
+} while(0)
b2d430
+
b2d430
+#define CURRENT_STATE() p_state
b2d430
+#define UPDATE_STATE(V) p_state = (enum state) (V);
b2d430
+#define RETURN(V)                                                    \
b2d430
+do {                                                                 \
b2d430
+  parser->state = CURRENT_STATE();                                   \
b2d430
+  return (V);                                                        \
b2d430
+} while (0);
b2d430
+#define REEXECUTE()                                                  \
b2d430
+  goto reexecute;                                                    \
b2d430
+
b2d430
+
b2d430
+#ifdef __GNUC__
b2d430
+# define LIKELY(X) __builtin_expect(!!(X), 1)
b2d430
+# define UNLIKELY(X) __builtin_expect(!!(X), 0)
b2d430
+#else
b2d430
+# define LIKELY(X) (X)
b2d430
+# define UNLIKELY(X) (X)
b2d430
+#endif
b2d430
+
b2d430
+
b2d430
+/* Run the notify callback FOR, returning ER if it fails */
b2d430
+#define CALLBACK_NOTIFY_(FOR, ER)                                    \
b2d430
+do {                                                                 \
b2d430
+  assert(HTTP_PARSER_ERRNO(parser) == HPE_OK);                       \
b2d430
+                                                                     \
b2d430
+  if (LIKELY(settings->on_##FOR)) {                                  \
b2d430
+    parser->state = CURRENT_STATE();                                 \
b2d430
+    if (UNLIKELY(0 != settings->on_##FOR(parser))) {                 \
b2d430
+      SET_ERRNO(HPE_CB_##FOR);                                       \
b2d430
+    }                                                                \
b2d430
+    UPDATE_STATE(parser->state);                                     \
b2d430
+                                                                     \
b2d430
+    /* We either errored above or got paused; get out */             \
b2d430
+    if (UNLIKELY(HTTP_PARSER_ERRNO(parser) != HPE_OK)) {             \
b2d430
+      return (ER);                                                   \
b2d430
+    }                                                                \
b2d430
+  }                                                                  \
b2d430
+} while (0)
b2d430
+
b2d430
+/* Run the notify callback FOR and consume the current byte */
b2d430
+#define CALLBACK_NOTIFY(FOR)            CALLBACK_NOTIFY_(FOR, p - data + 1)
b2d430
+
b2d430
+/* Run the notify callback FOR and don't consume the current byte */
b2d430
+#define CALLBACK_NOTIFY_NOADVANCE(FOR)  CALLBACK_NOTIFY_(FOR, p - data)
b2d430
+
b2d430
+/* Run data callback FOR with LEN bytes, returning ER if it fails */
b2d430
+#define CALLBACK_DATA_(FOR, LEN, ER)                                 \
b2d430
+do {                                                                 \
b2d430
+  assert(HTTP_PARSER_ERRNO(parser) == HPE_OK);                       \
b2d430
+                                                                     \
b2d430
+  if (FOR##_mark) {                                                  \
b2d430
+    if (LIKELY(settings->on_##FOR)) {                                \
b2d430
+      parser->state = CURRENT_STATE();                               \
b2d430
+      if (UNLIKELY(0 !=                                              \
b2d430
+                   settings->on_##FOR(parser, FOR##_mark, (LEN)))) { \
b2d430
+        SET_ERRNO(HPE_CB_##FOR);                                     \
b2d430
+      }                                                              \
b2d430
+      UPDATE_STATE(parser->state);                                   \
b2d430
+                                                                     \
b2d430
+      /* We either errored above or got paused; get out */           \
b2d430
+      if (UNLIKELY(HTTP_PARSER_ERRNO(parser) != HPE_OK)) {           \
b2d430
+        return (ER);                                                 \
b2d430
+      }                                                              \
b2d430
+    }                                                                \
b2d430
+    FOR##_mark = NULL;                                               \
b2d430
+  }                                                                  \
b2d430
+} while (0)
b2d430
+
b2d430
+/* Run the data callback FOR and consume the current byte */
b2d430
+#define CALLBACK_DATA(FOR)                                           \
b2d430
+    CALLBACK_DATA_(FOR, p - FOR##_mark, p - data + 1)
b2d430
+
b2d430
+/* Run the data callback FOR and don't consume the current byte */
b2d430
+#define CALLBACK_DATA_NOADVANCE(FOR)                                 \
b2d430
+    CALLBACK_DATA_(FOR, p - FOR##_mark, p - data)
b2d430
+
b2d430
+/* Set the mark FOR; non-destructive if mark is already set */
b2d430
+#define MARK(FOR)                                                    \
b2d430
+do {                                                                 \
b2d430
+  if (!FOR##_mark) {                                                 \
b2d430
+    FOR##_mark = p;                                                  \
b2d430
+  }                                                                  \
b2d430
+} while (0)
b2d430
+
b2d430
+/* Don't allow the total size of the HTTP headers (including the status
b2d430
+ * line) to exceed HTTP_MAX_HEADER_SIZE.  This check is here to protect
b2d430
+ * embedders against denial-of-service attacks where the attacker feeds
b2d430
+ * us a never-ending header that the embedder keeps buffering.
b2d430
+ *
b2d430
+ * This check is arguably the responsibility of embedders but we're doing
b2d430
+ * it on the embedder's behalf because most won't bother and this way we
b2d430
+ * make the web a little safer.  HTTP_MAX_HEADER_SIZE is still far bigger
b2d430
+ * than any reasonable request or response so this should never affect
b2d430
+ * day-to-day operation.
b2d430
+ */
b2d430
+#define COUNT_HEADER_SIZE(V)                                         \
b2d430
+do {                                                                 \
b2d430
+  parser->nread += (V);                                              \
b2d430
+  if (UNLIKELY(parser->nread > (HTTP_MAX_HEADER_SIZE))) {            \
b2d430
+    SET_ERRNO(HPE_HEADER_OVERFLOW);                                  \
b2d430
+    goto error;                                                      \
b2d430
+  }                                                                  \
b2d430
+} while (0)
b2d430
+
b2d430
+
b2d430
+#define PROXY_CONNECTION "proxy-connection"
b2d430
+#define CONNECTION "connection"
b2d430
+#define CONTENT_LENGTH "content-length"
b2d430
+#define TRANSFER_ENCODING "transfer-encoding"
b2d430
+#define UPGRADE "upgrade"
b2d430
+#define CHUNKED "chunked"
b2d430
+#define KEEP_ALIVE "keep-alive"
b2d430
+#define CLOSE "close"
b2d430
+
b2d430
+
b2d430
+static const char *method_strings[] =
b2d430
+  {
b2d430
+#define XX(num, name, string) #string,
b2d430
+  HTTP_METHOD_MAP(XX)
b2d430
+#undef XX
b2d430
+  };
b2d430
+
b2d430
+
b2d430
+/* Tokens as defined by rfc 2616. Also lowercases them.
b2d430
+ *        token       = 1*<any CHAR except CTLs or separators>
b2d430
+ *     separators     = "(" | ")" | "<" | ">" | "@"
b2d430
+ *                    | "," | ";" | ":" | "\" | <">
b2d430
+ *                    | "/" | "[" | "]" | "?" | "="
b2d430
+ *                    | "{" | "}" | SP | HT
b2d430
+ */
b2d430
+static const char tokens[256] = {
b2d430
+/*   0 nul    1 soh    2 stx    3 etx    4 eot    5 enq    6 ack    7 bel  */
b2d430
+        0,       0,       0,       0,       0,       0,       0,       0,
b2d430
+/*   8 bs     9 ht    10 nl    11 vt    12 np    13 cr    14 so    15 si   */
b2d430
+        0,       0,       0,       0,       0,       0,       0,       0,
b2d430
+/*  16 dle   17 dc1   18 dc2   19 dc3   20 dc4   21 nak   22 syn   23 etb */
b2d430
+        0,       0,       0,       0,       0,       0,       0,       0,
b2d430
+/*  24 can   25 em    26 sub   27 esc   28 fs    29 gs    30 rs    31 us  */
b2d430
+        0,       0,       0,       0,       0,       0,       0,       0,
b2d430
+/*  32 sp    33  !    34  "    35  #    36  $    37  %    38  &    39  '  */
b2d430
+        0,      '!',      0,      '#',     '$',     '%',     '&',    '\'',
b2d430
+/*  40  (    41  )    42  *    43  +    44  ,    45  -    46  .    47  /  */
b2d430
+        0,       0,      '*',     '+',      0,      '-',     '.',      0,
b2d430
+/*  48  0    49  1    50  2    51  3    52  4    53  5    54  6    55  7  */
b2d430
+       '0',     '1',     '2',     '3',     '4',     '5',     '6',     '7',
b2d430
+/*  56  8    57  9    58  :    59  ;    60  <    61  =    62  >    63  ?  */
b2d430
+       '8',     '9',      0,       0,       0,       0,       0,       0,
b2d430
+/*  64  @    65  A    66  B    67  C    68  D    69  E    70  F    71  G  */
b2d430
+        0,      'a',     'b',     'c',     'd',     'e',     'f',     'g',
b2d430
+/*  72  H    73  I    74  J    75  K    76  L    77  M    78  N    79  O  */
b2d430
+       'h',     'i',     'j',     'k',     'l',     'm',     'n',     'o',
b2d430
+/*  80  P    81  Q    82  R    83  S    84  T    85  U    86  V    87  W  */
b2d430
+       'p',     'q',     'r',     's',     't',     'u',     'v',     'w',
b2d430
+/*  88  X    89  Y    90  Z    91  [    92  \    93  ]    94  ^    95  _  */
b2d430
+       'x',     'y',     'z',      0,       0,       0,      '^',     '_',
b2d430
+/*  96  `    97  a    98  b    99  c   100  d   101  e   102  f   103  g  */
b2d430
+       '`',     'a',     'b',     'c',     'd',     'e',     'f',     'g',
b2d430
+/* 104  h   105  i   106  j   107  k   108  l   109  m   110  n   111  o  */
b2d430
+       'h',     'i',     'j',     'k',     'l',     'm',     'n',     'o',
b2d430
+/* 112  p   113  q   114  r   115  s   116  t   117  u   118  v   119  w  */
b2d430
+       'p',     'q',     'r',     's',     't',     'u',     'v',     'w',
b2d430
+/* 120  x   121  y   122  z   123  {   124  |   125  }   126  ~   127 del */
b2d430
+       'x',     'y',     'z',      0,      '|',      0,      '~',       0 };
b2d430
+
b2d430
+
b2d430
+static const int8_t unhex[256] =
b2d430
+  {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
b2d430
+  ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
b2d430
+  ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
b2d430
+  , 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1
b2d430
+  ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1
b2d430
+  ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
b2d430
+  ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1
b2d430
+  ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
b2d430
+  };
b2d430
+
b2d430
+
b2d430
+#if HTTP_PARSER_STRICT
b2d430
+# define T(v) 0
b2d430
+#else
b2d430
+# define T(v) v
b2d430
+#endif
b2d430
+
b2d430
+
b2d430
+static const uint8_t normal_url_char[32] = {
b2d430
+/*   0 nul    1 soh    2 stx    3 etx    4 eot    5 enq    6 ack    7 bel  */
b2d430
+        0    |   0    |   0    |   0    |   0    |   0    |   0    |   0,
b2d430
+/*   8 bs     9 ht    10 nl    11 vt    12 np    13 cr    14 so    15 si   */
b2d430
+        0    | T(2)   |   0    |   0    | T(16)  |   0    |   0    |   0,
b2d430
+/*  16 dle   17 dc1   18 dc2   19 dc3   20 dc4   21 nak   22 syn   23 etb */
b2d430
+        0    |   0    |   0    |   0    |   0    |   0    |   0    |   0,
b2d430
+/*  24 can   25 em    26 sub   27 esc   28 fs    29 gs    30 rs    31 us  */
b2d430
+        0    |   0    |   0    |   0    |   0    |   0    |   0    |   0,
b2d430
+/*  32 sp    33  !    34  "    35  #    36  $    37  %    38  &    39  '  */
b2d430
+        0    |   2    |   4    |   0    |   16   |   32   |   64   |  128,
b2d430
+/*  40  (    41  )    42  *    43  +    44  ,    45  -    46  .    47  /  */
b2d430
+        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
b2d430
+/*  48  0    49  1    50  2    51  3    52  4    53  5    54  6    55  7  */
b2d430
+        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
b2d430
+/*  56  8    57  9    58  :    59  ;    60  <    61  =    62  >    63  ?  */
b2d430
+        1    |   2    |   4    |   8    |   16   |   32   |   64   |   0,
b2d430
+/*  64  @    65  A    66  B    67  C    68  D    69  E    70  F    71  G  */
b2d430
+        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
b2d430
+/*  72  H    73  I    74  J    75  K    76  L    77  M    78  N    79  O  */
b2d430
+        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
b2d430
+/*  80  P    81  Q    82  R    83  S    84  T    85  U    86  V    87  W  */
b2d430
+        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
b2d430
+/*  88  X    89  Y    90  Z    91  [    92  \    93  ]    94  ^    95  _  */
b2d430
+        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
b2d430
+/*  96  `    97  a    98  b    99  c   100  d   101  e   102  f   103  g  */
b2d430
+        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
b2d430
+/* 104  h   105  i   106  j   107  k   108  l   109  m   110  n   111  o  */
b2d430
+        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
b2d430
+/* 112  p   113  q   114  r   115  s   116  t   117  u   118  v   119  w  */
b2d430
+        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
b2d430
+/* 120  x   121  y   122  z   123  {   124  |   125  }   126  ~   127 del */
b2d430
+        1    |   2    |   4    |   8    |   16   |   32   |   64   |   0, };
b2d430
+
b2d430
+#undef T
b2d430
+
b2d430
+enum state
b2d430
+  { s_dead = 1 /* important that this is > 0 */
b2d430
+
b2d430
+  , s_start_req_or_res
b2d430
+  , s_res_or_resp_H
b2d430
+  , s_start_res
b2d430
+  , s_res_H
b2d430
+  , s_res_HT
b2d430
+  , s_res_HTT
b2d430
+  , s_res_HTTP
b2d430
+  , s_res_first_http_major
b2d430
+  , s_res_http_major
b2d430
+  , s_res_first_http_minor
b2d430
+  , s_res_http_minor
b2d430
+  , s_res_first_status_code
b2d430
+  , s_res_status_code
b2d430
+  , s_res_status_start
b2d430
+  , s_res_status
b2d430
+  , s_res_line_almost_done
b2d430
+
b2d430
+  , s_start_req
b2d430
+
b2d430
+  , s_req_method
b2d430
+  , s_req_spaces_before_url
b2d430
+  , s_req_schema
b2d430
+  , s_req_schema_slash
b2d430
+  , s_req_schema_slash_slash
b2d430
+  , s_req_server_start
b2d430
+  , s_req_server
b2d430
+  , s_req_server_with_at
b2d430
+  , s_req_path
b2d430
+  , s_req_query_string_start
b2d430
+  , s_req_query_string
b2d430
+  , s_req_fragment_start
b2d430
+  , s_req_fragment
b2d430
+  , s_req_http_start
b2d430
+  , s_req_http_H
b2d430
+  , s_req_http_HT
b2d430
+  , s_req_http_HTT
b2d430
+  , s_req_http_HTTP
b2d430
+  , s_req_first_http_major
b2d430
+  , s_req_http_major
b2d430
+  , s_req_first_http_minor
b2d430
+  , s_req_http_minor
b2d430
+  , s_req_line_almost_done
b2d430
+
b2d430
+  , s_header_field_start
b2d430
+  , s_header_field
b2d430
+  , s_header_value_discard_ws
b2d430
+  , s_header_value_discard_ws_almost_done
b2d430
+  , s_header_value_discard_lws
b2d430
+  , s_header_value_start
b2d430
+  , s_header_value
b2d430
+  , s_header_value_lws
b2d430
+
b2d430
+  , s_header_almost_done
b2d430
+
b2d430
+  , s_chunk_size_start
b2d430
+  , s_chunk_size
b2d430
+  , s_chunk_parameters
b2d430
+  , s_chunk_size_almost_done
b2d430
+
b2d430
+  , s_headers_almost_done
b2d430
+  , s_headers_done
b2d430
+
b2d430
+  /* Important: 's_headers_done' must be the last 'header' state. All
b2d430
+   * states beyond this must be 'body' states. It is used for overflow
b2d430
+   * checking. See the PARSING_HEADER() macro.
b2d430
+   */
b2d430
+
b2d430
+  , s_chunk_data
b2d430
+  , s_chunk_data_almost_done
b2d430
+  , s_chunk_data_done
b2d430
+
b2d430
+  , s_body_identity
b2d430
+  , s_body_identity_eof
b2d430
+
b2d430
+  , s_message_done
b2d430
+  };
b2d430
+
b2d430
+
b2d430
+#define PARSING_HEADER(state) (state <= s_headers_done)
b2d430
+
b2d430
+
b2d430
+enum header_states
b2d430
+  { h_general = 0
b2d430
+  , h_C
b2d430
+  , h_CO
b2d430
+  , h_CON
b2d430
+
b2d430
+  , h_matching_connection
b2d430
+  , h_matching_proxy_connection
b2d430
+  , h_matching_content_length
b2d430
+  , h_matching_transfer_encoding
b2d430
+  , h_matching_upgrade
b2d430
+
b2d430
+  , h_connection
b2d430
+  , h_content_length
b2d430
+  , h_transfer_encoding
b2d430
+  , h_upgrade
b2d430
+
b2d430
+  , h_matching_transfer_encoding_chunked
b2d430
+  , h_matching_connection_token_start
b2d430
+  , h_matching_connection_keep_alive
b2d430
+  , h_matching_connection_close
b2d430
+  , h_matching_connection_upgrade
b2d430
+  , h_matching_connection_token
b2d430
+
b2d430
+  , h_transfer_encoding_chunked
b2d430
+  , h_connection_keep_alive
b2d430
+  , h_connection_close
b2d430
+  , h_connection_upgrade
b2d430
+  };
b2d430
+
b2d430
+enum http_host_state
b2d430
+  {
b2d430
+    s_http_host_dead = 1
b2d430
+  , s_http_userinfo_start
b2d430
+  , s_http_userinfo
b2d430
+  , s_http_host_start
b2d430
+  , s_http_host_v6_start
b2d430
+  , s_http_host
b2d430
+  , s_http_host_v6
b2d430
+  , s_http_host_v6_end
b2d430
+  , s_http_host_v6_zone_start
b2d430
+  , s_http_host_v6_zone
b2d430
+  , s_http_host_port_start
b2d430
+  , s_http_host_port
b2d430
+};
b2d430
+
b2d430
+/* Macros for character classes; depends on strict-mode  */
b2d430
+#define CR                  '\r'
b2d430
+#define LF                  '\n'
b2d430
+#define LOWER(c)            (unsigned char)(c | 0x20)
b2d430
+#define IS_ALPHA(c)         (LOWER(c) >= 'a' && LOWER(c) <= 'z')
b2d430
+#define IS_NUM(c)           ((c) >= '0' && (c) <= '9')
b2d430
+#define IS_ALPHANUM(c)      (IS_ALPHA(c) || IS_NUM(c))
b2d430
+#define IS_HEX(c)           (IS_NUM(c) || (LOWER(c) >= 'a' && LOWER(c) <= 'f'))
b2d430
+#define IS_MARK(c)          ((c) == '-' || (c) == '_' || (c) == '.' || \
b2d430
+  (c) == '!' || (c) == '~' || (c) == '*' || (c) == '\'' || (c) == '(' || \
b2d430
+  (c) == ')')
b2d430
+#define IS_USERINFO_CHAR(c) (IS_ALPHANUM(c) || IS_MARK(c) || (c) == '%' || \
b2d430
+  (c) == ';' || (c) == ':' || (c) == '&' || (c) == '=' || (c) == '+' || \
b2d430
+  (c) == '$' || (c) == ',')
b2d430
+
b2d430
+#define STRICT_TOKEN(c)     (tokens[(unsigned char)c])
b2d430
+
b2d430
+#if HTTP_PARSER_STRICT
b2d430
+#define TOKEN(c)            (tokens[(unsigned char)c])
b2d430
+#define IS_URL_CHAR(c)      (BIT_AT(normal_url_char, (unsigned char)c))
b2d430
+#define IS_HOST_CHAR(c)     (IS_ALPHANUM(c) || (c) == '.' || (c) == '-')
b2d430
+#else
b2d430
+#define TOKEN(c)            ((c == ' ') ? ' ' : tokens[(unsigned char)c])
b2d430
+#define IS_URL_CHAR(c)                                                         \
b2d430
+  (BIT_AT(normal_url_char, (unsigned char)c) || ((c) & 0x80))
b2d430
+#define IS_HOST_CHAR(c)                                                        \
b2d430
+  (IS_ALPHANUM(c) || (c) == '.' || (c) == '-' || (c) == '_')
b2d430
+#endif
b2d430
+
b2d430
+/**
b2d430
+ * Verify that a char is a valid visible (printable) US-ASCII
b2d430
+ * character or %x80-FF
b2d430
+ **/
b2d430
+#define IS_HEADER_CHAR(ch)                                                     \
b2d430
+  (ch == CR || ch == LF || ch == 9 || ((unsigned char)ch > 31 && ch != 127))
b2d430
+
b2d430
+#define start_state (parser->type == HTTP_REQUEST ? s_start_req : s_start_res)
b2d430
+
b2d430
+
b2d430
+#if HTTP_PARSER_STRICT
b2d430
+# define STRICT_CHECK(cond)                                          \
b2d430
+do {                                                                 \
b2d430
+  if (cond) {                                                        \
b2d430
+    SET_ERRNO(HPE_STRICT);                                           \
b2d430
+    goto error;                                                      \
b2d430
+  }                                                                  \
b2d430
+} while (0)
b2d430
+# define NEW_MESSAGE() (http_should_keep_alive(parser) ? start_state : s_dead)
b2d430
+#else
b2d430
+# define STRICT_CHECK(cond)
b2d430
+# define NEW_MESSAGE() start_state
b2d430
+#endif
b2d430
+
b2d430
+
b2d430
+/* Map errno values to strings for human-readable output */
b2d430
+#define HTTP_STRERROR_GEN(n, s) { "HPE_" #n, s },
b2d430
+static struct {
b2d430
+  const char *name;
b2d430
+  const char *description;
b2d430
+} http_strerror_tab[] = {
b2d430
+  HTTP_ERRNO_MAP(HTTP_STRERROR_GEN)
b2d430
+};
b2d430
+#undef HTTP_STRERROR_GEN
b2d430
+
b2d430
+int http_message_needs_eof(const http_parser *parser);
b2d430
+
b2d430
+/* Our URL parser.
b2d430
+ *
b2d430
+ * This is designed to be shared by http_parser_execute() for URL validation,
b2d430
+ * hence it has a state transition + byte-for-byte interface. In addition, it
b2d430
+ * is meant to be embedded in http_parser_parse_url(), which does the dirty
b2d430
+ * work of turning state transitions URL components for its API.
b2d430
+ *
b2d430
+ * This function should only be invoked with non-space characters. It is
b2d430
+ * assumed that the caller cares about (and can detect) the transition between
b2d430
+ * URL and non-URL states by looking for these.
b2d430
+ */
b2d430
+static enum state
b2d430
+parse_url_char(enum state s, const char ch)
b2d430
+{
b2d430
+  if (ch == ' ' || ch == '\r' || ch == '\n') {
b2d430
+    return s_dead;
b2d430
+  }
b2d430
+
b2d430
+#if HTTP_PARSER_STRICT
b2d430
+  if (ch == '\t' || ch == '\f') {
b2d430
+    return s_dead;
b2d430
+  }
b2d430
+#endif
b2d430
+
b2d430
+  switch (s) {
b2d430
+    case s_req_spaces_before_url:
b2d430
+      /* Proxied requests are followed by scheme of an absolute URI (alpha).
b2d430
+       * All methods except CONNECT are followed by '/' or '*'.
b2d430
+       */
b2d430
+
b2d430
+      if (ch == '/' || ch == '*') {
b2d430
+        return s_req_path;
b2d430
+      }
b2d430
+
b2d430
+      if (IS_ALPHA(ch)) {
b2d430
+        return s_req_schema;
b2d430
+      }
b2d430
+
b2d430
+      break;
b2d430
+
b2d430
+    case s_req_schema:
b2d430
+      if (IS_ALPHA(ch)) {
b2d430
+        return s;
b2d430
+      }
b2d430
+
b2d430
+      if (ch == ':') {
b2d430
+        return s_req_schema_slash;
b2d430
+      }
b2d430
+
b2d430
+      break;
b2d430
+
b2d430
+    case s_req_schema_slash:
b2d430
+      if (ch == '/') {
b2d430
+        return s_req_schema_slash_slash;
b2d430
+      }
b2d430
+
b2d430
+      break;
b2d430
+
b2d430
+    case s_req_schema_slash_slash:
b2d430
+      if (ch == '/') {
b2d430
+        return s_req_server_start;
b2d430
+      }
b2d430
+
b2d430
+      break;
b2d430
+
b2d430
+    case s_req_server_with_at:
b2d430
+      if (ch == '@') {
b2d430
+        return s_dead;
b2d430
+      }
b2d430
+
b2d430
+    /* FALLTHROUGH */
b2d430
+    case s_req_server_start:
b2d430
+    case s_req_server:
b2d430
+      if (ch == '/') {
b2d430
+        return s_req_path;
b2d430
+      }
b2d430
+
b2d430
+      if (ch == '?') {
b2d430
+        return s_req_query_string_start;
b2d430
+      }
b2d430
+
b2d430
+      if (ch == '@') {
b2d430
+        return s_req_server_with_at;
b2d430
+      }
b2d430
+
b2d430
+      if (IS_USERINFO_CHAR(ch) || ch == '[' || ch == ']') {
b2d430
+        return s_req_server;
b2d430
+      }
b2d430
+
b2d430
+      break;
b2d430
+
b2d430
+    case s_req_path:
b2d430
+      if (IS_URL_CHAR(ch)) {
b2d430
+        return s;
b2d430
+      }
b2d430
+
b2d430
+      switch (ch) {
b2d430
+        case '?':
b2d430
+          return s_req_query_string_start;
b2d430
+
b2d430
+        case '#':
b2d430
+          return s_req_fragment_start;
b2d430
+      }
b2d430
+
b2d430
+      break;
b2d430
+
b2d430
+    case s_req_query_string_start:
b2d430
+    case s_req_query_string:
b2d430
+      if (IS_URL_CHAR(ch)) {
b2d430
+        return s_req_query_string;
b2d430
+      }
b2d430
+
b2d430
+      switch (ch) {
b2d430
+        case '?':
b2d430
+          /* allow extra '?' in query string */
b2d430
+          return s_req_query_string;
b2d430
+
b2d430
+        case '#':
b2d430
+          return s_req_fragment_start;
b2d430
+      }
b2d430
+
b2d430
+      break;
b2d430
+
b2d430
+    case s_req_fragment_start:
b2d430
+      if (IS_URL_CHAR(ch)) {
b2d430
+        return s_req_fragment;
b2d430
+      }
b2d430
+
b2d430
+      switch (ch) {
b2d430
+        case '?':
b2d430
+          return s_req_fragment;
b2d430
+
b2d430
+        case '#':
b2d430
+          return s;
b2d430
+      }
b2d430
+
b2d430
+      break;
b2d430
+
b2d430
+    case s_req_fragment:
b2d430
+      if (IS_URL_CHAR(ch)) {
b2d430
+        return s;
b2d430
+      }
b2d430
+
b2d430
+      switch (ch) {
b2d430
+        case '?':
b2d430
+        case '#':
b2d430
+          return s;
b2d430
+      }
b2d430
+
b2d430
+      break;
b2d430
+
b2d430
+    default:
b2d430
+      break;
b2d430
+  }
b2d430
+
b2d430
+  /* We should never fall out of the switch above unless there's an error */
b2d430
+  return s_dead;
b2d430
+}
b2d430
+
b2d430
+size_t http_parser_execute (http_parser *parser,
b2d430
+                            const http_parser_settings *settings,
b2d430
+                            const char *data,
b2d430
+                            size_t len)
b2d430
+{
b2d430
+  char c, ch;
b2d430
+  int8_t unhex_val;
b2d430
+  const char *p = data;
b2d430
+  const char *header_field_mark = 0;
b2d430
+  const char *header_value_mark = 0;
b2d430
+  const char *url_mark = 0;
b2d430
+  const char *body_mark = 0;
b2d430
+  const char *status_mark = 0;
b2d430
+  enum state p_state = (enum state) parser->state;
b2d430
+  const unsigned int lenient = parser->lenient_http_headers;
b2d430
+
b2d430
+  /* We're in an error state. Don't bother doing anything. */
b2d430
+  if (HTTP_PARSER_ERRNO(parser) != HPE_OK) {
b2d430
+    return 0;
b2d430
+  }
b2d430
+
b2d430
+  if (len == 0) {
b2d430
+    switch (CURRENT_STATE()) {
b2d430
+      case s_body_identity_eof:
b2d430
+        /* Use of CALLBACK_NOTIFY() here would erroneously return 1 byte read if
b2d430
+         * we got paused.
b2d430
+         */
b2d430
+        CALLBACK_NOTIFY_NOADVANCE(message_complete);
b2d430
+        return 0;
b2d430
+
b2d430
+      case s_dead:
b2d430
+      case s_start_req_or_res:
b2d430
+      case s_start_res:
b2d430
+      case s_start_req:
b2d430
+        return 0;
b2d430
+
b2d430
+      default:
b2d430
+        SET_ERRNO(HPE_INVALID_EOF_STATE);
b2d430
+        return 1;
b2d430
+    }
b2d430
+  }
b2d430
+
b2d430
+
b2d430
+  if (CURRENT_STATE() == s_header_field)
b2d430
+    header_field_mark = data;
b2d430
+  if (CURRENT_STATE() == s_header_value)
b2d430
+    header_value_mark = data;
b2d430
+  switch (CURRENT_STATE()) {
b2d430
+  case s_req_path:
b2d430
+  case s_req_schema:
b2d430
+  case s_req_schema_slash:
b2d430
+  case s_req_schema_slash_slash:
b2d430
+  case s_req_server_start:
b2d430
+  case s_req_server:
b2d430
+  case s_req_server_with_at:
b2d430
+  case s_req_query_string_start:
b2d430
+  case s_req_query_string:
b2d430
+  case s_req_fragment_start:
b2d430
+  case s_req_fragment:
b2d430
+    url_mark = data;
b2d430
+    break;
b2d430
+  case s_res_status:
b2d430
+    status_mark = data;
b2d430
+    break;
b2d430
+  default:
b2d430
+    break;
b2d430
+  }
b2d430
+
b2d430
+  for (p=data; p != data + len; p++) {
b2d430
+    ch = *p;
b2d430
+
b2d430
+    if (PARSING_HEADER(CURRENT_STATE()))
b2d430
+      COUNT_HEADER_SIZE(1);
b2d430
+
b2d430
+reexecute:
b2d430
+    switch (CURRENT_STATE()) {
b2d430
+
b2d430
+      case s_dead:
b2d430
+        /* this state is used after a 'Connection: close' message
b2d430
+         * the parser will error out if it reads another message
b2d430
+         */
b2d430
+        if (LIKELY(ch == CR || ch == LF))
b2d430
+          break;
b2d430
+
b2d430
+        SET_ERRNO(HPE_CLOSED_CONNECTION);
b2d430
+        goto error;
b2d430
+
b2d430
+      case s_start_req_or_res:
b2d430
+      {
b2d430
+        if (ch == CR || ch == LF)
b2d430
+          break;
b2d430
+        parser->flags = 0;
b2d430
+        parser->content_length = ULLONG_MAX;
b2d430
+
b2d430
+        if (ch == 'H') {
b2d430
+          UPDATE_STATE(s_res_or_resp_H);
b2d430
+
b2d430
+          CALLBACK_NOTIFY(message_begin);
b2d430
+        } else {
b2d430
+          parser->type = HTTP_REQUEST;
b2d430
+          UPDATE_STATE(s_start_req);
b2d430
+          REEXECUTE();
b2d430
+        }
b2d430
+
b2d430
+        break;
b2d430
+      }
b2d430
+
b2d430
+      case s_res_or_resp_H:
b2d430
+        if (ch == 'T') {
b2d430
+          parser->type = HTTP_RESPONSE;
b2d430
+          UPDATE_STATE(s_res_HT);
b2d430
+        } else {
b2d430
+          if (UNLIKELY(ch != 'E')) {
b2d430
+            SET_ERRNO(HPE_INVALID_CONSTANT);
b2d430
+            goto error;
b2d430
+          }
b2d430
+
b2d430
+          parser->type = HTTP_REQUEST;
b2d430
+          parser->method = HTTP_HEAD;
b2d430
+          parser->index = 2;
b2d430
+          UPDATE_STATE(s_req_method);
b2d430
+        }
b2d430
+        break;
b2d430
+
b2d430
+      case s_start_res:
b2d430
+      {
b2d430
+        parser->flags = 0;
b2d430
+        parser->content_length = ULLONG_MAX;
b2d430
+
b2d430
+        switch (ch) {
b2d430
+          case 'H':
b2d430
+            UPDATE_STATE(s_res_H);
b2d430
+            break;
b2d430
+
b2d430
+          case CR:
b2d430
+          case LF:
b2d430
+            break;
b2d430
+
b2d430
+          default:
b2d430
+            SET_ERRNO(HPE_INVALID_CONSTANT);
b2d430
+            goto error;
b2d430
+        }
b2d430
+
b2d430
+        CALLBACK_NOTIFY(message_begin);
b2d430
+        break;
b2d430
+      }
b2d430
+
b2d430
+      case s_res_H:
b2d430
+        STRICT_CHECK(ch != 'T');
b2d430
+        UPDATE_STATE(s_res_HT);
b2d430
+        break;
b2d430
+
b2d430
+      case s_res_HT:
b2d430
+        STRICT_CHECK(ch != 'T');
b2d430
+        UPDATE_STATE(s_res_HTT);
b2d430
+        break;
b2d430
+
b2d430
+      case s_res_HTT:
b2d430
+        STRICT_CHECK(ch != 'P');
b2d430
+        UPDATE_STATE(s_res_HTTP);
b2d430
+        break;
b2d430
+
b2d430
+      case s_res_HTTP:
b2d430
+        STRICT_CHECK(ch != '/');
b2d430
+        UPDATE_STATE(s_res_first_http_major);
b2d430
+        break;
b2d430
+
b2d430
+      case s_res_first_http_major:
b2d430
+        if (UNLIKELY(ch < '0' || ch > '9')) {
b2d430
+          SET_ERRNO(HPE_INVALID_VERSION);
b2d430
+          goto error;
b2d430
+        }
b2d430
+
b2d430
+        parser->http_major = ch - '0';
b2d430
+        UPDATE_STATE(s_res_http_major);
b2d430
+        break;
b2d430
+
b2d430
+      /* major HTTP version or dot */
b2d430
+      case s_res_http_major:
b2d430
+      {
b2d430
+        if (ch == '.') {
b2d430
+          UPDATE_STATE(s_res_first_http_minor);
b2d430
+          break;
b2d430
+        }
b2d430
+
b2d430
+        if (!IS_NUM(ch)) {
b2d430
+          SET_ERRNO(HPE_INVALID_VERSION);
b2d430
+          goto error;
b2d430
+        }
b2d430
+
b2d430
+        parser->http_major *= 10;
b2d430
+        parser->http_major += ch - '0';
b2d430
+
b2d430
+        if (UNLIKELY(parser->http_major > 999)) {
b2d430
+          SET_ERRNO(HPE_INVALID_VERSION);
b2d430
+          goto error;
b2d430
+        }
b2d430
+
b2d430
+        break;
b2d430
+      }
b2d430
+
b2d430
+      /* first digit of minor HTTP version */
b2d430
+      case s_res_first_http_minor:
b2d430
+        if (UNLIKELY(!IS_NUM(ch))) {
b2d430
+          SET_ERRNO(HPE_INVALID_VERSION);
b2d430
+          goto error;
b2d430
+        }
b2d430
+
b2d430
+        parser->http_minor = ch - '0';
b2d430
+        UPDATE_STATE(s_res_http_minor);
b2d430
+        break;
b2d430
+
b2d430
+      /* minor HTTP version or end of request line */
b2d430
+      case s_res_http_minor:
b2d430
+      {
b2d430
+        if (ch == ' ') {
b2d430
+          UPDATE_STATE(s_res_first_status_code);
b2d430
+          break;
b2d430
+        }
b2d430
+
b2d430
+        if (UNLIKELY(!IS_NUM(ch))) {
b2d430
+          SET_ERRNO(HPE_INVALID_VERSION);
b2d430
+          goto error;
b2d430
+        }
b2d430
+
b2d430
+        parser->http_minor *= 10;
b2d430
+        parser->http_minor += ch - '0';
b2d430
+
b2d430
+        if (UNLIKELY(parser->http_minor > 999)) {
b2d430
+          SET_ERRNO(HPE_INVALID_VERSION);
b2d430
+          goto error;
b2d430
+        }
b2d430
+
b2d430
+        break;
b2d430
+      }
b2d430
+
b2d430
+      case s_res_first_status_code:
b2d430
+      {
b2d430
+        if (!IS_NUM(ch)) {
b2d430
+          if (ch == ' ') {
b2d430
+            break;
b2d430
+          }
b2d430
+
b2d430
+          SET_ERRNO(HPE_INVALID_STATUS);
b2d430
+          goto error;
b2d430
+        }
b2d430
+        parser->status_code = ch - '0';
b2d430
+        UPDATE_STATE(s_res_status_code);
b2d430
+        break;
b2d430
+      }
b2d430
+
b2d430
+      case s_res_status_code:
b2d430
+      {
b2d430
+        if (!IS_NUM(ch)) {
b2d430
+          switch (ch) {
b2d430
+            case ' ':
b2d430
+              UPDATE_STATE(s_res_status_start);
b2d430
+              break;
b2d430
+            case CR:
b2d430
+              UPDATE_STATE(s_res_line_almost_done);
b2d430
+              break;
b2d430
+            case LF:
b2d430
+              UPDATE_STATE(s_header_field_start);
b2d430
+              break;
b2d430
+            default:
b2d430
+              SET_ERRNO(HPE_INVALID_STATUS);
b2d430
+              goto error;
b2d430
+          }
b2d430
+          break;
b2d430
+        }
b2d430
+
b2d430
+        parser->status_code *= 10;
b2d430
+        parser->status_code += ch - '0';
b2d430
+
b2d430
+        if (UNLIKELY(parser->status_code > 999)) {
b2d430
+          SET_ERRNO(HPE_INVALID_STATUS);
b2d430
+          goto error;
b2d430
+        }
b2d430
+
b2d430
+        break;
b2d430
+      }
b2d430
+
b2d430
+      case s_res_status_start:
b2d430
+      {
b2d430
+        if (ch == CR) {
b2d430
+          UPDATE_STATE(s_res_line_almost_done);
b2d430
+          break;
b2d430
+        }
b2d430
+
b2d430
+        if (ch == LF) {
b2d430
+          UPDATE_STATE(s_header_field_start);
b2d430
+          break;
b2d430
+        }
b2d430
+
b2d430
+        MARK(status);
b2d430
+        UPDATE_STATE(s_res_status);
b2d430
+        parser->index = 0;
b2d430
+        break;
b2d430
+      }
b2d430
+
b2d430
+      case s_res_status:
b2d430
+        if (ch == CR) {
b2d430
+          UPDATE_STATE(s_res_line_almost_done);
b2d430
+          CALLBACK_DATA(status);
b2d430
+          break;
b2d430
+        }
b2d430
+
b2d430
+        if (ch == LF) {
b2d430
+          UPDATE_STATE(s_header_field_start);
b2d430
+          CALLBACK_DATA(status);
b2d430
+          break;
b2d430
+        }
b2d430
+
b2d430
+        break;
b2d430
+
b2d430
+      case s_res_line_almost_done:
b2d430
+        STRICT_CHECK(ch != LF);
b2d430
+        UPDATE_STATE(s_header_field_start);
b2d430
+        break;
b2d430
+
b2d430
+      case s_start_req:
b2d430
+      {
b2d430
+        if (ch == CR || ch == LF)
b2d430
+          break;
b2d430
+        parser->flags = 0;
b2d430
+        parser->content_length = ULLONG_MAX;
b2d430
+
b2d430
+        if (UNLIKELY(!IS_ALPHA(ch))) {
b2d430
+          SET_ERRNO(HPE_INVALID_METHOD);
b2d430
+          goto error;
b2d430
+        }
b2d430
+
b2d430
+        parser->method = (enum http_method) 0;
b2d430
+        parser->index = 1;
b2d430
+        switch (ch) {
b2d430
+          case 'A': parser->method = HTTP_ACL; break;
b2d430
+          case 'B': parser->method = HTTP_BIND; break;
b2d430
+          case 'C': parser->method = HTTP_CONNECT; /* or COPY, CHECKOUT */ break;
b2d430
+          case 'D': parser->method = HTTP_DELETE; break;
b2d430
+          case 'G': parser->method = HTTP_GET; break;
b2d430
+          case 'H': parser->method = HTTP_HEAD; break;
b2d430
+          case 'L': parser->method = HTTP_LOCK; /* or LINK */ break;
b2d430
+          case 'M': parser->method = HTTP_MKCOL; /* or MOVE, MKACTIVITY, MERGE, M-SEARCH, MKCALENDAR */ break;
b2d430
+          case 'N': parser->method = HTTP_NOTIFY; break;
b2d430
+          case 'O': parser->method = HTTP_OPTIONS; break;
b2d430
+          case 'P': parser->method = HTTP_POST;
b2d430
+            /* or PROPFIND|PROPPATCH|PUT|PATCH|PURGE */
b2d430
+            break;
b2d430
+          case 'R': parser->method = HTTP_REPORT; /* or REBIND */ break;
b2d430
+          case 'S': parser->method = HTTP_SUBSCRIBE; /* or SEARCH */ break;
b2d430
+          case 'T': parser->method = HTTP_TRACE; break;
b2d430
+          case 'U': parser->method = HTTP_UNLOCK; /* or UNSUBSCRIBE, UNBIND, UNLINK */ break;
b2d430
+          default:
b2d430
+            SET_ERRNO(HPE_INVALID_METHOD);
b2d430
+            goto error;
b2d430
+        }
b2d430
+        UPDATE_STATE(s_req_method);
b2d430
+
b2d430
+        CALLBACK_NOTIFY(message_begin);
b2d430
+
b2d430
+        break;
b2d430
+      }
b2d430
+
b2d430
+      case s_req_method:
b2d430
+      {
b2d430
+        const char *matcher;
b2d430
+        if (UNLIKELY(ch == '\0')) {
b2d430
+          SET_ERRNO(HPE_INVALID_METHOD);
b2d430
+          goto error;
b2d430
+        }
b2d430
+
b2d430
+        matcher = method_strings[parser->method];
b2d430
+        if (ch == ' ' && matcher[parser->index] == '\0') {
b2d430
+          UPDATE_STATE(s_req_spaces_before_url);
b2d430
+        } else if (ch == matcher[parser->index]) {
b2d430
+          ; /* nada */
b2d430
+        } else if (IS_ALPHA(ch)) {
b2d430
+
b2d430
+          switch (parser->method << 16 | parser->index << 8 | ch) {
b2d430
+#define XX(meth, pos, ch, new_meth) \
b2d430
+            case (HTTP_##meth << 16 | pos << 8 | ch): \
b2d430
+              parser->method = HTTP_##new_meth; break;
b2d430
+
b2d430
+            XX(POST,      1, 'U', PUT)
b2d430
+            XX(POST,      1, 'A', PATCH)
b2d430
+            XX(CONNECT,   1, 'H', CHECKOUT)
b2d430
+            XX(CONNECT,   2, 'P', COPY)
b2d430
+            XX(MKCOL,     1, 'O', MOVE)
b2d430
+            XX(MKCOL,     1, 'E', MERGE)
b2d430
+            XX(MKCOL,     2, 'A', MKACTIVITY)
b2d430
+            XX(MKCOL,     3, 'A', MKCALENDAR)
b2d430
+            XX(SUBSCRIBE, 1, 'E', SEARCH)
b2d430
+            XX(REPORT,    2, 'B', REBIND)
b2d430
+            XX(POST,      1, 'R', PROPFIND)
b2d430
+            XX(PROPFIND,  4, 'P', PROPPATCH)
b2d430
+            XX(PUT,       2, 'R', PURGE)
b2d430
+            XX(LOCK,      1, 'I', LINK)
b2d430
+            XX(UNLOCK,    2, 'S', UNSUBSCRIBE)
b2d430
+            XX(UNLOCK,    2, 'B', UNBIND)
b2d430
+            XX(UNLOCK,    3, 'I', UNLINK)
b2d430
+#undef XX
b2d430
+
b2d430
+            default:
b2d430
+              SET_ERRNO(HPE_INVALID_METHOD);
b2d430
+              goto error;
b2d430
+          }
b2d430
+        } else if (ch == '-' &&
b2d430
+                   parser->index == 1 &&
b2d430
+                   parser->method == HTTP_MKCOL) {
b2d430
+          parser->method = HTTP_MSEARCH;
b2d430
+        } else {
b2d430
+          SET_ERRNO(HPE_INVALID_METHOD);
b2d430
+          goto error;
b2d430
+        }
b2d430
+
b2d430
+        ++parser->index;
b2d430
+        break;
b2d430
+      }
b2d430
+
b2d430
+      case s_req_spaces_before_url:
b2d430
+      {
b2d430
+        if (ch == ' ') break;
b2d430
+
b2d430
+        MARK(url);
b2d430
+        if (parser->method == HTTP_CONNECT) {
b2d430
+          UPDATE_STATE(s_req_server_start);
b2d430
+        }
b2d430
+
b2d430
+        UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch));
b2d430
+        if (UNLIKELY(CURRENT_STATE() == s_dead)) {
b2d430
+          SET_ERRNO(HPE_INVALID_URL);
b2d430
+          goto error;
b2d430
+        }
b2d430
+
b2d430
+        break;
b2d430
+      }
b2d430
+
b2d430
+      case s_req_schema:
b2d430
+      case s_req_schema_slash:
b2d430
+      case s_req_schema_slash_slash:
b2d430
+      case s_req_server_start:
b2d430
+      {
b2d430
+        switch (ch) {
b2d430
+          /* No whitespace allowed here */
b2d430
+          case ' ':
b2d430
+          case CR:
b2d430
+          case LF:
b2d430
+            SET_ERRNO(HPE_INVALID_URL);
b2d430
+            goto error;
b2d430
+          default:
b2d430
+            UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch));
b2d430
+            if (UNLIKELY(CURRENT_STATE() == s_dead)) {
b2d430
+              SET_ERRNO(HPE_INVALID_URL);
b2d430
+              goto error;
b2d430
+            }
b2d430
+        }
b2d430
+
b2d430
+        break;
b2d430
+      }
b2d430
+
b2d430
+      case s_req_server:
b2d430
+      case s_req_server_with_at:
b2d430
+      case s_req_path:
b2d430
+      case s_req_query_string_start:
b2d430
+      case s_req_query_string:
b2d430
+      case s_req_fragment_start:
b2d430
+      case s_req_fragment:
b2d430
+      {
b2d430
+        switch (ch) {
b2d430
+          case ' ':
b2d430
+            UPDATE_STATE(s_req_http_start);
b2d430
+            CALLBACK_DATA(url);
b2d430
+            break;
b2d430
+          case CR:
b2d430
+          case LF:
b2d430
+            parser->http_major = 0;
b2d430
+            parser->http_minor = 9;
b2d430
+            UPDATE_STATE((ch == CR) ?
b2d430
+              s_req_line_almost_done :
b2d430
+              s_header_field_start);
b2d430
+            CALLBACK_DATA(url);
b2d430
+            break;
b2d430
+          default:
b2d430
+            UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch));
b2d430
+            if (UNLIKELY(CURRENT_STATE() == s_dead)) {
b2d430
+              SET_ERRNO(HPE_INVALID_URL);
b2d430
+              goto error;
b2d430
+            }
b2d430
+        }
b2d430
+        break;
b2d430
+      }
b2d430
+
b2d430
+      case s_req_http_start:
b2d430
+        switch (ch) {
b2d430
+          case 'H':
b2d430
+            UPDATE_STATE(s_req_http_H);
b2d430
+            break;
b2d430
+          case ' ':
b2d430
+            break;
b2d430
+          default:
b2d430
+            SET_ERRNO(HPE_INVALID_CONSTANT);
b2d430
+            goto error;
b2d430
+        }
b2d430
+        break;
b2d430
+
b2d430
+      case s_req_http_H:
b2d430
+        STRICT_CHECK(ch != 'T');
b2d430
+        UPDATE_STATE(s_req_http_HT);
b2d430
+        break;
b2d430
+
b2d430
+      case s_req_http_HT:
b2d430
+        STRICT_CHECK(ch != 'T');
b2d430
+        UPDATE_STATE(s_req_http_HTT);
b2d430
+        break;
b2d430
+
b2d430
+      case s_req_http_HTT:
b2d430
+        STRICT_CHECK(ch != 'P');
b2d430
+        UPDATE_STATE(s_req_http_HTTP);
b2d430
+        break;
b2d430
+
b2d430
+      case s_req_http_HTTP:
b2d430
+        STRICT_CHECK(ch != '/');
b2d430
+        UPDATE_STATE(s_req_first_http_major);
b2d430
+        break;
b2d430
+
b2d430
+      /* first digit of major HTTP version */
b2d430
+      case s_req_first_http_major:
b2d430
+        if (UNLIKELY(ch < '1' || ch > '9')) {
b2d430
+          SET_ERRNO(HPE_INVALID_VERSION);
b2d430
+          goto error;
b2d430
+        }
b2d430
+
b2d430
+        parser->http_major = ch - '0';
b2d430
+        UPDATE_STATE(s_req_http_major);
b2d430
+        break;
b2d430
+
b2d430
+      /* major HTTP version or dot */
b2d430
+      case s_req_http_major:
b2d430
+      {
b2d430
+        if (ch == '.') {
b2d430
+          UPDATE_STATE(s_req_first_http_minor);
b2d430
+          break;
b2d430
+        }
b2d430
+
b2d430
+        if (UNLIKELY(!IS_NUM(ch))) {
b2d430
+          SET_ERRNO(HPE_INVALID_VERSION);
b2d430
+          goto error;
b2d430
+        }
b2d430
+
b2d430
+        parser->http_major *= 10;
b2d430
+        parser->http_major += ch - '0';
b2d430
+
b2d430
+        if (UNLIKELY(parser->http_major > 999)) {
b2d430
+          SET_ERRNO(HPE_INVALID_VERSION);
b2d430
+          goto error;
b2d430
+        }
b2d430
+
b2d430
+        break;
b2d430
+      }
b2d430
+
b2d430
+      /* first digit of minor HTTP version */
b2d430
+      case s_req_first_http_minor:
b2d430
+        if (UNLIKELY(!IS_NUM(ch))) {
b2d430
+          SET_ERRNO(HPE_INVALID_VERSION);
b2d430
+          goto error;
b2d430
+        }
b2d430
+
b2d430
+        parser->http_minor = ch - '0';
b2d430
+        UPDATE_STATE(s_req_http_minor);
b2d430
+        break;
b2d430
+
b2d430
+      /* minor HTTP version or end of request line */
b2d430
+      case s_req_http_minor:
b2d430
+      {
b2d430
+        if (ch == CR) {
b2d430
+          UPDATE_STATE(s_req_line_almost_done);
b2d430
+          break;
b2d430
+        }
b2d430
+
b2d430
+        if (ch == LF) {
b2d430
+          UPDATE_STATE(s_header_field_start);
b2d430
+          break;
b2d430
+        }
b2d430
+
b2d430
+        /* XXX allow spaces after digit? */
b2d430
+
b2d430
+        if (UNLIKELY(!IS_NUM(ch))) {
b2d430
+          SET_ERRNO(HPE_INVALID_VERSION);
b2d430
+          goto error;
b2d430
+        }
b2d430
+
b2d430
+        parser->http_minor *= 10;
b2d430
+        parser->http_minor += ch - '0';
b2d430
+
b2d430
+        if (UNLIKELY(parser->http_minor > 999)) {
b2d430
+          SET_ERRNO(HPE_INVALID_VERSION);
b2d430
+          goto error;
b2d430
+        }
b2d430
+
b2d430
+        break;
b2d430
+      }
b2d430
+
b2d430
+      /* end of request line */
b2d430
+      case s_req_line_almost_done:
b2d430
+      {
b2d430
+        if (UNLIKELY(ch != LF)) {
b2d430
+          SET_ERRNO(HPE_LF_EXPECTED);
b2d430
+          goto error;
b2d430
+        }
b2d430
+
b2d430
+        UPDATE_STATE(s_header_field_start);
b2d430
+        break;
b2d430
+      }
b2d430
+
b2d430
+      case s_header_field_start:
b2d430
+      {
b2d430
+        if (ch == CR) {
b2d430
+          UPDATE_STATE(s_headers_almost_done);
b2d430
+          break;
b2d430
+        }
b2d430
+
b2d430
+        if (ch == LF) {
b2d430
+          /* they might be just sending \n instead of \r\n so this would be
b2d430
+           * the second \n to denote the end of headers*/
b2d430
+          UPDATE_STATE(s_headers_almost_done);
b2d430
+          REEXECUTE();
b2d430
+        }
b2d430
+
b2d430
+        c = TOKEN(ch);
b2d430
+
b2d430
+        if (UNLIKELY(!c)) {
b2d430
+          SET_ERRNO(HPE_INVALID_HEADER_TOKEN);
b2d430
+          goto error;
b2d430
+        }
b2d430
+
b2d430
+        MARK(header_field);
b2d430
+
b2d430
+        parser->index = 0;
b2d430
+        UPDATE_STATE(s_header_field);
b2d430
+
b2d430
+        switch (c) {
b2d430
+          case 'c':
b2d430
+            parser->header_state = h_C;
b2d430
+            break;
b2d430
+
b2d430
+          case 'p':
b2d430
+            parser->header_state = h_matching_proxy_connection;
b2d430
+            break;
b2d430
+
b2d430
+          case 't':
b2d430
+            parser->header_state = h_matching_transfer_encoding;
b2d430
+            break;
b2d430
+
b2d430
+          case 'u':
b2d430
+            parser->header_state = h_matching_upgrade;
b2d430
+            break;
b2d430
+
b2d430
+          default:
b2d430
+            parser->header_state = h_general;
b2d430
+            break;
b2d430
+        }
b2d430
+        break;
b2d430
+      }
b2d430
+
b2d430
+      case s_header_field:
b2d430
+      {
b2d430
+        const char* start = p;
b2d430
+        for (; p != data + len; p++) {
b2d430
+          ch = *p;
b2d430
+          c = TOKEN(ch);
b2d430
+
b2d430
+          if (!c)
b2d430
+            break;
b2d430
+
b2d430
+          switch (parser->header_state) {
b2d430
+            case h_general:
b2d430
+              break;
b2d430
+
b2d430
+            case h_C:
b2d430
+              parser->index++;
b2d430
+              parser->header_state = (c == 'o' ? h_CO : h_general);
b2d430
+              break;
b2d430
+
b2d430
+            case h_CO:
b2d430
+              parser->index++;
b2d430
+              parser->header_state = (c == 'n' ? h_CON : h_general);
b2d430
+              break;
b2d430
+
b2d430
+            case h_CON:
b2d430
+              parser->index++;
b2d430
+              switch (c) {
b2d430
+                case 'n':
b2d430
+                  parser->header_state = h_matching_connection;
b2d430
+                  break;
b2d430
+                case 't':
b2d430
+                  parser->header_state = h_matching_content_length;
b2d430
+                  break;
b2d430
+                default:
b2d430
+                  parser->header_state = h_general;
b2d430
+                  break;
b2d430
+              }
b2d430
+              break;
b2d430
+
b2d430
+            /* connection */
b2d430
+
b2d430
+            case h_matching_connection:
b2d430
+              parser->index++;
b2d430
+              if (parser->index > sizeof(CONNECTION)-1
b2d430
+                  || c != CONNECTION[parser->index]) {
b2d430
+                parser->header_state = h_general;
b2d430
+              } else if (parser->index == sizeof(CONNECTION)-2) {
b2d430
+                parser->header_state = h_connection;
b2d430
+              }
b2d430
+              break;
b2d430
+
b2d430
+            /* proxy-connection */
b2d430
+
b2d430
+            case h_matching_proxy_connection:
b2d430
+              parser->index++;
b2d430
+              if (parser->index > sizeof(PROXY_CONNECTION)-1
b2d430
+                  || c != PROXY_CONNECTION[parser->index]) {
b2d430
+                parser->header_state = h_general;
b2d430
+              } else if (parser->index == sizeof(PROXY_CONNECTION)-2) {
b2d430
+                parser->header_state = h_connection;
b2d430
+              }
b2d430
+              break;
b2d430
+
b2d430
+            /* content-length */
b2d430
+
b2d430
+            case h_matching_content_length:
b2d430
+              parser->index++;
b2d430
+              if (parser->index > sizeof(CONTENT_LENGTH)-1
b2d430
+                  || c != CONTENT_LENGTH[parser->index]) {
b2d430
+                parser->header_state = h_general;
b2d430
+              } else if (parser->index == sizeof(CONTENT_LENGTH)-2) {
b2d430
+                if (parser->flags & F_CONTENTLENGTH) {
b2d430
+                  SET_ERRNO(HPE_UNEXPECTED_CONTENT_LENGTH);
b2d430
+                  goto error;
b2d430
+                }
b2d430
+                parser->header_state = h_content_length;
b2d430
+                parser->flags |= F_CONTENTLENGTH;
b2d430
+              }
b2d430
+              break;
b2d430
+
b2d430
+            /* transfer-encoding */
b2d430
+
b2d430
+            case h_matching_transfer_encoding:
b2d430
+              parser->index++;
b2d430
+              if (parser->index > sizeof(TRANSFER_ENCODING)-1
b2d430
+                  || c != TRANSFER_ENCODING[parser->index]) {
b2d430
+                parser->header_state = h_general;
b2d430
+              } else if (parser->index == sizeof(TRANSFER_ENCODING)-2) {
b2d430
+                parser->header_state = h_transfer_encoding;
b2d430
+              }
b2d430
+              break;
b2d430
+
b2d430
+            /* upgrade */
b2d430
+
b2d430
+            case h_matching_upgrade:
b2d430
+              parser->index++;
b2d430
+              if (parser->index > sizeof(UPGRADE)-1
b2d430
+                  || c != UPGRADE[parser->index]) {
b2d430
+                parser->header_state = h_general;
b2d430
+              } else if (parser->index == sizeof(UPGRADE)-2) {
b2d430
+                parser->header_state = h_upgrade;
b2d430
+              }
b2d430
+              break;
b2d430
+
b2d430
+            case h_connection:
b2d430
+            case h_content_length:
b2d430
+            case h_transfer_encoding:
b2d430
+            case h_upgrade:
b2d430
+              if (ch != ' ') parser->header_state = h_general;
b2d430
+              break;
b2d430
+
b2d430
+            default:
b2d430
+              assert(0 && "Unknown header_state");
b2d430
+              break;
b2d430
+          }
b2d430
+        }
b2d430
+
b2d430
+        COUNT_HEADER_SIZE(p - start);
b2d430
+
b2d430
+        if (p == data + len) {
b2d430
+          --p;
b2d430
+          break;
b2d430
+        }
b2d430
+
b2d430
+        if (ch == ':') {
b2d430
+          UPDATE_STATE(s_header_value_discard_ws);
b2d430
+          CALLBACK_DATA(header_field);
b2d430
+          break;
b2d430
+        }
b2d430
+
b2d430
+        SET_ERRNO(HPE_INVALID_HEADER_TOKEN);
b2d430
+        goto error;
b2d430
+      }
b2d430
+
b2d430
+      case s_header_value_discard_ws:
b2d430
+        if (ch == ' ' || ch == '\t') break;
b2d430
+
b2d430
+        if (ch == CR) {
b2d430
+          UPDATE_STATE(s_header_value_discard_ws_almost_done);
b2d430
+          break;
b2d430
+        }
b2d430
+
b2d430
+        if (ch == LF) {
b2d430
+          UPDATE_STATE(s_header_value_discard_lws);
b2d430
+          break;
b2d430
+        }
b2d430
+
b2d430
+        /* FALLTHROUGH */
b2d430
+
b2d430
+      case s_header_value_start:
b2d430
+      {
b2d430
+        MARK(header_value);
b2d430
+
b2d430
+        UPDATE_STATE(s_header_value);
b2d430
+        parser->index = 0;
b2d430
+
b2d430
+        c = LOWER(ch);
b2d430
+
b2d430
+        switch (parser->header_state) {
b2d430
+          case h_upgrade:
b2d430
+            parser->flags |= F_UPGRADE;
b2d430
+            parser->header_state = h_general;
b2d430
+            break;
b2d430
+
b2d430
+          case h_transfer_encoding:
b2d430
+            /* looking for 'Transfer-Encoding: chunked' */
b2d430
+            if ('c' == c) {
b2d430
+              parser->header_state = h_matching_transfer_encoding_chunked;
b2d430
+            } else {
b2d430
+              parser->header_state = h_general;
b2d430
+            }
b2d430
+            break;
b2d430
+
b2d430
+          case h_content_length:
b2d430
+            if (UNLIKELY(!IS_NUM(ch))) {
b2d430
+              SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
b2d430
+              goto error;
b2d430
+            }
b2d430
+
b2d430
+            parser->content_length = ch - '0';
b2d430
+            break;
b2d430
+
b2d430
+          case h_connection:
b2d430
+            /* looking for 'Connection: keep-alive' */
b2d430
+            if (c == 'k') {
b2d430
+              parser->header_state = h_matching_connection_keep_alive;
b2d430
+            /* looking for 'Connection: close' */
b2d430
+            } else if (c == 'c') {
b2d430
+              parser->header_state = h_matching_connection_close;
b2d430
+            } else if (c == 'u') {
b2d430
+              parser->header_state = h_matching_connection_upgrade;
b2d430
+            } else {
b2d430
+              parser->header_state = h_matching_connection_token;
b2d430
+            }
b2d430
+            break;
b2d430
+
b2d430
+          /* Multi-value `Connection` header */
b2d430
+          case h_matching_connection_token_start:
b2d430
+            break;
b2d430
+
b2d430
+          default:
b2d430
+            parser->header_state = h_general;
b2d430
+            break;
b2d430
+        }
b2d430
+        break;
b2d430
+      }
b2d430
+
b2d430
+      case s_header_value:
b2d430
+      {
b2d430
+        const char* start = p;
b2d430
+        enum header_states h_state = (enum header_states) parser->header_state;
b2d430
+        for (; p != data + len; p++) {
b2d430
+          ch = *p;
b2d430
+          if (ch == CR) {
b2d430
+            UPDATE_STATE(s_header_almost_done);
b2d430
+            parser->header_state = h_state;
b2d430
+            CALLBACK_DATA(header_value);
b2d430
+            break;
b2d430
+          }
b2d430
+
b2d430
+          if (ch == LF) {
b2d430
+            UPDATE_STATE(s_header_almost_done);
b2d430
+            COUNT_HEADER_SIZE(p - start);
b2d430
+            parser->header_state = h_state;
b2d430
+            CALLBACK_DATA_NOADVANCE(header_value);
b2d430
+            REEXECUTE();
b2d430
+          }
b2d430
+
b2d430
+          if (!lenient && !IS_HEADER_CHAR(ch)) {
b2d430
+            SET_ERRNO(HPE_INVALID_HEADER_TOKEN);
b2d430
+            goto error;
b2d430
+          }
b2d430
+
b2d430
+          c = LOWER(ch);
b2d430
+
b2d430
+          switch (h_state) {
b2d430
+            case h_general:
b2d430
+            {
b2d430
+              const char* p_cr;
b2d430
+              const char* p_lf;
b2d430
+              size_t limit = data + len - p;
b2d430
+
b2d430
+              limit = MIN(limit, HTTP_MAX_HEADER_SIZE);
b2d430
+
b2d430
+              p_cr = (const char*) memchr(p, CR, limit);
b2d430
+              p_lf = (const char*) memchr(p, LF, limit);
b2d430
+              if (p_cr != NULL) {
b2d430
+                if (p_lf != NULL && p_cr >= p_lf)
b2d430
+                  p = p_lf;
b2d430
+                else
b2d430
+                  p = p_cr;
b2d430
+              } else if (UNLIKELY(p_lf != NULL)) {
b2d430
+                p = p_lf;
b2d430
+              } else {
b2d430
+                p = data + len;
b2d430
+              }
b2d430
+              --p;
b2d430
+
b2d430
+              break;
b2d430
+            }
b2d430
+
b2d430
+            case h_connection:
b2d430
+            case h_transfer_encoding:
b2d430
+              assert(0 && "Shouldn't get here.");
b2d430
+              break;
b2d430
+
b2d430
+            case h_content_length:
b2d430
+            {
b2d430
+              uint64_t t;
b2d430
+
b2d430
+              if (ch == ' ') break;
b2d430
+
b2d430
+              if (UNLIKELY(!IS_NUM(ch))) {
b2d430
+                SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
b2d430
+                parser->header_state = h_state;
b2d430
+                goto error;
b2d430
+              }
b2d430
+
b2d430
+              t = parser->content_length;
b2d430
+              t *= 10;
b2d430
+              t += ch - '0';
b2d430
+
b2d430
+              /* Overflow? Test against a conservative limit for simplicity. */
b2d430
+              if (UNLIKELY((ULLONG_MAX - 10) / 10 < parser->content_length)) {
b2d430
+                SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
b2d430
+                parser->header_state = h_state;
b2d430
+                goto error;
b2d430
+              }
b2d430
+
b2d430
+              parser->content_length = t;
b2d430
+              break;
b2d430
+            }
b2d430
+
b2d430
+            /* Transfer-Encoding: chunked */
b2d430
+            case h_matching_transfer_encoding_chunked:
b2d430
+              parser->index++;
b2d430
+              if (parser->index > sizeof(CHUNKED)-1
b2d430
+                  || c != CHUNKED[parser->index]) {
b2d430
+                h_state = h_general;
b2d430
+              } else if (parser->index == sizeof(CHUNKED)-2) {
b2d430
+                h_state = h_transfer_encoding_chunked;
b2d430
+              }
b2d430
+              break;
b2d430
+
b2d430
+            case h_matching_connection_token_start:
b2d430
+              /* looking for 'Connection: keep-alive' */
b2d430
+              if (c == 'k') {
b2d430
+                h_state = h_matching_connection_keep_alive;
b2d430
+              /* looking for 'Connection: close' */
b2d430
+              } else if (c == 'c') {
b2d430
+                h_state = h_matching_connection_close;
b2d430
+              } else if (c == 'u') {
b2d430
+                h_state = h_matching_connection_upgrade;
b2d430
+              } else if (STRICT_TOKEN(c)) {
b2d430
+                h_state = h_matching_connection_token;
b2d430
+              } else if (c == ' ' || c == '\t') {
b2d430
+                /* Skip lws */
b2d430
+              } else {
b2d430
+                h_state = h_general;
b2d430
+              }
b2d430
+              break;
b2d430
+
b2d430
+            /* looking for 'Connection: keep-alive' */
b2d430
+            case h_matching_connection_keep_alive:
b2d430
+              parser->index++;
b2d430
+              if (parser->index > sizeof(KEEP_ALIVE)-1
b2d430
+                  || c != KEEP_ALIVE[parser->index]) {
b2d430
+                h_state = h_matching_connection_token;
b2d430
+              } else if (parser->index == sizeof(KEEP_ALIVE)-2) {
b2d430
+                h_state = h_connection_keep_alive;
b2d430
+              }
b2d430
+              break;
b2d430
+
b2d430
+            /* looking for 'Connection: close' */
b2d430
+            case h_matching_connection_close:
b2d430
+              parser->index++;
b2d430
+              if (parser->index > sizeof(CLOSE)-1 || c != CLOSE[parser->index]) {
b2d430
+                h_state = h_matching_connection_token;
b2d430
+              } else if (parser->index == sizeof(CLOSE)-2) {
b2d430
+                h_state = h_connection_close;
b2d430
+              }
b2d430
+              break;
b2d430
+
b2d430
+            /* looking for 'Connection: upgrade' */
b2d430
+            case h_matching_connection_upgrade:
b2d430
+              parser->index++;
b2d430
+              if (parser->index > sizeof(UPGRADE) - 1 ||
b2d430
+                  c != UPGRADE[parser->index]) {
b2d430
+                h_state = h_matching_connection_token;
b2d430
+              } else if (parser->index == sizeof(UPGRADE)-2) {
b2d430
+                h_state = h_connection_upgrade;
b2d430
+              }
b2d430
+              break;
b2d430
+
b2d430
+            case h_matching_connection_token:
b2d430
+              if (ch == ',') {
b2d430
+                h_state = h_matching_connection_token_start;
b2d430
+                parser->index = 0;
b2d430
+              }
b2d430
+              break;
b2d430
+
b2d430
+            case h_transfer_encoding_chunked:
b2d430
+              if (ch != ' ') h_state = h_general;
b2d430
+              break;
b2d430
+
b2d430
+            case h_connection_keep_alive:
b2d430
+            case h_connection_close:
b2d430
+            case h_connection_upgrade:
b2d430
+              if (ch == ',') {
b2d430
+                if (h_state == h_connection_keep_alive) {
b2d430
+                  parser->flags |= F_CONNECTION_KEEP_ALIVE;
b2d430
+                } else if (h_state == h_connection_close) {
b2d430
+                  parser->flags |= F_CONNECTION_CLOSE;
b2d430
+                } else if (h_state == h_connection_upgrade) {
b2d430
+                  parser->flags |= F_CONNECTION_UPGRADE;
b2d430
+                }
b2d430
+                h_state = h_matching_connection_token_start;
b2d430
+                parser->index = 0;
b2d430
+              } else if (ch != ' ') {
b2d430
+                h_state = h_matching_connection_token;
b2d430
+              }
b2d430
+              break;
b2d430
+
b2d430
+            default:
b2d430
+              UPDATE_STATE(s_header_value);
b2d430
+              h_state = h_general;
b2d430
+              break;
b2d430
+          }
b2d430
+        }
b2d430
+        parser->header_state = h_state;
b2d430
+
b2d430
+        COUNT_HEADER_SIZE(p - start);
b2d430
+
b2d430
+        if (p == data + len)
b2d430
+          --p;
b2d430
+        break;
b2d430
+      }
b2d430
+
b2d430
+      case s_header_almost_done:
b2d430
+      {
b2d430
+        if (UNLIKELY(ch != LF)) {
b2d430
+          SET_ERRNO(HPE_LF_EXPECTED);
b2d430
+          goto error;
b2d430
+        }
b2d430
+
b2d430
+        UPDATE_STATE(s_header_value_lws);
b2d430
+        break;
b2d430
+      }
b2d430
+
b2d430
+      case s_header_value_lws:
b2d430
+      {
b2d430
+        if (ch == ' ' || ch == '\t') {
b2d430
+          UPDATE_STATE(s_header_value_start);
b2d430
+          REEXECUTE();
b2d430
+        }
b2d430
+
b2d430
+        /* finished the header */
b2d430
+        switch (parser->header_state) {
b2d430
+          case h_connection_keep_alive:
b2d430
+            parser->flags |= F_CONNECTION_KEEP_ALIVE;
b2d430
+            break;
b2d430
+          case h_connection_close:
b2d430
+            parser->flags |= F_CONNECTION_CLOSE;
b2d430
+            break;
b2d430
+          case h_transfer_encoding_chunked:
b2d430
+            parser->flags |= F_CHUNKED;
b2d430
+            break;
b2d430
+          case h_connection_upgrade:
b2d430
+            parser->flags |= F_CONNECTION_UPGRADE;
b2d430
+            break;
b2d430
+          default:
b2d430
+            break;
b2d430
+        }
b2d430
+
b2d430
+        UPDATE_STATE(s_header_field_start);
b2d430
+        REEXECUTE();
b2d430
+      }
b2d430
+
b2d430
+      case s_header_value_discard_ws_almost_done:
b2d430
+      {
b2d430
+        STRICT_CHECK(ch != LF);
b2d430
+        UPDATE_STATE(s_header_value_discard_lws);
b2d430
+        break;
b2d430
+      }
b2d430
+
b2d430
+      case s_header_value_discard_lws:
b2d430
+      {
b2d430
+        if (ch == ' ' || ch == '\t') {
b2d430
+          UPDATE_STATE(s_header_value_discard_ws);
b2d430
+          break;
b2d430
+        } else {
b2d430
+          switch (parser->header_state) {
b2d430
+            case h_connection_keep_alive:
b2d430
+              parser->flags |= F_CONNECTION_KEEP_ALIVE;
b2d430
+              break;
b2d430
+            case h_connection_close:
b2d430
+              parser->flags |= F_CONNECTION_CLOSE;
b2d430
+              break;
b2d430
+            case h_connection_upgrade:
b2d430
+              parser->flags |= F_CONNECTION_UPGRADE;
b2d430
+              break;
b2d430
+            case h_transfer_encoding_chunked:
b2d430
+              parser->flags |= F_CHUNKED;
b2d430
+              break;
b2d430
+            default:
b2d430
+              break;
b2d430
+          }
b2d430
+
b2d430
+          /* header value was empty */
b2d430
+          MARK(header_value);
b2d430
+          UPDATE_STATE(s_header_field_start);
b2d430
+          CALLBACK_DATA_NOADVANCE(header_value);
b2d430
+          REEXECUTE();
b2d430
+        }
b2d430
+      }
b2d430
+
b2d430
+      case s_headers_almost_done:
b2d430
+      {
b2d430
+        STRICT_CHECK(ch != LF);
b2d430
+
b2d430
+        if (parser->flags & F_TRAILING) {
b2d430
+          /* End of a chunked request */
b2d430
+          UPDATE_STATE(s_message_done);
b2d430
+          CALLBACK_NOTIFY_NOADVANCE(chunk_complete);
b2d430
+          REEXECUTE();
b2d430
+        }
b2d430
+
b2d430
+        /* Cannot use chunked encoding and a content-length header together
b2d430
+           per the HTTP specification. */
b2d430
+        if ((parser->flags & F_CHUNKED) &&
b2d430
+            (parser->flags & F_CONTENTLENGTH)) {
b2d430
+          SET_ERRNO(HPE_UNEXPECTED_CONTENT_LENGTH);
b2d430
+          goto error;
b2d430
+        }
b2d430
+
b2d430
+        UPDATE_STATE(s_headers_done);
b2d430
+
b2d430
+        /* Set this here so that on_headers_complete() callbacks can see it */
b2d430
+        parser->upgrade =
b2d430
+          ((parser->flags & (F_UPGRADE | F_CONNECTION_UPGRADE)) ==
b2d430
+           (F_UPGRADE | F_CONNECTION_UPGRADE) ||
b2d430
+           parser->method == HTTP_CONNECT);
b2d430
+
b2d430
+        /* Here we call the headers_complete callback. This is somewhat
b2d430
+         * different than other callbacks because if the user returns 1, we
b2d430
+         * will interpret that as saying that this message has no body. This
b2d430
+         * is needed for the annoying case of recieving a response to a HEAD
b2d430
+         * request.
b2d430
+         *
b2d430
+         * We'd like to use CALLBACK_NOTIFY_NOADVANCE() here but we cannot, so
b2d430
+         * we have to simulate it by handling a change in errno below.
b2d430
+         */
b2d430
+        if (settings->on_headers_complete) {
b2d430
+          switch (settings->on_headers_complete(parser)) {
b2d430
+            case 0:
b2d430
+              break;
b2d430
+
b2d430
+            case 2:
b2d430
+              parser->upgrade = 1;
b2d430
+
b2d430
+            case 1:
b2d430
+              parser->flags |= F_SKIPBODY;
b2d430
+              break;
b2d430
+
b2d430
+            default:
b2d430
+              SET_ERRNO(HPE_CB_headers_complete);
b2d430
+              RETURN(p - data); /* Error */
b2d430
+          }
b2d430
+        }
b2d430
+
b2d430
+        if (HTTP_PARSER_ERRNO(parser) != HPE_OK) {
b2d430
+          RETURN(p - data);
b2d430
+        }
b2d430
+
b2d430
+        REEXECUTE();
b2d430
+      }
b2d430
+
b2d430
+      case s_headers_done:
b2d430
+      {
b2d430
+        int hasBody;
b2d430
+        STRICT_CHECK(ch != LF);
b2d430
+
b2d430
+        parser->nread = 0;
b2d430
+
b2d430
+        hasBody = parser->flags & F_CHUNKED ||
b2d430
+          (parser->content_length > 0 && parser->content_length != ULLONG_MAX);
b2d430
+        if (parser->upgrade && (parser->method == HTTP_CONNECT ||
b2d430
+                                (parser->flags & F_SKIPBODY) || !hasBody)) {
b2d430
+          /* Exit, the rest of the message is in a different protocol. */
b2d430
+          UPDATE_STATE(NEW_MESSAGE());
b2d430
+          CALLBACK_NOTIFY(message_complete);
b2d430
+          RETURN((p - data) + 1);
b2d430
+        }
b2d430
+
b2d430
+        if (parser->flags & F_SKIPBODY) {
b2d430
+          UPDATE_STATE(NEW_MESSAGE());
b2d430
+          CALLBACK_NOTIFY(message_complete);
b2d430
+        } else if (parser->flags & F_CHUNKED) {
b2d430
+          /* chunked encoding - ignore Content-Length header */
b2d430
+          UPDATE_STATE(s_chunk_size_start);
b2d430
+        } else {
b2d430
+          if (parser->content_length == 0) {
b2d430
+            /* Content-Length header given but zero: Content-Length: 0\r\n */
b2d430
+            UPDATE_STATE(NEW_MESSAGE());
b2d430
+            CALLBACK_NOTIFY(message_complete);
b2d430
+          } else if (parser->content_length != ULLONG_MAX) {
b2d430
+            /* Content-Length header given and non-zero */
b2d430
+            UPDATE_STATE(s_body_identity);
b2d430
+          } else {
b2d430
+            if (!http_message_needs_eof(parser)) {
b2d430
+              /* Assume content-length 0 - read the next */
b2d430
+              UPDATE_STATE(NEW_MESSAGE());
b2d430
+              CALLBACK_NOTIFY(message_complete);
b2d430
+            } else {
b2d430
+              /* Read body until EOF */
b2d430
+              UPDATE_STATE(s_body_identity_eof);
b2d430
+            }
b2d430
+          }
b2d430
+        }
b2d430
+
b2d430
+        break;
b2d430
+      }
b2d430
+
b2d430
+      case s_body_identity:
b2d430
+      {
b2d430
+        uint64_t to_read = MIN(parser->content_length,
b2d430
+                               (uint64_t) ((data + len) - p));
b2d430
+
b2d430
+        assert(parser->content_length != 0
b2d430
+            && parser->content_length != ULLONG_MAX);
b2d430
+
b2d430
+        /* The difference between advancing content_length and p is because
b2d430
+         * the latter will automaticaly advance on the next loop iteration.
b2d430
+         * Further, if content_length ends up at 0, we want to see the last
b2d430
+         * byte again for our message complete callback.
b2d430
+         */
b2d430
+        MARK(body);
b2d430
+        parser->content_length -= to_read;
b2d430
+        p += to_read - 1;
b2d430
+
b2d430
+        if (parser->content_length == 0) {
b2d430
+          UPDATE_STATE(s_message_done);
b2d430
+
b2d430
+          /* Mimic CALLBACK_DATA_NOADVANCE() but with one extra byte.
b2d430
+           *
b2d430
+           * The alternative to doing this is to wait for the next byte to
b2d430
+           * trigger the data callback, just as in every other case. The
b2d430
+           * problem with this is that this makes it difficult for the test
b2d430
+           * harness to distinguish between complete-on-EOF and
b2d430
+           * complete-on-length. It's not clear that this distinction is
b2d430
+           * important for applications, but let's keep it for now.
b2d430
+           */
b2d430
+          CALLBACK_DATA_(body, p - body_mark + 1, p - data);
b2d430
+          REEXECUTE();
b2d430
+        }
b2d430
+
b2d430
+        break;
b2d430
+      }
b2d430
+
b2d430
+      /* read until EOF */
b2d430
+      case s_body_identity_eof:
b2d430
+        MARK(body);
b2d430
+        p = data + len - 1;
b2d430
+
b2d430
+        break;
b2d430
+
b2d430
+      case s_message_done:
b2d430
+        UPDATE_STATE(NEW_MESSAGE());
b2d430
+        CALLBACK_NOTIFY(message_complete);
b2d430
+        if (parser->upgrade) {
b2d430
+          /* Exit, the rest of the message is in a different protocol. */
b2d430
+          RETURN((p - data) + 1);
b2d430
+        }
b2d430
+        break;
b2d430
+
b2d430
+      case s_chunk_size_start:
b2d430
+      {
b2d430
+        assert(parser->nread == 1);
b2d430
+        assert(parser->flags & F_CHUNKED);
b2d430
+
b2d430
+        unhex_val = unhex[(unsigned char)ch];
b2d430
+        if (UNLIKELY(unhex_val == -1)) {
b2d430
+          SET_ERRNO(HPE_INVALID_CHUNK_SIZE);
b2d430
+          goto error;
b2d430
+        }
b2d430
+
b2d430
+        parser->content_length = unhex_val;
b2d430
+        UPDATE_STATE(s_chunk_size);
b2d430
+        break;
b2d430
+      }
b2d430
+
b2d430
+      case s_chunk_size:
b2d430
+      {
b2d430
+        uint64_t t;
b2d430
+
b2d430
+        assert(parser->flags & F_CHUNKED);
b2d430
+
b2d430
+        if (ch == CR) {
b2d430
+          UPDATE_STATE(s_chunk_size_almost_done);
b2d430
+          break;
b2d430
+        }
b2d430
+
b2d430
+        unhex_val = unhex[(unsigned char)ch];
b2d430
+
b2d430
+        if (unhex_val == -1) {
b2d430
+          if (ch == ';' || ch == ' ') {
b2d430
+            UPDATE_STATE(s_chunk_parameters);
b2d430
+            break;
b2d430
+          }
b2d430
+
b2d430
+          SET_ERRNO(HPE_INVALID_CHUNK_SIZE);
b2d430
+          goto error;
b2d430
+        }
b2d430
+
b2d430
+        t = parser->content_length;
b2d430
+        t *= 16;
b2d430
+        t += unhex_val;
b2d430
+
b2d430
+        /* Overflow? Test against a conservative limit for simplicity. */
b2d430
+        if (UNLIKELY((ULLONG_MAX - 16) / 16 < parser->content_length)) {
b2d430
+          SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
b2d430
+          goto error;
b2d430
+        }
b2d430
+
b2d430
+        parser->content_length = t;
b2d430
+        break;
b2d430
+      }
b2d430
+
b2d430
+      case s_chunk_parameters:
b2d430
+      {
b2d430
+        assert(parser->flags & F_CHUNKED);
b2d430
+        /* just ignore this shit. TODO check for overflow */
b2d430
+        if (ch == CR) {
b2d430
+          UPDATE_STATE(s_chunk_size_almost_done);
b2d430
+          break;
b2d430
+        }
b2d430
+        break;
b2d430
+      }
b2d430
+
b2d430
+      case s_chunk_size_almost_done:
b2d430
+      {
b2d430
+        assert(parser->flags & F_CHUNKED);
b2d430
+        STRICT_CHECK(ch != LF);
b2d430
+
b2d430
+        parser->nread = 0;
b2d430
+
b2d430
+        if (parser->content_length == 0) {
b2d430
+          parser->flags |= F_TRAILING;
b2d430
+          UPDATE_STATE(s_header_field_start);
b2d430
+        } else {
b2d430
+          UPDATE_STATE(s_chunk_data);
b2d430
+        }
b2d430
+        CALLBACK_NOTIFY(chunk_header);
b2d430
+        break;
b2d430
+      }
b2d430
+
b2d430
+      case s_chunk_data:
b2d430
+      {
b2d430
+        uint64_t to_read = MIN(parser->content_length,
b2d430
+                               (uint64_t) ((data + len) - p));
b2d430
+
b2d430
+        assert(parser->flags & F_CHUNKED);
b2d430
+        assert(parser->content_length != 0
b2d430
+            && parser->content_length != ULLONG_MAX);
b2d430
+
b2d430
+        /* See the explanation in s_body_identity for why the content
b2d430
+         * length and data pointers are managed this way.
b2d430
+         */
b2d430
+        MARK(body);
b2d430
+        parser->content_length -= to_read;
b2d430
+        p += to_read - 1;
b2d430
+
b2d430
+        if (parser->content_length == 0) {
b2d430
+          UPDATE_STATE(s_chunk_data_almost_done);
b2d430
+        }
b2d430
+
b2d430
+        break;
b2d430
+      }
b2d430
+
b2d430
+      case s_chunk_data_almost_done:
b2d430
+        assert(parser->flags & F_CHUNKED);
b2d430
+        assert(parser->content_length == 0);
b2d430
+        STRICT_CHECK(ch != CR);
b2d430
+        UPDATE_STATE(s_chunk_data_done);
b2d430
+        CALLBACK_DATA(body);
b2d430
+        break;
b2d430
+
b2d430
+      case s_chunk_data_done:
b2d430
+        assert(parser->flags & F_CHUNKED);
b2d430
+        STRICT_CHECK(ch != LF);
b2d430
+        parser->nread = 0;
b2d430
+        UPDATE_STATE(s_chunk_size_start);
b2d430
+        CALLBACK_NOTIFY(chunk_complete);
b2d430
+        break;
b2d430
+
b2d430
+      default:
b2d430
+        assert(0 && "unhandled state");
b2d430
+        SET_ERRNO(HPE_INVALID_INTERNAL_STATE);
b2d430
+        goto error;
b2d430
+    }
b2d430
+  }
b2d430
+
b2d430
+  /* Run callbacks for any marks that we have leftover after we ran our of
b2d430
+   * bytes. There should be at most one of these set, so it's OK to invoke
b2d430
+   * them in series (unset marks will not result in callbacks).
b2d430
+   *
b2d430
+   * We use the NOADVANCE() variety of callbacks here because 'p' has already
b2d430
+   * overflowed 'data' and this allows us to correct for the off-by-one that
b2d430
+   * we'd otherwise have (since CALLBACK_DATA() is meant to be run with a 'p'
b2d430
+   * value that's in-bounds).
b2d430
+   */
b2d430
+
b2d430
+  assert(((header_field_mark ? 1 : 0) +
b2d430
+          (header_value_mark ? 1 : 0) +
b2d430
+          (url_mark ? 1 : 0)  +
b2d430
+          (body_mark ? 1 : 0) +
b2d430
+          (status_mark ? 1 : 0)) <= 1);
b2d430
+
b2d430
+  CALLBACK_DATA_NOADVANCE(header_field);
b2d430
+  CALLBACK_DATA_NOADVANCE(header_value);
b2d430
+  CALLBACK_DATA_NOADVANCE(url);
b2d430
+  CALLBACK_DATA_NOADVANCE(body);
b2d430
+  CALLBACK_DATA_NOADVANCE(status);
b2d430
+
b2d430
+  RETURN(len);
b2d430
+
b2d430
+error:
b2d430
+  if (HTTP_PARSER_ERRNO(parser) == HPE_OK) {
b2d430
+    SET_ERRNO(HPE_UNKNOWN);
b2d430
+  }
b2d430
+
b2d430
+  RETURN(p - data);
b2d430
+}
b2d430
+
b2d430
+
b2d430
+/* Does the parser need to see an EOF to find the end of the message? */
b2d430
+int
b2d430
+http_message_needs_eof (const http_parser *parser)
b2d430
+{
b2d430
+  if (parser->type == HTTP_REQUEST) {
b2d430
+    return 0;
b2d430
+  }
b2d430
+
b2d430
+  /* See RFC 2616 section 4.4 */
b2d430
+  if (parser->status_code / 100 == 1 || /* 1xx e.g. Continue */
b2d430
+      parser->status_code == 204 ||     /* No Content */
b2d430
+      parser->status_code == 304 ||     /* Not Modified */
b2d430
+      parser->flags & F_SKIPBODY) {     /* response to a HEAD request */
b2d430
+    return 0;
b2d430
+  }
b2d430
+
b2d430
+  if ((parser->flags & F_CHUNKED) || parser->content_length != ULLONG_MAX) {
b2d430
+    return 0;
b2d430
+  }
b2d430
+
b2d430
+  return 1;
b2d430
+}
b2d430
+
b2d430
+
b2d430
+int
b2d430
+http_should_keep_alive (const http_parser *parser)
b2d430
+{
b2d430
+  if (parser->http_major > 0 && parser->http_minor > 0) {
b2d430
+    /* HTTP/1.1 */
b2d430
+    if (parser->flags & F_CONNECTION_CLOSE) {
b2d430
+      return 0;
b2d430
+    }
b2d430
+  } else {
b2d430
+    /* HTTP/1.0 or earlier */
b2d430
+    if (!(parser->flags & F_CONNECTION_KEEP_ALIVE)) {
b2d430
+      return 0;
b2d430
+    }
b2d430
+  }
b2d430
+
b2d430
+  return !http_message_needs_eof(parser);
b2d430
+}
b2d430
+
b2d430
+
b2d430
+const char *
b2d430
+http_method_str (enum http_method m)
b2d430
+{
b2d430
+  return ELEM_AT(method_strings, m, "<unknown>");
b2d430
+}
b2d430
+
b2d430
+
b2d430
+void
b2d430
+http_parser_init (http_parser *parser, enum http_parser_type t)
b2d430
+{
b2d430
+  void *data = parser->data; /* preserve application data */
b2d430
+  memset(parser, 0, sizeof(*parser));
b2d430
+  parser->data = data;
b2d430
+  parser->type = t;
b2d430
+  parser->state = (t == HTTP_REQUEST ? s_start_req : (t == HTTP_RESPONSE ? s_start_res : s_start_req_or_res));
b2d430
+  parser->http_errno = HPE_OK;
b2d430
+}
b2d430
+
b2d430
+void
b2d430
+http_parser_settings_init(http_parser_settings *settings)
b2d430
+{
b2d430
+  memset(settings, 0, sizeof(*settings));
b2d430
+}
b2d430
+
b2d430
+const char *
b2d430
+http_errno_name(enum http_errno err) {
b2d430
+  assert(((size_t) err) < ARRAY_SIZE(http_strerror_tab));
b2d430
+  return http_strerror_tab[err].name;
b2d430
+}
b2d430
+
b2d430
+const char *
b2d430
+http_errno_description(enum http_errno err) {
b2d430
+  assert(((size_t) err) < ARRAY_SIZE(http_strerror_tab));
b2d430
+  return http_strerror_tab[err].description;
b2d430
+}
b2d430
+
b2d430
+static enum http_host_state
b2d430
+http_parse_host_char(enum http_host_state s, const char ch) {
b2d430
+  switch(s) {
b2d430
+    case s_http_userinfo:
b2d430
+    case s_http_userinfo_start:
b2d430
+      if (ch == '@') {
b2d430
+        return s_http_host_start;
b2d430
+      }
b2d430
+
b2d430
+      if (IS_USERINFO_CHAR(ch)) {
b2d430
+        return s_http_userinfo;
b2d430
+      }
b2d430
+      break;
b2d430
+
b2d430
+    case s_http_host_start:
b2d430
+      if (ch == '[') {
b2d430
+        return s_http_host_v6_start;
b2d430
+      }
b2d430
+
b2d430
+      if (IS_HOST_CHAR(ch)) {
b2d430
+        return s_http_host;
b2d430
+      }
b2d430
+
b2d430
+      break;
b2d430
+
b2d430
+    case s_http_host:
b2d430
+      if (IS_HOST_CHAR(ch)) {
b2d430
+        return s_http_host;
b2d430
+      }
b2d430
+
b2d430
+    /* FALLTHROUGH */
b2d430
+    case s_http_host_v6_end:
b2d430
+      if (ch == ':') {
b2d430
+        return s_http_host_port_start;
b2d430
+      }
b2d430
+
b2d430
+      break;
b2d430
+
b2d430
+    case s_http_host_v6:
b2d430
+      if (ch == ']') {
b2d430
+        return s_http_host_v6_end;
b2d430
+      }
b2d430
+
b2d430
+    /* FALLTHROUGH */
b2d430
+    case s_http_host_v6_start:
b2d430
+      if (IS_HEX(ch) || ch == ':' || ch == '.') {
b2d430
+        return s_http_host_v6;
b2d430
+      }
b2d430
+
b2d430
+      if (s == s_http_host_v6 && ch == '%') {
b2d430
+        return s_http_host_v6_zone_start;
b2d430
+      }
b2d430
+      break;
b2d430
+
b2d430
+    case s_http_host_v6_zone:
b2d430
+      if (ch == ']') {
b2d430
+        return s_http_host_v6_end;
b2d430
+      }
b2d430
+
b2d430
+    /* FALLTHROUGH */
b2d430
+    case s_http_host_v6_zone_start:
b2d430
+      /* RFC 6874 Zone ID consists of 1*( unreserved / pct-encoded) */
b2d430
+      if (IS_ALPHANUM(ch) || ch == '%' || ch == '.' || ch == '-' || ch == '_' ||
b2d430
+          ch == '~') {
b2d430
+        return s_http_host_v6_zone;
b2d430
+      }
b2d430
+      break;
b2d430
+
b2d430
+    case s_http_host_port:
b2d430
+    case s_http_host_port_start:
b2d430
+      if (IS_NUM(ch)) {
b2d430
+        return s_http_host_port;
b2d430
+      }
b2d430
+
b2d430
+      break;
b2d430
+
b2d430
+    default:
b2d430
+      break;
b2d430
+  }
b2d430
+  return s_http_host_dead;
b2d430
+}
b2d430
+
b2d430
+static int
b2d430
+http_parse_host(const char * buf, struct http_parser_url *u, int found_at) {
b2d430
+  enum http_host_state s;
b2d430
+
b2d430
+  const char *p;
b2d430
+  size_t buflen = u->field_data[UF_HOST].off + u->field_data[UF_HOST].len;
b2d430
+
b2d430
+  assert(u->field_set & (1 << UF_HOST));
b2d430
+
b2d430
+  u->field_data[UF_HOST].len = 0;
b2d430
+
b2d430
+  s = found_at ? s_http_userinfo_start : s_http_host_start;
b2d430
+
b2d430
+  for (p = buf + u->field_data[UF_HOST].off; p < buf + buflen; p++) {
b2d430
+    enum http_host_state new_s = http_parse_host_char(s, *p);
b2d430
+
b2d430
+    if (new_s == s_http_host_dead) {
b2d430
+      return 1;
b2d430
+    }
b2d430
+
b2d430
+    switch(new_s) {
b2d430
+      case s_http_host:
b2d430
+        if (s != s_http_host) {
b2d430
+          u->field_data[UF_HOST].off = p - buf;
b2d430
+        }
b2d430
+        u->field_data[UF_HOST].len++;
b2d430
+        break;
b2d430
+
b2d430
+      case s_http_host_v6:
b2d430
+        if (s != s_http_host_v6) {
b2d430
+          u->field_data[UF_HOST].off = p - buf;
b2d430
+        }
b2d430
+        u->field_data[UF_HOST].len++;
b2d430
+        break;
b2d430
+
b2d430
+      case s_http_host_v6_zone_start:
b2d430
+      case s_http_host_v6_zone:
b2d430
+        u->field_data[UF_HOST].len++;
b2d430
+        break;
b2d430
+
b2d430
+      case s_http_host_port:
b2d430
+        if (s != s_http_host_port) {
b2d430
+          u->field_data[UF_PORT].off = p - buf;
b2d430
+          u->field_data[UF_PORT].len = 0;
b2d430
+          u->field_set |= (1 << UF_PORT);
b2d430
+        }
b2d430
+        u->field_data[UF_PORT].len++;
b2d430
+        break;
b2d430
+
b2d430
+      case s_http_userinfo:
b2d430
+        if (s != s_http_userinfo) {
b2d430
+          u->field_data[UF_USERINFO].off = p - buf ;
b2d430
+          u->field_data[UF_USERINFO].len = 0;
b2d430
+          u->field_set |= (1 << UF_USERINFO);
b2d430
+        }
b2d430
+        u->field_data[UF_USERINFO].len++;
b2d430
+        break;
b2d430
+
b2d430
+      default:
b2d430
+        break;
b2d430
+    }
b2d430
+    s = new_s;
b2d430
+  }
b2d430
+
b2d430
+  /* Make sure we don't end somewhere unexpected */
b2d430
+  switch (s) {
b2d430
+    case s_http_host_start:
b2d430
+    case s_http_host_v6_start:
b2d430
+    case s_http_host_v6:
b2d430
+    case s_http_host_v6_zone_start:
b2d430
+    case s_http_host_v6_zone:
b2d430
+    case s_http_host_port_start:
b2d430
+    case s_http_userinfo:
b2d430
+    case s_http_userinfo_start:
b2d430
+      return 1;
b2d430
+    default:
b2d430
+      break;
b2d430
+  }
b2d430
+
b2d430
+  return 0;
b2d430
+}
b2d430
+
b2d430
+void
b2d430
+http_parser_url_init(struct http_parser_url *u) {
b2d430
+  memset(u, 0, sizeof(*u));
b2d430
+}
b2d430
+
b2d430
+int
b2d430
+http_parser_parse_url(const char *buf, size_t buflen, int is_connect,
b2d430
+                      struct http_parser_url *u)
b2d430
+{
b2d430
+  enum state s;
b2d430
+  const char *p;
b2d430
+  enum http_parser_url_fields uf, old_uf;
b2d430
+  int found_at = 0;
b2d430
+
b2d430
+  u->port = u->field_set = 0;
b2d430
+  s = is_connect ? s_req_server_start : s_req_spaces_before_url;
b2d430
+  old_uf = UF_MAX;
b2d430
+
b2d430
+  for (p = buf; p < buf + buflen; p++) {
b2d430
+    s = parse_url_char(s, *p);
b2d430
+
b2d430
+    /* Figure out the next field that we're operating on */
b2d430
+    switch (s) {
b2d430
+      case s_dead:
b2d430
+        return 1;
b2d430
+
b2d430
+      /* Skip delimeters */
b2d430
+      case s_req_schema_slash:
b2d430
+      case s_req_schema_slash_slash:
b2d430
+      case s_req_server_start:
b2d430
+      case s_req_query_string_start:
b2d430
+      case s_req_fragment_start:
b2d430
+        continue;
b2d430
+
b2d430
+      case s_req_schema:
b2d430
+        uf = UF_SCHEMA;
b2d430
+        break;
b2d430
+
b2d430
+      case s_req_server_with_at:
b2d430
+        found_at = 1;
b2d430
+
b2d430
+      /* FALLTROUGH */
b2d430
+      case s_req_server:
b2d430
+        uf = UF_HOST;
b2d430
+        break;
b2d430
+
b2d430
+      case s_req_path:
b2d430
+        uf = UF_PATH;
b2d430
+        break;
b2d430
+
b2d430
+      case s_req_query_string:
b2d430
+        uf = UF_QUERY;
b2d430
+        break;
b2d430
+
b2d430
+      case s_req_fragment:
b2d430
+        uf = UF_FRAGMENT;
b2d430
+        break;
b2d430
+
b2d430
+      default:
b2d430
+        assert(!"Unexpected state");
b2d430
+        return 1;
b2d430
+    }
b2d430
+
b2d430
+    /* Nothing's changed; soldier on */
b2d430
+    if (uf == old_uf) {
b2d430
+      u->field_data[uf].len++;
b2d430
+      continue;
b2d430
+    }
b2d430
+
b2d430
+    u->field_data[uf].off = p - buf;
b2d430
+    u->field_data[uf].len = 1;
b2d430
+
b2d430
+    u->field_set |= (1 << uf);
b2d430
+    old_uf = uf;
b2d430
+  }
b2d430
+
b2d430
+  /* host must be present if there is a schema */
b2d430
+  /* parsing http:///toto will fail */
b2d430
+  if ((u->field_set & (1 << UF_SCHEMA)) &&
b2d430
+      (u->field_set & (1 << UF_HOST)) == 0) {
b2d430
+    return 1;
b2d430
+  }
b2d430
+
b2d430
+  if (u->field_set & (1 << UF_HOST)) {
b2d430
+    if (http_parse_host(buf, u, found_at) != 0) {
b2d430
+      return 1;
b2d430
+    }
b2d430
+  }
b2d430
+
b2d430
+  /* CONNECT requests can only contain "hostname:port" */
b2d430
+  if (is_connect && u->field_set != ((1 << UF_HOST)|(1 << UF_PORT))) {
b2d430
+    return 1;
b2d430
+  }
b2d430
+
b2d430
+  if (u->field_set & (1 << UF_PORT)) {
b2d430
+    /* Don't bother with endp; we've already validated the string */
b2d430
+    unsigned long v = strtoul(buf + u->field_data[UF_PORT].off, NULL, 10);
b2d430
+
b2d430
+    /* Ports have a max value of 2^16 */
b2d430
+    if (v > 0xffff) {
b2d430
+      return 1;
b2d430
+    }
b2d430
+
b2d430
+    u->port = (uint16_t) v;
b2d430
+  }
b2d430
+
b2d430
+  return 0;
b2d430
+}
b2d430
+
b2d430
+void
b2d430
+http_parser_pause(http_parser *parser, int paused) {
b2d430
+  /* Users should only be pausing/unpausing a parser that is not in an error
b2d430
+   * state. In non-debug builds, there's not much that we can do about this
b2d430
+   * other than ignore it.
b2d430
+   */
b2d430
+  if (HTTP_PARSER_ERRNO(parser) == HPE_OK ||
b2d430
+      HTTP_PARSER_ERRNO(parser) == HPE_PAUSED) {
b2d430
+    SET_ERRNO((paused) ? HPE_PAUSED : HPE_OK);
b2d430
+  } else {
b2d430
+    assert(0 && "Attempting to pause parser in error state");
b2d430
+  }
b2d430
+}
b2d430
+
b2d430
+int
b2d430
+http_body_is_final(const struct http_parser *parser) {
b2d430
+    return parser->state == s_message_done;
b2d430
+}
b2d430
+
b2d430
+unsigned long
b2d430
+http_parser_version(void) {
b2d430
+  return HTTP_PARSER_VERSION_MAJOR * 0x10000 |
b2d430
+         HTTP_PARSER_VERSION_MINOR * 0x00100 |
b2d430
+         HTTP_PARSER_VERSION_PATCH * 0x00001;
b2d430
+}
b2d430
diff --git a/src/responder/secrets/http_parser.h b/src/responder/secrets/http_parser.h
b2d430
new file mode 100644
b2d430
index 0000000000000000000000000000000000000000..105ae510a8ab57509de2c68bbe1504e39ffb165e
b2d430
--- /dev/null
b2d430
+++ b/src/responder/secrets/http_parser.h
b2d430
@@ -0,0 +1,362 @@
b2d430
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
b2d430
+ *
b2d430
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
b2d430
+ * of this software and associated documentation files (the "Software"), to
b2d430
+ * deal in the Software without restriction, including without limitation the
b2d430
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
b2d430
+ * sell copies of the Software, and to permit persons to whom the Software is
b2d430
+ * furnished to do so, subject to the following conditions:
b2d430
+ *
b2d430
+ * The above copyright notice and this permission notice shall be included in
b2d430
+ * all copies or substantial portions of the Software.
b2d430
+ *
b2d430
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
b2d430
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
b2d430
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
b2d430
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
b2d430
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
b2d430
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
b2d430
+ * IN THE SOFTWARE.
b2d430
+ */
b2d430
+#ifndef http_parser_h
b2d430
+#define http_parser_h
b2d430
+#ifdef __cplusplus
b2d430
+extern "C" {
b2d430
+#endif
b2d430
+
b2d430
+/* Also update SONAME in the Makefile whenever you change these. */
b2d430
+#define HTTP_PARSER_VERSION_MAJOR 2
b2d430
+#define HTTP_PARSER_VERSION_MINOR 7
b2d430
+#define HTTP_PARSER_VERSION_PATCH 0
b2d430
+
b2d430
+#include <sys/types.h>
b2d430
+#if defined(_WIN32) && !defined(__MINGW32__) && \
b2d430
+  (!defined(_MSC_VER) || _MSC_VER<1600) && !defined(__WINE__)
b2d430
+#include <BaseTsd.h>
b2d430
+#include <stddef.h>
b2d430
+typedef __int8 int8_t;
b2d430
+typedef unsigned __int8 uint8_t;
b2d430
+typedef __int16 int16_t;
b2d430
+typedef unsigned __int16 uint16_t;
b2d430
+typedef __int32 int32_t;
b2d430
+typedef unsigned __int32 uint32_t;
b2d430
+typedef __int64 int64_t;
b2d430
+typedef unsigned __int64 uint64_t;
b2d430
+#else
b2d430
+#include <stdint.h>
b2d430
+#endif
b2d430
+
b2d430
+/* Compile with -DHTTP_PARSER_STRICT=0 to make less checks, but run
b2d430
+ * faster
b2d430
+ */
b2d430
+#ifndef HTTP_PARSER_STRICT
b2d430
+# define HTTP_PARSER_STRICT 1
b2d430
+#endif
b2d430
+
b2d430
+/* Maximium header size allowed. If the macro is not defined
b2d430
+ * before including this header then the default is used. To
b2d430
+ * change the maximum header size, define the macro in the build
b2d430
+ * environment (e.g. -DHTTP_MAX_HEADER_SIZE=<value>). To remove
b2d430
+ * the effective limit on the size of the header, define the macro
b2d430
+ * to a very large number (e.g. -DHTTP_MAX_HEADER_SIZE=0x7fffffff)
b2d430
+ */
b2d430
+#ifndef HTTP_MAX_HEADER_SIZE
b2d430
+# define HTTP_MAX_HEADER_SIZE (80*1024)
b2d430
+#endif
b2d430
+
b2d430
+typedef struct http_parser http_parser;
b2d430
+typedef struct http_parser_settings http_parser_settings;
b2d430
+
b2d430
+
b2d430
+/* Callbacks should return non-zero to indicate an error. The parser will
b2d430
+ * then halt execution.
b2d430
+ *
b2d430
+ * The one exception is on_headers_complete. In a HTTP_RESPONSE parser
b2d430
+ * returning '1' from on_headers_complete will tell the parser that it
b2d430
+ * should not expect a body. This is used when receiving a response to a
b2d430
+ * HEAD request which may contain 'Content-Length' or 'Transfer-Encoding:
b2d430
+ * chunked' headers that indicate the presence of a body.
b2d430
+ *
b2d430
+ * Returning `2` from on_headers_complete will tell parser that it should not
b2d430
+ * expect neither a body nor any futher responses on this connection. This is
b2d430
+ * useful for handling responses to a CONNECT request which may not contain
b2d430
+ * `Upgrade` or `Connection: upgrade` headers.
b2d430
+ *
b2d430
+ * http_data_cb does not return data chunks. It will be called arbitrarily
b2d430
+ * many times for each string. E.G. you might get 10 callbacks for "on_url"
b2d430
+ * each providing just a few characters more data.
b2d430
+ */
b2d430
+typedef int (*http_data_cb) (http_parser*, const char *at, size_t length);
b2d430
+typedef int (*http_cb) (http_parser*);
b2d430
+
b2d430
+
b2d430
+/* Request Methods */
b2d430
+#define HTTP_METHOD_MAP(XX)         \
b2d430
+  XX(0,  DELETE,      DELETE)       \
b2d430
+  XX(1,  GET,         GET)          \
b2d430
+  XX(2,  HEAD,        HEAD)         \
b2d430
+  XX(3,  POST,        POST)         \
b2d430
+  XX(4,  PUT,         PUT)          \
b2d430
+  /* pathological */                \
b2d430
+  XX(5,  CONNECT,     CONNECT)      \
b2d430
+  XX(6,  OPTIONS,     OPTIONS)      \
b2d430
+  XX(7,  TRACE,       TRACE)        \
b2d430
+  /* WebDAV */                      \
b2d430
+  XX(8,  COPY,        COPY)         \
b2d430
+  XX(9,  LOCK,        LOCK)         \
b2d430
+  XX(10, MKCOL,       MKCOL)        \
b2d430
+  XX(11, MOVE,        MOVE)         \
b2d430
+  XX(12, PROPFIND,    PROPFIND)     \
b2d430
+  XX(13, PROPPATCH,   PROPPATCH)    \
b2d430
+  XX(14, SEARCH,      SEARCH)       \
b2d430
+  XX(15, UNLOCK,      UNLOCK)       \
b2d430
+  XX(16, BIND,        BIND)         \
b2d430
+  XX(17, REBIND,      REBIND)       \
b2d430
+  XX(18, UNBIND,      UNBIND)       \
b2d430
+  XX(19, ACL,         ACL)          \
b2d430
+  /* subversion */                  \
b2d430
+  XX(20, REPORT,      REPORT)       \
b2d430
+  XX(21, MKACTIVITY,  MKACTIVITY)   \
b2d430
+  XX(22, CHECKOUT,    CHECKOUT)     \
b2d430
+  XX(23, MERGE,       MERGE)        \
b2d430
+  /* upnp */                        \
b2d430
+  XX(24, MSEARCH,     M-SEARCH)     \
b2d430
+  XX(25, NOTIFY,      NOTIFY)       \
b2d430
+  XX(26, SUBSCRIBE,   SUBSCRIBE)    \
b2d430
+  XX(27, UNSUBSCRIBE, UNSUBSCRIBE)  \
b2d430
+  /* RFC-5789 */                    \
b2d430
+  XX(28, PATCH,       PATCH)        \
b2d430
+  XX(29, PURGE,       PURGE)        \
b2d430
+  /* CalDAV */                      \
b2d430
+  XX(30, MKCALENDAR,  MKCALENDAR)   \
b2d430
+  /* RFC-2068, section 19.6.1.2 */  \
b2d430
+  XX(31, LINK,        LINK)         \
b2d430
+  XX(32, UNLINK,      UNLINK)       \
b2d430
+
b2d430
+enum http_method
b2d430
+  {
b2d430
+#define XX(num, name, string) HTTP_##name = num,
b2d430
+  HTTP_METHOD_MAP(XX)
b2d430
+#undef XX
b2d430
+  };
b2d430
+
b2d430
+
b2d430
+enum http_parser_type { HTTP_REQUEST, HTTP_RESPONSE, HTTP_BOTH };
b2d430
+
b2d430
+
b2d430
+/* Flag values for http_parser.flags field */
b2d430
+enum flags
b2d430
+  { F_CHUNKED               = 1 << 0
b2d430
+  , F_CONNECTION_KEEP_ALIVE = 1 << 1
b2d430
+  , F_CONNECTION_CLOSE      = 1 << 2
b2d430
+  , F_CONNECTION_UPGRADE    = 1 << 3
b2d430
+  , F_TRAILING              = 1 << 4
b2d430
+  , F_UPGRADE               = 1 << 5
b2d430
+  , F_SKIPBODY              = 1 << 6
b2d430
+  , F_CONTENTLENGTH         = 1 << 7
b2d430
+  };
b2d430
+
b2d430
+
b2d430
+/* Map for errno-related constants
b2d430
+ *
b2d430
+ * The provided argument should be a macro that takes 2 arguments.
b2d430
+ */
b2d430
+#define HTTP_ERRNO_MAP(XX)                                           \
b2d430
+  /* No error */                                                     \
b2d430
+  XX(OK, "success")                                                  \
b2d430
+                                                                     \
b2d430
+  /* Callback-related errors */                                      \
b2d430
+  XX(CB_message_begin, "the on_message_begin callback failed")       \
b2d430
+  XX(CB_url, "the on_url callback failed")                           \
b2d430
+  XX(CB_header_field, "the on_header_field callback failed")         \
b2d430
+  XX(CB_header_value, "the on_header_value callback failed")         \
b2d430
+  XX(CB_headers_complete, "the on_headers_complete callback failed") \
b2d430
+  XX(CB_body, "the on_body callback failed")                         \
b2d430
+  XX(CB_message_complete, "the on_message_complete callback failed") \
b2d430
+  XX(CB_status, "the on_status callback failed")                     \
b2d430
+  XX(CB_chunk_header, "the on_chunk_header callback failed")         \
b2d430
+  XX(CB_chunk_complete, "the on_chunk_complete callback failed")     \
b2d430
+                                                                     \
b2d430
+  /* Parsing-related errors */                                       \
b2d430
+  XX(INVALID_EOF_STATE, "stream ended at an unexpected time")        \
b2d430
+  XX(HEADER_OVERFLOW,                                                \
b2d430
+     "too many header bytes seen; overflow detected")                \
b2d430
+  XX(CLOSED_CONNECTION,                                              \
b2d430
+     "data received after completed connection: close message")      \
b2d430
+  XX(INVALID_VERSION, "invalid HTTP version")                        \
b2d430
+  XX(INVALID_STATUS, "invalid HTTP status code")                     \
b2d430
+  XX(INVALID_METHOD, "invalid HTTP method")                          \
b2d430
+  XX(INVALID_URL, "invalid URL")                                     \
b2d430
+  XX(INVALID_HOST, "invalid host")                                   \
b2d430
+  XX(INVALID_PORT, "invalid port")                                   \
b2d430
+  XX(INVALID_PATH, "invalid path")                                   \
b2d430
+  XX(INVALID_QUERY_STRING, "invalid query string")                   \
b2d430
+  XX(INVALID_FRAGMENT, "invalid fragment")                           \
b2d430
+  XX(LF_EXPECTED, "LF character expected")                           \
b2d430
+  XX(INVALID_HEADER_TOKEN, "invalid character in header")            \
b2d430
+  XX(INVALID_CONTENT_LENGTH,                                         \
b2d430
+     "invalid character in content-length header")                   \
b2d430
+  XX(UNEXPECTED_CONTENT_LENGTH,                                      \
b2d430
+     "unexpected content-length header")                             \
b2d430
+  XX(INVALID_CHUNK_SIZE,                                             \
b2d430
+     "invalid character in chunk size header")                       \
b2d430
+  XX(INVALID_CONSTANT, "invalid constant string")                    \
b2d430
+  XX(INVALID_INTERNAL_STATE, "encountered unexpected internal state")\
b2d430
+  XX(STRICT, "strict mode assertion failed")                         \
b2d430
+  XX(PAUSED, "parser is paused")                                     \
b2d430
+  XX(UNKNOWN, "an unknown error occurred")
b2d430
+
b2d430
+
b2d430
+/* Define HPE_* values for each errno value above */
b2d430
+#define HTTP_ERRNO_GEN(n, s) HPE_##n,
b2d430
+enum http_errno {
b2d430
+  HTTP_ERRNO_MAP(HTTP_ERRNO_GEN)
b2d430
+};
b2d430
+#undef HTTP_ERRNO_GEN
b2d430
+
b2d430
+
b2d430
+/* Get an http_errno value from an http_parser */
b2d430
+#define HTTP_PARSER_ERRNO(p)            ((enum http_errno) (p)->http_errno)
b2d430
+
b2d430
+
b2d430
+struct http_parser {
b2d430
+  /** PRIVATE **/
b2d430
+  unsigned int type : 2;         /* enum http_parser_type */
b2d430
+  unsigned int flags : 8;        /* F_* values from 'flags' enum; semi-public */
b2d430
+  unsigned int state : 7;        /* enum state from http_parser.c */
b2d430
+  unsigned int header_state : 7; /* enum header_state from http_parser.c */
b2d430
+  unsigned int index : 7;        /* index into current matcher */
b2d430
+  unsigned int lenient_http_headers : 1;
b2d430
+
b2d430
+  uint32_t nread;          /* # bytes read in various scenarios */
b2d430
+  uint64_t content_length; /* # bytes in body (0 if no Content-Length header) */
b2d430
+
b2d430
+  /** READ-ONLY **/
b2d430
+  unsigned short http_major;
b2d430
+  unsigned short http_minor;
b2d430
+  unsigned int status_code : 16; /* responses only */
b2d430
+  unsigned int method : 8;       /* requests only */
b2d430
+  unsigned int http_errno : 7;
b2d430
+
b2d430
+  /* 1 = Upgrade header was present and the parser has exited because of that.
b2d430
+   * 0 = No upgrade header present.
b2d430
+   * Should be checked when http_parser_execute() returns in addition to
b2d430
+   * error checking.
b2d430
+   */
b2d430
+  unsigned int upgrade : 1;
b2d430
+
b2d430
+  /** PUBLIC **/
b2d430
+  void *data; /* A pointer to get hook to the "connection" or "socket" object */
b2d430
+};
b2d430
+
b2d430
+
b2d430
+struct http_parser_settings {
b2d430
+  http_cb      on_message_begin;
b2d430
+  http_data_cb on_url;
b2d430
+  http_data_cb on_status;
b2d430
+  http_data_cb on_header_field;
b2d430
+  http_data_cb on_header_value;
b2d430
+  http_cb      on_headers_complete;
b2d430
+  http_data_cb on_body;
b2d430
+  http_cb      on_message_complete;
b2d430
+  /* When on_chunk_header is called, the current chunk length is stored
b2d430
+   * in parser->content_length.
b2d430
+   */
b2d430
+  http_cb      on_chunk_header;
b2d430
+  http_cb      on_chunk_complete;
b2d430
+};
b2d430
+
b2d430
+
b2d430
+enum http_parser_url_fields
b2d430
+  { UF_SCHEMA           = 0
b2d430
+  , UF_HOST             = 1
b2d430
+  , UF_PORT             = 2
b2d430
+  , UF_PATH             = 3
b2d430
+  , UF_QUERY            = 4
b2d430
+  , UF_FRAGMENT         = 5
b2d430
+  , UF_USERINFO         = 6
b2d430
+  , UF_MAX              = 7
b2d430
+  };
b2d430
+
b2d430
+
b2d430
+/* Result structure for http_parser_parse_url().
b2d430
+ *
b2d430
+ * Callers should index into field_data[] with UF_* values iff field_set
b2d430
+ * has the relevant (1 << UF_*) bit set. As a courtesy to clients (and
b2d430
+ * because we probably have padding left over), we convert any port to
b2d430
+ * a uint16_t.
b2d430
+ */
b2d430
+struct http_parser_url {
b2d430
+  uint16_t field_set;           /* Bitmask of (1 << UF_*) values */
b2d430
+  uint16_t port;                /* Converted UF_PORT string */
b2d430
+
b2d430
+  struct {
b2d430
+    uint16_t off;               /* Offset into buffer in which field starts */
b2d430
+    uint16_t len;               /* Length of run in buffer */
b2d430
+  } field_data[UF_MAX];
b2d430
+};
b2d430
+
b2d430
+
b2d430
+/* Returns the library version. Bits 16-23 contain the major version number,
b2d430
+ * bits 8-15 the minor version number and bits 0-7 the patch level.
b2d430
+ * Usage example:
b2d430
+ *
b2d430
+ *   unsigned long version = http_parser_version();
b2d430
+ *   unsigned major = (version >> 16) & 255;
b2d430
+ *   unsigned minor = (version >> 8) & 255;
b2d430
+ *   unsigned patch = version & 255;
b2d430
+ *   printf("http_parser v%u.%u.%u\n", major, minor, patch);
b2d430
+ */
b2d430
+unsigned long http_parser_version(void);
b2d430
+
b2d430
+void http_parser_init(http_parser *parser, enum http_parser_type type);
b2d430
+
b2d430
+
b2d430
+/* Initialize http_parser_settings members to 0
b2d430
+ */
b2d430
+void http_parser_settings_init(http_parser_settings *settings);
b2d430
+
b2d430
+
b2d430
+/* Executes the parser. Returns number of parsed bytes. Sets
b2d430
+ * `parser->http_errno` on error. */
b2d430
+size_t http_parser_execute(http_parser *parser,
b2d430
+                           const http_parser_settings *settings,
b2d430
+                           const char *data,
b2d430
+                           size_t len);
b2d430
+
b2d430
+
b2d430
+/* If http_should_keep_alive() in the on_headers_complete or
b2d430
+ * on_message_complete callback returns 0, then this should be
b2d430
+ * the last message on the connection.
b2d430
+ * If you are the server, respond with the "Connection: close" header.
b2d430
+ * If you are the client, close the connection.
b2d430
+ */
b2d430
+int http_should_keep_alive(const http_parser *parser);
b2d430
+
b2d430
+/* Returns a string version of the HTTP method. */
b2d430
+const char *http_method_str(enum http_method m);
b2d430
+
b2d430
+/* Return a string name of the given error */
b2d430
+const char *http_errno_name(enum http_errno err);
b2d430
+
b2d430
+/* Return a string description of the given error */
b2d430
+const char *http_errno_description(enum http_errno err);
b2d430
+
b2d430
+/* Initialize all http_parser_url members to 0 */
b2d430
+void http_parser_url_init(struct http_parser_url *u);
b2d430
+
b2d430
+/* Parse a URL; return nonzero on failure */
b2d430
+int http_parser_parse_url(const char *buf, size_t buflen,
b2d430
+                          int is_connect,
b2d430
+                          struct http_parser_url *u);
b2d430
+
b2d430
+/* Pause or un-pause the parser; a nonzero value pauses */
b2d430
+void http_parser_pause(http_parser *parser, int paused);
b2d430
+
b2d430
+/* Checks if this is the final chunk of the body. */
b2d430
+int http_body_is_final(const http_parser *parser);
b2d430
+
b2d430
+#ifdef __cplusplus
b2d430
+}
b2d430
+#endif
b2d430
+#endif
b2d430
diff --git a/src/responder/secrets/secsrv_private.h b/src/responder/secrets/secsrv_private.h
b2d430
index c3b663996e138bbb14f1ab74db75398ffd0bd5c7..47ed7642cf8da5ecfc8f995199746596a12b3e1d 100644
b2d430
--- a/src/responder/secrets/secsrv_private.h
b2d430
+++ b/src/responder/secrets/secsrv_private.h
b2d430
@@ -25,7 +25,7 @@
b2d430
 #include "config.h"
b2d430
 #include "responder/common/responder.h"
b2d430
 #include "responder/secrets/secsrv.h"
b2d430
-#include <http_parser.h>
b2d430
+#include "http_parser.h"
b2d430
 
b2d430
 struct sec_kvp {
b2d430
     char *name;
b2d430
-- 
b2d430
2.4.11
b2d430