nyara 0.0.1.pre.2 → 0.0.1.pre.3

Sign up to get free protection for your applications and to get access to all the features.
data/ext/request.c CHANGED
@@ -1,187 +1,17 @@
1
+ /* request parsing and request object */
2
+
1
3
  #include "nyara.h"
2
4
  #include <ruby/encoding.h>
3
- #include <multipart_parser.h>
4
- #include <errno.h>
5
- #ifndef write
6
- #include <unistd.h>
7
- #endif
8
-
9
- typedef struct {
10
- http_parser hparser;
11
- multipart_parser* mparser;
12
- enum http_method method;
13
- VALUE header;
14
- VALUE accept; // mime array sorted with q
15
- VALUE format; // string ext without dot
16
- VALUE fiber;
17
- VALUE scope; // mapped prefix
18
- VALUE path_with_query;
19
- VALUE path;
20
- VALUE query;
21
- VALUE last_field;
22
- VALUE last_value;
23
- VALUE self;
24
- int fd;
25
-
26
- // response
27
- int status;
28
- VALUE response_content_type;
29
- VALUE response_header;
30
- VALUE response_header_extra_lines;
31
- } Request;
32
-
33
- // typedef int (*http_data_cb) (http_parser*, const char *at, size_t length);
34
- // typedef int (*http_cb) (http_parser*);
35
-
36
- static ID id_not_found;
5
+ #include "request.h"
6
+
37
7
  static VALUE str_html;
38
8
  static rb_encoding* u8_encoding;
39
9
  static VALUE request_class;
40
- static VALUE method_override_key;
41
- static VALUE str_accept;
42
- static VALUE nyara_http_methods;
43
-
44
- static VALUE fd_request_map;
45
- #define MAX_RECEIVE_DATA 65536
46
- static char received_data[MAX_RECEIVE_DATA];
47
-
48
- static VALUE _fiber_func(VALUE _, VALUE args) {
49
- VALUE instance = rb_ary_pop(args);
50
- VALUE meth = rb_ary_pop(args);
51
- rb_apply(instance, SYM2ID(meth), args);
52
- return Qnil;
53
- }
54
-
55
- static void _upcase_method(VALUE str) {
56
- char* s = RSTRING_PTR(str);
57
- long len = RSTRING_LEN(str);
58
- for (long i = 0; i < len; i++) {
59
- if (s[i] >= 'a' && s[i] <= 'z') {
60
- s[i] = 'A' + (s[i] - 'a');
61
- }
62
- }
63
- }
64
-
65
- static inline void _close_fd(int fd) {
66
- rb_hash_delete(fd_request_map, INT2FIX(fd));
67
- close(fd);
68
- }
69
-
70
- static int on_url(http_parser* parser, const char* s, size_t len) {
71
- Request* p = (Request*)parser;
72
- p->method = parser->method;
73
-
74
- if (p->path_with_query == Qnil) {
75
- p->path_with_query = rb_str_new(s, len);
76
- } else {
77
- rb_str_cat(p->path_with_query, s, len);
78
- }
79
- return 0;
80
- }
81
-
82
- static int on_message_complete(http_parser* parser) {
83
- Request* p = (Request*)parser;
84
- if (p->fiber == Qnil) {
85
- return 1;
86
- } else {
87
- VALUE state = rb_fiber_resume(p->fiber, 0, NULL);
88
- if (state == Qnil) { // terminated (todo check raise error)
89
- if (p->status == 200) {
90
- write(p->fd, "0\r\n\r\n", 5);
91
- }
92
- _close_fd(p->fd);
93
- p->fd = 0;
94
- } else if (SYM2ID(state) == rb_intern("term_close")) {
95
- write(p->fd, "0\r\n\r\n", 5);
96
- _close_fd(p->fd);
97
- p->fd = 0;
98
- }
99
- return 0;
100
- }
101
- }
102
-
103
- static int on_header_field(http_parser* parser, const char* s, size_t len) {
104
- Request* p = (Request*)parser;
105
- if (p->last_field == Qnil) {
106
- p->last_field = rb_str_new(s, len);
107
- p->last_value = Qnil;
108
- } else {
109
- rb_str_cat(p->last_field, s, len);
110
- }
111
- return 0;
112
- }
113
-
114
- static int on_header_value(http_parser* parser, const char* s, size_t len) {
115
- Request* p = (Request*)parser;
116
- if (p->last_field == Qnil) {
117
- if (p->last_value == Qnil) {
118
- rb_bug("on_header_value called when neither last_field nor last_value exist");
119
- return 1;
120
- }
121
- rb_str_cat(p->last_value, s, len);
122
- } else {
123
- nyara_headerlize(p->last_field);
124
- p->last_value = rb_str_new(s, len);
125
- rb_hash_aset(p->header, p->last_field, p->last_value);
126
- p->last_field = Qnil;
127
- }
128
- return 0;
129
- }
130
-
131
- // may override POST by _method in query
132
- static void _parse_path_and_query(Request* p) {
133
- char* s = RSTRING_PTR(p->path_with_query);
134
- long len = RSTRING_LEN(p->path_with_query);
135
- long query_i = nyara_parse_path(p->path, s, len);
136
- if (query_i < len) {
137
- nyara_parse_param(p->query, s + query_i, len - query_i);
138
-
139
- // do method override with _method=xxx in query
140
- if (p->method == HTTP_POST) {
141
- VALUE meth = rb_hash_aref(p->query, method_override_key);
142
- if (TYPE(meth) == T_STRING) {
143
- _upcase_method(meth);
144
- VALUE meth_num = rb_hash_aref(nyara_http_methods, meth);
145
- if (meth_num != Qnil) {
146
- p->method = FIX2INT(meth_num);
147
- }
148
- }
149
- }
150
- }
151
- }
10
+ static VALUE sym_writing;
152
11
 
