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.
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
+ }