nyara 0.0.1.pre
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.
- checksums.yaml +7 -0
- data/example/design.rb +62 -0
- data/example/fib.rb +15 -0
- data/example/hello.rb +5 -0
- data/example/stream.rb +10 -0
- data/ext/accept.c +133 -0
- data/ext/event.c +89 -0
- data/ext/extconf.rb +34 -0
- data/ext/hashes.c +130 -0
- data/ext/http-parser/AUTHORS +41 -0
- data/ext/http-parser/CONTRIBUTIONS +4 -0
- data/ext/http-parser/LICENSE-MIT +23 -0
- data/ext/http-parser/contrib/parsertrace.c +156 -0
- data/ext/http-parser/contrib/url_parser.c +44 -0
- data/ext/http-parser/http_parser.c +2175 -0
- data/ext/http-parser/http_parser.h +304 -0
- data/ext/http-parser/test.c +3425 -0
- data/ext/http_parser.c +1 -0
- data/ext/inc/epoll.h +60 -0
- data/ext/inc/kqueue.h +77 -0
- data/ext/inc/status_codes.inc +64 -0
- data/ext/inc/str_intern.h +66 -0
- data/ext/inc/version.inc +1 -0
- data/ext/mime.c +107 -0
- data/ext/multipart-parser-c/README.md +18 -0
- data/ext/multipart-parser-c/multipart_parser.c +309 -0
- data/ext/multipart-parser-c/multipart_parser.h +48 -0
- data/ext/multipart_parser.c +1 -0
- data/ext/nyara.c +56 -0
- data/ext/nyara.h +59 -0
- data/ext/request.c +474 -0
- data/ext/route.cc +325 -0
- data/ext/url_encoded.c +304 -0
- data/hello.rb +5 -0
- data/lib/nyara/config.rb +64 -0
- data/lib/nyara/config_hash.rb +51 -0
- data/lib/nyara/controller.rb +336 -0
- data/lib/nyara/cookie.rb +31 -0
- data/lib/nyara/cpu_counter.rb +65 -0
- data/lib/nyara/header_hash.rb +18 -0
- data/lib/nyara/mime_types.rb +612 -0
- data/lib/nyara/nyara.rb +82 -0
- data/lib/nyara/param_hash.rb +5 -0
- data/lib/nyara/request.rb +144 -0
- data/lib/nyara/route.rb +138 -0
- data/lib/nyara/route_entry.rb +43 -0
- data/lib/nyara/session.rb +104 -0
- data/lib/nyara/view.rb +317 -0
- data/lib/nyara.rb +25 -0
- data/nyara.gemspec +20 -0
- data/rakefile +91 -0
- data/readme.md +35 -0
- data/spec/ext_mime_match_spec.rb +27 -0
- data/spec/ext_parse_accept_value_spec.rb +29 -0
- data/spec/ext_parse_spec.rb +138 -0
- data/spec/ext_route_spec.rb +70 -0
- data/spec/hashes_spec.rb +71 -0
- data/spec/path_helper_spec.rb +77 -0
- data/spec/request_delegate_spec.rb +67 -0
- data/spec/request_spec.rb +56 -0
- data/spec/route_entry_spec.rb +12 -0
- data/spec/route_spec.rb +84 -0
- data/spec/session_spec.rb +66 -0
- data/spec/spec_helper.rb +52 -0
- data/spec/view_spec.rb +87 -0
- data/tools/bench-cookie.rb +22 -0
- metadata +111 -0
    
        data/ext/http_parser.c
    ADDED
    
    | @@ -0,0 +1 @@ | |
| 1 | 
            +
            #include "http-parser/http_parser.c"
         | 
    
        data/ext/inc/epoll.h
    ADDED
    
    | @@ -0,0 +1,60 @@ | |
| 1 | 
            +
            #pragma once
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            #include <sys/epoll.h>
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            static struct epoll_event qevents[MAX_E];
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            static void ADD_E(int fd, uint64_t etype) {
         | 
| 8 | 
            +
              struct epoll_event e;
         | 
| 9 | 
            +
              e.events = EPOLLIN;
         | 
| 10 | 
            +
              e.data.u64 = (etype << 32) | (uint64_t)fd;
         | 
| 11 | 
            +
             | 
| 12 | 
            +
              // todo timeout
         | 
| 13 | 
            +
            # ifdef NDEBUG
         | 
| 14 | 
            +
              epoll_ctl(qfd, EPOLL_CTL_ADD, fd, &e);
         | 
| 15 | 
            +
            # else
         | 
| 16 | 
            +
              if (epoll_ctl(qfd, EPOLL_CTL_ADD, fd, &e))
         | 
| 17 | 
            +
                printf("%s: %s\n", __func__, strerror(errno));
         | 
| 18 | 
            +
            # endif
         | 
| 19 | 
            +
            }
         | 
| 20 | 
            +
             | 
| 21 | 
            +
            static void DEL_E(int fd) {
         | 
| 22 | 
            +
              struct epoll_event e;
         | 
| 23 | 
            +
              e.events = EPOLLIN;
         | 
| 24 | 
            +
              e.data.ptr = NULL;
         | 
| 25 | 
            +
             | 
| 26 | 
            +
            # ifdef NDEBUG
         | 
| 27 | 
            +
              epoll_ctl(qfd, EPOLL_CTL_DEL, fd, &e);
         | 
| 28 | 
            +
            # else
         | 
| 29 | 
            +
              if (epoll_ctl(qfd, EPOLL_CTL_DEL, fd, &e))
         | 
| 30 | 
            +
                printf("%s: %s\n", __func__, strerror(errno));
         | 
| 31 | 
            +
            # endif
         | 
| 32 | 
            +
            }
         | 
| 33 | 
            +
             | 
| 34 | 
            +
            static void INIT_E() {
         | 
| 35 | 
            +
              qfd = epoll_create(10); // size not important
         | 
| 36 | 
            +
              if (qfd == -1) {
         | 
| 37 | 
            +
                printf("%s\n", strerror(errno));
         | 
| 38 | 
            +
                exit(-1);
         | 
| 39 | 
            +
              }
         | 
| 40 | 
            +
            }
         | 
| 41 | 
            +
             | 