153
- static int on_headers_complete(http_parser* parser) {
154
- Request* p = (Request*)parser;
155
- p->last_field = Qnil;
156
- p->last_value = Qnil;
157
-
158
- _parse_path_and_query(p);
159
- p->accept = ext_parse_accept_value(Qnil, rb_hash_aref(p->header, str_accept));
160
- volatile RouteResult result = nyara_lookup_route(p->method, p->path, p->accept);
161
- if (RTEST(result.controller)) {
162
- rb_ary_push(result.args, rb_class_new_instance(1, &(p->self), result.controller));
163
- // result.args is on stack, no need to worry gc
164
- p->fiber = rb_fiber_new(_fiber_func, result.args);
165
- p->scope = result.scope;
166
- p->format = result.format;
167
- p->response_header = rb_class_new_instance(0, NULL, nyara_header_hash_class);
168
- p->response_header_extra_lines = rb_ary_new();
169
- return 0;
170
- }
171
- rb_funcall(p->self, id_not_found, 0);
172
- return 1;
173
- }
174
-
175
- static http_parser_settings request_settings = {
176
- .on_message_begin = NULL,
177
- .on_url = on_url,
178
- .on_status_complete = NULL,
179
- .on_header_field = on_header_field,
180
- .on_header_value = on_header_value,
181
- .on_headers_complete = on_headers_complete,
182
- .on_body = NULL, // data_cb
183
- .on_message_complete = on_message_complete
184
- };
12
+ #define P \
13
+ Request* p;\
14
+ Data_Get_Struct(self, Request, p);
185
15
 
186
16
  static void request_mark(void* pp) {
187
17
  Request* p = pp;
@@ -199,6 +29,7 @@ static void request_mark(void* pp) {
199
29
  rb_gc_mark_maybe(p->response_content_type);
200
30
  rb_gc_mark_maybe(p->response_header);
201
31
  rb_gc_mark_maybe(p->response_header_extra_lines);
32
+ rb_gc_mark_maybe(p->watched_fds);
202
33
  }
203
34
  }
204
35
 
@@ -206,19 +37,25 @@ static void request_free(void* pp) {
206
37
  Request* p = pp;
207
38
  if (p) {
208
39
  if (p->fd) {
209
- _close_fd(p->fd);
40
+ nyara_detach_fd(p->fd);
210
41
  }
211
42
  xfree(p);
212
43
  }
213
44
  }
214
45
 
