iodine 0.4.7 → 0.4.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of iodine might be problematic. Click here for more details.
- checksums.yaml +4 -4
 - data/CHANGELOG.md +8 -0
 - data/README.md +1 -1
 - data/ext/iodine/http.c +34 -4
 - data/ext/iodine/http.h +8 -0
 - data/ext/iodine/http1.c +269 -160
 - data/ext/iodine/http1_parser.c +276 -0
 - data/ext/iodine/http1_parser.h +111 -0
 - data/ext/iodine/http1_response.c +8 -8
 - data/ext/iodine/http_response.c +1 -16
 - data/lib/iodine/version.rb +1 -1
 - metadata +4 -4
 - data/ext/iodine/http1_simple_parser.c +0 -496
 - data/ext/iodine/http1_simple_parser.h +0 -68
 
| 
         @@ -0,0 +1,276 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            /*
         
     | 
| 
      
 2 
     | 
    
         
            +
            Copyright: Boaz segev, 2017
         
     | 
| 
      
 3 
     | 
    
         
            +
            License: MIT
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            Feel free to copy, use and enjoy according to the license provided.
         
     | 
| 
      
 6 
     | 
    
         
            +
            */
         
     | 
| 
      
 7 
     | 
    
         
            +
            #ifndef __GNU_SOURCE
         
     | 
| 
      
 8 
     | 
    
         
            +
            #define __GNU_SOURCE
         
     | 
| 
      
 9 
     | 
    
         
            +
            #endif
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
            #include "http1_parser.h"
         
     | 
| 
      
 12 
     | 
    
         
            +
            #include <ctype.h>
         
     | 
| 
      
 13 
     | 
    
         
            +
            #include <stdio.h>
         
     | 
| 
      
 14 
     | 
    
         
            +
            #include <string.h>
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
            #define PREFER_MEMCHAR 0
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
            /* *****************************************************************************
         
     | 
| 
      
 19 
     | 
    
         
            +
            Seeking for characters in a string
         
     | 
| 
      
 20 
     | 
    
         
            +
            ***************************************************************************** */
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
            #if PREFER_MEMCHAR
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
            /* a helper that seeks any char, converts it to NUL and returns 1 if found. */
         
     | 
| 
      
 25 
     | 
    
         
            +
            inline static uint8_t seek2ch(uint8_t **pos, uint8_t *const limit, uint8_t ch) {
         
     | 
| 
      
 26 
     | 
    
         
            +
              /* This is library based alternative that is sometimes slower  */
         
     | 
| 
      
 27 
     | 
    
         
            +
              if (*pos >= limit || **pos == ch) {
         
     | 
| 
      
 28 
     | 
    
         
            +
                return 0;
         
     | 
| 
      
 29 
     | 
    
         
            +
              }
         
     | 
| 
      
 30 
     | 
    
         
            +
              uint8_t *tmp = memchr(*pos, ch, limit - (*pos));
         
     | 
| 
      
 31 
     | 
    
         
            +
              if (tmp) {
         
     | 
| 
      
 32 
     | 
    
         
            +
                *pos = tmp;
         
     | 
| 
      
 33 
     | 
    
         
            +
                *tmp = 0;
         
     | 
| 
      
 34 
     | 
    
         
            +
                return 1;
         
     | 
| 
      
 35 
     | 
    
         
            +
              }
         
     | 
| 
      
 36 
     | 
    
         
            +
              *pos = limit;
         
     | 
| 
      
 37 
     | 
    
         
            +
              return 0;
         
     | 
| 
      
 38 
     | 
    
         
            +
            }
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
            #else
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
            /* a helper that seeks any char, converts it to NUL and returns 1 if found. */
         
     | 
| 
      
 43 
     | 
    
         
            +
            static inline uint8_t seek2ch(uint8_t **buffer, const uint8_t *const limit,
         
     | 
| 
      
 44 
     | 
    
         
            +
                                          const uint8_t c) {
         
     | 
| 
      
 45 
     | 
    
         
            +
              /* this single char lookup is better when target is closer... */
         
     | 
| 
      
 46 
     | 
    
         
            +
              if (**buffer == c) {
         
     | 
| 
      
 47 
     | 
    
         
            +
                **buffer = 0;
         
     | 
| 
      
 48 
     | 
    
         
            +
                return 1;
         
     | 
| 
      
 49 
     | 
    
         
            +
              }
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
              uint64_t wanted = 0x0101010101010101ULL * c;
         
     | 
| 
      
 52 
     | 
    
         
            +
              uint64_t *lpos = (uint64_t *)*buffer;
         
     | 
| 
      
 53 
     | 
    
         
            +
              uint64_t *llimit = ((uint64_t *)limit) - 1;
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
              for (; lpos < llimit; lpos++) {
         
     | 
| 
      
 56 
     | 
    
         
            +
                const uint64_t eq = ~((*lpos) ^ wanted);
         
     | 
| 
      
 57 
     | 
    
         
            +
                const uint64_t t0 = (eq & 0x7f7f7f7f7f7f7f7fllu) + 0x0101010101010101llu;
         
     | 
| 
      
 58 
     | 
    
         
            +
                const uint64_t t1 = (eq & 0x8080808080808080llu);
         
     | 
| 
      
 59 
     | 
    
         
            +
                if ((t0 & t1)) {
         
     | 
| 
      
 60 
     | 
    
         
            +
                  break;
         
     | 
| 
      
 61 
     | 
    
         
            +
                }
         
     | 
| 
      
 62 
     | 
    
         
            +
              }
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
              *buffer = (uint8_t *)lpos;
         
     | 
| 
      
 65 
     | 
    
         
            +
              while (*buffer < limit) {
         
     | 
| 
      
 66 
     | 
    
         
            +
                if (**buffer == c) {
         
     | 
| 
      
 67 
     | 
    
         
            +
                  **buffer = 0;
         
     | 
| 
      
 68 
     | 
    
         
            +
                  return 1;
         
     | 
| 
      
 69 
     | 
    
         
            +
                }
         
     | 
| 
      
 70 
     | 
    
         
            +
                (*buffer)++;
         
     | 
| 
      
 71 
     | 
    
         
            +
              }
         
     | 
| 
      
 72 
     | 
    
         
            +
              return 0;
         
     | 
| 
      
 73 
     | 
    
         
            +
            }
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
            #endif
         
     | 
