agoo 2.5.4 → 2.5.5
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 +4 -4
- data/CHANGELOG.md +10 -0
- data/bin/agoo +3 -1
- data/ext/agoo/base64.h +3 -3
- data/ext/agoo/bind.c +1 -1
- data/ext/agoo/bind.h +3 -3
- data/ext/agoo/con.c +4 -2
- data/ext/agoo/con.h +3 -3
- data/ext/agoo/debug.c +7 -0
- data/ext/agoo/debug.h +13 -6
- data/ext/agoo/doc.c +159 -0
- data/ext/agoo/doc.h +34 -0
- data/ext/agoo/dtime.h +3 -3
- data/ext/agoo/err.h +3 -3
- data/ext/agoo/error_stream.h +3 -3
- data/ext/agoo/extconf.rb +1 -1
- data/ext/agoo/gqlintro.c +333 -0
- data/ext/agoo/gqlintro.h +10 -0
- data/ext/agoo/gqlvalue.c +1035 -0
- data/ext/agoo/gqlvalue.h +88 -0
- data/ext/agoo/graphql.c +1078 -0
- data/ext/agoo/graphql.h +198 -0
- data/ext/agoo/hook.c +2 -0
- data/ext/agoo/hook.h +4 -3
- data/ext/agoo/http.c +2 -1
- data/ext/agoo/http.h +3 -3
- data/ext/agoo/kinds.h +3 -3
- data/ext/agoo/log.h +3 -3
- data/ext/agoo/log_queue.h +3 -3
- data/ext/agoo/method.h +3 -3
- data/ext/agoo/page.h +3 -3
- data/ext/agoo/pub.h +3 -3
- data/ext/agoo/queue.h +3 -3
- data/ext/agoo/rack_logger.h +3 -3
- data/ext/agoo/req.c +2 -2
- data/ext/agoo/req.h +3 -3
- data/ext/agoo/request.h +3 -3
- data/ext/agoo/res.h +3 -3
- data/ext/agoo/response.h +3 -3
- data/ext/agoo/rhook.c +2 -2
- data/ext/agoo/rhook.h +4 -3
- data/ext/agoo/rlog.h +3 -3
- data/ext/agoo/rresponse.h +3 -3
- data/ext/agoo/rserver.c +64 -0
- data/ext/agoo/rserver.h +3 -3
- data/ext/agoo/rupgraded.c +1 -1
- data/ext/agoo/rupgraded.h +3 -3
- data/ext/agoo/sdl.c +334 -0
- data/ext/agoo/sdl.h +10 -0
- data/ext/agoo/seg.h +3 -3
- data/ext/agoo/server.c +3 -1
- data/ext/agoo/server.h +5 -4
- data/ext/agoo/sha1.h +3 -3
- data/ext/agoo/sse.h +3 -3
- data/ext/agoo/sub.h +3 -3
- data/ext/agoo/subject.h +3 -3
- data/ext/agoo/text.h +3 -3
- data/ext/agoo/upgraded.h +3 -3
- data/ext/agoo/websocket.h +3 -3
- data/lib/agoo/version.rb +1 -1
- data/lib/rack/handler/agoo.rb +3 -1
- metadata +12 -12
- data/ext/agoo/foo/agoo.c +0 -109
- data/ext/agoo/foo/con.c +0 -1220
- data/ext/agoo/foo/con.h +0 -65
- data/ext/agoo/foo/page.c +0 -699
- data/ext/agoo/foo/pub.c +0 -131
- data/ext/agoo/foo/pub.h +0 -40
- data/ext/agoo/foo/rserver.c +0 -1016
- data/ext/agoo/foo/server.c +0 -303
- data/ext/agoo/foo/server.h +0 -67
- data/ext/agoo/foo/upgraded.c +0 -182
data/ext/agoo/foo/pub.c
DELETED
@@ -1,131 +0,0 @@
|
|
1
|
-
// Copyright (c) 2018, Peter Ohler, All rights reserved.
|
2
|
-
|
3
|
-
#include <stdio.h>
|
4
|
-
#include <stdlib.h>
|
5
|
-
#include <string.h>
|
6
|
-
|
7
|
-
#include "debug.h"
|
8
|
-
#include "pub.h"
|
9
|
-
#include "subject.h"
|
10
|
-
#include "text.h"
|
11
|
-
#include "upgraded.h"
|
12
|
-
|
13
|
-
Pub
|
14
|
-
pub_close(Upgraded up) {
|
15
|
-
Pub p = (Pub)malloc(sizeof(struct _Pub));
|
16
|
-
|
17
|
-
if (NULL != p) {
|
18
|
-
DEBUG_ALLOC(mem_pub, p);
|
19
|
-
p->next = NULL;
|
20
|
-
p->kind = PUB_CLOSE;
|
21
|
-
p->up = up;
|
22
|
-
p->subject = NULL;
|
23
|
-
p->msg = NULL;
|
24
|
-
}
|
25
|
-
return p;
|
26
|
-
}
|
27
|
-
|
28
|
-
Pub
|
29
|
-
pub_subscribe(Upgraded up, const char *subject, int slen) {
|
30
|
-
Pub p = (Pub)malloc(sizeof(struct _Pub));
|
31
|
-
|
32
|
-
if (NULL != p) {
|
33
|
-
DEBUG_ALLOC(mem_pub, p);
|
34
|
-
p->next = NULL;
|
35
|
-
p->kind = PUB_SUB;
|
36
|
-
p->up = up;
|
37
|
-
p->subject = subject_create(subject, slen);
|
38
|
-
p->msg = NULL;
|
39
|
-
}
|
40
|
-
return p;
|
41
|
-
}
|
42
|
-
|
43
|
-
Pub
|
44
|
-
pub_unsubscribe(Upgraded up, const char *subject, int slen) {
|
45
|
-
Pub p = (Pub)malloc(sizeof(struct _Pub));
|
46
|
-
|
47
|
-
if (NULL != p) {
|
48
|
-
DEBUG_ALLOC(mem_pub, p);
|
49
|
-
p->next = NULL;
|
50
|
-
p->kind = PUB_UN;
|
51
|
-
p->up = up;
|
52
|
-
if (NULL != subject) {
|
53
|
-
p->subject = subject_create(subject, slen);
|
54
|
-
} else {
|
55
|
-
p->subject = NULL;
|
56
|
-
}
|
57
|
-
p->msg = NULL;
|
58
|
-
}
|
59
|
-
return p;
|
60
|
-
}
|
61
|
-
|
62
|
-
Pub
|
63
|
-
pub_publish(const char *subject, int slen, const char *message, size_t mlen) {
|
64
|
-
Pub p = (Pub)malloc(sizeof(struct _Pub));
|
65
|
-
|
66
|
-
if (NULL != p) {
|
67
|
-
DEBUG_ALLOC(mem_pub, p);
|
68
|
-
p->next = NULL;
|
69
|
-
p->kind = PUB_MSG;
|
70
|
-
p->up = NULL;
|
71
|
-
p->subject = subject_create(subject, slen);
|
72
|
-
// Allocate an extra 24 bytes so the message can be expanded in place
|
73
|
-
// if a WebSocket or SSE write.
|
74
|
-
p->msg = text_append(text_allocate((int)mlen + 24), message, (int)mlen);
|
75
|
-
text_ref(p->msg);
|
76
|
-
}
|
77
|
-
return p;
|
78
|
-
}
|
79
|
-
|
80
|
-
Pub
|
81
|
-
pub_write(Upgraded up, const char *message, size_t mlen, bool bin) {
|
82
|
-
// Allocate an extra 16 bytes so the message can be expanded in place if a
|
83
|
-
// WebSocket write.
|
84
|
-
Pub p = (Pub)malloc(sizeof(struct _Pub));
|
85
|
-
|
86
|
-
if (NULL != p) {
|
87
|
-
DEBUG_ALLOC(mem_pub, p);
|
88
|
-
p->next = NULL;
|
89
|
-
p->kind = PUB_WRITE;
|
90
|
-
p->up = up;
|
91
|
-
p->subject = NULL;
|
92
|
-
// Allocate an extra 16 bytes so the message can be expanded in place
|
93
|
-
// if a WebSocket write.
|
94
|
-
p->msg = text_append(text_allocate((int)mlen + 16), message, (int)mlen);
|
95
|
-
p->msg->bin = bin;
|
96
|
-
text_ref(p->msg);
|
97
|
-
}
|
98
|
-
return p;
|
99
|
-
}
|
100
|
-
|
101
|
-
Pub
|
102
|
-
pub_dup(Pub src) {
|
103
|
-
Pub p = (Pub)malloc(sizeof(struct _Pub));
|
104
|
-
|
105
|
-
if (NULL != p) {
|
106
|
-
DEBUG_ALLOC(mem_pub, p);
|
107
|
-
p->next = NULL;
|
108
|
-
p->kind = src->kind;
|
109
|
-
p->up = src->up;
|
110
|
-
p->subject = subject_create(src->subject->pattern, strlen(src->subject->pattern));
|
111
|
-
p->msg = src->msg;
|
112
|
-
text_ref(p->msg);
|
113
|
-
}
|
114
|
-
return p;
|
115
|
-
}
|
116
|
-
|
117
|
-
void
|
118
|
-
pub_destroy(Pub pub) {
|
119
|
-
if (NULL != pub->msg) {
|
120
|
-
text_release(pub->msg);
|
121
|
-
}
|
122
|
-
if (NULL != pub->subject) {
|
123
|
-
subject_destroy(pub->subject);
|
124
|
-
}
|
125
|
-
if (NULL != pub->up) {
|
126
|
-
upgraded_release(pub->up);
|
127
|
-
}
|
128
|
-
DEBUG_FREE(mem_pub, pub);
|
129
|
-
free(pub);
|
130
|
-
}
|
131
|
-
|
data/ext/agoo/foo/pub.h
DELETED
@@ -1,40 +0,0 @@
|
|
1
|
-
// Copyright (c) 2018, Peter Ohler, All rights reserved.
|
2
|
-
|
3
|
-
#ifndef __AGOO_PUB_H__
|
4
|
-
#define __AGOO_PUB_H__
|
5
|
-
|
6
|
-
#include <stdbool.h>
|
7
|
-
#include <stdint.h>
|
8
|
-
#include <stdlib.h>
|
9
|
-
|
10
|
-
struct _Text;
|
11
|
-
struct _Upgraded;
|
12
|
-
struct _Subject;
|
13
|
-
|
14
|
-
typedef enum {
|
15
|
-
PUB_SUB = 'S',
|
16
|
-
PUB_CLOSE = 'C',
|
17
|
-
PUB_UN = 'U',
|
18
|
-
PUB_MSG = 'M',
|
19
|
-
PUB_WRITE = 'W',
|
20
|
-
} PubKind;
|
21
|
-
|
22
|
-
// Generated by extened handlers and placed on the pub_queue to be pulled off
|
23
|
-
// in the con_loop.
|
24
|
-
typedef struct _Pub {
|
25
|
-
struct _Pub *next;
|
26
|
-
PubKind kind;
|
27
|
-
struct _Upgraded *up;
|
28
|
-
struct _Subject *subject;
|
29
|
-
struct _Text *msg;
|
30
|
-
} *Pub;
|
31
|
-
|
32
|
-
extern Pub pub_close(struct _Upgraded *up);
|
33
|
-
extern Pub pub_subscribe(struct _Upgraded *up, const char *subject, int slen);
|
34
|
-
extern Pub pub_unsubscribe(struct _Upgraded *up, const char *subject, int slen);
|
35
|
-
extern Pub pub_publish(const char *subject, int slen, const char *message, size_t mlen);
|
36
|
-
extern Pub pub_write(struct _Upgraded *up, const char *message, size_t mlen, bool bin);
|
37
|
-
extern Pub pub_dup(Pub src);
|
38
|
-
extern void pub_destroy(Pub pub);
|
39
|
-
|
40
|
-
#endif // __AGOO_PUB_H__
|
data/ext/agoo/foo/rserver.c
DELETED
@@ -1,1016 +0,0 @@
|
|
1
|
-
// Copyright (c) 2018, Peter Ohler, All rights reserved.
|
2
|
-
|
3
|
-
#include <netdb.h>
|
4
|
-
#include <netinet/tcp.h>
|
5
|
-
#include <signal.h>
|
6
|
-
#include <stdio.h>
|
7
|
-
#include <sys/wait.h>
|
8
|
-
|
9
|
-
#include <ruby.h>
|
10
|
-
#include <ruby/thread.h>
|
11
|
-
#include <ruby/encoding.h>
|
12
|
-
|
13
|
-
#include "bind.h"
|
14
|
-
#include "con.h"
|
15
|
-
#include "debug.h"
|
16
|
-
#include "dtime.h"
|
17
|
-
#include "err.h"
|
18
|
-
#include "graphql.h"
|
19
|
-
#include "http.h"
|
20
|
-
#include "log.h"
|
21
|
-
#include "page.h"
|
22
|
-
#include "pub.h"
|
23
|
-
#include "request.h"
|
24
|
-
#include "res.h"
|
25
|
-
#include "response.h"
|
26
|
-
#include "rhook.h"
|
27
|
-
#include "rresponse.h"
|
28
|
-
#include "rserver.h"
|
29
|
-
#include "rupgraded.h"
|
30
|
-
#include "server.h"
|
31
|
-
#include "sse.h"
|
32
|
-
#include "sub.h"
|
33
|
-
#include "upgraded.h"
|
34
|
-
#include "websocket.h"
|
35
|
-
|
36
|
-
extern void agoo_shutdown();
|
37
|
-
|
38
|
-
static VALUE server_mod = Qundef;
|
39
|
-
|
40
|
-
static VALUE connect_sym;
|
41
|
-
static VALUE delete_sym;
|
42
|
-
static VALUE get_sym;
|
43
|
-
static VALUE head_sym;
|
44
|
-
static VALUE options_sym;
|
45
|
-
static VALUE post_sym;
|
46
|
-
static VALUE push_env_key;
|
47
|
-
static VALUE put_sym;
|
48
|
-
|
49
|
-
static VALUE rserver;
|
50
|
-
|
51
|
-
static ID call_id;
|
52
|
-
static ID each_id;
|
53
|
-
static ID on_close_id;
|
54
|
-
static ID on_drained_id;
|
55
|
-
static ID on_error_id;
|
56
|
-
static ID on_message_id;
|
57
|
-
static ID on_request_id;
|
58
|
-
static ID to_i_id;
|
59
|
-
|
60
|
-
static const char err500[] = "HTTP/1.1 500 Internal Server Error\r\n";
|
61
|
-
|
62
|
-
struct _RServer the_rserver = {};
|
63
|
-
|
64
|
-
static void
|
65
|
-
server_mark(void *ptr) {
|
66
|
-
Upgraded up;
|
67
|
-
|
68
|
-
rb_gc_mark(rserver);
|
69
|
-
pthread_mutex_lock(&the_server.up_lock);
|
70
|
-
for (up = the_server.up_list; NULL != up; up = up->next) {
|
71
|
-
if (Qnil != (VALUE)up->ctx) {
|
72
|
-
rb_gc_mark((VALUE)up->ctx);
|
73
|
-
}
|
74
|
-
if (Qnil != (VALUE)up->env) {
|
75
|
-
rb_gc_mark((VALUE)up->env);
|
76
|
-
}
|
77
|
-
if (Qnil != (VALUE)up->wrap) {
|
78
|
-
rb_gc_mark((VALUE)up->wrap);
|
79
|
-
}
|
80
|
-
}
|
81
|
-
pthread_mutex_unlock(&the_server.up_lock);
|
82
|
-
}
|
83
|
-
|
84
|
-
static void
|
85
|
-
url_bind(VALUE rurl) {
|
86
|
-
struct _Err err = ERR_INIT;
|
87
|
-
Bind b = bind_url(&err, StringValuePtr(rurl));
|
88
|
-
|
89
|
-
if (ERR_OK != err.code) {
|
90
|
-
rb_raise(rb_eArgError, "%s", err.msg);
|
91
|
-
}
|
92
|
-
server_bind(b);
|
93
|
-
}
|
94
|
-
|
95
|
-
static int
|
96
|
-
configure(Err err, int port, const char *root, VALUE options) {
|
97
|
-
pages_set_root(root);
|
98
|
-
the_server.thread_cnt = 0;
|
99
|
-
the_rserver.worker_cnt = 1;
|
100
|
-
the_server.running = 0;
|
101
|
-
the_server.listen_thread = 0;
|
102
|
-
the_server.con_loops = NULL;
|
103
|
-
the_server.root_first = false;
|
104
|
-
the_server.binds = NULL;
|
105
|
-
|
106
|
-
if (Qnil != options) {
|
107
|
-
VALUE v;
|
108
|
-
|
109
|
-
if (Qnil != (v = rb_hash_lookup(options, ID2SYM(rb_intern("thread_count"))))) {
|
110
|
-
int tc = FIX2INT(v);
|
111
|
-
|
112
|
-
if (1 <= tc || tc < 1000) {
|
113
|
-
the_server.thread_cnt = tc;
|
114
|
-
} else {
|
115
|
-
rb_raise(rb_eArgError, "thread_count must be between 1 and 1000.");
|
116
|
-
}
|
117
|
-
}
|
118
|
-
if (Qnil != (v = rb_hash_lookup(options, ID2SYM(rb_intern("worker_count"))))) {
|
119
|
-
int wc = FIX2INT(v);
|
120
|
-
|
121
|
-
if (1 <= wc || wc < MAX_WORKERS) {
|
122
|
-
the_rserver.worker_cnt = wc;
|
123
|
-
} else {
|
124
|
-
rb_raise(rb_eArgError, "thread_count must be between 1 and %d.", MAX_WORKERS);
|
125
|
-
}
|
126
|
-
}
|
127
|
-
if (Qnil != (v = rb_hash_lookup(options, ID2SYM(rb_intern("max_push_pending"))))) {
|
128
|
-
int mpp = FIX2INT(v);
|
129
|
-
|
130
|
-
if (0 <= mpp || mpp < 1000) {
|
131
|
-
the_server.max_push_pending = mpp;
|
132
|
-
} else {
|
133
|
-
rb_raise(rb_eArgError, "max_push_pending must be between 0 and 1000.");
|
134
|
-
}
|
135
|
-
}
|
136
|
-
if (Qnil != (v = rb_hash_lookup(options, ID2SYM(rb_intern("pedantic"))))) {
|
137
|
-
the_server.pedantic = (Qtrue == v);
|
138
|
-
}
|
139
|
-
if (Qnil != (v = rb_hash_lookup(options, ID2SYM(rb_intern("root_first"))))) {
|
140
|
-
the_server.root_first = (Qtrue == v);
|
141
|
-
}
|
142
|
-
if (Qnil != (v = rb_hash_lookup(options, ID2SYM(rb_intern("Port"))))) {
|
143
|
-
if (rb_cInteger == rb_obj_class(v)) {
|
144
|
-
port = NUM2INT(v);
|
145
|
-
} else {
|
146
|
-
switch (rb_type(v)) {
|
147
|
-
case T_STRING:
|
148
|
-
port = atoi(StringValuePtr(v));
|
149
|
-
break;
|
150
|
-
case T_FIXNUM:
|
151
|
-
port = NUM2INT(v);
|
152
|
-
break;
|
153
|
-
default:
|
154
|
-
break;
|
155
|
-
}
|
156
|
-
}
|
157
|
-
}
|
158
|
-
if (Qnil != (v = rb_hash_lookup(options, ID2SYM(rb_intern("bind"))))) {
|
159
|
-
int len;
|
160
|
-
int i;
|
161
|
-
|
162
|
-
switch (rb_type(v)) {
|
163
|
-
case T_STRING:
|
164
|
-
url_bind(v);
|
165
|
-
break;
|
166
|
-
case T_ARRAY:
|
167
|
-
len = (int)RARRAY_LEN(v);
|
168
|
-
for (i = 0; i < len; i++) {
|
169
|
-
url_bind(rb_ary_entry(v, i));
|
170
|
-
}
|
171
|
-
break;
|
172
|
-
default:
|
173
|
-
rb_raise(rb_eArgError, "bind option must be a String or Array of Strings.");
|
174
|
-
break;
|
175
|
-
}
|
176
|
-
}
|
177
|
-
if (Qnil != (v = rb_hash_lookup(options, ID2SYM(rb_intern("graphql"))))) {
|
178
|
-
const char *path;
|
179
|
-
Hook hook;
|
180
|
-
char schema_path[256];
|
181
|
-
long plen;
|
182
|
-
|
183
|
-
rb_check_type(v, T_STRING);
|
184
|
-
if (ERR_OK != gql_init(err)) {
|
185
|
-
return err->code;
|
186
|
-
}
|
187
|
-
path = StringValuePtr(v);
|
188
|
-
plen = (long)RSTRING_LEN(v);
|
189
|
-
if ((int)sizeof(schema_path) - 8 < plen) {
|
190
|
-
rb_raise(rb_eArgError, "A graphql schema path is limited to %d characters.", (int)(sizeof(schema_path) - 8));
|
191
|
-
}
|
192
|
-
memcpy(schema_path, path, plen);
|
193
|
-
memcpy(schema_path + plen, "/schema", 8);
|
194
|
-
|
195
|
-
hook = hook_func_create(GET, schema_path, gql_dump_hook, &the_server.eval_queue);
|
196
|
-
hook->next = the_server.hooks;
|
197
|
-
the_server.hooks = hook;
|
198
|
-
|
199
|
-
hook = hook_func_create(GET, path, gql_eval_hook, &the_server.eval_queue);
|
200
|
-
hook->next = the_server.hooks;
|
201
|
-
the_server.hooks = hook;
|
202
|
-
}
|
203
|
-
if (Qnil != (v = rb_hash_lookup(options, ID2SYM(rb_intern("quiet"))))) {
|
204
|
-
if (Qtrue == v) {
|
205
|
-
info_cat.on = false;
|
206
|
-
}
|
207
|
-
}
|
208
|
-
if (Qnil != (v = rb_hash_lookup(options, ID2SYM(rb_intern("debug"))))) {
|
209
|
-
if (Qtrue == v) {
|
210
|
-
error_cat.on = true;
|
211
|
-
warn_cat.on = true;
|
212
|
-
info_cat.on = true;
|
213
|
-
debug_cat.on = true;
|
214
|
-
con_cat.on = true;
|
215
|
-
req_cat.on = true;
|
216
|
-
resp_cat.on = true;
|
217
|
-
eval_cat.on = true;
|
218
|
-
push_cat.on = true;
|
219
|
-
}
|
220
|
-
}
|
221
|
-
}
|
222
|
-
if (0 < port) {
|
223
|
-
Bind b = bind_port(err, port);
|
224
|
-
|
225
|
-
if (ERR_OK != err->code) {
|
226
|
-
rb_raise(rb_eArgError, "%s", err->msg);
|
227
|
-
}
|
228
|
-
server_bind(b);
|
229
|
-
}
|
230
|
-
return ERR_OK;
|
231
|
-
}
|
232
|
-
|
233
|
-
/* Document-method: init
|
234
|
-
*
|
235
|
-
* call-seq: init(port, root, options)
|
236
|
-
*
|
237
|
-
* Configures the server that will listen on the designated _port_ and using
|
238
|
-
* the _root_ as the root of the static resources. Logging is feature based
|
239
|
-
* and not level based and the options reflect that approach. If bind option
|
240
|
-
* is to be used instead of the port then set the port to zero.
|
241
|
-
*
|
242
|
-
* - *options* [_Hash_] server options
|
243
|
-
*
|
244
|
-
* - *:pedantic* [_true_|_false_] if true response header and status codes are checked and an exception raised if they violate the rack spec at https://github.com/rack/rack/blob/master/SPEC, https://tools.ietf.org/html/rfc3875#section-4.1.18, or https://tools.ietf.org/html/rfc7230.
|
245
|
-
*
|
246
|
-
* - *:thread_count* [_Integer_] number of ruby worker threads. Defaults to one. If zero then the _start_ function will not return but instead will proess using the thread that called _start_. Usually the default is best unless the workers are making IO calls.
|
247
|
-
*
|
248
|
-
* - *:worker_count* [_Integer_] number of workers to fork. Defaults to one which is not to fork.
|
249
|
-
*
|
250
|
-
* - *:bind* [_String_|_Array_] a binding or array of binds. Examples are: "http://127.0.0.1:6464", "unix:///tmp/agoo.socket", "http://[::1]:6464, or to not restrict the address "http://:6464".
|
251
|
-
*/
|
252
|
-
static VALUE
|
253
|
-
rserver_init(int argc, VALUE *argv, VALUE self) {
|
254
|
-
struct _Err err = ERR_INIT;
|
255
|
-
int port;
|
256
|
-
const char *root;
|
257
|
-
VALUE options = Qnil;
|
258
|
-
|
259
|
-
if (argc < 2 || 3 < argc) {
|
260
|
-
rb_raise(rb_eArgError, "Wrong number of arguments to Agoo::Server.configure.");
|
261
|
-
}
|
262
|
-
port = FIX2INT(argv[0]);
|
263
|
-
rb_check_type(argv[1], T_STRING);
|
264
|
-
root = StringValuePtr(argv[1]);
|
265
|
-
if (3 <= argc) {
|
266
|
-
options = argv[2];
|
267
|
-
}
|
268
|
-
server_setup();
|
269
|
-
the_server.ctx_nil_value = (void*)Qnil;
|
270
|
-
the_server.env_nil_value = (void*)Qnil;
|
271
|
-
|
272
|
-
if (ERR_OK != configure(&err, port, root, options)) {
|
273
|
-
rb_raise(rb_eArgError, "%s", err.msg);
|
274
|
-
}
|
275
|
-
the_server.inited = true;
|
276
|
-
|
277
|
-
return Qnil;
|
278
|
-
}
|
279
|
-
|
280
|
-
static const char bad500[] = "HTTP/1.1 500 Internal Error\r\nConnection: Close\r\nContent-Length: ";
|
281
|
-
|
282
|
-
static VALUE
|
283
|
-
rescue_error(VALUE x) {
|
284
|
-
Req req = (Req)x;
|
285
|
-
volatile VALUE info = rb_errinfo();
|
286
|
-
volatile VALUE msg = rb_funcall(info, rb_intern("message"), 0);
|
287
|
-
const char *classname = rb_obj_classname(info);
|
288
|
-
const char *ms = rb_string_value_ptr(&msg);
|
289
|
-
|
290
|
-
if (NULL == req->up) {
|
291
|
-
char buf[1024];
|
292
|
-
int len = (int)(strlen(classname) + 2 + strlen(ms));
|
293
|
-
int cnt;
|
294
|
-
Text message;
|
295
|
-
|
296
|
-
if ((int)(sizeof(buf) - sizeof(bad500) + 7) <= len) {
|
297
|
-
len = sizeof(buf) - sizeof(bad500) + 7;
|
298
|
-
}
|
299
|
-
cnt = snprintf(buf, sizeof(buf), "%s%d\r\n\r\n%s: %s", bad500, len, classname, ms);
|
300
|
-
message = text_create(buf, cnt);
|
301
|
-
|
302
|
-
req->res->close = true;
|
303
|
-
res_set_message(req->res, message);
|
304
|
-
queue_wakeup(&the_server.con_queue);
|
305
|
-
} else {
|
306
|
-
/*
|
307
|
-
volatile VALUE bt = rb_funcall(info, rb_intern("backtrace"), 0);
|
308
|
-
int blen = RARRAY_LEN(bt);
|
309
|
-
int i;
|
310
|
-
VALUE rline;
|
311
|
-
|
312
|
-
for (i = 0; i < blen; i++) {
|
313
|
-
rline = rb_ary_entry(bt, i);
|
314
|
-
}
|
315
|
-
*/
|
316
|
-
log_cat(&error_cat, "%s: %s", classname, ms);
|
317
|
-
}
|
318
|
-
return Qfalse;
|
319
|
-
}
|
320
|
-
|
321
|
-
static VALUE
|
322
|
-
handle_base_inner(void *x) {
|
323
|
-
Req req = (Req)x;
|
324
|
-
volatile VALUE rr = request_wrap(req);
|
325
|
-
volatile VALUE rres = response_new();
|
326
|
-
|
327
|
-
if (NULL != req->hook) {
|
328
|
-
rb_funcall((VALUE)req->hook->handler, on_request_id, 2, rr, rres);
|
329
|
-
}
|
330
|
-
DATA_PTR(rr) = NULL;
|
331
|
-
res_set_message(req->res, response_text(rres));
|
332
|
-
queue_wakeup(&the_server.con_queue);
|
333
|
-
|
334
|
-
return Qfalse;
|
335
|
-
}
|
336
|
-
|
337
|
-
static void*
|
338
|
-
handle_base(void *x) {
|
339
|
-
rb_rescue2(handle_base_inner, (VALUE)x, rescue_error, (VALUE)x, rb_eException, 0);
|
340
|
-
|
341
|
-
return NULL;
|
342
|
-
}
|
343
|
-
|
344
|
-
static int
|
345
|
-
header_cb(VALUE key, VALUE value, Text *tp) {
|
346
|
-
const char *ks = StringValuePtr(key);
|
347
|
-
int klen = (int)RSTRING_LEN(key);
|
348
|
-
const char *vs = StringValuePtr(value);
|
349
|
-
int vlen = (int)RSTRING_LEN(value);
|
350
|
-
struct _Err err = ERR_INIT;
|
351
|
-
|
352
|
-
if (the_server.pedantic) {
|
353
|
-
if (ERR_OK != http_header_ok(&err, ks, klen, vs, vlen)) {
|
354
|
-
rb_raise(rb_eArgError, "%s", err.msg);
|
355
|
-
}
|
356
|
-
}
|
357
|
-
if (0 != strcasecmp("Content-Length", ks)) {
|
358
|
-
*tp = text_append(*tp, ks, klen);
|
359
|
-
*tp = text_append(*tp, ": ", 2);
|
360
|
-
*tp = text_append(*tp, vs, vlen);
|
361
|
-
*tp = text_append(*tp, "\r\n", 2);
|
362
|
-
}
|
363
|
-
return ST_CONTINUE;
|
364
|
-
}
|
365
|
-
|
366
|
-
static VALUE
|
367
|
-
header_each_cb(VALUE kv, Text *tp) {
|
368
|
-
header_cb(rb_ary_entry(kv, 0), rb_ary_entry(kv, 1), tp);
|
369
|
-
|
370
|
-
return Qnil;
|
371
|
-
}
|
372
|
-
|
373
|
-
static VALUE
|
374
|
-
body_len_cb(VALUE v, int *sizep) {
|
375
|
-
*sizep += (int)RSTRING_LEN(v);
|
376
|
-
|
377
|
-
return Qnil;
|
378
|
-
}
|
379
|
-
|
380
|
-
static VALUE
|
381
|
-
body_append_cb(VALUE v, Text *tp) {
|
382
|
-
*tp = text_append(*tp, StringValuePtr(v), (int)RSTRING_LEN(v));
|
383
|
-
|
384
|
-
return Qnil;
|
385
|
-
}
|
386
|
-
|
387
|
-
static VALUE
|
388
|
-
handle_rack_inner(void *x) {
|
389
|
-
Req req = (Req)x;
|
390
|
-
Text t;
|
391
|
-
volatile VALUE env = request_env(req, request_wrap(req));
|
392
|
-
volatile VALUE res = Qnil;
|
393
|
-
volatile VALUE hv;
|
394
|
-
volatile VALUE bv;
|
395
|
-
int code;
|
396
|
-
const char *status_msg;
|
397
|
-
int bsize = 0;
|
398
|
-
|
399
|
-
if (NULL == req->hook) {
|
400
|
-
return Qfalse;
|
401
|
-
}
|
402
|
-
res = rb_funcall((VALUE)req->hook->handler, call_id, 1, env);
|
403
|
-
if (req->res->con->hijacked) {
|
404
|
-
queue_wakeup(&the_server.con_queue);
|
405
|
-
return Qfalse;
|
406
|
-
}
|
407
|
-
rb_check_type(res, T_ARRAY);
|
408
|
-
if (3 != RARRAY_LEN(res)) {
|
409
|
-
rb_raise(rb_eArgError, "a rack call() response must be an array of 3 members.");
|
410
|
-
}
|
411
|
-
hv = rb_ary_entry(res, 0);
|
412
|
-
if (RUBY_T_FIXNUM == rb_type(hv)) {
|
413
|
-
code = NUM2INT(hv);
|
414
|
-
} else {
|
415
|
-
code = NUM2INT(rb_funcall(hv, to_i_id, 0));
|
416
|
-
}
|
417
|
-
status_msg = http_code_message(code);
|
418
|
-
if ('\0' == *status_msg) {
|
419
|
-
rb_raise(rb_eArgError, "invalid rack call() response status code (%d).", code);
|
420
|
-
}
|
421
|
-
hv = rb_ary_entry(res, 1);
|
422
|
-
if (!rb_respond_to(hv, each_id)) {
|
423
|
-
rb_raise(rb_eArgError, "invalid rack call() response headers does not respond to each.");
|
424
|
-
}
|
425
|
-
bv = rb_ary_entry(res, 2);
|
426
|
-
if (!rb_respond_to(bv, each_id)) {
|
427
|
-
rb_raise(rb_eArgError, "invalid rack call() response body does not respond to each.");
|
428
|
-
}
|
429
|
-
if (NULL == (t = text_allocate(1024))) {
|
430
|
-
rb_raise(rb_eArgError, "failed to allocate response.");
|
431
|
-
}
|
432
|
-
if (T_ARRAY == rb_type(bv)) {
|
433
|
-
int i;
|
434
|
-
int bcnt = (int)RARRAY_LEN(bv);
|
435
|
-
|
436
|
-
for (i = 0; i < bcnt; i++) {
|
437
|
-
bsize += (int)RSTRING_LEN(rb_ary_entry(bv, i));
|
438
|
-
}
|
439
|
-
} else {
|
440
|
-
if (HEAD == req->method) {
|
441
|
-
// Rack wraps the response in two layers, Rack::Lint and
|
442
|
-
// Rack::BodyProxy. It each is called on either with the HEAD
|
443
|
-
// method an exception is raised so the length can not be
|
444
|
-
// determined. This digs down to get the actual response so the
|
445
|
-
// length can be calculated. A very special case.
|
446
|
-
if (0 == strcmp("Rack::BodyProxy", rb_obj_classname(bv))) {
|
447
|
-
volatile VALUE body = rb_ivar_get(bv, rb_intern("@body"));
|
448
|
-
|
449
|
-
if (Qnil != body) {
|
450
|
-
body = rb_ivar_get(body, rb_intern("@body"));
|
451
|
-
}
|
452
|
-
if (Qnil != body) {
|
453
|
-
body = rb_ivar_get(body, rb_intern("@body"));
|
454
|
-
}
|
455
|
-
if (rb_respond_to(body, rb_intern("each"))) {
|
456
|
-
rb_iterate(rb_each, body, body_len_cb, (VALUE)&bsize);
|
457
|
-
}
|
458
|
-
} else {
|
459
|
-
rb_iterate(rb_each, bv, body_len_cb, (VALUE)&bsize);
|
460
|
-
}
|
461
|
-
} else {
|
462
|
-
rb_iterate(rb_each, bv, body_len_cb, (VALUE)&bsize);
|
463
|
-
}
|
464
|
-
}
|
465
|
-
switch (code) {
|
466
|
-
case 100:
|
467
|
-
case 101:
|
468
|
-
case 102:
|
469
|
-
case 204:
|
470
|
-
case 205:
|
471
|
-
case 304:
|
472
|
-
// Content-Type and Content-Length can not be present
|
473
|
-
t->len = snprintf(t->text, 1024, "HTTP/1.1 %d %s\r\n", code, status_msg);
|
474
|
-
break;
|
475
|
-
default:
|
476
|
-
// Note that using simply sprintf causes an abort with travis OSX tests.
|
477
|
-
t->len = snprintf(t->text, 1024, "HTTP/1.1 %d %s\r\nContent-Length: %d\r\n", code, status_msg, bsize);
|
478
|
-
break;
|
479
|
-
}
|
480
|
-
if (code < 300) {
|
481
|
-
VALUE handler = Qnil;
|
482
|
-
|
483
|
-
switch (req->upgrade) {
|
484
|
-
case UP_WS:
|
485
|
-
if (CON_WS != req->res->con_kind ||
|
486
|
-
Qnil == (handler = rb_hash_lookup(env, push_env_key))) {
|
487
|
-
strcpy(t->text, err500);
|
488
|
-
t->len = sizeof(err500) - 1;
|
489
|
-
break;
|
490
|
-
}
|
491
|
-
req->hook = hook_create(NONE, NULL, (void*)handler, PUSH_HOOK, &the_server.eval_queue);
|
492
|
-
rupgraded_create(req->res->con, handler, request_env(req, Qnil));
|
493
|
-
|
494
|
-
t->len = snprintf(t->text, 1024, "HTTP/1.1 101 %s\r\n", status_msg);
|
495
|
-
t = ws_add_headers(req, t);
|
496
|
-
break;
|
497
|
-
case UP_SSE:
|
498
|
-
if (CON_SSE != req->res->con_kind ||
|
499
|
-
Qnil == (handler = rb_hash_lookup(env, push_env_key))) {
|
500
|
-
strcpy(t->text, err500);
|
501
|
-
t->len = sizeof(err500) - 1;
|
502
|
-
break;
|
503
|
-
}
|
504
|
-
req->hook = hook_create(NONE, NULL, (void*)handler, PUSH_HOOK, &the_server.eval_queue);
|
505
|
-
rupgraded_create(req->res->con, handler, request_env(req, Qnil));
|
506
|
-
t = sse_upgrade(req, t);
|
507
|
-
res_set_message(req->res, t);
|
508
|
-
queue_wakeup(&the_server.con_queue);
|
509
|
-
return Qfalse;
|
510
|
-
default:
|
511
|
-
break;
|
512
|
-
}
|
513
|
-
}
|
514
|
-
if (HEAD == req->method) {
|
515
|
-
bsize = 0;
|
516
|
-
} else {
|
517
|
-
if (T_HASH == rb_type(hv)) {
|
518
|
-
rb_hash_foreach(hv, header_cb, (VALUE)&t);
|
519
|
-
} else {
|
520
|
-
rb_iterate(rb_each, hv, header_each_cb, (VALUE)&t);
|
521
|
-
}
|
522
|
-
}
|
523
|
-
t = text_append(t, "\r\n", 2);
|
524
|
-
if (0 < bsize) {
|
525
|
-
if (T_ARRAY == rb_type(bv)) {
|
526
|
-
VALUE v;
|
527
|
-
int i;
|
528
|
-
int bcnt = (int)RARRAY_LEN(bv);
|
529
|
-
|
530
|
-
for (i = 0; i < bcnt; i++) {
|
531
|
-
v = rb_ary_entry(bv, i);
|
532
|
-
t = text_append(t, StringValuePtr(v), (int)RSTRING_LEN(v));
|
533
|
-
}
|
534
|
-
} else {
|
535
|
-
rb_iterate(rb_each, bv, body_append_cb, (VALUE)&t);
|
536
|
-
}
|
537
|
-
}
|
538
|
-
res_set_message(req->res, t);
|
539
|
-
queue_wakeup(&the_server.con_queue);
|
540
|
-
|
541
|
-
return Qfalse;
|
542
|
-
}
|
543
|
-
|
544
|
-
static void*
|
545
|
-
handle_rack(void *x) {
|
546
|
-
//rb_gc_disable();
|
547
|
-
rb_rescue2(handle_rack_inner, (VALUE)x, rescue_error, (VALUE)x, rb_eException, 0);
|
548
|
-
//rb_gc_enable();
|
549
|
-
//rb_gc();
|
550
|
-
|
551
|
-
return NULL;
|
552
|
-
}
|
553
|
-
|
554
|
-
static VALUE
|
555
|
-
handle_wab_inner(void *x) {
|
556
|
-
Req req = (Req)x;
|
557
|
-
volatile VALUE rr = request_wrap(req);
|
558
|
-
volatile VALUE rres = response_new();
|
559
|
-
|
560
|
-
if (NULL != req->hook) {
|
561
|
-
rb_funcall((VALUE)req->hook->handler, on_request_id, 2, rr, rres);
|
562
|
-
}
|
563
|
-
DATA_PTR(rr) = NULL;
|
564
|
-
res_set_message(req->res, response_text(rres));
|
565
|
-
queue_wakeup(&the_server.con_queue);
|
566
|
-
|
567
|
-
return Qfalse;
|
568
|
-
}
|
569
|
-
|
570
|
-
static void*
|
571
|
-
handle_wab(void *x) {
|
572
|
-
rb_rescue2(handle_wab_inner, (VALUE)x, rescue_error, (VALUE)x, rb_eException, 0);
|
573
|
-
|
574
|
-
return NULL;
|
575
|
-
}
|
576
|
-
|
577
|
-
static VALUE
|
578
|
-
handle_push_inner(void *x) {
|
579
|
-
Req req = (Req)x;
|
580
|
-
|
581
|
-
switch (req->method) {
|
582
|
-
case ON_MSG:
|
583
|
-
if (req->up->on_msg && NULL != req->hook) {
|
584
|
-
rb_funcall((VALUE)req->hook->handler, on_message_id, 2, (VALUE)req->up->wrap, rb_str_new(req->msg, req->mlen));
|
585
|
-
}
|
586
|
-
break;
|
587
|
-
case ON_BIN:
|
588
|
-
if (req->up->on_msg && NULL != req->hook) {
|
589
|
-
volatile VALUE rstr = rb_str_new(req->msg, req->mlen);
|
590
|
-
|
591
|
-
rb_enc_associate(rstr, rb_ascii8bit_encoding());
|
592
|
-
rb_funcall((VALUE)req->hook->handler, on_message_id, 2, (VALUE)req->up->wrap, rstr);
|
593
|
-
}
|
594
|
-
break;
|
595
|
-
case ON_CLOSE:
|
596
|
-
upgraded_ref(req->up);
|
597
|
-
server_publish(pub_close(req->up));
|
598
|
-
if (req->up->on_close && NULL != req->hook) {
|
599
|
-
rb_funcall((VALUE)req->hook->handler, on_close_id, 1, (VALUE)req->up->wrap);
|
600
|
-
}
|
601
|
-
break;
|
602
|
-
case ON_SHUTDOWN:
|
603
|
-
if (NULL != req->hook) {
|
604
|
-
rb_funcall((VALUE)req->hook->handler, rb_intern("on_shutdown"), 1, (VALUE)req->up->wrap);
|
605
|
-
}
|
606
|
-
break;
|
607
|
-
case ON_ERROR:
|
608
|
-
if (req->up->on_error && NULL != req->hook) {
|
609
|
-
volatile VALUE rstr = rb_str_new(req->msg, req->mlen);
|
610
|
-
|
611
|
-
rb_enc_associate(rstr, rb_ascii8bit_encoding());
|
612
|
-
rb_funcall((VALUE)req->hook->handler, on_error_id, 2, (VALUE)req->up->wrap, rstr);
|
613
|
-
}
|
614
|
-
break;
|
615
|
-
case ON_EMPTY:
|
616
|
-
if (req->up->on_empty && NULL != req->hook) {
|
617
|
-
rb_funcall((VALUE)req->hook->handler, on_drained_id, 1, (VALUE)req->up->wrap);
|
618
|
-
}
|
619
|
-
break;
|
620
|
-
default:
|
621
|
-
break;
|
622
|
-
}
|
623
|
-
upgraded_release(req->up);
|
624
|
-
|
625
|
-
return Qfalse;
|
626
|
-
}
|
627
|
-
|
628
|
-
static void*
|
629
|
-
handle_push(void *x) {
|
630
|
-
rb_rescue2(handle_push_inner, (VALUE)x, rescue_error, (VALUE)x, rb_eException, 0);
|
631
|
-
return NULL;
|
632
|
-
}
|
633
|
-
|
634
|
-
static void
|
635
|
-
handle_protected(Req req, bool gvi) {
|
636
|
-
if (NULL == req->hook) {
|
637
|
-
return;
|
638
|
-
}
|
639
|
-
switch (req->hook->type) {
|
640
|
-
case BASE_HOOK:
|
641
|
-
if (gvi) {
|
642
|
-
rb_thread_call_with_gvl(handle_base, req);
|
643
|
-
} else {
|
644
|
-
handle_base(req);
|
645
|
-
}
|
646
|
-
break;
|
647
|
-
case RACK_HOOK:
|
648
|
-
if (gvi) {
|
649
|
-
rb_thread_call_with_gvl(handle_rack, req);
|
650
|
-
} else {
|
651
|
-
handle_rack(req);
|
652
|
-
}
|
653
|
-
break;
|
654
|
-
case WAB_HOOK:
|
655
|
-
if (gvi) {
|
656
|
-
rb_thread_call_with_gvl(handle_wab, req);
|
657
|
-
} else {
|
658
|
-
handle_wab(req);
|
659
|
-
}
|
660
|
-
break;
|
661
|
-
case PUSH_HOOK:
|
662
|
-
if (gvi) {
|
663
|
-
rb_thread_call_with_gvl(handle_push, req);
|
664
|
-
} else {
|
665
|
-
handle_push(req);
|
666
|
-
}
|
667
|
-
break;
|
668
|
-
case FUNC_HOOK:
|
669
|
-
req->hook->func(req);
|
670
|
-
queue_wakeup(&the_server.con_queue);
|
671
|
-
break;
|
672
|
-
default: {
|
673
|
-
char buf[256];
|
674
|
-
int cnt = snprintf(buf, sizeof(buf), "HTTP/1.1 500 Internal Error\r\nConnection: Close\r\nContent-Length: 0\r\n\r\n");
|
675
|
-
Text message = text_create(buf, cnt);
|
676
|
-
|
677
|
-
req->res->close = true;
|
678
|
-
res_set_message(req->res, message);
|
679
|
-
queue_wakeup(&the_server.con_queue);
|
680
|
-
break;
|
681
|
-
}
|
682
|
-
}
|
683
|
-
}
|
684
|
-
|
685
|
-
static void*
|
686
|
-
process_loop(void *ptr) {
|
687
|
-
Req req;
|
688
|
-
|
689
|
-
atomic_fetch_add(&the_server.running, 1);
|
690
|
-
while (the_server.active) {
|
691
|
-
if (NULL != (req = (Req)queue_pop(&the_server.eval_queue, 0.1))) {
|
692
|
-
handle_protected(req, true);
|
693
|
-
req_destroy(req);
|
694
|
-
}
|
695
|
-
}
|
696
|
-
atomic_fetch_sub(&the_server.running, 1);
|
697
|
-
|
698
|
-
return NULL;
|
699
|
-
}
|
700
|
-
|
701
|
-
static VALUE
|
702
|
-
wrap_process_loop(void *ptr) {
|
703
|
-
rb_thread_call_without_gvl(process_loop, NULL, RUBY_UBF_IO, NULL);
|
704
|
-
return Qnil;
|
705
|
-
}
|
706
|
-
|
707
|
-
/* Document-method: start
|
708
|
-
*
|
709
|
-
* call-seq: start()
|
710
|
-
*
|
711
|
-
* Start the server.
|
712
|
-
*/
|
713
|
-
static VALUE
|
714
|
-
rserver_start(VALUE self) {
|
715
|
-
VALUE *vp;
|
716
|
-
int i;
|
717
|
-
int pid;
|
718
|
-
double giveup;
|
719
|
-
struct _Err err = ERR_INIT;
|
720
|
-
VALUE agoo = rb_const_get_at(rb_cObject, rb_intern("Agoo"));
|
721
|
-
VALUE v = rb_const_get_at(agoo, rb_intern("VERSION"));
|
722
|
-
|
723
|
-
*the_rserver.worker_pids = getpid();
|
724
|
-
|
725
|
-
if (ERR_OK != setup_listen(&err)) {
|
726
|
-
rb_raise(rb_eIOError, "%s", err.msg);
|
727
|
-
}
|
728
|
-
for (i = 1; i < the_rserver.worker_cnt; i++) {
|
729
|
-
VALUE rpid = rb_funcall(rb_cObject, rb_intern("fork"), 0);
|
730
|
-
|
731
|
-
if (Qnil == rpid) {
|
732
|
-
pid = 0;
|
733
|
-
} else {
|
734
|
-
pid = NUM2INT(rpid);
|
735
|
-
}
|
736
|
-
if (0 > pid) { // error, use single process
|
737
|
-
log_cat(&error_cat, "Failed to fork. %s.", strerror(errno));
|
738
|
-
break;
|
739
|
-
} else if (0 == pid) {
|
740
|
-
log_start(true);
|
741
|
-
break;
|
742
|
-
} else {
|
743
|
-
the_rserver.worker_pids[i] = pid;
|
744
|
-
}
|
745
|
-
}
|
746
|
-
if (ERR_OK != server_start(&err, "Agoo", StringValuePtr(v))) {
|
747
|
-
rb_raise(rb_eStandardError, "%s", err.msg);
|
748
|
-
}
|
749
|
-
if (0 >= the_server.thread_cnt) {
|
750
|
-
Req req;
|
751
|
-
|
752
|
-
while (the_server.active) {
|
753
|
-
if (NULL != (req = (Req)queue_pop(&the_server.eval_queue, 0.1))) {
|
754
|
-
handle_protected(req, false);
|
755
|
-
req_destroy(req);
|
756
|
-
} else {
|
757
|
-
rb_thread_schedule();
|
758
|
-
}
|
759
|
-
|
760
|
-
}
|
761
|
-
} else {
|
762
|
-
the_rserver.eval_threads = (VALUE*)malloc(sizeof(VALUE) * (the_server.thread_cnt + 1));
|
763
|
-
DEBUG_ALLOC(mem_eval_threads, the_server.eval_threads);
|
764
|
-
|
765
|
-
for (i = the_server.thread_cnt, vp = the_rserver.eval_threads; 0 < i; i--, vp++) {
|
766
|
-
*vp = rb_thread_create(wrap_process_loop, NULL);
|
767
|
-
}
|
768
|
-
*vp = Qnil;
|
769
|
-
|
770
|
-
giveup = dtime() + 1.0;
|
771
|
-
while (dtime() < giveup) {
|
772
|
-
// The processing threads will not start until this thread
|
773
|
-
// releases ownership so do that and then see if the threads has
|
774
|
-
// been started yet.
|
775
|
-
rb_thread_schedule();
|
776
|
-
if (2 + the_server.thread_cnt <= atomic_load(&the_server.running)) {
|
777
|
-
break;
|
778
|
-
}
|
779
|
-
}
|
780
|
-
}
|
781
|
-
return Qnil;
|
782
|
-
}
|
783
|
-
|
784
|
-
static void
|
785
|
-
stop_runners() {
|
786
|
-
// The preferred method of waiting for the ruby threads would be either a
|
787
|
-
// join or even a kill but since we may not have the gvl here that would
|
788
|
-
// cause a segfault. Instead we set a timeout and wait for the running
|
789
|
-
// counter to drop to zero.
|
790
|
-
if (NULL != the_rserver.eval_threads) {
|
791
|
-
double timeout = dtime() + 2.0;
|
792
|
-
|
793
|
-
while (dtime() < timeout) {
|
794
|
-
if (0 >= atomic_load(&the_server.running)) {
|
795
|
-
break;
|
796
|
-
}
|
797
|
-
dsleep(0.02);
|
798
|
-
}
|
799
|
-
DEBUG_FREE(mem_eval_threads, the_rserver.eval_threads);
|
800
|
-
free(the_rserver.eval_threads);
|
801
|
-
the_rserver.eval_threads = NULL;
|
802
|
-
}
|
803
|
-
}
|
804
|
-
|
805
|
-
/* Document-method: shutdown
|
806
|
-
*
|
807
|
-
* call-seq: shutdown()
|
808
|
-
*
|
809
|
-
* Shutdown the server. Logs and queues are flushed before shutting down.
|
810
|
-
*/
|
811
|
-
VALUE
|
812
|
-
rserver_shutdown(VALUE self) {
|
813
|
-
if (the_server.inited) {
|
814
|
-
server_shutdown("Agoo", stop_runners);
|
815
|
-
|
816
|
-
if (1 < the_rserver.worker_cnt && getpid() == *the_rserver.worker_pids) {
|
817
|
-
int i;
|
818
|
-
int status;
|
819
|
-
int exit_cnt = 1;
|
820
|
-
int j;
|
821
|
-
|
822
|
-
for (i = 1; i < the_rserver.worker_cnt; i++) {
|
823
|
-
kill(the_rserver.worker_pids[i], SIGKILL);
|
824
|
-
}
|
825
|
-
for (j = 0; j < 20; j++) {
|
826
|
-
for (i = 1; i < the_rserver.worker_cnt; i++) {
|
827
|
-
if (0 == the_rserver.worker_pids[i]) {
|
828
|
-
continue;
|
829
|
-
}
|
830
|
-
if (0 < waitpid(the_rserver.worker_pids[i], &status, WNOHANG)) {
|
831
|
-
if (WIFEXITED(status)) {
|
832
|
-
//printf("exited, status=%d for %d\n", the_server.worker_pids[i], WEXITSTATUS(status));
|
833
|
-
the_rserver.worker_pids[i] = 0;
|
834
|
-
exit_cnt++;
|
835
|
-
} else if (WIFSIGNALED(status)) {
|
836
|
-
//printf("*** killed by signal %d for %d\n", the_server.worker_pids[i], WTERMSIG(status));
|
837
|
-
the_rserver.worker_pids[i] = 0;
|
838
|
-
exit_cnt++;
|
839
|
-
}
|
840
|
-
}
|
841
|
-
}
|
842
|
-
if (the_rserver.worker_cnt <= exit_cnt) {
|
843
|
-
break;
|
844
|
-
}
|
845
|
-
dsleep(0.2);
|
846
|
-
}
|
847
|
-
if (exit_cnt < the_rserver.worker_cnt) {
|
848
|
-
printf("*-*-* Some workers did not exit.\n");
|
849
|
-
}
|
850
|
-
}
|
851
|
-
}
|
852
|
-
return Qnil;
|
853
|
-
}
|
854
|
-
|
855
|
-
/* Document-method: handle
|
856
|
-
*
|
857
|
-
* call-seq: handle(method, pattern, handler)
|
858
|
-
*
|
859
|
-
* Registers a handler for the HTTP method and path pattern specified. The
|
860
|
-
* path pattern follows glob like rules in that a single * matches a single
|
861
|
-
* token bounded by the `/` character and a double ** matches all remaining.
|
862
|
-
*/
|
863
|
-
static VALUE
|
864
|
-
handle(VALUE self, VALUE method, VALUE pattern, VALUE handler) {
|
865
|
-
Hook hook;
|
866
|
-
Method meth = ALL;
|
867
|
-
const char *pat;
|
868
|
-
|
869
|
-
rb_check_type(pattern, T_STRING);
|
870
|
-
pat = StringValuePtr(pattern);
|
871
|
-
|
872
|
-
if (connect_sym == method) {
|
873
|
-
meth = CONNECT;
|
874
|
-
} else if (delete_sym == method) {
|
875
|
-
meth = DELETE;
|
876
|
-
} else if (get_sym == method) {
|
877
|
-
meth = GET;
|
878
|
-
} else if (head_sym == method) {
|
879
|
-
meth = HEAD;
|
880
|
-
} else if (options_sym == method) {
|
881
|
-
meth = OPTIONS;
|
882
|
-
} else if (post_sym == method) {
|
883
|
-
meth = POST;
|
884
|
-
} else if (put_sym == method) {
|
885
|
-
meth = PUT;
|
886
|
-
} else if (Qnil == method) {
|
887
|
-
meth = ALL;
|
888
|
-
} else {
|
889
|
-
rb_raise(rb_eArgError, "invalid method");
|
890
|
-
}
|
891
|
-
if (NULL == (hook = rhook_create(meth, pat, handler, &the_server.eval_queue))) {
|
892
|
-
rb_raise(rb_eStandardError, "out of memory.");
|
893
|
-
} else {
|
894
|
-
Hook h;
|
895
|
-
Hook prev = NULL;
|
896
|
-
|
897
|
-
for (h = the_server.hooks; NULL != h; h = h->next) {
|
898
|
-
prev = h;
|
899
|
-
}
|
900
|
-
if (NULL != prev) {
|
901
|
-
prev->next = hook;
|
902
|
-
} else {
|
903
|
-
the_server.hooks = hook;
|
904
|
-
}
|
905
|
-
rb_gc_register_address((VALUE*)&hook->handler);
|
906
|
-
}
|
907
|
-
return Qnil;
|
908
|
-
}
|
909
|
-
|
910
|
-
/* Document-method: handle_not_found
|
911
|
-
*
|
912
|
-
* call-seq: not_found_handle(handler)
|
913
|
-
*
|
914
|
-
* Registers a handler to be called when no other hook is found and no static
|
915
|
-
* file is found.
|
916
|
-
*/
|
917
|
-
static VALUE
|
918
|
-
handle_not_found(VALUE self, VALUE handler) {
|
919
|
-
if (NULL == (the_server.hook404 = rhook_create(GET, "/", handler, &the_server.eval_queue))) {
|
920
|
-
rb_raise(rb_eStandardError, "out of memory.");
|
921
|
-
}
|
922
|
-
rb_gc_register_address((VALUE*)&the_server.hook404->handler);
|
923
|
-
|
924
|
-
return Qnil;
|
925
|
-
}
|
926
|
-
|
927
|
-
/* Document-method: add_mime
|
928
|
-
*
|
929
|
-
* call-seq: add_mime(suffix, type)
|
930
|
-
*
|
931
|
-
* Adds a mime type by associating a type string with a suffix. This is used
|
932
|
-
* for static files.
|
933
|
-
*/
|
934
|
-
static VALUE
|
935
|
-
add_mime(VALUE self, VALUE suffix, VALUE type) {
|
936
|
-
struct _Err err = ERR_INIT;
|
937
|
-
|
938
|
-
if (ERR_OK != mime_set(&err, StringValuePtr(suffix), StringValuePtr(type))) {
|
939
|
-
rb_raise(rb_eArgError, "%s", err.msg);
|
940
|
-
}
|
941
|
-
return Qnil;
|
942
|
-
}
|
943
|
-
|
944
|
-
/* Document-method: path_group
|
945
|
-
*
|
946
|
-
* call-seq: path_group(path, dirs)
|
947
|
-
*
|
948
|
-
* Sets up a path group where the path defines a group of directories to
|
949
|
-
* search for a file. For example a path of '/assets' could be mapped to a set
|
950
|
-
* of [ 'home/user/images', '/home/user/app/assets/images' ].
|
951
|
-
*/
|
952
|
-
static VALUE
|
953
|
-
path_group(VALUE self, VALUE path, VALUE dirs) {
|
954
|
-
Group g;
|
955
|
-
|
956
|
-
rb_check_type(path, T_STRING);
|
957
|
-
rb_check_type(dirs, T_ARRAY);
|
958
|
-
|
959
|
-
if (NULL != (g = group_create(StringValuePtr(path)))) {
|
960
|
-
int i;
|
961
|
-
int dcnt = (int)RARRAY_LEN(dirs);
|
962
|
-
VALUE entry;
|
963
|
-
|
964
|
-
for (i = dcnt - 1; 0 <= i; i--) {
|
965
|
-
entry = rb_ary_entry(dirs, i);
|
966
|
-
if (T_STRING != rb_type(entry)) {
|
967
|
-
entry = rb_funcall(entry, rb_intern("to_s"), 0);
|
968
|
-
}
|
969
|
-
group_add(g, StringValuePtr(entry));
|
970
|
-
}
|
971
|
-
}
|
972
|
-
return Qnil;
|
973
|
-
}
|
974
|
-
|
975
|
-
/* Document-class: Agoo::Server
|
976
|
-
*
|
977
|
-
* An HTTP server that support the rack API as well as some other optimized
|
978
|
-
* APIs for handling HTTP requests.
|
979
|
-
*/
|
980
|
-
void
|
981
|
-
server_init(VALUE mod) {
|
982
|
-
server_mod = rb_define_module_under(mod, "Server");
|
983
|
-
|
984
|
-
rb_define_module_function(server_mod, "init", rserver_init, -1);
|
985
|
-
rb_define_module_function(server_mod, "start", rserver_start, 0);
|
986
|
-
rb_define_module_function(server_mod, "shutdown", rserver_shutdown, 0);
|
987
|
-
|
988
|
-
rb_define_module_function(server_mod, "handle", handle, 3);
|
989
|
-
rb_define_module_function(server_mod, "handle_not_found", handle_not_found, 1);
|
990
|
-
rb_define_module_function(server_mod, "add_mime", add_mime, 2);
|
991
|
-
rb_define_module_function(server_mod, "path_group", path_group, 2);
|
992
|
-
|
993
|
-
call_id = rb_intern("call");
|
994
|
-
each_id = rb_intern("each");
|
995
|
-
on_close_id = rb_intern("on_close");
|
996
|
-
on_drained_id = rb_intern("on_drained");
|
997
|
-
on_error_id = rb_intern("on_error");
|
998
|
-
on_message_id = rb_intern("on_message");
|
999
|
-
on_request_id = rb_intern("on_request");
|
1000
|
-
to_i_id = rb_intern("to_i");
|
1001
|
-
|
1002
|
-
connect_sym = ID2SYM(rb_intern("CONNECT")); rb_gc_register_address(&connect_sym);
|
1003
|
-
delete_sym = ID2SYM(rb_intern("DELETE")); rb_gc_register_address(&delete_sym);
|
1004
|
-
get_sym = ID2SYM(rb_intern("GET")); rb_gc_register_address(&get_sym);
|
1005
|
-
head_sym = ID2SYM(rb_intern("HEAD")); rb_gc_register_address(&head_sym);
|
1006
|
-
options_sym = ID2SYM(rb_intern("OPTIONS")); rb_gc_register_address(&options_sym);
|
1007
|
-
post_sym = ID2SYM(rb_intern("POST")); rb_gc_register_address(&post_sym);
|
1008
|
-
put_sym = ID2SYM(rb_intern("PUT")); rb_gc_register_address(&put_sym);
|
1009
|
-
|
1010
|
-
push_env_key = rb_str_new_cstr("rack.upgrade"); rb_gc_register_address(&push_env_key);
|
1011
|
-
|
1012
|
-
rserver = Data_Wrap_Struct(rb_cObject, server_mark, NULL, strdup("dummy"));
|
1013
|
-
rb_gc_register_address(&rserver);
|
1014
|
-
|
1015
|
-
http_init();
|
1016
|
-
}
|