nyara 0.0.1.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. checksums.yaml +7 -0
  2. data/example/design.rb +62 -0
  3. data/example/fib.rb +15 -0
  4. data/example/hello.rb +5 -0
  5. data/example/stream.rb +10 -0
  6. data/ext/accept.c +133 -0
  7. data/ext/event.c +89 -0
  8. data/ext/extconf.rb +34 -0
  9. data/ext/hashes.c +130 -0
  10. data/ext/http-parser/AUTHORS +41 -0
  11. data/ext/http-parser/CONTRIBUTIONS +4 -0
  12. data/ext/http-parser/LICENSE-MIT +23 -0
  13. data/ext/http-parser/contrib/parsertrace.c +156 -0
  14. data/ext/http-parser/contrib/url_parser.c +44 -0
  15. data/ext/http-parser/http_parser.c +2175 -0
  16. data/ext/http-parser/http_parser.h +304 -0
  17. data/ext/http-parser/test.c +3425 -0
  18. data/ext/http_parser.c +1 -0
  19. data/ext/inc/epoll.h +60 -0
  20. data/ext/inc/kqueue.h +77 -0
  21. data/ext/inc/status_codes.inc +64 -0
  22. data/ext/inc/str_intern.h +66 -0
  23. data/ext/inc/version.inc +1 -0
  24. data/ext/mime.c +107 -0
  25. data/ext/multipart-parser-c/README.md +18 -0
  26. data/ext/multipart-parser-c/multipart_parser.c +309 -0
  27. data/ext/multipart-parser-c/multipart_parser.h +48 -0
  28. data/ext/multipart_parser.c +1 -0
  29. data/ext/nyara.c +56 -0
  30. data/ext/nyara.h +59 -0
  31. data/ext/request.c +474 -0
  32. data/ext/route.cc +325 -0
  33. data/ext/url_encoded.c +304 -0
  34. data/hello.rb +5 -0
  35. data/lib/nyara/config.rb +64 -0
  36. data/lib/nyara/config_hash.rb +51 -0
  37. data/lib/nyara/controller.rb +336 -0
  38. data/lib/nyara/cookie.rb +31 -0
  39. data/lib/nyara/cpu_counter.rb +65 -0
  40. data/lib/nyara/header_hash.rb +18 -0
  41. data/lib/nyara/mime_types.rb +612 -0
  42. data/lib/nyara/nyara.rb +82 -0
  43. data/lib/nyara/param_hash.rb +5 -0
  44. data/lib/nyara/request.rb +144 -0
  45. data/lib/nyara/route.rb +138 -0
  46. data/lib/nyara/route_entry.rb +43 -0
  47. data/lib/nyara/session.rb +104 -0
  48. data/lib/nyara/view.rb +317 -0
  49. data/lib/nyara.rb +25 -0
  50. data/nyara.gemspec +20 -0
  51. data/rakefile +91 -0
  52. data/readme.md +35 -0
  53. data/spec/ext_mime_match_spec.rb +27 -0
  54. data/spec/ext_parse_accept_value_spec.rb +29 -0
  55. data/spec/ext_parse_spec.rb +138 -0
  56. data/spec/ext_route_spec.rb +70 -0
  57. data/spec/hashes_spec.rb +71 -0
  58. data/spec/path_helper_spec.rb +77 -0
  59. data/spec/request_delegate_spec.rb +67 -0
  60. data/spec/request_spec.rb +56 -0
  61. data/spec/route_entry_spec.rb +12 -0
  62. data/spec/route_spec.rb +84 -0
  63. data/spec/session_spec.rb +66 -0
  64. data/spec/spec_helper.rb +52 -0
  65. data/spec/view_spec.rb +87 -0
  66. data/tools/bench-cookie.rb +22 -0
  67. 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)
@@ -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
+ }