| 
      
 76 
     | 
    
         
            +
             
     | 
| 
      
 77 
     | 
    
         
            +
            /* a helper that seeks the EOL, converts it to NUL and returns it's length */
         
     | 
| 
      
 78 
     | 
    
         
            +
            inline static uint8_t seek2eol(uint8_t **pos, uint8_t *const limit) {
         
     | 
| 
      
 79 
     | 
    
         
            +
              /* single char lookup using memchr might be better when target is far... */
         
     | 
| 
      
 80 
     | 
    
         
            +
              if (!seek2ch(pos, limit, '\n'))
         
     | 
| 
      
 81 
     | 
    
         
            +
                return 0;
         
     | 
| 
      
 82 
     | 
    
         
            +
              if ((*pos)[-1] == '\r') {
         
     | 
| 
      
 83 
     | 
    
         
            +
                (*pos)[-1] = 0;
         
     | 
| 
      
 84 
     | 
    
         
            +
                return 2;
         
     | 
| 
      
 85 
     | 
    
         
            +
              }
         
     | 
| 
      
 86 
     | 
    
         
            +
              return 1;
         
     | 
| 
      
 87 
     | 
    
         
            +
            }
         
     | 
| 
      
 88 
     | 
    
         
            +
             
     | 
| 
      
 89 
     | 
    
         
            +
            /* *****************************************************************************
         
     | 
| 
      
 90 
     | 
    
         
            +
            HTTP/1.1 parsre stages
         
     | 
| 
      
 91 
     | 
    
         
            +
            ***************************************************************************** */
         
     | 
| 
      
 92 
     | 
    
         
            +
             
     | 
| 
      
 93 
     | 
    
         
            +
            inline static int consume_response_line(struct http1_fio_parser_args_s *args,
         
     | 
| 
      
 94 
     | 
    
         
            +
                                                    uint8_t *start, uint8_t *end) {
         
     | 
| 
      
 95 
     | 
    
         
            +
              args->parser->state.reserved |= 128;
         
     | 
| 
      
 96 
     | 
    
         
            +
              uint8_t *tmp = start;
         
     | 
| 
      
 97 
     | 
    
         
            +
              if (!seek2ch(&tmp, end, ' '))
         
     | 
| 
      
 98 
     | 
    
         
            +
                return -1;
         
     | 
| 
      
 99 
     | 
    
         
            +
              if (args->on_http_version(args->parser, (char *)start, tmp - start))
         
     | 
| 
      
 100 
     | 
    
         
            +
                return -1;
         
     | 
| 
      
 101 
     | 
    
         
            +
              tmp = start = tmp + 1;
         
     | 
| 
      
 102 
     | 
    
         
            +
              if (!seek2ch(&tmp, end, ' '))
         
     | 
| 
      
 103 
     | 
    
         
            +
                return -1;
         
     | 
| 
      
 104 
     | 
    
         
            +
              if (args->on_status(args->parser, atol((char *)start), (char *)(tmp + 1),
         
     | 
| 
      
 105 
     | 
    
         
            +
                                  end - tmp))
         
     | 
| 
      
 106 
     | 
    
         
            +
                return -1;
         
     | 
| 
      
 107 
     | 
    
         
            +
              return 0;
         
     | 
| 
      
 108 
     | 
    
         
            +
            }
         
     | 
| 
      
 109 
     | 
    
         
            +
             
     | 