| 42 | 
            +
            static void LOOP_E() {
         | 
| 43 | 
            +
              while (1) {
         | 
| 44 | 
            +
                // heart beat of 0.1 sec, allow ruby signal interrupts to be inserted
         | 
| 45 | 
            +
                int sz = epoll_wait(qfd, qevents, MAX_E, 100);
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                for (int i = 0; i < sz; i++) {
         | 
| 48 | 
            +
                  switch (qevents[i].events) {
         | 
| 49 | 
            +
                    case EPOLLIN: {
         | 
| 50 | 
            +
                      int fd = (int)(qevents[i].data.u64 & 0xFFFFFFFF);
         | 
| 51 | 
            +
                      int etype = (int)(qevents[i].data.u64 >> 32);
         | 
| 52 | 
            +
                      loop_body(fd, etype);
         | 
| 53 | 
            +
                      break;
         | 
| 54 | 
            +
                    }
         | 
| 55 | 
            +
                  }
         | 
| 56 | 
            +
                }
         | 
| 57 | 
            +
                // execute other thread / interrupts
         | 
| 58 | 
            +
                rb_thread_schedule();
         | 
| 59 | 
            +
              }
         | 
| 60 | 
            +
            }
         | 
    
        data/ext/inc/kqueue.h
    ADDED
    
    | @@ -0,0 +1,77 @@ | |
| 1 | 
            +
            #pragma once
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            #include <sys/types.h>
         | 
| 4 | 
            +
            #include <sys/time.h>
         | 
| 5 | 
            +
            #ifdef HAVE_SYS_EVENT_H
         | 
| 6 | 
            +
            # include <sys/event.h>
         | 
| 7 | 
            +
            #else
         | 
| 8 | 
            +
            # include <sys/queue.h>
         | 
| 9 | 
            +
            #endif
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            static void loop_body(int fd, int etype);
         | 
| 12 | 
            +
             | 
| 13 | 
            +
            #define MAX_E 1024
         | 
| 14 | 
            +
            static int qfd;
         | 
| 15 | 
            +
            static struct kevent qevents[MAX_E];
         | 
| 16 | 
            +
             | 
| 17 | 
            +
            static void ADD_E(int fd, uint64_t etype) {
         | 
| 18 | 
            +
              struct kevent e;
         | 
| 19 | 
            +
              EV_SET(&e, fd, EVFILT_READ, EV_ADD, 0, 0, (void*)etype);
         | 
| 20 | 
            +
              // todo timeout
         | 
| 21 | 
            +
            # ifdef NDEBUG
         | 
| 22 | 
            +
              kevent(qfd, &e, 1, NULL, 0, NULL);
         | 
| 23 | 
            +
            # else
         | 
| 24 | 
            +
              if (kevent(qfd, &e, 1, NULL, 0, NULL))
         | 
| 25 | 
            +
                printf("%s: %s\n", __func__, strerror(errno));
         | 
| 26 | 
            +
            # endif
         | 
| 27 | 
            +
            }
         | 
| 28 | 
            +
             | 
| 29 | 
            +
            static void DEL_E(int fd) {
         | 
| 30 | 
            +
              struct kevent e;
         | 
| 31 | 
            +
              EV_SET(&e, fd, EVFILT_READ, EV_DELETE, 0, 0, NULL);
         | 
| 32 | 
            +
            # ifdef NDEBUG
         | 
| 33 | 
            +
              kevent(qfd, &e, 1, NULL, 0, NULL);
         | 
| 34 | 
            +
            # else
         | 
| 35 | 
            +
              if (kevent(qfd, &e, 1, NULL, 0, NULL))
         | 
| 36 | 
            +
                printf("%s: %s\n", __func__, strerror(errno));
         | 
| 37 | 
            +
            # endif
         | 
| 38 | 
            +
            }
         | 
| 39 | 
            +
             | 
| 40 | 
            +
            static void INIT_E() {
         | 
| 41 | 
            +
              qfd = kqueue();
         | 
| 42 | 
            +
              if (qfd == -1) {
         | 
| 43 | 
            +
                printf("%s\n", strerror(errno));
         | 
| 44 | 
            +
                exit(-1);
         | 
| 45 | 
            +
              }
         | 
| 46 | 
            +
            }
         | 
| 47 | 
            +
             | 
| 48 | 
            +
            static void LOOP_E() {
         | 
| 49 | 
            +
              // printf("%d,%d,%d,\n%d,%d,%d,\n%d,%d,%d,\n",
         | 
| 50 | 
            +
              // EV_ADD, EV_ENABLE, EV_DISABLE,
         | 
| 51 | 
            +
              // EV_DELETE, EV_RECEIPT, EV_ONESHOT,
         | 
| 52 | 
            +
              // EV_CLEAR, EV_EOF, EV_ERROR);
         | 
| 53 | 
            +
             | 
| 54 | 
            +
              struct timespec ts = {0, 1000 * 1000 * 100};
         | 
| 55 | 
            +
              while (1) {
         | 
| 56 | 
            +
                // heart beat of 0.1 sec, allow ruby signal interrupts to be inserted
         | 
| 57 | 
            +
                int sz = kevent(qfd, NULL, 0, qevents, MAX_E, &ts);
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                for (int i = 0; i < sz; i++) {
         | 
| 60 | 
            +
                  switch (qevents[i].filter) {
         | 
| 61 | 
            +
                    case EVFILT_READ: {
         | 
| 62 | 
            +
                      int fd = (int)qevents[i].ident;
         | 
| 63 | 
            +
                      // EV_EOF is set if the read side of the socket is shutdown
         | 
| 64 | 
            +
                      // the event can keep flipping back to consume cpu if we don't remove it
         | 
| 65 | 
            +
                      if ((qevents[i].flags & EV_EOF)) {
         | 
| 66 | 
            +
                        DEL_E(fd);
         | 
| 67 | 
            +
                      } else {
         | 
| 68 | 
            +
                        loop_body(fd, (int)qevents[i].udata);
         | 
| 69 | 
            +
                      }
         | 
| 70 | 
            +
                      break;
         | 
| 71 | 
            +
                    }
         | 
| 72 | 
            +
                  }
         | 
| 73 | 
            +
                }
         | 
| 74 | 
            +
                // execute other thread / interrupts
         | 
| 75 | 
            +
                rb_thread_schedule();
         | 
| 76 | 
            +
              }
         | 
| 77 | 
            +
            }
         | 
