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.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +61 -0
- data/ext/agoo/agoo.c +19 -0
- data/ext/agoo/con.c +515 -0
- data/ext/agoo/con.h +46 -0
- data/ext/agoo/dtime.c +52 -0
- data/ext/agoo/dtime.h +10 -0
- data/ext/agoo/err.c +78 -0
- data/ext/agoo/err.h +48 -0
- data/ext/agoo/error_stream.c +92 -0
- data/ext/agoo/error_stream.h +13 -0
- data/ext/agoo/extconf.rb +13 -0
- data/ext/agoo/hook.c +74 -0
- data/ext/agoo/hook.h +33 -0
- data/ext/agoo/http.c +617 -0
- data/ext/agoo/http.h +13 -0
- data/ext/agoo/log.c +497 -0
- data/ext/agoo/log.h +106 -0
- data/ext/agoo/log_queue.h +30 -0
- data/ext/agoo/page.c +342 -0
- data/ext/agoo/page.h +39 -0
- data/ext/agoo/queue.c +191 -0
- data/ext/agoo/queue.h +39 -0
- data/ext/agoo/request.c +563 -0
- data/ext/agoo/request.h +36 -0
- data/ext/agoo/res.c +38 -0
- data/ext/agoo/res.h +28 -0
- data/ext/agoo/response.c +271 -0
- data/ext/agoo/response.h +33 -0
- data/ext/agoo/server.c +891 -0
- data/ext/agoo/server.h +47 -0
- data/ext/agoo/text.c +66 -0
- data/ext/agoo/text.h +24 -0
- data/ext/agoo/types.h +18 -0
- data/lib/agoo.rb +9 -0
- data/lib/agoo/version.rb +5 -0
- data/test/base_handler_test.rb +170 -0
- data/test/log_test.rb +269 -0
- data/test/rack_handler_test.rb +147 -0
- data/test/static_test.rb +81 -0
- data/test/tests.rb +8 -0
- metadata +159 -0
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__ */
|
data/ext/agoo/request.c
ADDED
@@ -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
|
+
}
|