| 
      
 110 
     | 
    
         
            +
            inline static int consume_request_line(struct http1_fio_parser_args_s *args,
         
     | 
| 
      
 111 
     | 
    
         
            +
                                                   uint8_t *start, uint8_t *end) {
         
     | 
| 
      
 112 
     | 
    
         
            +
              uint8_t *tmp = start;
         
     | 
| 
      
 113 
     | 
    
         
            +
              if (!seek2ch(&tmp, end, ' '))
         
     | 
| 
      
 114 
     | 
    
         
            +
                return -1;
         
     | 
| 
      
 115 
     | 
    
         
            +
              if (args->on_method(args->parser, (char *)start, tmp - start))
         
     | 
| 
      
 116 
     | 
    
         
            +
                return -1;
         
     | 
| 
      
 117 
     | 
    
         
            +
              tmp = start = tmp + 1;
         
     | 
| 
      
 118 
     | 
    
         
            +
              if (seek2ch(&tmp, end, '?')) {
         
     | 
| 
      
 119 
     | 
    
         
            +
                if (args->on_path(args->parser, (char *)start, tmp - start))
         
     | 
| 
      
 120 
     | 
    
         
            +
                  return -1;
         
     | 
| 
      
 121 
     | 
    
         
            +
                tmp = start = tmp + 1;
         
     | 
| 
      
 122 
     | 
    
         
            +
                if (!seek2ch(&tmp, end, ' '))
         
     | 
| 
      
 123 
     | 
    
         
            +
                  return -1;
         
     | 
| 
      
 124 
     | 
    
         
            +
                if (tmp - start > 0 &&
         
     | 
| 
      
 125 
     | 
    
         
            +
                    args->on_query(args->parser, (char *)start, tmp - start))
         
     | 
| 
      
 126 
     | 
    
         
            +
                  return -1;
         
     | 
| 
      
 127 
     | 
    
         
            +
              } else {
         
     | 
| 
      
 128 
     | 
    
         
            +
                tmp = start;
         
     | 
| 
      
 129 
     | 
    
         
            +
                if (!seek2ch(&tmp, end, ' '))
         
     | 
| 
      
 130 
     | 
    
         
            +
                  return -1;
         
     | 
| 
      
 131 
     | 
    
         
            +
                if (args->on_path(args->parser, (char *)start, tmp - start))
         
     | 
| 
      
 132 
     | 
    
         
            +
                  return -1;
         
     | 
| 
      
 133 
     | 
    
         
            +
              }
         
     | 
| 
      
 134 
     | 
    
         
            +
              start = tmp + 1;
         
     | 
| 
      
 135 
     | 
    
         
            +
              if (start + 7 >= end)
         
     | 
| 
      
 136 
     | 
    
         
            +
                return -1;
         
     | 
| 
      
 137 
     | 
    
         
            +
              if (args->on_http_version(args->parser, (char *)start, end - start))
         
     | 
| 
      
 138 
     | 
    
         
            +
                return -1;
         
     | 
| 
      
 139 
     | 
    
         
            +
              return 0;
         
     | 
| 
      
 140 
     | 
    
         
            +
            }
         
     | 
| 
      
 141 
     | 
    
         
            +
             
     | 
| 
      
 142 
     | 
    
         
            +
            inline static int consume_header(struct http1_fio_parser_args_s *args,
         
     | 
| 
      
 143 
     | 
    
         
            +
                                             uint8_t *start, uint8_t *end) {
         
     | 
| 
      
 144 
     | 
    
         
            +
              uint8_t t2 = 1;
         
     | 
| 
      
 145 
     | 
    
         
            +
              uint8_t *tmp = start;
         
     | 
| 
      
 146 
     | 
    
         
            +
              /* divide header name from data */
         
     | 
| 
      
 147 
     | 
    
         
            +
              if (!seek2ch(&tmp, end, ':'))
         
     | 
| 
      
 148 
     | 
    
         
            +
                return -1;
         
     | 
| 
      
 149 
     | 
    
         
            +
            #if HTTP_HEADERS_LOWERCASE
         
     | 
| 
      
 150 
     | 
    
         
            +
              for (uint8_t *t3 = start; t3 < tmp; t3++) {
         
     | 
| 
      
 151 
     | 
    
         
            +
                *t3 = tolower(*t3);
         
     | 
| 
      
 152 
     | 
    
         
            +
              }
         
     | 
| 
      
 153 
     | 
    
         
            +
            #endif
         
     | 
| 
      
 154 
     | 
    
         
            +
             
     | 
| 
      
 155 
     | 
    
         
            +
              tmp++;
         
     | 
| 
      
 156 
     | 
    
         
            +
              if (tmp[0] == ' ') {
         
     | 
| 
      
 157 
     | 
    
         
            +
                tmp++;
         
     | 
| 
      
 158 
     | 
    
         
            +
                t2++;
         
     | 
| 
      
 159 
     | 
    
         
            +
              };
         
     | 
| 
      
 160 
     | 
    
         
            +
            #if HTTP_HEADERS_LOWERCASE
         
     | 
| 
      
 161 
     | 
    
         
            +
              if ((tmp - start) - t2 == 14 &&
         
     | 
| 
      
 162 
     | 
    
         
            +
                  *((uint64_t *)start) == *((uint64_t *)"content-") &&
         
     | 
| 
      
 163 
     | 
    
         
            +
                  *((uint64_t *)(start + 6)) == *((uint64_t *)"t-length")) {
         
     | 
| 
      
 164 
     | 
    
         
            +
                /* handle the special `content-length` header */
         
     | 
| 
      
 165 
     | 
    
         
            +
                args->parser->state.content_length = atol((char *)tmp);
         
     | 
| 
      
 166 
     | 
    
         
            +
              }
         
     | 
| 
      
 167 
     | 
    
         
            +
            #else
         
     | 
| 
      
 168 
     | 
    
         
            +
              if ((tmp - start) - t2 == 14 &&
         
     | 
| 
      
 169 
     | 
    
         
            +
                  HEADER_NAME_IS_EQ((char *)start, "content-length", 14)) {
         
     | 
| 
      
 170 
     | 
    
         
            +
                /* handle the special `content-length` header */
         
     | 
| 
      
 171 
     | 
    
         
            +
                args->parser->state.content_length = atol((char *)tmp);
         
     | 
| 
      
 172 
     | 
    
         
            +
              }
         
     | 
| 
      
 173 
     | 
    
         
            +
            #endif
         
     | 
| 
      
 174 
     | 
    
         
            +
              /* perform callback */
         
     | 
| 
      
 175 
     | 
    
         
            +
              if (args->on_header(args->parser, (char *)start, (tmp - start) - t2,
         
     | 
| 
      
 176 
     | 
    
         
            +
                                  (char *)tmp, end - tmp))
         
     | 
| 
      
 177 
     | 
    
         
            +
                return -1;
         
     | 
| 
      
 178 
     | 
    
         
            +
              return 0;
         
     | 
| 
      
 179 
     | 
    
         
            +
            }
         
     | 
| 
      
 180 
     | 
    
         
            +
             
     | 