| @@ -0,0 +1,64 @@ | |
| 1 | 
            +
            #define HTTP_STATUS_CODES(XX)\
         | 
| 2 | 
            +
              XX(100, "Continue");\
         | 
| 3 | 
            +
              XX(101, "Switching Protocols");\
         | 
| 4 | 
            +
              XX(102, "Processing");\
         | 
| 5 | 
            +
              XX(200, "OK");\
         | 
| 6 | 
            +
              XX(201, "Created");\
         | 
| 7 | 
            +
              XX(202, "Accepted");\
         | 
| 8 | 
            +
              XX(203, "Non-Authoritative Information");\
         | 
| 9 | 
            +
              XX(204, "No Content");\
         | 
| 10 | 
            +
              XX(205, "Reset Content");\
         | 
| 11 | 
            +
              XX(206, "Partial Content");\
         | 
| 12 | 
            +
              XX(207, "Multi-Status");\
         | 
| 13 | 
            +
              XX(208, "Already Reported");\
         | 
| 14 | 
            +
              XX(226, "IM Used");\
         | 
| 15 | 
            +
              XX(300, "Multiple Choices");\
         | 
| 16 | 
            +
              XX(301, "Moved Permanently");\
         | 
| 17 | 
            +
              XX(302, "Found");\
         | 
| 18 | 
            +
              XX(303, "See Other");\
         | 
| 19 | 
            +
              XX(304, "Not Modified");\
         | 
| 20 | 
            +
              XX(305, "Use Proxy");\
         | 
| 21 | 
            +
              XX(306, "Reserved");\
         | 
| 22 | 
            +
              XX(307, "Temporary Redirect");\
         | 
| 23 | 
            +
              XX(308, "Permanent Redirect");\
         | 
| 24 | 
            +
              XX(400, "Bad Request");\
         | 
| 25 | 
            +
              XX(401, "Unauthorized");\
         | 
| 26 | 
            +
              XX(402, "Payment Required");\
         | 
| 27 | 
            +
              XX(403, "Forbidden");\
         | 
| 28 | 
            +
              XX(404, "Not Found");\
         | 
| 29 | 
            +
              XX(405, "Method Not Allowed");\
         | 
| 30 | 
            +
              XX(406, "Not Acceptable");\
         | 
| 31 | 
            +
              XX(407, "Proxy Authentication Required");\
         | 
| 32 | 
            +
              XX(408, "Request Timeout");\
         | 
| 33 | 
            +
              XX(409, "Conflict");\
         | 
| 34 | 
            +
              XX(410, "Gone");\
         | 
| 35 | 
            +
              XX(411, "Length Required");\
         | 
| 36 | 
            +
              XX(412, "Precondition Failed");\
         | 
| 37 | 
            +
              XX(413, "Request Entity Too Large");\
         | 
| 38 | 
            +
              XX(414, "Request-URI Too Long");\
         | 
| 39 | 
            +
              XX(415, "Unsupported Media Type");\
         | 
| 40 | 
            +
              XX(416, "Requested Range Not Satisfiable");\
         | 
| 41 | 
            +
              XX(417, "Expectation Failed");\
         | 
| 42 | 
            +
              XX(422, "Unprocessable Entity");\
         | 
| 43 | 
            +
              XX(423, "Locked");\
         | 
| 44 | 
            +
              XX(424, "Failed Dependency");\
         | 
| 45 | 
            +
              XX(425, "Unassigned");\
         | 
| 46 | 
            +
              XX(426, "Upgrade Required");\
         | 
| 47 | 
            +
              XX(427, "Unassigned");\
         | 
| 48 | 
            +
              XX(428, "Precondition Required");\
         | 
| 49 | 
            +
              XX(429, "Too Many Requests");\
         | 
| 50 | 
            +
              XX(430, "Unassigned");\
         | 
| 51 | 
            +
              XX(431, "Request Header Fields Too Large");\
         | 
| 52 | 
            +
              XX(500, "Internal Server Error");\
         | 
| 53 | 
            +
              XX(501, "Not Implemented");\
         | 
| 54 | 
            +
              XX(502, "Bad Gateway");\
         | 
| 55 | 
            +
              XX(503, "Service Unavailable");\
         | 
| 56 | 
            +
              XX(504, "Gateway Timeout");\
         | 
| 57 | 
            +
              XX(505, "HTTP Version Not Supported");\
         | 
| 58 | 
            +
              XX(506, "Variant Also Negotiates (Experimental)");\
         | 
| 59 | 
            +
              XX(507, "Insufficient Storage");\
         | 
| 60 | 
            +
              XX(508, "Loop Detected");\
         | 
| 61 | 
            +
              XX(509, "Unassigned");\
         | 
| 62 | 
            +
              XX(510, "Not Extended");\
         | 
| 63 | 
            +
              XX(511, "Network Authentication Required");\
         | 
| 64 | 
            +
            // end define
         | 
| @@ -0,0 +1,66 @@ | |
| 1 | 
            +
            #pragma once
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            // from string.c
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            #define RUBY_MAX_CHAR_LEN 16
         | 
| 6 | 
            +
            #define STR_TMPLOCK FL_USER7
         | 
| 7 | 
            +
            #define STR_NOEMBED FL_USER1
         | 
| 8 | 
            +
            #define STR_SHARED  FL_USER2 /* = ELTS_SHARED */
         | 
| 9 | 
            +
            #define STR_ASSOC   FL_USER3
         | 
| 10 | 
            +
            #define STR_SHARED_P(s) FL_ALL((s), STR_NOEMBED|ELTS_SHARED)
         | 
| 11 | 
            +
            #define STR_ASSOC_P(s)  FL_ALL((s), STR_NOEMBED|STR_ASSOC)
         | 
| 12 | 
            +
            #define STR_NOCAPA  (STR_NOEMBED|ELTS_SHARED|STR_ASSOC)
         | 
| 13 | 
            +
            #define STR_NOCAPA_P(s) (FL_TEST((s),STR_NOEMBED) && FL_ANY((s),ELTS_SHARED|STR_ASSOC))
         | 
| 14 | 
            +
            #define STR_UNSET_NOCAPA(s) do {\
         | 
| 15 | 
            +
                if (FL_TEST((s),STR_NOEMBED)) FL_UNSET((s),(ELTS_SHARED|STR_ASSOC));\
         | 
