agoo 0.9.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of agoo might be problematic. Click here for more details.

data/ext/agoo/page.h ADDED
@@ -0,0 +1,39 @@
1
+ // Copyright 2016, 2018 by Peter Ohler, All Rights Reserved
2
+
3
+ #ifndef __AGOO_PAGE_H__
4
+ #define __AGOO_PAGE_H__
5
+
6
+ #include <stdint.h>
7
+ #include <time.h>
8
+
9
+ #include "err.h"
10
+ #include "text.h"
11
+
12
+ #define MAX_KEY_LEN 1024
13
+ #define PAGE_BUCKET_SIZE 1024
14
+ #define PAGE_BUCKET_MASK 1023
15
+
16
+ typedef struct _Page {
17
+ Text resp;
18
+ char *path;
19
+ time_t mtime;
20
+ double last_check;
21
+ } *Page;
22
+
23
+ typedef struct _Slot {
24
+ struct _Slot *next;
25
+ char key[MAX_KEY_LEN + 1];
26
+ Page value;
27
+ uint64_t hash;
28
+ int klen;
29
+ } *Slot;
30
+
31
+ typedef struct _Cache {
32
+ Slot buckets[PAGE_BUCKET_SIZE];
33
+ } *Cache;
34
+
35
+ extern void cache_init(Cache cache);
36
+ extern void page_destroy(Page p);
37
+ extern Page page_get(Err err, Cache cache, const char *dir, const char *path, int plen);
38
+
39
+ #endif /* __AGOO_PAGE_H__ */
data/ext/agoo/queue.c ADDED
@@ -0,0 +1,191 @@
1
+ // Copyright 2015, 2016, 2018 by Peter Ohler, All Rights Reserved
2
+
3
+ #include <fcntl.h>
4
+ #include <stdlib.h>
5
+ #include <string.h>
6
+ #include <sys/socket.h>
7
+ #include <unistd.h>
8
+
9
+ #include "dtime.h"
10
+ #include "queue.h"
11
+
12
+ // lower gives faster response but burns more CPU. This is a reasonable compromise.
13
+ #define RETRY_SECS 0.0001
14
+
15
+ #define NOT_WAITING 0
16
+ #define WAITING 1
17
+ #define NOTIFIED 2
18
+
19
+ // head and tail both increment and wrap.
20
+ // tail points to next open space.
21
+ // When head == tail the queue is full. This happens when tail catches up with head.
22
+ //
23
+
24
+ void
25
+ queue_init(Queue q, size_t qsize) {
26
+ queue_multi_init(q, qsize, false, false);
27
+ }
28
+
29
+ void
30
+ queue_multi_init(Queue q, size_t qsize, bool multi_push, bool multi_pop) {
31
+ if (qsize < 4) {
32
+ qsize = 4;
33
+ }
34
+ q->q = (QItem*)malloc(sizeof(QItem) * qsize);
35
+ q->end = q->q + qsize;
36
+
37
+ memset(q->q, 0, sizeof(QItem) * qsize);
38
+ q->head = q->q;
39
+ q->tail = q->q + 1;
40
+ atomic_flag_clear(&q->push_lock);
41
+ atomic_flag_clear(&q->pop_lock);
42
+ q->wait_state = 0;
43
+ q->multi_push = multi_push;
44
+ q->multi_pop = multi_pop;
45
+ // Create when/if needed.
46
+ q->rsock = 0;
47
+ q->wsock = 0;
48
+ }
49
+
50
+ void
51
+ queue_cleanup(Queue q) {
52
+ free(q->q);
53
+ q->q = NULL;
54
+ q->end = NULL;
55
+ if (0 < q->wsock) {
56
+ close(q->wsock);
57
+ }
58
+ if (0 < q->rsock) {
59
+ close(q->rsock);
60
+ }
61
+ }
62
+
63
+ void
64
+ queue_push(Queue q, QItem item) {
65
+ QItem *tail;
66
+
67
+ if (q->multi_push) {
68
+ while (atomic_flag_test_and_set(&q->push_lock)) {
69
+ dsleep(RETRY_SECS);
70
+ }
71
+ }
72
+ // Wait for head to move on.
73
+ while (atomic_load(&q->head) == q->tail) {
74
+ dsleep(RETRY_SECS);
75
+ }
76
+ *q->tail = item;
77
+ tail = q->tail + 1;
78
+
79
+ if (q->end <= tail) {
80
+ tail = q->q;
81
+ }
82
+ atomic_store(&q->tail, tail);
83
+ if (q->multi_push) {
84
+ atomic_flag_clear(&q->push_lock);
85
+ }
86
+ if (0 != q->wsock && WAITING == atomic_load(&q->wait_state)) {
87
+ if (write(q->wsock, ".", 1)) {}
88
+ atomic_store(&q->wait_state, NOTIFIED);
89
+ }
90
+ }
91
+
92
+ void
93
+ queue_wakeup(Queue q) {
94
+ if (0 != q->wsock) {
95
+ if (write(q->wsock, ".", 1)) {}
96
+ }
97
+ }
98
+
99
+ QItem
100
+ queue_pop(Queue q, double timeout) {
101
+ QItem item;
102
+ QItem *next;
103
+
104
+ if (q->multi_pop) {
105
+ while (atomic_flag_test_and_set(&q->pop_lock)) {
106
+ dsleep(RETRY_SECS);
107
+ }
108
+ }
109
+ item = *q->head;
110
+
111
+ if (NULL != item) {
112
+ *q->head = NULL;
113
+ if (q->multi_pop) {
114
+ atomic_flag_clear(&q->pop_lock);
115
+ }
116
+ return item;
117
+ }
118
+ next = q->head + 1;
119
+
120
+ if (q->end <= next) {
121
+ next = q->q;
122
+ }
123
+ // If the next is the tail then wait for something to be appended.
124
+ for (int cnt = (int)(timeout / RETRY_SECS); atomic_load(&q->tail) == next; cnt--) {
125
+ if (cnt <= 0) {
126
+ if (q->multi_pop) {
127
+ atomic_flag_clear(&q->pop_lock);
128
+ }
129
+ return NULL;
130
+ }
131
+ dsleep(RETRY_SECS);
132
+ }
133
+ atomic_store(&q->head, next);
134
+
135
+ item = *q->head;
136
+ *q->head = NULL;
137
+ if (q->multi_pop) {
138
+ atomic_flag_clear(&q->pop_lock);
139
+ }
140
+ return item;
141
+ }
142
+
143
+ // Called by the popper usually.
144
+ bool
145
+ queue_empty(Queue q) {
146
+ QItem *head = atomic_load(&q->head);
147
+ QItem *next = head + 1;
148
+
149
+ if (q->end <= next) {
150
+ next = q->q;
151
+ }
152
+ if (NULL == *head && q->tail == next) {
153
+ return true;
154
+ }
155
+ return false;
156
+ }
157
+
158
+ int
159
+ queue_listen(Queue q) {
160
+ if (0 == q->rsock) {
161
+ int fd[2];
162
+
163
+ if (0 == pipe(fd)) {
164
+ fcntl(fd[0], F_SETFL, O_NONBLOCK);
165
+ fcntl(fd[1], F_SETFL, O_NONBLOCK);
166
+ q->rsock = fd[0];
167
+ q->wsock = fd[1];
168
+ }
169
+ }
170
+ atomic_store(&q->wait_state, WAITING);
171
+
172
+ return q->rsock;
173
+ }
174
+
175
+ void
176
+ queue_release(Queue q) {
177
+ char buf[8];
178
+
179
+ // clear pipe
180
+ while (0 < read(q->rsock, buf, sizeof(buf))) {
181
+ }
182
+ atomic_store(&q->wait_state, NOT_WAITING);
183
+ }
184
+
185
+ int
186
+ queue_count(Queue q) {
187
+ int size = q->end - q->q;
188
+
189
+ return (q->tail - q->head + size) % size;
190
+ }
191
+
data/ext/agoo/queue.h ADDED
@@ -0,0 +1,39 @@
1
+ // Copyright 2015, 2016, 2018 by Peter Ohler, All Rights Reserved
2
+
3
+ #ifndef __AGOO_QUEUE_H__
4
+ #define __AGOO_QUEUE_H__
5
+
6
+ #include <stdatomic.h>
7
+ #include <stdbool.h>
8
+
9
+ typedef void *QItem;
10
+
11
+ typedef struct _Queue {
12
+ QItem *q;
13
+ QItem *end;
14
+ _Atomic(QItem*) head;
15
+ _Atomic(QItem*) tail;
16
+ bool multi_push;
17
+ bool multi_pop;
18
+ atomic_flag push_lock; // set to true when push in progress
19
+ atomic_flag pop_lock; // set to true when push in progress
20
+ atomic_int wait_state;
21
+ int rsock;
22
+ int wsock;
23
+ } *Queue;
24
+
25
+ extern void queue_init(Queue q, size_t qsize);
26
+
27
+ extern void queue_multi_init(Queue q, size_t qsize, bool multi_push, bool multi_pop);
28
+
29
+ extern void queue_cleanup(Queue q);
30
+ extern void queue_push(Queue q, QItem item);
31
+ extern QItem queue_pop(Queue q, double timeout);
32
+ extern bool queue_empty(Queue q);
33
+ extern int queue_listen(Queue q);
34
+ extern void queue_release(Queue q);
35
+ extern int queue_count(Queue q);
36
+
37
+ extern void queue_wakeup(Queue q);
38
+
39
+ #endif /* __AGOO_QUEUE_H__ */
@@ -0,0 +1,563 @@
1
+ // Copyright (c) 2018, Peter Ohler, All rights reserved.
2
+
3
+ #include <stdio.h>
4
+
5
+ #include "con.h"
6
+ #include "error_stream.h"
7
+ #include "request.h"
8
+
9
+ static VALUE req_class = Qundef;
10
+
11
+ static VALUE connect_val = Qundef;
12
+ static VALUE content_length_val = Qundef;
13
+ static VALUE content_type_val = Qundef;
14
+ static VALUE delete_val = Qundef;
15
+ static VALUE empty_val = Qundef;
16
+ static VALUE get_val = Qundef;
17
+ static VALUE head_val = Qundef;
18
+ static VALUE http_val = Qundef;
19
+ static VALUE options_val = Qundef;
20
+ static VALUE path_info_val = Qundef;
21
+ static VALUE post_val = Qundef;
22
+ static VALUE put_val = Qundef;
23
+ static VALUE query_string_val = Qundef;
24
+ static VALUE rack_errors_val = Qundef;
25
+ static VALUE rack_input_val = Qundef;
26
+ static VALUE rack_multiprocess_val = Qundef;
27
+ static VALUE rack_multithread_val = Qundef;
28
+ static VALUE rack_run_once_val = Qundef;
29
+ static VALUE rack_url_scheme_val = Qundef;
30
+ static VALUE rack_version_val = Qundef;
31
+ static VALUE rack_version_val_val = Qundef;
32
+ static VALUE request_method_val = Qundef;
33
+ static VALUE script_name_val = Qundef;
34
+ static VALUE server_name_val = Qundef;
35
+ static VALUE server_port_val = Qundef;
36
+ static VALUE slash_val = Qundef;
37
+
38
+ static VALUE stringio_class = Qundef;
39
+
40
+ static ID new_id;
41
+
42
+ static const char content_type[] = "Content-Type";
43
+ static const char content_length[] = "Content-Length";
44
+
45
+ static VALUE
46
+ req_method(Req r) {
47
+ VALUE m;
48
+
49
+ if (NULL == r) {
50
+ rb_raise(rb_eArgError, "Request is no longer valid.");
51
+ }
52
+ switch (r->method) {
53
+ case CONNECT: m = connect_val; break;
54
+ case DELETE: m = delete_val; break;
55
+ case GET: m = get_val; break;
56
+ case HEAD: m = head_val; break;
57
+ case OPTIONS: m = options_val; break;
58
+ case POST: m = post_val; break;
59
+ case PUT: m = put_val; break;
60
+ default: m = Qnil; break;
61
+ }
62
+ return m;
63
+ }
64
+
65
+ /* Document-method: request_method
66
+ *
67
+ * call-seq: request_method()
68
+ *
69
+ * Returns the HTTP method of the request.
70
+ */
71
+ static VALUE
72
+ method(VALUE self) {
73
+ return req_method((Req)DATA_PTR(self));
74
+ }
75
+
76
+ static VALUE
77
+ req_script_name(Req r) {
78
+ // The logic is a bit tricky here and for path_info. If the HTTP path is /
79
+ // then the script_name must be empty and the path_info will be /. All
80
+ // other cases are handled with the full path in script_name and path_info
81
+ // empty.
82
+ if (NULL == r) {
83
+ rb_raise(rb_eArgError, "Request is no longer valid.");
84
+ }
85
+ if (0 == r->path.len || (1 == r->path.len && '/' == *r->path.start)) {
86
+ return empty_val;
87
+ }
88
+ return rb_str_new(r->path.start, r->path.len);
89
+ }
90
+
91
+ /* Document-method: script_name
92
+ *
93
+ * call-seq: script_name()
94
+ *
95
+ * Returns the path info which is assumed to be the full path unless the root
96
+ * and then the rack restrictions are followed on what the script name and
97
+ * path info should be.
98
+ */
99
+ static VALUE
100
+ script_name(VALUE self) {
101
+ return req_script_name((Req)DATA_PTR(self));
102
+ }
103
+
104
+ static VALUE
105
+ req_path_info(Req r) {
106
+ if (NULL == r) {
107
+ rb_raise(rb_eArgError, "Request is no longer valid.");
108
+ }
109
+ if (0 == r->path.len || (1 == r->path.len && '/' == *r->path.start)) {
110
+ return slash_val;
111
+ }
112
+ return empty_val;
113
+ }
114
+
115
+ /* Document-method: path_info
116
+ *
117
+ * call-seq: path_info()
118
+ *
119
+ * Returns the script name which is assumed to be either '/' or the empty
120
+ * according to the rack restrictions are followed on what the script name and
121
+ * path info should be.
122
+ */
123
+ static VALUE
124
+ path_info(VALUE self) {
125
+ return req_path_info((Req)DATA_PTR(self));
126
+ }
127
+
128
+ static VALUE
129
+ req_query_string(Req r) {
130
+ if (NULL == r) {
131
+ rb_raise(rb_eArgError, "Request is no longer valid.");
132
+ }
133
+ if (NULL == r->query.start) {
134
+ return empty_val;
135
+ }
136
+ return rb_str_new(r->query.start, r->query.len);
137
+ }
138
+
139
+ /* Document-method: query_string
140
+ *
141
+ * call-seq: query_string()
142
+ *
143
+ * Returns the query string of the request.
144
+ */
145
+ static VALUE
146
+ query_string(VALUE self) {
147
+ return req_query_string((Req)DATA_PTR(self));
148
+ }
149
+
150
+ static VALUE
151
+ req_server_name(Req r) {
152
+ int len;
153
+ const char *host;
154
+ const char *colon;
155
+
156
+ if (NULL == r) {
157
+ rb_raise(rb_eArgError, "Request is no longer valid.");
158
+ }
159
+ if (NULL == (host = con_header_value(r->header.start, r->header.len, "Host", &len))) {
160
+ return Qnil;
161
+ }
162
+ for (colon = host + len - 1; host < colon; colon--) {
163
+ if (':' == *colon) {
164
+ break;
165
+ }
166
+ }
167
+ if (host == colon) {
168
+ return Qnil;
169
+ }
170
+ return rb_str_new(host, colon - host);
171
+ }
172
+
173
+ /* Document-method: server_name
174
+ *
175
+ * call-seq: server_name()
176
+ *
177
+ * Returns the server or host name.
178
+ */
179
+ static VALUE
180
+ server_name(VALUE self) {
181
+ return req_server_name((Req)DATA_PTR(self));
182
+ }
183
+
184
+ static VALUE
185
+ req_server_port(Req r) {
186
+ int len;
187
+ const char *host;
188
+ const char *colon;
189
+
190
+ if (NULL == r) {
191
+ rb_raise(rb_eArgError, "Request is no longer valid.");
192
+ }
193
+ if (NULL == (host = con_header_value(r->header.start, r->header.len, "Host", &len))) {
194
+ return Qnil;
195
+ }
196
+ for (colon = host + len - 1; host < colon; colon--) {
197
+ if (':' == *colon) {
198
+ break;
199
+ }
200
+ }
201
+ if (host == colon) {
202
+ return Qnil;
203
+ }
204
+ return rb_str_new(colon + 1, host + len - colon - 1);
205
+ }
206
+
207
+ /* Document-method: server_port
208
+ *
209
+ * call-seq: server_port()
210
+ *
211
+ * Returns the server or host port as a string.
212
+ */
213
+ static VALUE
214
+ server_port(VALUE self) {
215
+ return req_server_port((Req)DATA_PTR(self));
216
+ }
217
+
218
+ /* Document-method: rack_version
219
+ *
220
+ * call-seq: rack_version()
221
+ *
222
+ * Returns the rack version the request is compliant with.
223
+ */
224
+ static VALUE
225
+ rack_version(VALUE self) {
226
+ return rack_version_val_val;
227
+ }
228
+
229
+ static VALUE
230
+ req_rack_url_scheme(Req r) {
231
+ // TBD http or https when ssl is supported
232
+ return http_val;
233
+ }
234
+
235
+ /* Document-method: rack_url_scheme
236
+ *
237
+ * call-seq: rack_url_scheme()
238
+ *
239
+ * Returns the URL scheme or either _http_ or _https_ as a string.
240
+ */
241
+ static VALUE
242
+ rack_url_scheme(VALUE self) {
243
+ return req_rack_url_scheme((Req)DATA_PTR(self));
244
+ }
245
+
246
+ static VALUE
247
+ req_rack_input(Req r) {
248
+ if (NULL == r) {
249
+ rb_raise(rb_eArgError, "Request is no longer valid.");
250
+ }
251
+ if (NULL == r->body.start) {
252
+ return Qnil;
253
+ }
254
+ return rb_funcall(stringio_class, new_id, 1, rb_str_new(r->body.start, r->body.len));
255
+ }
256
+
257
+ /* Document-method: rack_input
258
+ *
259
+ * call-seq: rack_input()
260
+ *
261
+ * Returns an input stream for the request body. If no body is present then
262
+ * _nil_ is returned.
263
+ */
264
+ static VALUE
265
+ rack_input(VALUE self) {
266
+ return req_rack_input((Req)DATA_PTR(self));
267
+ }
268
+
269
+ static VALUE
270
+ req_rack_errors(Req r) {
271
+ return error_stream_new(r->server);
272
+ }
273
+
274
+ /* Document-method: rack_errors
275
+ *
276
+ * call-seq: rack_errors()
277
+ *
278
+ * Returns an error stream for the request. This stream is used to write error
279
+ * log entries.
280
+ */
281
+ static VALUE
282
+ rack_errors(VALUE self) {
283
+ return req_rack_errors((Req)DATA_PTR(self));
284
+ }
285
+
286
+ static VALUE
287
+ req_rack_multithread(Req r) {
288
+ if (NULL == r) {
289
+ rb_raise(rb_eArgError, "Request is no longer valid.");
290
+ }
291
+ if (NULL != r->server && 1 < r->server->thread_cnt) {
292
+ return Qtrue;
293
+ }
294
+ return Qfalse;
295
+ }
296
+
297
+ /* Document-method: rack_multithread
298
+ *
299
+ * call-seq: rack_multithread()
300
+ *
301
+ * Returns true is the server is using multiple handler worker threads.
302
+ */
303
+ static VALUE
304
+ rack_multithread(VALUE self) {
305
+ return req_rack_multithread((Req)DATA_PTR(self));
306
+ }
307
+
308
+ /* Document-method: rack_multiprocess
309
+ *
310
+ * call-seq: rack_multiprocess()
311
+ *
312
+ * Returns false since the server is a single process.
313
+ */
314
+ static VALUE
315
+ rack_multiprocess(VALUE self) {
316
+ return Qfalse;
317
+ }
318
+
319
+ /* Document-method: rack_run_once
320
+ *
321
+ * call-seq: rack_run_once()
322
+ *
323
+ * Returns false.
324
+ */
325
+ static VALUE
326
+ rack_run_once(VALUE self) {
327
+ return Qfalse;
328
+ }
329
+
330
+ static void
331
+ add_header_value(VALUE hh, const char *key, int klen, const char *val, int vlen) {
332
+ if (sizeof(content_type) - 1 == klen && 0 == strncasecmp(key, content_type, sizeof(content_type) - 1)) {
333
+ rb_hash_aset(hh, content_type_val, rb_str_new(val, vlen));
334
+ } else if (sizeof(content_length) - 1 == klen && 0 == strncasecmp(key, content_length, sizeof(content_length) - 1)) {
335
+ rb_hash_aset(hh, content_length_val, rb_str_new(val, vlen));
336
+ } else {
337
+ char hkey[1024];
338
+ char *k = hkey;
339
+
340
+ strcpy(hkey, "HTTP_");
341
+ k = hkey + 5;
342
+ if ((int)(sizeof(hkey) - 5) <= klen) {
343
+ klen = sizeof(hkey) - 6;
344
+ }
345
+ strncpy(k, key, klen);
346
+ hkey[klen + 5] = '\0';
347
+
348
+ rb_hash_aset(hh, rb_str_new(hkey, klen + 5), rb_str_new(val, vlen));
349
+ }
350
+ }
351
+
352
+ static void
353
+ fill_headers(Req r, VALUE hash) {
354
+ char *h = r->header.start;
355
+ char *end = h + r->header.len;
356
+ char *key = h;
357
+ char *kend;
358
+ char *val = NULL;
359
+ char *vend;
360
+
361
+ if (NULL == r) {
362
+ rb_raise(rb_eArgError, "Request is no longer valid.");
363
+ }
364
+ for (; h < end; h++) {
365
+ switch (*h) {
366
+ case ':':
367
+ kend = h;
368
+ val = h + 1;
369
+ break;
370
+ case ' ':
371
+ if (NULL != val) {
372
+ val++;
373
+ } else {
374
+ // TBD handle trailing spaces as well
375
+ key++;
376
+ }
377
+ break;
378
+ case '\r':
379
+ if (NULL != val) {
380
+ vend = h;
381
+ }
382
+ if ('\n' == *(h + 1)) {
383
+ h++;
384
+ }
385
+ add_header_value(hash, key, kend - key, val, vend - val);
386
+ key = h + 1;
387
+ kend = NULL;
388
+ val = NULL;
389
+ vend = NULL;
390
+ break;
391
+ default:
392
+ break;
393
+ }
394
+ }
395
+ }
396
+
397
+ /* Document-method: headers
398
+ *
399
+ * call-seq: headers()
400
+ *
401
+ * Returns the header of the request as a Hash.
402
+ */
403
+ static VALUE
404
+ headers(VALUE self) {
405
+ Req r = DATA_PTR(self);
406
+ volatile VALUE h;
407
+
408
+ if (NULL == r) {
409
+ rb_raise(rb_eArgError, "Request is no longer valid.");
410
+ }
411
+ h = rb_hash_new();
412
+ fill_headers(r, h);
413
+
414
+ return h;
415
+ }
416
+
417
+ /* Document-method: body
418
+ *
419
+ * call-seq: body()
420
+ *
421
+ * Returns the body of the request as a String. If there is no body then _nil_
422
+ * is returned.
423
+ */
424
+ static VALUE
425
+ body(VALUE self) {
426
+ Req r = DATA_PTR(self);
427
+
428
+ if (NULL == r) {
429
+ rb_raise(rb_eArgError, "Request is no longer valid.");
430
+ }
431
+ if (NULL == r->body.start) {
432
+ return Qnil;
433
+ }
434
+ return rb_str_new(r->body.start, r->body.len);
435
+ }
436
+
437
+ /* Document-class: Agoo::Request
438
+ *
439
+ * A Request is passes to handler that respond to the _on_request_ method. The
440
+ * request is a more efficient encapsulation of the rack environment.
441
+ */
442
+ VALUE
443
+ request_env(Req req) {
444
+ volatile VALUE env = rb_hash_new();
445
+
446
+ // As described by
447
+ // http://www.rubydoc.info/github/rack/rack/master/file/SPEC and
448
+ // https://github.com/rack/rack/blob/master/SPEC.
449
+
450
+ rb_hash_aset(env, request_method_val, req_method(req));
451
+ rb_hash_aset(env, script_name_val, req_script_name(req));
452
+ rb_hash_aset(env, path_info_val, req_path_info(req));
453
+ rb_hash_aset(env, query_string_val, req_query_string(req));
454
+ rb_hash_aset(env, server_name_val, req_server_name(req));
455
+ rb_hash_aset(env, server_port_val, req_server_port(req));
456
+ fill_headers(req, env);
457
+ rb_hash_aset(env, rack_version_val, rack_version_val_val);
458
+ rb_hash_aset(env, rack_url_scheme_val, req_rack_url_scheme(req));
459
+ rb_hash_aset(env, rack_input_val, req_rack_input(req));
460
+ rb_hash_aset(env, rack_errors_val, req_rack_errors(req));
461
+ rb_hash_aset(env, rack_multithread_val, req_rack_multithread(req));
462
+ rb_hash_aset(env, rack_multiprocess_val, Qfalse);
463
+ rb_hash_aset(env, rack_run_once_val, Qfalse);
464
+
465
+ return env;
466
+ }
467
+
468
+ /* Document-method: to_h
469
+ *
470
+ * call-seq: to_h()
471
+ *
472
+ * Returns a Hash representation of the request which is the same as a rack
473
+ * environment Hash.
474
+ */
475
+ static VALUE
476
+ to_h(VALUE self) {
477
+ Req r = DATA_PTR(self);
478
+
479
+ if (NULL == r) {
480
+ rb_raise(rb_eArgError, "Request is no longer valid.");
481
+ }
482
+ return request_env(r);
483
+ }
484
+
485
+ /* Document-method: to_s
486
+ *
487
+ * call-seq: to_s()
488
+ *
489
+ * Returns a string representation of the request.
490
+ */
491
+ static VALUE
492
+ to_s(VALUE self) {
493
+ volatile VALUE h = to_h(self);
494
+
495
+ return rb_funcall(h, rb_intern("to_s"), 0);
496
+ }
497
+
498
+ VALUE
499
+ request_wrap(Req req) {
500
+ return Data_Wrap_Struct(req_class, NULL, NULL, req);
501
+ }
502
+
503
+ /* Document-class: Agoo::Request
504
+ *
505
+ * A representation of an HTTP request that is used with a handler that
506
+ * responds to the _on_request_ method. The request is a more efficient
507
+ * encapsulation of the rack environment.
508
+ */
509
+ void
510
+ request_init(VALUE mod) {
511
+ req_class = rb_define_class_under(mod, "Request", rb_cObject);
512
+
513
+ rb_define_method(req_class, "to_s", to_s, 0);
514
+ rb_define_method(req_class, "to_h", to_h, 0);
515
+ rb_define_method(req_class, "environment", to_h, 0);
516
+ rb_define_method(req_class, "env", to_h, 0);
517
+ rb_define_method(req_class, "request_method", method, 0);
518
+ rb_define_method(req_class, "script_name", script_name, 0);
519
+ rb_define_method(req_class, "path_info", path_info, 0);
520
+ rb_define_method(req_class, "query_string", query_string, 0);
521
+ rb_define_method(req_class, "server_name", server_name, 0);
522
+ rb_define_method(req_class, "server_port", server_port, 0);
523
+ rb_define_method(req_class, "rack_version", rack_version, 0);
524
+ rb_define_method(req_class, "rack_url_scheme", rack_url_scheme, 0);
525
+ rb_define_method(req_class, "rack_input", rack_input, 0);
526
+ rb_define_method(req_class, "rack_errors", rack_errors, 0);
527
+ rb_define_method(req_class, "rack_multithread", rack_multithread, 0);
528
+ rb_define_method(req_class, "rack_multiprocess", rack_multiprocess, 0);
529
+ rb_define_method(req_class, "rack_run_once", rack_run_once, 0);
530
+ rb_define_method(req_class, "headers", headers, 0);
531
+ rb_define_method(req_class, "body", body, 0);
532
+
533
+ new_id = rb_intern("new");
534
+
535
+ stringio_class = rb_const_get(rb_cObject, rb_intern("StringIO"));
536
+
537
+ connect_val = rb_str_new_cstr("CONNECT"); rb_gc_register_address(&connect_val);
538
+ content_length_val = rb_str_new_cstr("CONTENT_LENGTH"); rb_gc_register_address(&content_length_val);
539
+ content_type_val = rb_str_new_cstr("CONTENT_TYPE"); rb_gc_register_address(&content_type_val);
540
+ delete_val = rb_str_new_cstr("DELETE"); rb_gc_register_address(&delete_val);
541
+ empty_val = rb_str_new_cstr(""); rb_gc_register_address(&empty_val);
542
+ get_val = rb_str_new_cstr("GET"); rb_gc_register_address(&get_val);
543
+ head_val = rb_str_new_cstr("HEAD"); rb_gc_register_address(&head_val);
544
+ http_val = rb_str_new_cstr("http"); rb_gc_register_address(&http_val);
545
+ options_val = rb_str_new_cstr("OPTIONS"); rb_gc_register_address(&options_val);
546
+ path_info_val = rb_str_new_cstr("PATH_INFO"); rb_gc_register_address(&path_info_val);
547
+ post_val = rb_str_new_cstr("POST"); rb_gc_register_address(&post_val);
548
+ put_val = rb_str_new_cstr("PUT"); rb_gc_register_address(&put_val);
549
+ query_string_val = rb_str_new_cstr("QUERY_STRING"); rb_gc_register_address(&query_string_val);
550
+ rack_errors_val = rb_str_new_cstr("rack.errors"); rb_gc_register_address(&rack_errors_val);
551
+ rack_input_val = rb_str_new_cstr("rack.input"); rb_gc_register_address(&rack_input_val);
552
+ rack_multiprocess_val = rb_str_new_cstr("rack.multiprocess");rb_gc_register_address(&rack_multiprocess_val);
553
+ rack_multithread_val = rb_str_new_cstr("rack.multithread");rb_gc_register_address(&rack_multithread_val);
554
+ rack_run_once_val = rb_str_new_cstr("rack.run_once"); rb_gc_register_address(&rack_run_once_val);
555
+ rack_url_scheme_val = rb_str_new_cstr("rack.url_scheme"); rb_gc_register_address(&rack_url_scheme_val);
556
+ rack_version_val = rb_str_new_cstr("rack.version"); rb_gc_register_address(&rack_version_val);
557
+ rack_version_val_val = rb_str_new_cstr("2.0.3"); rb_gc_register_address(&rack_version_val_val);
558
+ request_method_val = rb_str_new_cstr("REQUEST_METHOD"); rb_gc_register_address(&request_method_val);
559
+ script_name_val = rb_str_new_cstr("SCRIPT_NAME"); rb_gc_register_address(&script_name_val);
560
+ server_name_val = rb_str_new_cstr("SERVER_NAME"); rb_gc_register_address(&server_name_val);
561
+ server_port_val = rb_str_new_cstr("SERVER_PORT"); rb_gc_register_address(&server_port_val);
562
+ slash_val = rb_str_new_cstr("/"); rb_gc_register_address(&slash_val);
563
+ }