| 
      
 181 
     | 
    
         
            +
            /* *****************************************************************************
         
     | 
| 
      
 182 
     | 
    
         
            +
            HTTP/1.1 parsre function
         
     | 
| 
      
 183 
     | 
    
         
            +
            ***************************************************************************** */
         
     | 
| 
      
 184 
     | 
    
         
            +
             
     | 
| 
      
 185 
     | 
    
         
            +
            size_t http1_fio_parser_fn(struct http1_fio_parser_args_s *args) {
         
     | 
| 
      
 186 
     | 
    
         
            +
              uint8_t *start = args->buffer;
         
     | 
| 
      
 187 
     | 
    
         
            +
              uint8_t *end = start;
         
     | 
| 
      
 188 
     | 
    
         
            +
              uint8_t *const stop = start + args->length;
         
     | 
| 
      
 189 
     | 
    
         
            +
              uint8_t eol_len = 0;
         
     | 
| 
      
 190 
     | 
    
         
            +
            #define CONSUMED ((size_t)((uintptr_t)start - (uintptr_t)args->buffer))
         
     | 
| 
      
 191 
     | 
    
         
            +
              // fprintf(stderr, "** resuming with at %p with %.*s...(%lu)\n", args->buffer,
         
     | 
| 
      
 192 
     | 
    
         
            +
              // 4,
         
     | 
| 
      
 193 
     | 
    
         
            +
              //         start, args->length);
         
     | 
| 
      
 194 
     | 
    
         
            +
              switch ((args->parser->state.reserved & 31)) {
         
     | 
| 
      
 195 
     | 
    
         
            +
             
     | 
| 
      
 196 
     | 
    
         
            +
              /* request / response line */
         
     | 
| 
      
 197 
     | 
    
         
            +
              case 0:
         
     | 
| 
      
 198 
     | 
    
         
            +
                /* clear out any leadinng white space */
         
     | 
| 
      
 199 
     | 
    
         
            +
                while (*start == '\r' || *start == '\r' || *start == ' ' || *start == 0) {
         
     | 
| 
      
 200 
     | 
    
         
            +
                  start++;
         
     | 
| 
      
 201 
     | 
    
         
            +
                }
         
     | 
| 
      
 202 
     | 
    
         
            +
                end = start;
         
     | 
| 
      
 203 
     | 
    
         
            +
                /* make sure the whole line is available*/
         
     | 
| 
      
 204 
     | 
    
         
            +
                if (!(eol_len = seek2eol(&end, stop)))
         
     | 
| 
      
 205 
     | 
    
         
            +
                  return CONSUMED;
         
     | 
| 
      
 206 
     | 
    
         
            +
             
     | 
| 
      
 207 
     | 
    
         
            +
                if (start[0] >= '1' && start[0] <= '9') {
         
     | 
| 
      
 208 
     | 
    
         
            +
                  /* HTTP response */
         
     | 
| 
      
 209 
     | 
    
         
            +
                  if (consume_response_line(args, start, end - eol_len + 1))
         
     | 
| 
      
 210 
     | 
    
         
            +
                    goto error;
         
     | 
| 
      
 211 
     | 
    
         
            +
                } else {
         
     | 
| 
      
 212 
     | 
    
         
            +
                  /* HTTP request */
         
     | 
| 
      
 213 
     | 
    
         
            +
                  if (consume_request_line(args, start, end - eol_len + 1))
         
     | 
| 
      
 214 
     | 
    
         
            +
                    goto error;
         
     | 
| 
      
 215 
     | 
    
         
            +
                }
         
     | 
| 
      
 216 
     | 
    
         
            +
                end = start = end + 1;
         
     | 
| 
      
 217 
     | 
    
         
            +
                args->parser->state.reserved |= 1;
         
     | 
| 
      
 218 
     | 
    
         
            +
             
     | 
| 
      
 219 
     | 
    
         
            +
              /* fallthrough */
         
     | 
| 
      
 220 
     | 
    
         
            +
              /* headers */
         
     | 
| 
      
 221 
     | 
    
         
            +
              case 1:
         
     | 
| 
      
 222 
     | 
    
         
            +
                do {
         
     | 
| 
      
 223 
     | 
    
         
            +
                  if (!(eol_len = seek2eol(&end, stop)))
         
     | 
| 
      
 224 
     | 
    
         
            +
                    return CONSUMED;
         
     | 
| 
      
 225 
     | 
    
         
            +
                  /* test for header ending */
         
     | 
| 
      
 226 
     | 
    
         
            +
                  if (*start == 0)
         
     | 
| 
      
 227 
     | 
    
         
            +
                    goto finished_headers; /* break the do..while loop, not the switch
         
     | 
| 
      
 228 
     | 
    
         
            +
                                              statement */
         
     | 
| 
      
 229 
     | 
    
         
            +
                  if (consume_header(args, start, end - eol_len + 1))
         
     | 
| 
      
 230 
     | 
    
         
            +
                    goto error;
         
     | 
| 
      
 231 
     | 
    
         
            +
                  end = start = end + 1;
         
     | 
| 
      
 232 
     | 
    
         
            +
                } while ((args->parser->state.reserved & 2) == 0);
         
     | 
| 
      
 233 
     | 
    
         
            +
              finished_headers:
         
     | 
| 
      
 234 
     | 
    
         
            +
                end = start = end + 1;
         
     | 
| 
      
 235 
     | 
    
         
            +
                args->parser->state.reserved |= 2;
         
     | 
| 
      
 236 
     | 
    
         
            +
                if (args->parser->state.content_length == 0)
         
     | 
| 
      
 237 
     | 
    
         
            +
                  goto finish;
         
     | 
| 
      
 238 
     | 
    
         
            +
                if (end >= stop)
         
     | 
| 
      
 239 
     | 
    
         
            +
                  return args->length;
         
     | 
| 
      
 240 
     | 
    
         
            +
             
     | 
| 
      
 241 
     | 
    
         
            +
              /* fallthrough */
         
     | 
| 
      
 242 
     | 
    
         
            +
              /* request body */
         
     | 
| 
      
 243 
     | 
    
         
            +
              case 3: /*  2 | 1 == 3 */
         
     | 
| 
      
 244 
     | 
    
         
            +
                end = start + args->parser->state.content_length - args->parser->state.read;
         
     | 
| 
      
 245 
     | 
    
         
            +
                if (end > stop)
         
     | 
| 
      
 246 
     | 
    
         
            +
                  end = stop;
         
     | 
| 
      
 247 
     | 
    
         
            +
                if (end == start)
         
     | 
| 
      
 248 
     | 
    
         
            +
                  return CONSUMED;
         
     | 
| 
      
 249 
     | 
    
         
            +
                // fprintf(stderr, "Consuming body at (%lu/%lu):%.*s\n", end - start,
         
     | 
| 
      
 250 
     | 
    
         
            +
                //         args->parser->state.content_length, (int)(end - start), start);
         
     | 
| 
      
 251 
     | 
    
         
            +
                if (args->on_body_chunk(args->parser, (char *)start, end - start))
         
     | 
| 
      
 252 
     | 
    
         
            +
                  goto error;
         
     | 
| 
      
 253 
     | 
    
         
            +
                args->parser->state.read += (end - start);
         
     | 
| 
      
 254 
     | 
    
         
            +
                start = end;
         
     | 
| 
      
 255 
     | 
    
         
            +
                if (args->parser->state.content_length <= args->parser->state.read)
         
     | 
| 
      
 256 
     | 
    
         
            +
                  goto finish;
         
     | 
| 
      
 257 
     | 
    
         
            +
                return CONSUMED;
         
     | 
| 
      
 258 
     | 
    
         
            +
                break;
         
     | 
| 
      
 259 
     | 
    
         
            +
              }
         
     | 
| 
      
 260 
     | 
    
         
            +
             
     | 
| 
      
 261 
     | 
    
         
            +
            error:
         
     | 
| 
      
 262 
     | 
    
         
            +
              args->on_error(args->parser);
         
     | 
| 
      
 263 
     | 
    
         
            +
              args->parser->state =
         
     | 
| 
      
 264 
     | 
    
         
            +
                  (struct http1_parser_protected_read_only_state_s){0, 0, 0};
         
     | 
| 
      
 265 
     | 
    
         
            +
              return args->length;
         
     | 
| 
      
 266 
     | 
    
         
            +
             
     | 
| 
      
 267 
     | 
    
         
            +
            finish:
         
     | 
| 
      
 268 
     | 
    
         
            +
              if (((args->parser->state.reserved & 128) ? args->on_response
         
     | 
| 
      
 269 
     | 
    
         
            +
                                                        : args->on_request)(args->parser))
         
     | 
| 
      
 270 
     | 
    
         
            +
                goto error;
         
     | 
| 
      
 271 
     | 
    
         
            +
              args->parser->state =
         
     | 
| 
      
 272 
     | 
    
         
            +
                  (struct http1_parser_protected_read_only_state_s){0, 0, 0};
         
     | 
| 
      
 273 
     | 
    
         
            +
              return CONSUMED;
         
     | 
| 
      
 274 
     | 
    
         
            +
            }
         
     | 