| 16 | 
            +
            } while (0)
         | 
| 17 | 
            +
             | 
| 18 | 
            +
            #define STR_SET_NOEMBED(str) do {\
         | 
| 19 | 
            +
                FL_SET((str), STR_NOEMBED);\
         | 
| 20 | 
            +
                STR_SET_EMBED_LEN((str), 0);\
         | 
| 21 | 
            +
            } while (0)
         | 
| 22 | 
            +
            #define STR_SET_EMBED(str) FL_UNSET((str), STR_NOEMBED)
         | 
| 23 | 
            +
            #define STR_EMBED_P(str) (!FL_TEST((str), STR_NOEMBED))
         | 
| 24 | 
            +
            #define STR_SET_EMBED_LEN(str, n) do { \
         | 
| 25 | 
            +
                long tmp_n = (n);\
         | 
| 26 | 
            +
                RBASIC(str)->flags &= ~RSTRING_EMBED_LEN_MASK;\
         | 
| 27 | 
            +
                RBASIC(str)->flags |= (tmp_n) << RSTRING_EMBED_LEN_SHIFT;\
         | 
| 28 | 
            +
            } while (0)
         | 
| 29 | 
            +
             | 
| 30 | 
            +
            #define STR_SET_LEN(str, n) do { \
         | 
| 31 | 
            +
                if (STR_EMBED_P(str)) {\
         | 
| 32 | 
            +
                    STR_SET_EMBED_LEN((str), (n));\
         | 
| 33 | 
            +
                }\
         | 
| 34 | 
            +
                else {\
         | 
| 35 | 
            +
                    RSTRING(str)->as.heap.len = (n);\
         | 
| 36 | 
            +
                }\
         | 
| 37 | 
            +
            } while (0)
         | 
| 38 | 
            +
             | 
| 39 | 
            +
            #define STR_DEC_LEN(str) do {\
         | 
| 40 | 
            +
                if (STR_EMBED_P(str)) {\
         | 
| 41 | 
            +
                    long n = RSTRING_LEN(str);\
         | 
| 42 | 
            +
                    n--;\
         | 
| 43 | 
            +
                    STR_SET_EMBED_LEN((str), n);\
         | 
| 44 | 
            +
                }\
         | 
| 45 | 
            +
                else {\
         | 
| 46 | 
            +
                    RSTRING(str)->as.heap.len--;\
         | 
| 47 | 
            +
                }\
         | 
| 48 | 
            +
            } while (0)
         | 
| 49 | 
            +
             | 
| 50 | 
            +
            #define RESIZE_CAPA(str,capacity) do {\
         | 
| 51 | 
            +
                if (STR_EMBED_P(str)) {\
         | 
| 52 | 
            +
                    if ((capacity) > RSTRING_EMBED_LEN_MAX) {\
         | 
| 53 | 
            +
                        char *tmp = ALLOC_N(char, (capacity)+1);\
         | 
| 54 | 
            +
                        memcpy(tmp, RSTRING_PTR(str), RSTRING_LEN(str));\
         | 
| 55 | 
            +
                        RSTRING(str)->as.heap.ptr = tmp;\
         | 
| 56 | 
            +
                        RSTRING(str)->as.heap.len = RSTRING_LEN(str);\
         | 
| 57 | 
            +
                        STR_SET_NOEMBED(str);\
         | 
| 58 | 
            +
                        RSTRING(str)->as.heap.aux.capa = (capacity);\
         | 
| 59 | 
            +
                    }\
         | 
| 60 | 
            +
                }\
         | 
| 61 | 
            +
                else {\
         | 
| 62 | 
            +
                    REALLOC_N(RSTRING(str)->as.heap.ptr, char, (capacity)+1);\
         | 
| 63 | 
            +
                    if (!STR_NOCAPA_P(str))\
         | 
| 64 | 
            +
                        RSTRING(str)->as.heap.aux.capa = (capacity);\
         | 
| 65 | 
            +
                }\
         | 
| 66 | 
            +
            } while (0)
         | 
    
        data/ext/inc/version.inc
    ADDED
    
    | @@ -0,0 +1 @@ | |
| 1 | 
            +
            #define NYARA_VERSION "0.0.1.pre"
         | 
    
        data/ext/mime.c
    ADDED
    
    | @@ -0,0 +1,107 @@ | |
| 1 | 
            +
            #include "nyara.h"
         | 
| 2 | 
            +
            #include <string.h>
         | 
| 3 | 
            +
            #include <ctype.h>
         | 
| 4 | 
            +
            #include "inc/str_intern.h"
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            static const char* _strnchr(const char* s, long len, char c) {
         | 
| 7 | 
            +
              for (long i = 0; i < len; i++) {
         | 
| 8 | 
            +
                if (s[i] == c) {
         | 
| 9 | 
            +
                  return s + i;
         | 
| 10 | 
            +
                }
         | 
| 11 | 
            +
              }
         | 
| 12 | 
            +
              return NULL;
         | 
| 13 | 
            +
            }
         | 
| 14 | 
            +
             | 