215
- static Request* request_alloc() {
46
+ static Request* _request_alloc() {
216
47
  Request* p = ALLOC(Request);
217
48
  http_parser_init(&(p->hparser), HTTP_REQUEST);
49
+ p->mparser = NULL;
50
+
51
+ p->method = HTTP_GET;
52
+ p->fd = 0;
53
+ p->parse_state = 0;
54
+ p->status = 200;
55
+
218
56
  volatile VALUE header = rb_class_new_instance(0, NULL, nyara_header_hash_class);
219
57
  volatile VALUE path = rb_enc_str_new("", 0, u8_encoding);
220
58
  volatile VALUE query = rb_class_new_instance(0, NULL, nyara_param_hash_class);
221
- p->mparser = NULL;
222
59
  p->header = header;
223
60
  p->accept = Qnil;
224
61
  p->format = Qnil;
@@ -229,66 +66,36 @@ static Request* request_alloc() {
229
66
  p->query = query;
230
67
  p->last_field = Qnil;
231
68
  p->last_value = Qnil;
232
- p->fd = 0;
233
- p->status = 200;
234
69
  p->response_content_type = Qnil;
235
70
  p->response_header = Qnil;
236
71
  p->response_header_extra_lines = Qnil;
72
+
73
+ volatile VALUE watched_fds = rb_ary_new();
74
+ p->watched_fds = watched_fds;
75
+
237
76
  p->self = Data_Wrap_Struct(request_class, request_mark, request_free, p);
238
77
  return p;
239
78
  }
240
79
 
241
- static VALUE request_alloc_func(VALUE klass) {
242
- return request_alloc()->self;
80
+ VALUE nyara_request_new(int fd) {
81
+ Request* p = _request_alloc();
82
+ p->fd = fd;
83
+ return p->self;
243
84
  }
244
85
 
245
- /* client entrance
246
- invoke order:
247
- - find/create request
248
- - http_parser_execute
249
- - on_headers_complete => 404 or create request
250
- - on_message_complete => run action
251
- */
252
- void nyara_handle_request(int fd) {
253
- Request* p = NULL;
254
- bool first_time = false;
255
-
256
- {
257
- VALUE v_fd = INT2FIX(fd);
258
- VALUE request = rb_hash_aref(fd_request_map, v_fd);
259
- if (request == Qnil) {
260
- p = request_alloc();
261
- p->fd = fd;
262
- rb_hash_aset(fd_request_map, v_fd, p->self);
263
- first_time = true;
264
- } else {
265
- Data_Get_Struct(request, Request, p);
266
- }
267
- }
268
-
269
- long len = read(fd, received_data, MAX_RECEIVE_DATA);
270
- if (len < 0) {
271
- if (errno != EAGAIN) {
272
- // todo log the bug
273
- if (p->fd) {
274
- _close_fd(p->fd);
275
- p->fd = 0;
276
- }
277
- }
278
- } else {
279
- if (first_time && !len) {
280
- // todo log this exception
281
- return;
282
- }
283
- // note: when len == 0, means eof reached, that also informs http_parser the eof
284
- http_parser_execute(&(p->hparser), &request_settings, received_data, len);
86
+ void nyara_request_term_close(VALUE self, bool write_last_chunk) {
87
+ P;
88
+ if (write_last_chunk || p->status == 200) {
89
+ // usually this succeeds, while not, it doesn't matter cause we are closing it
90
+ # pragma GCC diagnostic push
91
+ # pragma GCC diagnostic ignored "-Wunused-result"
92
+ write(p->fd, "0\r\n\r\n", 5);
93
+ # pragma GCC diagnostic pop
285
94
  }
95
+ nyara_detach_fd(p->fd);
96
+ p->fd = 0;
286
97
  }
287
98
 
288
- #define P \
289
- Request* p;\
290
- Data_Get_Struct(self, Request, p);
291
-
292
99
  static VALUE request_http_method(VALUE self) {
293
100
  P;
294
101
  return rb_str_new2(http_method_str(p->method));
@@ -361,50 +168,72 @@ static VALUE ext_request_set_status(VALUE _, VALUE self, VALUE n) {
361
168
  return n;
362
169
  }
363
170
 
364
- static VALUE ext_send_data(VALUE _, VALUE self, VALUE data) {
365
- P;
366
- char* buf = RSTRING_PTR(data);
367
- long len = RSTRING_LEN(data);
368
-
171
+ // return true if success
172
+ static bool _send_data(int fd, const char* buf, long len) {
369
173
  while(len) {
370
- long written = write(p->fd, buf, len);
371
- if (written == 0)
372
- return Qnil;
373
- if (written == -1) {
374
- if (errno == EWOULDBLOCK || errno == EAGAIN) {
375
- // todo enqueue data and set state
174
+ long written = write(fd, buf, len);
175
+ if (written <= 0) {
176
+ if (errno == EAGAIN || errno == EWOULDBLOCK) {
177
+ rb_fiber_yield(1, &sym_writing);
178
+ } else {
179
+ return false;
180
+ }
181
+ } else {
182
+ buf += written;
183
+ len -= written;
184
+ if (len) {
185
+ rb_fiber_yield(1, &sym_writing);
376
186
  }
377
- return Qnil;
378
187
  }
379
- buf += written;
380
- len -= written;
381
188
  }
189
+ return true;
190
+ }
191
+
192
+ static VALUE ext_request_send_data(VALUE _, VALUE self, VALUE data) {
193
+ P;
194
+ char* buf = RSTRING_PTR(data);
195
+ long len = RSTRING_LEN(data);
196
+ _send_data(p->fd, buf, len);
382
197
  return Qnil;
383
198
  }
384
199
 
385
- static VALUE ext_send_chunk(VALUE _, VALUE self, VALUE str) {
200
+ static VALUE ext_request_send_chunk(VALUE _, VALUE self, VALUE str) {
386
201
  long len = RSTRING_LEN(str);
387
202
  if (!len) {
388
203
  return Qnil;
389
204
  }
390
- // todo len overflow?
391
205
  P;
392
- long res = dprintf(p->fd, "%lx\r\n%.*s\r\n", len, (int)len, RSTRING_PTR(str));
393
- if (res < 0) {
394
- rb_raise(rb_eRuntimeError, "%s", strerror(errno));
206
+
207
+ char pre_buf[20]; // enough space to hold a long + 2 chars
208
+ long pre_len = sprintf(pre_buf, "%lx\r\n", len);
209
+ if (pre_len <= 0) {
210
+ rb_raise(rb_eRuntimeError, "fail to format chunk length for len: %ld", len);
211
+ }
212
+ bool success = \
213
+ _send_data(p->fd, pre_buf, pre_len) &&
214
+ _send_data(p->fd, RSTRING_PTR(str), len) &&
215
+ _send_data(p->fd, "\r\n", 2);
216
+
217
+ if (!success) {
218
+ rb_sys_fail("write(2)");
395
219
  }
220
+
396
221
  return Qnil;
397
222
  }
398
223
 
399
224
  // for test: find or create a request with a fd
400
- static VALUE ext_handle_request(VALUE _, VALUE v_fd) {
401
- int fd = FIX2INT(v_fd);
402
- nyara_handle_request(fd);
403
- return rb_hash_aref(fd_request_map, v_fd);
225
+ static VALUE ext_request_new(VALUE _) {
226
+ return _request_alloc()->self;
227
+ }
228
+
229
+ static VALUE ext_request_set_fd(VALUE _, VALUE self, VALUE vfd) {
230
+ P;
231
+ p->fd = NUM2INT(vfd);
232
+ return Qnil;
404
233
  }
405
234
 
406
235
  // set internal attrs in the request object
407
- static VALUE ext_set_request_attrs(VALUE _, VALUE self, VALUE attrs) {
236
+ static VALUE ext_request_set_attrs(VALUE _, VALUE self, VALUE attrs) {
408
237
  # define ATTR(key) rb_hash_delete(attrs, ID2SYM(rb_intern(key)))
409
238
  # define HEADER_HASH_NEW rb_class_new_instance(0, NULL, nyara_header_hash_class)
410
239
  P;
@@ -437,22 +266,14 @@ static VALUE ext_set_request_attrs(VALUE _, VALUE self, VALUE attrs) {
437
266
  }
438
267
 
439
268
  void Init_request(VALUE nyara, VALUE ext) {
440
- id_not_found = rb_intern("not_found");
441
269
  str_html = rb_str_new2("html");
442
270
  OBJ_FREEZE(str_html);
443
271
  rb_gc_register_mark_object(str_html);
444
272
  u8_encoding = rb_utf8_encoding();
445
- method_override_key = rb_str_new2("_method");
446
- rb_const_set(nyara, rb_intern("METHOD_OVERRIDE_KEY"), method_override_key);
447
- nyara_http_methods = rb_const_get(nyara, rb_intern("HTTP_METHODS"));
448
- fd_request_map = rb_hash_new();
449
- rb_gc_register_mark_object(fd_request_map);
450
- str_accept = rb_str_new2("Accept");
451
- rb_gc_register_mark_object(str_accept);
273
+ sym_writing = ID2SYM(rb_intern("writing"));
452
274
 
453
275
  // request
454
276
  request_class = rb_define_class_under(nyara, "Request", rb_cObject);
455
- rb_define_alloc_func(request_class, request_alloc_func);
456
277
  rb_define_method(request_class, "http_method", request_http_method, 0);
457
278
  rb_define_method(request_class, "header", request_header, 0);
458
279
  rb_define_method(request_class, "scope", request_scope, 0);
@@ -470,9 +291,10 @@ void Init_request(VALUE nyara, VALUE ext) {
470
291
 
471
292
  // hide internal methods in ext
472
293
  rb_define_singleton_method(ext, "request_set_status", ext_request_set_status, 2);
473
- rb_define_singleton_method(ext, "send_data", ext_send_data, 2);
474
- rb_define_singleton_method(ext, "send_chunk", ext_send_chunk, 2);
294
+ rb_define_singleton_method(ext, "request_send_data", ext_request_send_data, 2);
295
+ rb_define_singleton_method(ext, "request_send_chunk", ext_request_send_chunk, 2);
475
296
  // for test
476
- rb_define_singleton_method(ext, "handle_request", ext_handle_request, 1);
477
- rb_define_singleton_method(ext, "set_request_attrs", ext_set_request_attrs, 2);
297
+ rb_define_singleton_method(ext, "request_new", ext_request_new, 0);
298
+ rb_define_singleton_method(ext, "request_set_fd", ext_request_set_fd, 2);
299
+ rb_define_singleton_method(ext, "request_set_attrs", ext_request_set_attrs, 2);
478
300
  }
data/ext/request.h ADDED
@@ -0,0 +1,43 @@
1
+ #pragma once
2
+
3
+ #include "nyara.h"
4
+ #include <multipart_parser.h>
5
+ #include <errno.h>
6
+ #ifndef write
7
+ #include <unistd.h>
8
+ #endif
9
+
10
+ enum ParseState {
11
+ PS_INIT, PS_HEADERS_COMPLETE, PS_MESSAGE_COMPLETE, PS_ERROR
12
+ };
13
+
14
+ typedef struct {
15
+ http_parser hparser;
16
+ multipart_parser* mparser;
17
+
18
+ enum http_method method;
19
+ int fd;
20
+ enum ParseState parse_state;
21
+ int status; // response status
22
+
23
+ VALUE self;
24
+
25
+ // request
26
+ VALUE header;
27
+ VALUE accept; // mime array sorted with q
28
+ VALUE format; // string ext without dot
29
+ VALUE fiber;
30
+ VALUE scope; // mapped prefix
31
+ VALUE path_with_query;
32
+ VALUE path;
33
+ VALUE query;
34
+ VALUE last_field;
35
+ VALUE last_value;
36
+
37
+ // response
38
+ VALUE response_content_type;
39
+ VALUE response_header;
40
+ VALUE response_header_extra_lines;
41
+
42
+ VALUE watched_fds;
43
+ } Request;
@@ -0,0 +1,123 @@
1
+ /* request parse callbacks */
2
+
3
+ #include "nyara.h"
4
+ #include "request.h"
5
+
6
+ static VALUE str_accept;
7
+ static VALUE method_override_key;
8
+ static VALUE nyara_http_methods;
9
+
10
+ static int on_url(http_parser* parser, const char* s, size_t len) {
11
+ Request* p = (Request*)parser;
12
+ p->method = parser->method;
13
+
14
+ if (p->path_with_query == Qnil) {
15
+ p->path_with_query = rb_str_new(s, len);
16
+ } else {
17
+ rb_str_cat(p->path_with_query, s, len);
18
+ }
19
+ return 0;
20
+ }
21
+
22
+ static int on_header_field(http_parser* parser, const char* s, size_t len) {
23
+ Request* p = (Request*)parser;
24
+ if (p->last_field == Qnil) {
25
+ p->last_field = rb_str_new(s, len);
26
+ p->last_value = Qnil;
27
+ } else {
28
+ rb_str_cat(p->last_field, s, len);
29
+ }
30
+ return 0;
31
+ }
32
+
33
+ static int on_header_value(http_parser* parser, const char* s, size_t len) {
34
+ Request* p = (Request*)parser;
35
+ if (p->last_field == Qnil) {
36
+ if (p->last_value == Qnil) {
37
+ p->parse_state = PS_ERROR;
38
+ return 1;
39
+ }
40
+ rb_str_cat(p->last_value, s, len);
41
+ } else {
42
+ nyara_headerlize(p->last_field);
43
+ p->last_value = rb_str_new(s, len);
44
+ rb_hash_aset(p->header, p->last_field, p->last_value);
45
+ p->last_field = Qnil;
46
+ }
47
+ return 0;
48
+ }
49
+
50
+ static void _upcase_method(VALUE str) {
51
+ char* s = RSTRING_PTR(str);
52
+ long len = RSTRING_LEN(str);
53
+ for (long i = 0; i < len; i++) {
54
+ if (s[i] >= 'a' && s[i] <= 'z') {
55
+ s[i] = 'A' + (s[i] - 'a');
56
+ }
57
+ }
58
+ }
59
+
60
+ // may override POST by _method in query
61
+ static void _parse_path_and_query(Request* p) {
62
+ char* s = RSTRING_PTR(p->path_with_query);
63
+ long len = RSTRING_LEN(p->path_with_query);
64
+ long query_i = nyara_parse_path(p->path, s, len);
65
+ if (query_i < len) {
66
+ nyara_parse_param(p->query, s + query_i, len - query_i);
67
+
68
+ // do method override with _method=xxx in query
69
+ if (p->method == HTTP_POST) {
70
+ VALUE meth = rb_hash_aref(p->query, method_override_key);
71
+ if (TYPE(meth) == T_STRING) {
72
+ _upcase_method(meth);
73
+ VALUE meth_num = rb_hash_aref(nyara_http_methods, meth);
74
+ if (meth_num != Qnil) {
75
+ p->method = FIX2INT(meth_num);
76
+ }
77
+ }
78
+ }
79
+ }
80
+ }
81
+
82
+ static int on_headers_complete(http_parser* parser) {
83
+ Request* p = (Request*)parser;
84
+ p->last_field = Qnil;
85
+ p->last_value = Qnil;
86
+
87
+ _parse_path_and_query(p);
88
+ p->accept = ext_parse_accept_value(Qnil, rb_hash_aref(p->header, str_accept));
89
+ p->parse_state = PS_HEADERS_COMPLETE;
90
+ return 0;
91
+ }
92
+
93
+ static int on_body(http_parser* parser, const char* s, size_t len) {
94
+ // todo
95
+ return 0;
96
+ }
97
+
98
+ static int on_message_complete(http_parser* parser) {
99
+ Request* p = (Request*)parser;
100
+ p->parse_state = PS_MESSAGE_COMPLETE;
101
+ return 0;
102
+ }
103
+
104
+ // used in event.c
105
+ http_parser_settings nyara_request_parse_settings = {
106
+ .on_message_begin = NULL,
107
+ .on_url = on_url,
108
+ .on_status_complete = NULL,
109
+ .on_header_field = on_header_field,
110
+ .on_header_value = on_header_value,
111
+ .on_headers_complete = on_headers_complete,
112
+ .on_body = on_body,
113
+ .on_message_complete = on_message_complete
114
+ };
115
+
116
+ void Init_request_parse(VALUE nyara) {
117
+ str_accept = rb_str_new2("Accept");
118
+ rb_gc_register_mark_object(str_accept);
119
+ method_override_key = rb_str_new2("_method");
120
+ OBJ_FREEZE(method_override_key);
121
+ rb_const_set(nyara, rb_intern("METHOD_OVERRIDE_KEY"), method_override_key);
122
+ nyara_http_methods = rb_const_get(nyara, rb_intern("HTTP_METHODS"));
123
+ }
data/ext/route.cc CHANGED
@@ -1,3 +1,5 @@
1
+ /* route register and search */
2
+
1
3
  extern "C" {
2
4
  #include "nyara.h"
3
5
  }