| 
      
 275 
     | 
    
         
            +
             
     | 
| 
      
 276 
     | 
    
         
            +
            #undef CONSUMED
         
     | 
| 
         @@ -0,0 +1,111 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            #ifndef H_HTTP1_PARSER_H
         
     | 
| 
      
 2 
     | 
    
         
            +
            /*
         
     | 
| 
      
 3 
     | 
    
         
            +
            Copyright: Boaz segev, 2017
         
     | 
| 
      
 4 
     | 
    
         
            +
            License: MIT
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            Feel free to copy, use and enjoy according to the license provided.
         
     | 
| 
      
 7 
     | 
    
         
            +
            */
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
            /**
         
     | 
| 
      
 10 
     | 
    
         
            +
            This is a callback based parser. It parses the skeleton of the HTTP/1.x protocol
         
     | 
| 
      
 11 
     | 
    
         
            +
            and leaves most of the work (validation, error checks, etc') to the callbacks.
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
            This is an attempt to replace the existing HTTP/1.x parser with something easier
         
     | 
| 
      
 14 
     | 
    
         
            +
            to maintain and that could be used for an HTTP/1.x client as well.
         
     | 
| 
      
 15 
     | 
    
         
            +
            */
         
     | 
| 
      
 16 
     | 
    
         
            +
            #define H_HTTP1_PARSER_H
         
     | 
| 
      
 17 
     | 
    
         
            +
            #include <stdint.h>
         
     | 
| 
      
 18 
     | 
    
         
            +
            #include <stdlib.h>
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
            #ifndef HTTP_HEADERS_LOWERCASE
         
     | 
| 
      
 21 
     | 
    
         
            +
            /** when defined, HTTP headers will be converted to lowercase and header
         
     | 
| 
      
 22 
     | 
    
         
            +
             * searches will be case sensitive. */
         
     | 
| 
      
 23 
     | 
    
         
            +
            #define HTTP_HEADERS_LOWERCASE 1
         
     | 