| 15 | 
            +
            static bool mime_match_seg(const char* m1_ptr, long m1_len, VALUE v1, VALUE v2) {
         | 
| 16 | 
            +
              const char* m2_ptr = _strnchr(m1_ptr, m1_len, '/');
         | 
| 17 | 
            +
              long m2_len;
         | 
| 18 | 
            +
              if (m2_ptr) {
         | 
| 19 | 
            +
                m2_ptr++;
         | 
| 20 | 
            +
                m2_len = m1_len - (m2_ptr - m1_ptr);
         | 
| 21 | 
            +
                m1_len = (m2_ptr - m1_ptr) - 1;
         | 
| 22 | 
            +
              } else {
         | 
| 23 | 
            +
                m2_len = 0;
         | 
| 24 | 
            +
              }
         | 
| 25 | 
            +
             | 
| 26 | 
            +
              const char* v1_ptr = RSTRING_PTR(v1);
         | 
| 27 | 
            +
              const char* v2_ptr = RSTRING_PTR(v2);
         | 
| 28 | 
            +
              const long  v1_len = RSTRING_LEN(v1);
         | 
| 29 | 
            +
              const long  v2_len = RSTRING_LEN(v2);
         | 
| 30 | 
            +
             | 
| 31 | 
            +
            # define EQL_STAR(s, len) (len == 1 && s[0] == '*')
         | 
| 32 | 
            +
            # define EQL(s1, len1, s2, len2) (len1 == len2 && strncmp(s1, s2, len1) == 0)
         | 
| 33 | 
            +
             | 
| 34 | 
            +
              /*
         | 
| 35 | 
            +
              if m1 == '*'
         | 
| 36 | 
            +
                if m2.nil? || m2 == '*'
         | 
| 37 | 
            +
                  return true
         | 
| 38 | 
            +
                elsif m2 == v2
         | 
| 39 | 
            +
                  return true
         | 
| 40 | 
            +
                else
         | 
| 41 | 
            +
                  return false
         | 
| 42 | 
            +
                end
         | 
| 43 | 
            +
              end
         | 
| 44 | 
            +
              return false if v1 != m1
         | 
| 45 | 
            +
              return true if m2.nil? || m2 == '*'
         | 
| 46 | 
            +
              m2 == v2
         | 
| 47 | 
            +
              */
         | 
| 48 | 
            +
             | 
| 49 | 
            +
              if (EQL_STAR(m1_ptr, m1_len)) {
         | 
| 50 | 
            +
                if (m2_len == 0 || EQL_STAR(m2_ptr, m2_len)) {
         | 
| 51 | 
            +
                  return true;
         | 
| 52 | 
            +
                } else if (EQL(m2_ptr, m2_len, v2_ptr, v2_len)) {
         | 
| 53 | 
            +
                  return true;
         | 
| 54 | 
            +
                } else {
         | 
| 55 | 
            +
                  return false;
         | 
| 56 | 
            +
                }
         | 
| 57 | 
            +
              }
         | 
| 58 | 
            +
              if (!EQL(v1_ptr, v1_len, m1_ptr, m1_len)) {
         | 
| 59 | 
            +
                return false;
         | 
| 60 | 
            +
              }
         | 
| 61 | 
            +
              if (m2_len == 0 || EQL_STAR(m2_ptr, m2_len)) {
         | 
| 62 | 
            +
                return true;
         | 
| 63 | 
            +
              }
         | 
| 64 | 
            +
              return EQL(m2_ptr, m2_len, v2_ptr, v2_len);
         | 
| 65 | 
            +
             | 
| 66 | 
            +
            # undef EQL
         | 
| 67 | 
            +
            # undef EQL_STAR
         | 
| 68 | 
            +
            }
         | 
| 69 | 
            +
             | 
| 70 | 
            +
            // for test
         | 
| 71 | 
            +
            static VALUE ext_mime_match_seg(VALUE self, VALUE m, VALUE v1, VALUE v2) {
         | 
| 72 | 
            +
              if (mime_match_seg(RSTRING_PTR(m), RSTRING_LEN(m), v1, v2)) {
         | 
| 73 | 
            +
                return Qtrue;
         | 
| 74 | 
            +
              } else {
         | 
| 75 | 
            +
                return Qfalse;
         | 
| 76 | 
            +
              }
         | 
| 77 | 
            +
            }
         | 
| 78 | 
            +
             | 
| 79 | 
            +
            // returns matched ext or nil
         | 
| 80 | 
            +
            VALUE ext_mime_match(VALUE self, VALUE request_accept, VALUE accept_mimes) {
         | 
| 81 | 
            +
              Check_Type(request_accept, T_ARRAY);
         | 
| 82 | 
            +
              Check_Type(accept_mimes, T_ARRAY);
         | 
| 83 | 
            +
             | 
| 84 | 
            +
              VALUE* accepts = RARRAY_PTR(request_accept);
         | 
| 85 | 
            +
              long accepts_len = RARRAY_LEN(request_accept);
         | 
| 86 | 
            +
              VALUE* values = RARRAY_PTR(accept_mimes);
         | 
| 87 | 
            +
              long values_len = RARRAY_LEN(accept_mimes);
         | 
| 88 | 
            +
             | 
| 89 | 
            +
              for (long j = 0; j < accepts_len; j++) {
         | 
| 90 | 
            +
                char* s = RSTRING_PTR(accepts[j]);
         | 
| 91 | 
            +
                long len = RSTRING_LEN(accepts[j]);
         | 
| 92 | 
            +
                for (long i = 0; i < values_len; i++) {
         | 
| 93 | 
            +
                  Check_Type(values[i], T_ARRAY);
         | 
| 94 | 
            +
                  VALUE* arr = RARRAY_PTR(values[i]);
         | 
| 95 | 
            +
                  if (mime_match_seg(s, len, arr[0], arr[1])) {
         | 
| 96 | 
            +
                    return arr[2];
         | 
| 97 | 
            +
                  }
         | 
| 98 | 
            +
                }
         | 
| 99 | 
            +
              }
         | 
| 100 | 
            +
              return Qnil;
         | 
| 101 | 
            +
            }
         | 
| 102 | 
            +
             | 
| 103 | 
            +
            void Init_mime(VALUE ext) {
         | 
| 104 | 
            +
              rb_define_singleton_method(ext, "mime_match", ext_mime_match, 2);
         | 
| 105 | 
            +
              // for test
         | 
| 106 | 
            +
              rb_define_singleton_method(ext, "mime_match_seg", ext_mime_match_seg, 3);
         | 
| 107 | 
            +
            }
         | 
| @@ -0,0 +1,18 @@ | |
| 1 | 
            +
            ## Multipart form data parser
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            ### Features
         | 
| 4 | 
            +
            * No dependencies
         | 
| 5 | 
            +
            * Works with chunks of a data - no need to buffer the whole request
         | 