| 
      
 24 
     | 
    
         
            +
            #endif
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
            #if HTTP_HEADERS_LOWERCASE
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
            #define HEADER_NAME_IS_EQ(var_name, const_name, len)                           \
         
     | 
| 
      
 29 
     | 
    
         
            +
              (!memcmp((var_name), (const_name), (len)))
         
     | 
| 
      
 30 
     | 
    
         
            +
            #else
         
     | 
| 
      
 31 
     | 
    
         
            +
            #define HEADER_NAME_IS_EQ(var_name, const_name, len)                           \
         
     | 
| 
      
 32 
     | 
    
         
            +
              (!strncasecmp((var_name), (const_name), (len)))
         
     | 
| 
      
 33 
     | 
    
         
            +
            #endif
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
            /** this struct contains the state of the parser. */
         
     | 
| 
      
 36 
     | 
    
         
            +
            typedef struct http1_parser_s {
         
     | 
| 
      
 37 
     | 
    
         
            +
              void *udata;
         
     | 
| 
      
 38 
     | 
    
         
            +
              struct http1_parser_protected_read_only_state_s {
         
     | 
| 
      
 39 
     | 
    
         
            +
                size_t content_length;
         
     | 
| 
      
 40 
     | 
    
         
            +
                size_t read;
         
     | 
| 
      
 41 
     | 
    
         
            +
                uint8_t reserved;
         
     | 
| 
      
 42 
     | 
    
         
            +
              } state;
         
     | 
| 
      
 43 
     | 
    
         
            +
            } http1_parser_s;
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
            /**
         
     | 
| 
      
 46 
     | 
    
         
            +
             * Available options for the parsing function.
         
     | 
| 
      
 47 
     | 
    
         
            +
             *
         
     | 
| 
      
 48 
     | 
    
         
            +
             * Callbacks should return 0 unless an error occured.
         
     | 
| 
      
 49 
     | 
    
         
            +
             */
         
     | 
| 
      
 50 
     | 
    
         
            +
            struct http1_fio_parser_args_s {
         
     | 
| 
      
 51 
     | 
    
         
            +
              /** REQUIRED: the parser object that manages the parser's state. */
         
     | 
| 
      
 52 
     | 
    
         
            +
              http1_parser_s *parser;
         
     | 
| 
      
 53 
     | 
    
         
            +
              /** REQUIRED: the data to be parsed. */
         
     | 
| 
      
 54 
     | 
    
         
            +
              void *buffer;
         
     | 
| 
      
 55 
     | 
    
         
            +
              /** REQUIRED: the length of the data to be parsed. */
         
     | 
| 
      
 56 
     | 
    
         
            +
              size_t length;
         
     | 
| 
      
 57 
     | 
    
         
            +
              /** called when a request was received. */
         
     | 
| 
      
 58 
     | 
    
         
            +
              int (*const on_request)(http1_parser_s *parser);
         
     | 
| 
      
 59 
     | 
    
         
            +
              /** called when a response was received. */
         
     | 
| 
      
 60 
     | 
    
         
            +
              int (*const on_response)(http1_parser_s *parser);
         
     | 
| 
      
 61 
     | 
    
         
            +
              /** called when a request method is parsed. */
         
     | 
| 
      
 62 
     | 
    
         
            +
              int (*const on_method)(http1_parser_s *parser, char *method,
         
     | 
| 
      
 63 
     | 
    
         
            +
                                     size_t method_len);
         
     | 
| 
      
 64 
     | 
    
         
            +
              /** called when a response status is parsed. the status_str is the string
         
     | 
| 
      
 65 
     | 
    
         
            +
               * without the prefixed numerical status indicator.*/
         
     | 
| 
      
 66 
     | 
    
         
            +
              int (*const on_status)(http1_parser_s *parser, size_t status,
         
     | 
| 
      
 67 
     | 
    
         
            +
                                     char *status_str, size_t len);
         
     | 
| 
      
 68 
     | 
    
         
            +
              /** called when a request path (excluding query) is parsed. */
         
     | 
| 
      
 69 
     | 
    
         
            +
              int (*const on_path)(http1_parser_s *parser, char *path, size_t path_len);
         
     | 
| 
      
 70 
     | 
    
         
            +
              /** called when a request path (excluding query) is parsed. */
         
     | 
| 
      
 71 
     | 
    
         
            +
              int (*const on_query)(http1_parser_s *parser, char *query, size_t query_len);
         
     | 
| 
      
 72 
     | 
    
         
            +
              /** called when a the HTTP/1.x version is parsed. */
         
     | 
| 
      
 73 
     | 
    
         
            +
              int (*const on_http_version)(http1_parser_s *parser, char *version,
         
     | 
| 
      
 74 
     | 
    
         
            +
                                           size_t len);
         
     | 
| 
      
 75 
     | 
    
         
            +
              /** called when a header is parsed. */
         
     | 
| 
      
 76 
     | 
    
         
            +
              int (*const on_header)(http1_parser_s *parser, char *name, size_t name_len,
         
     | 
| 
      
 77 
     | 
    
         
            +
                                     char *data, size_t data_len);
         
     | 
| 
      
 78 
     | 
    
         
            +
              /** called when a body chunk is parsed. */
         
     | 
| 
      
 79 
     | 
    
         
            +
              int (*const on_body_chunk)(http1_parser_s *parser, char *data,
         
     | 
| 
      
 80 
     | 
    
         
            +
                                         size_t data_len);
         
     | 
| 
      
 81 
     | 
    
         
            +
              /** called when a protocol error occured. */
         
     | 
| 
      
 82 
     | 
    
         
            +
              int (*const on_error)(http1_parser_s *parser);
         
     | 
| 
      
 83 
     | 
    
         
            +
            };
         
     | 
| 
      
 84 
     | 
    
         
            +
             
     | 
| 
      
 85 
     | 
    
         
            +
            /**
         
     | 
| 
      
 86 
     | 
    
         
            +
             * Returns the amount of data actually consumed by the parser.
         
     | 
| 
      
 87 
     | 
    
         
            +
             *
         
     | 
| 
      
 88 
     | 
    
         
            +
             * The value 0 indicates there wasn't enough data to be parsed and the same
         
     | 
| 
      
 89 
     | 
    
         
            +
             * buffer (with more data) should be resubmitted.
         
     | 
| 
      
 90 
     | 
    
         
            +
             *
         
     | 
| 
      
 91 
     | 
    
         
            +
             * A value smaller than the buffer size indicates that EITHER a request /
         
     | 
| 
      
 92 
     | 
    
         
            +
             * response was detected OR that the leftover could not be consumed because more
         
     | 
| 
      
 93 
     | 
    
         
            +
             * data was required.
         
     | 
| 
      
 94 
     | 
    
         
            +
             *
         
     | 
| 
      
 95 
     | 
    
         
            +
             * Simply resubmit the reminder of the data to continue parsing.
         
     | 
| 
      
 96 
     | 
    
         
            +
             *
         
     | 
| 
      
 97 
     | 
    
         
            +
             * A request / response callback automatically stops the parsing process,
         
     | 
| 
      
 98 
     | 
    
         
            +
             * allowing the user to adjust or refresh the state of the data.
         
     | 
| 
      
 99 
     | 
    
         
            +
             */
         
     | 
| 
      
 100 
     | 
    
         
            +
            size_t http1_fio_parser_fn(struct http1_fio_parser_args_s *args);
         
     | 
| 
      
 101 
     | 
    
         
            +
             
     | 
| 
      
 102 
     | 
    
         
            +
            static inline __attribute__((unused)) size_t
         
     | 
| 
      
 103 
     | 
    
         
            +
            http1_fio_parser(struct http1_fio_parser_args_s args) {
         
     | 
| 
      
 104 
     | 
    
         
            +
              return http1_fio_parser_fn(&args);
         
     | 
| 
      
 105 
     | 
    
         
            +
            }
         
     | 
| 
      
 106 
     | 
    
         
            +
            #if __STDC_VERSION__ >= 199901L
         
     | 
| 
      
 107 
     | 
    
         
            +
            #define http1_fio_parser(...)                                                  \
         
     | 
| 
      
 108 
     | 
    
         
            +
              http1_fio_parser((struct http1_fio_parser_args_s){__VA_ARGS__})
         
     | 
| 
      
 109 
     | 
    
         
            +
            #endif
         
     | 
| 
      
 110 
     | 
    
         
            +
             
     | 
| 
      
 111 
     | 
    
         
            +
            #endif
         
     | 
    
        data/ext/iodine/http1_response.c
    CHANGED
    
    | 
         @@ -85,11 +85,11 @@ initialize: 
     | 
|
| 
       85 
85 
     | 
    
         
             
              return (http_response_s *)http1_response_pool.pool_mem;
         
     | 
| 
       86 
86 
     | 
    
         
             
            }
         
     | 
| 
       87 
87 
     | 
    
         | 
| 
       88 
     | 
    
         
            -
            static void  
     | 