| 6 | 
            +
            * Almost no internal buffering. Buffer size doesn't exceed the size of the boundary (~60-70 bytes)
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            Tested as part of [Cosmonaut](https://github.com/iafonov/cosmonaut) HTTP server.
         | 
| 9 | 
            +
             | 
| 10 | 
            +
            Implementation based on [node-formidable](https://github.com/felixge/node-formidable) by [Felix Geisendörfer](https://github.com/felixge).
         | 
| 11 | 
            +
             | 
| 12 | 
            +
            Inspired by [http-parser](https://github.com/joyent/http-parser) by [Ryan Dahl](https://github.com/ry).
         | 
| 13 | 
            +
             | 
| 14 | 
            +
            ### Contributors:
         | 
| 15 | 
            +
            * [Daniel T. Wagner](http://www.danieltwagner.de/)
         | 
| 16 | 
            +
            * [James McLaughlin](http://udp.github.com/)
         | 
| 17 | 
            +
             | 
| 18 | 
            +
            © 2012 [Igor Afonov](http://iafonov.github.com)
         | 
| @@ -0,0 +1,309 @@ | |
| 1 | 
            +
            /* Based on node-formidable by Felix Geisendörfer 
         | 
| 2 | 
            +
             * Igor Afonov - afonov@gmail.com - 2012
         | 
| 3 | 
            +
             * MIT License - http://www.opensource.org/licenses/mit-license.php
         | 
| 4 | 
            +
             */
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            #include "multipart_parser.h"
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            #include <stdio.h>
         | 
| 9 | 
            +
            #include <stdarg.h>
         | 
| 10 | 
            +
            #include <string.h>
         | 
| 11 | 
            +
             | 
| 12 | 
            +
            static void multipart_log(const char * format, ...)
         | 
| 13 | 
            +
            {
         | 
| 14 | 
            +
            #ifdef DEBUG_MULTIPART
         | 
| 15 | 
            +
                va_list args;
         | 
| 16 | 
            +
                va_start(args, format);
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                fprintf(stderr, "[HTTP_MULTIPART_PARSER] %s:%d: ", __FILE__, __LINE__);
         | 
| 19 | 
            +
                vfprintf(stderr, format, args);
         | 
| 20 | 
            +
                fprintf(stderr, "\n");
         | 
| 21 | 
            +
            #endif
         | 
| 22 | 
            +
            }
         | 
| 23 | 
            +
             | 
| 24 | 
            +
            #define NOTIFY_CB(FOR)                                                 \
         | 
| 25 | 
            +
            do {                                                                   \
         | 
| 26 | 
            +
              if (p->settings->on_##FOR) {                                     \
         | 
| 27 | 
            +
                if (p->settings->on_##FOR(p) != 0) {                           \
         | 
| 28 | 
            +
                  return i;                                                        \
         | 
| 29 | 
            +
                }                                                                  \
         | 
| 30 | 
            +
              }                                                                    \
         | 
| 31 | 
            +
            } while (0)
         | 
| 32 | 
            +
             | 
| 33 | 
            +
            #define EMIT_DATA_CB(FOR, ptr, len)                                    \
         | 
| 34 | 
            +
            do {                                                                   \
         | 
| 35 | 
            +
              if (p->settings->on_##FOR) {                                         \
         | 
| 36 | 
            +
                if (p->settings->on_##FOR(p, ptr, len) != 0) {                     \
         | 
| 37 | 
            +
                  return i;                                                        \
         | 
| 38 | 
            +
                }                                                                  \
         | 
| 39 | 
            +
              }                                                                    \
         | 
| 40 | 
            +
            } while (0)
         | 
| 41 | 
            +
             | 
| 42 | 
            +
             | 
| 43 | 
            +
            #define LF 10
         | 
| 44 | 
            +
            #define CR 13
         | 
| 45 | 
            +
             | 
| 46 | 
            +
            struct multipart_parser {
         | 
| 47 | 
            +
              void * data;
         | 
| 48 | 
            +
             | 
| 49 | 
            +
              size_t index;
         | 
| 50 | 
            +
              size_t boundary_length;
         | 
| 51 | 
            +
             | 
| 52 | 
            +
              unsigned char state;
         | 
| 53 | 
            +
             | 
| 54 | 
            +
              const multipart_parser_settings* settings;
         | 
| 55 | 
            +
             | 
| 56 | 
            +
              char* lookbehind;
         | 
| 57 | 
            +
              char multipart_boundary[1];
         | 
| 58 | 
            +
            };
         | 
| 59 | 
            +
             | 
| 60 | 
            +
            enum state {
         | 
| 61 | 
            +
              s_uninitialized = 1,
         | 
| 62 | 
            +
              s_start,
         | 
| 63 | 
            +
              s_start_boundary,
         | 
| 64 | 
            +
              s_header_field_start,
         | 
| 65 | 
            +
              s_header_field,
         | 
| 66 | 
            +
              s_headers_almost_done,
         | 
| 67 | 
            +
              s_header_value_start,
         | 
| 68 | 
            +
              s_header_value,
         | 
| 69 | 
            +
              s_header_value_almost_done,
         | 
| 70 | 
            +
              s_part_data_start,
         | 
| 71 | 
            +
              s_part_data,
         | 
| 72 | 
            +
              s_part_data_almost_boundary,
         | 
| 73 | 
            +
              s_part_data_boundary,
         | 
| 74 | 
            +
              s_part_data_almost_end,
         | 
| 75 | 
            +
              s_part_data_end,
         | 
| 76 | 
            +
              s_part_data_final_hyphen,
         | 
| 77 | 
            +
              s_end
         | 
| 78 | 
            +
            };
         | 
| 79 | 
            +
             | 
| 80 | 
            +
            multipart_parser* multipart_parser_init
         | 
| 81 | 
            +
                (const char *boundary, const multipart_parser_settings* settings) {
         | 
| 82 | 
            +
             | 
| 83 | 
            +
              multipart_parser* p = malloc(sizeof(multipart_parser) +
         | 
| 84 | 
            +
                                           strlen(boundary) +
         | 
| 85 | 
            +
                                           strlen(boundary) + 9);
         | 
| 86 | 
            +
             | 
| 87 | 
            +
              strcpy(p->multipart_boundary, boundary);
         | 
| 88 | 
            +
              p->boundary_length = strlen(boundary);
         | 
| 89 | 
            +
              
         | 
| 90 | 
            +
              p->lookbehind = (p->multipart_boundary + p->boundary_length + 1);
         | 
| 91 | 
            +
             | 
| 92 | 
            +
              p->index = 0;
         | 
| 93 | 
            +
              p->state = s_start;
         | 
| 94 | 
            +
              p->settings = settings;
         | 
| 95 | 
            +
             | 
| 96 | 
            +
              return p;
         | 
| 97 | 
            +
            }
         | 
| 98 | 
            +
             | 
| 99 | 
            +
            void multipart_parser_free(multipart_parser* p) {
         | 
| 100 | 
            +
              free(p);
         | 
| 101 | 
            +
            }
         | 
| 102 | 
            +
             | 
| 103 | 
            +
            void multipart_parser_set_data(multipart_parser *p, void *data) {
         | 
| 104 | 
            +
                p->data = data;
         | 
| 105 | 
            +
            }
         | 
| 106 | 
            +
             | 
| 107 | 
            +
            void *multipart_parser_get_data(multipart_parser *p) {
         | 
| 108 | 
            +
                return p->data;
         | 
| 109 | 
            +
            }
         | 
| 110 | 
            +
             | 
| 111 | 
            +
            size_t multipart_parser_execute(multipart_parser* p, const char *buf, size_t len) {
         | 
| 112 | 
            +
              size_t i = 0;
         | 
| 113 | 
            +
              size_t mark = 0;
         | 
| 114 | 
            +
              char c, cl;
         | 
| 115 | 
            +
              int is_last = 0;
         | 
| 116 | 
            +
             | 
| 117 | 
            +
              while(!is_last) {
         | 
| 118 | 
            +
                c = buf[i];
         | 
| 119 | 
            +
                is_last = (i == (len - 1));
         | 
| 120 | 
            +
                switch (p->state) {
         | 
| 121 | 
            +
                  case s_start:
         | 
| 122 | 
            +
                    multipart_log("s_start");
         | 
| 123 | 
            +
                    p->index = 0;
         | 
| 124 | 
            +
                    p->state = s_start_boundary;
         | 
| 125 | 
            +
             | 
| 126 | 
            +
                  /* fallthrough */
         | 
| 127 | 
            +
                  case s_start_boundary:
         | 
| 128 | 
            +
                    multipart_log("s_start_boundary");
         | 
| 129 | 
            +
                    if (p->index == p->boundary_length) {
         | 
| 130 | 
            +
                      if (c != CR) {
         | 
| 131 | 
            +
                        return i;
         | 
| 132 | 
            +
                      }
         | 
| 133 | 
            +
                      p->index++;
         | 
| 134 | 
            +
                      break;
         | 
| 135 | 
            +
                    } else if (p->index == (p->boundary_length + 1)) {
         | 
| 136 | 
            +
                      if (c != LF) {
         | 
| 137 | 
            +
                        return i;
         | 
| 138 | 
            +
                      }
         | 
| 139 | 
            +
                      p->index = 0;
         | 
| 140 | 
            +
                      NOTIFY_CB(part_data_begin);
         | 
| 141 | 
            +
                      p->state = s_header_field_start;
         | 
| 142 | 
            +
                      break;
         | 
| 143 | 
            +
                    }
         | 
| 144 | 
            +
                    if (c != p->multipart_boundary[p->index]) {
         | 
| 145 | 
            +
                      return i;
         | 
| 146 | 
            +
                    }
         | 
| 147 | 
            +
                    p->index++;
         | 
| 148 | 
            +
                    break;
         | 
| 149 | 
            +
             | 
| 150 | 
            +
                  case s_header_field_start:
         | 
| 151 | 
            +
                    multipart_log("s_header_field_start");
         | 
| 152 | 
            +
                    mark = i;
         | 
| 153 | 
            +
                    p->state = s_header_field;
         | 
| 154 | 
            +
             | 
| 155 | 
            +
                  /* fallthrough */
         | 
| 156 | 
            +
                  case s_header_field:
         | 
| 157 | 
            +
                    multipart_log("s_header_field");
         | 
| 158 | 
            +
                    if (c == CR) {
         | 
| 159 | 
            +
                      p->state = s_headers_almost_done;
         | 
| 160 | 
            +
                      break;
         | 
| 161 | 
            +
                    }
         | 
| 162 | 
            +
             | 
| 163 | 
            +
                    if (c == '-') {
         | 
| 164 | 
            +
                      break;
         | 
| 165 | 
            +
                    }
         | 
| 166 | 
            +
             | 
| 167 | 
            +
                    if (c == ':') {
         | 
| 168 | 
            +
                      EMIT_DATA_CB(header_field, buf + mark, i - mark);
         | 
| 169 | 
            +
                      p->state = s_header_value_start;
         | 
| 170 | 
            +
                      break;
         | 
| 171 | 
            +
                    }
         | 
| 172 | 
            +
             | 
| 173 | 
            +
                    cl = tolower(c);
         | 
| 174 | 
            +
                    if (cl < 'a' || cl > 'z') {
         | 
| 175 | 
            +
                      multipart_log("invalid character in header name");
         | 
| 176 | 
            +
                      return i;
         | 
| 177 | 
            +
                    }
         | 
| 178 | 
            +
                    if (is_last)
         | 
| 179 | 
            +
                        EMIT_DATA_CB(header_field, buf + mark, (i - mark) + 1);
         | 
| 180 | 
            +
                    break;
         | 
| 181 | 
            +
             | 
| 182 | 
            +
                  case s_headers_almost_done:
         | 
| 183 | 
            +
                    multipart_log("s_headers_almost_done");
         | 
| 184 | 
            +
                    if (c != LF) {
         | 
| 185 | 
            +
                      return i;
         | 
| 186 | 
            +
                    }
         | 
| 187 | 
            +
             | 
| 188 | 
            +
                    p->state = s_part_data_start;
         | 
| 189 | 
            +
                    break;
         | 
| 190 | 
            +
             | 
| 191 | 
            +
                  case s_header_value_start:
         | 
| 192 | 
            +
                    multipart_log("s_header_value_start");
         | 
| 193 | 
            +
                    if (c == ' ') {
         | 
| 194 | 
            +
                      break;
         | 
| 195 | 
            +
                    }
         | 
| 196 | 
            +
             | 
| 197 | 
            +
                    mark = i;
         | 
| 198 | 
            +
                    p->state = s_header_value;
         | 
| 199 | 
            +
             | 
| 200 | 
            +
                  /* fallthrough */
         | 
| 201 | 
            +
                  case s_header_value:
         | 
| 202 | 
            +
                    multipart_log("s_header_value");
         | 
| 203 | 
            +
                    if (c == CR) {
         | 
| 204 | 
            +
                      EMIT_DATA_CB(header_value, buf + mark, i - mark);
         | 
| 205 | 
            +
                      p->state = s_header_value_almost_done;
         | 
| 206 | 
            +
                    }
         | 
| 207 | 
            +
                    if (is_last)
         | 
| 208 | 
            +
                        EMIT_DATA_CB(header_value, buf + mark, (i - mark) + 1);
         | 
| 209 | 
            +
                    break;
         | 
| 210 | 
            +
             | 
| 211 | 
            +
                  case s_header_value_almost_done:
         | 
| 212 | 
            +
                    multipart_log("s_header_value_almost_done");
         | 
| 213 | 
            +
                    if (c != LF) {
         | 
| 214 | 
            +
                      return i;
         | 
| 215 | 
            +
                    }
         | 
| 216 | 
            +
                    p->state = s_header_field_start;
         | 
| 217 | 
            +
                    break;
         | 
| 218 | 
            +
             | 
| 219 | 
            +
                  case s_part_data_start:
         | 
| 220 | 
            +
                    multipart_log("s_part_data_start");
         | 
| 221 | 
            +
                    NOTIFY_CB(headers_complete);
         | 
| 222 | 
            +
                    mark = i;
         | 
| 223 | 
            +
                    p->state = s_part_data;
         | 
| 224 | 
            +
             | 
| 225 | 
            +
                  /* fallthrough */
         | 
| 226 | 
            +
                  case s_part_data:
         | 
| 227 | 
            +
                    multipart_log("s_part_data");
         | 
| 228 | 
            +
                    if (c == CR) {
         | 
| 229 | 
            +
                        EMIT_DATA_CB(part_data, buf + mark, i - mark);
         | 
| 230 | 
            +
                        mark = i;
         | 
| 231 | 
            +
                        p->state = s_part_data_almost_boundary;
         | 
| 232 | 
            +
                        p->lookbehind[0] = CR;
         | 
| 233 | 
            +
                        break;
         | 
| 234 | 
            +
                    }
         | 
| 235 | 
            +
                    if (is_last)
         | 
| 236 | 
            +
                        EMIT_DATA_CB(part_data, buf + mark, (i - mark) + 1);
         | 
| 237 | 
            +
                    break;
         | 
| 238 | 
            +
             | 
| 239 | 
            +
                  case s_part_data_almost_boundary:
         | 
| 240 | 
            +
                    multipart_log("s_part_data_almost_boundary");
         | 
| 241 | 
            +
                    if (c == LF) {
         | 
| 242 | 
            +
                        p->state = s_part_data_boundary;
         | 
| 243 | 
            +
                        p->lookbehind[1] = LF;
         | 
| 244 | 
            +
                        p->index = 0;
         | 
| 245 | 
            +
                        break;
         | 
| 246 | 
            +
                    }
         | 
| 247 | 
            +
                    EMIT_DATA_CB(part_data, p->lookbehind, 1);
         | 
| 248 | 
            +
                    p->state = s_part_data;
         | 
| 249 | 
            +
                    mark = i --;
         | 
| 250 | 
            +
                    break;
         | 
| 251 | 
            +
             | 
| 252 | 
            +
                  case s_part_data_boundary:
         | 
| 253 | 
            +
                    multipart_log("s_part_data_boundary");
         | 
| 254 | 
            +
                    if (p->multipart_boundary[p->index] != c) {
         | 
| 255 | 
            +
                      EMIT_DATA_CB(part_data, p->lookbehind, 2 + p->index);
         | 
| 256 | 
            +
                      p->state = s_part_data;
         | 
| 257 | 
            +
                      mark = i --;
         | 
| 258 | 
            +
                      break;
         | 
| 259 | 
            +
                    }
         | 
| 260 | 
            +
                    p->lookbehind[2 + p->index] = c;
         | 
| 261 | 
            +
                    if ((++ p->index) == p->boundary_length) {
         | 
| 262 | 
            +
                        NOTIFY_CB(part_data_end);
         | 
| 263 | 
            +
                        p->state = s_part_data_almost_end;
         | 
| 264 | 
            +
                    }
         | 
| 265 | 
            +
                    break;
         | 
| 266 | 
            +
             | 
| 267 | 
            +
                  case s_part_data_almost_end:
         | 
| 268 | 
            +
                    multipart_log("s_part_data_almost_end");
         | 
| 269 | 
            +
                    if (c == '-') {
         | 
| 270 | 
            +
                        p->state = s_part_data_final_hyphen;
         | 
| 271 | 
            +
                        break;
         | 
| 272 | 
            +
                    }
         | 
| 273 | 
            +
                    if (c == CR) {
         | 
| 274 | 
            +
                        p->state = s_part_data_end;
         | 
| 275 | 
            +
                        break;
         | 
| 276 | 
            +
                    }
         | 
| 277 | 
            +
                    return i;
         | 
| 278 | 
            +
               
         | 
| 279 | 
            +
                  case s_part_data_final_hyphen:
         | 
| 280 | 
            +
                    multipart_log("s_part_data_final_hyphen");
         | 
| 281 | 
            +
                    if (c == '-') {
         | 
| 282 | 
            +
                        NOTIFY_CB(body_end);
         | 
| 283 | 
            +
                        p->state = s_end;
         | 
| 284 | 
            +
                        break;
         | 
| 285 | 
            +
                    }
         | 
| 286 | 
            +
                    return i;
         | 
| 287 | 
            +
             | 
| 288 | 
            +
                  case s_part_data_end:
         | 
| 289 | 
            +
                    multipart_log("s_part_data_end");
         | 
| 290 | 
            +
                    if (c == LF) {
         | 
| 291 | 
            +
                        p->state = s_header_field_start;
         | 
| 292 | 
            +
                        NOTIFY_CB(part_data_begin);
         | 
| 293 | 
            +
                        break;
         | 
| 294 | 
            +
                    }
         | 
| 295 | 
            +
                    return i;
         | 
| 296 | 
            +
             | 
| 297 | 
            +
                  case s_end:
         | 
| 298 | 
            +
                    multipart_log("s_end: %02X", (int) c);
         | 
| 299 | 
            +
                    break;
         | 
| 300 | 
            +
             | 
| 301 | 
            +
                  default:
         | 
| 302 | 
            +
                    multipart_log("Multipart parser unrecoverable error");
         | 
| 303 | 
            +
                    return 0;
         | 
| 304 | 
            +
                }
         | 
| 305 | 
            +
                ++ i;
         | 
| 306 | 
            +
              }
         | 
| 307 | 
            +
             | 
| 308 | 
            +
              return len;
         | 
| 309 | 
            +
            }
         |