| 
      
 88 
     | 
    
         
            +
            static void http1_response_deferred_destroy(void *rs_, void *ignr) {
         
     | 
| 
       89 
89 
     | 
    
         
             
              (void)(ignr);
         
     | 
| 
       90 
90 
     | 
    
         
             
              http1_response_s *rs = rs_;
         
     | 
| 
       91 
91 
     | 
    
         
             
              if (spn_trylock(&rs->lock)) {
         
     | 
| 
       92 
     | 
    
         
            -
                defer( 
     | 
| 
      
 92 
     | 
    
         
            +
                defer(http1_response_deferred_destroy, rs, NULL);
         
     | 
| 
       93 
93 
     | 
    
         
             
                return;
         
     | 
| 
       94 
94 
     | 
    
         
             
              }
         
     | 
| 
       95 
95 
     | 
    
         
             
              rs->use_count -= 1;
         
     | 
| 
         @@ -118,7 +118,7 @@ use_free: 
     | 
|
| 
       118 
118 
     | 
    
         
             
            /** Destroys the response object. No data is sent.*/
         
     | 
| 
       119 
119 
     | 
    
         
             
            void http1_response_destroy(http_response_s *rs) {
         
     | 
| 
       120 
120 
     | 
    
         
             
              ((http1_response_s *)rs)->dest_count++;
         
     | 
| 
       121 
     | 
    
         
            -
               
     | 
| 
      
 121 
     | 
    
         
            +
              http1_response_deferred_destroy(rs, NULL);
         
     | 
| 
       122 
122 
     | 
    
         
             
            }
         
     | 
| 
       123 
123 
     | 
    
         | 
| 
       124 
124 
     | 
    
         
             
            /* *****************************************************************************
         
     | 
| 
         @@ -162,15 +162,15 @@ static void http1_response_finalize_headers(http1_response_s *rs) { 
     | 
|
| 
       162 
162 
     | 
    
         
             
                  rs->response.date = rs->response.last_modified;
         
     | 
| 
       163 
163 
     | 
    
         
             
                struct tm t;
         
     | 
| 
       164 
164 
     | 
    
         
             
                /* date header */
         
     | 
| 
       165 
     | 
    
         
            -
                http_gmtime(&rs->response.date, &t);
         
     | 
| 
       166 
165 
     | 
    
         
             
                h1p_protected_copy(rs, "Date: ", 6);
         
     | 
| 
       167 
     | 
    
         
            -
                rs->buffer_end += 
     | 
| 
      
 166 
     | 
    
         
            +
                rs->buffer_end +=
         
     | 
| 
      
 167 
     | 
    
         
            +
                    http_time2str(rs->buffer + rs->buffer_end, rs->response.date);
         
     | 
| 
       168 
168 
     | 
    
         
             
                rs->buffer[rs->buffer_end++] = '\r';
         
     | 
| 
       169 
169 
     | 
    
         
             
                rs->buffer[rs->buffer_end++] = '\n';
         
     | 
| 
       170 
170 
     | 
    
         
             
                /* last-modified header */
         
     | 
| 
       171 
     | 
    
         
            -
                http_gmtime(&rs->response.last_modified, &t);
         
     | 
| 
       172 
171 
     | 
    
         
             
                h1p_protected_copy(rs, "Last-Modified: ", 15);
         
     | 
| 
       173 
     | 
    
         
            -
                rs->buffer_end += 
     | 
| 
      
 172 
     | 
    
         
            +
                rs->buffer_end +=
         
     | 
| 
      
 173 
     | 
    
         
            +
                    http_time2str(rs->buffer + rs->buffer_end, rs->response.last_modified);
         
     | 
| 
       174 
174 
     | 
    
         
             
                rs->buffer[rs->buffer_end++] = '\r';
         
     | 
| 
       175 
175 
     | 
    
         
             
                rs->buffer[rs->buffer_end++] = '\n';
         
     | 
| 
       176 
176 
     | 
    
         
             
              }
         
     | 
| 
         @@ -229,7 +229,7 @@ void http1_response_finish(http_response_s *rs) { 
     | 
|
| 
       229 
229 
     | 
    
         
             
                http1_response_send_headers((http1_response_s *)rs);
         
     | 
| 
       230 
230 
     | 
    
         
             
              if (rs->should_close)
         
     | 
| 
       231 
231 
     | 
    
         
             
                sock_close(rs->fd);
         
     | 
| 
       232 
     | 
    
         
            -
               
     | 
| 
      
 232 
     | 
    
         
            +
              http1_response_deferred_destroy(rs, NULL);
         
     | 
| 
       233 
233 
     | 
    
         
             
            }
         
     | 
| 
       234 
234 
     | 
    
         | 
| 
       235 
235 
     | 
    
         
             
            /* *****************************************************************************
         
     | 
    
        data/ext/iodine/http_response.c
    CHANGED
    
    | 
         @@ -485,20 +485,6 @@ static void http_response_log_finish(http_response_s *response) { 
     | 
|
| 
       485 
485 
     | 
    
         
             
                      ? ((get_clock_mili() - response->clock_start) / CLOCK_RESOLUTION)
         
     | 
| 
       486 
486 
     | 
    
         
             
                      : 0;
         
     | 
| 
       487 
487 
     | 
    
         | 
| 
       488 
     | 
    
         
            -
              /* pre-print log message every 1 or 2 seconds or so. */
         
     | 
| 
       489 
     | 
    
         
            -
              static __thread time_t cached_tick;
         
     | 
| 
       490 
     | 
    
         
            -
              static __thread char cached_httpdate[48];
         
     | 
| 
       491 
     | 
    
         
            -
              static __thread size_t chached_len;
         
     | 
| 
       492 
     | 
    
         
            -
              time_t last_tick = facil_last_tick();
         
     | 
| 
       493 
     | 
    
         
            -
              if (last_tick > cached_tick) {
         
     | 
| 
       494 
     | 
    
         
            -
                struct tm tm;
         
     | 
| 
       495 
     | 
    
         
            -
                cached_tick = last_tick | 1;
         
     | 
| 
       496 
     | 
    
         
            -
                http_gmtime(&last_tick, &tm);
         
     | 
| 
       497 
     | 
    
         
            -
                chached_len = http_date2str(cached_httpdate, &tm);
         
     | 
| 
       498 
     | 
    
         
            -
                fprintf(stderr, "Updated date cache to (%lu) %s\n", chached_len,
         
     | 
| 
       499 
     | 
    
         
            -
                        cached_httpdate);
         
     | 
| 
       500 
     | 
    
         
            -
              }
         
     | 
| 
       501 
     | 
    
         
            -
             
     | 
| 
       502 
488 
     | 
    
         
             
              // TODO Guess IP address from headers (forwarded) where possible
         
     | 
| 
       503 
489 
     | 
    
         
             
              sock_peer_addr_s addrinfo = sock_peer_addr(response->fd);
         
     | 
| 
       504 
490 
     | 
    
         | 
| 
         @@ -519,8 +505,7 @@ static void http_response_log_finish(http_response_s *response) { 
     | 
|
| 
       519 
505 
     | 
    
         
             
              }
         
     | 
| 
       520 
506 
     | 
    
         
             
              memcpy(buffer + pos, " - - [", 6);
         
     | 
| 
       521 
507 
     | 
    
         
             
              pos += 6;
         
     | 
| 
       522 
     | 
    
         
            -
               
     | 
| 
       523 
     | 
    
         
            -
              pos += chached_len;
         
     | 
| 
      
 508 
     | 
    
         
            +
              pos += http_time2str(buffer + pos, facil_last_tick());
         
     | 
| 
       524 
509 
     | 
    
         
             
              buffer[pos++] = ']';
         
     | 
| 
       525 
510 
     | 
    
         
             
              buffer[pos++] = ' ';
         
     | 
| 
       526 
511 
     | 
    
         
             
              buffer[pos++] = '"';
         
     |