ebb 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,130 @@
1
+ /* libebb web server library
2
+ * Copyright 2008 ryah dahl, ry at tiny clouds punkt org
3
+ *
4
+ * This software may be distributed under the "MIT" license included in the
5
+ * README
6
+ */
7
+ #ifndef EBB_H
8
+ #define EBB_H
9
+
10
+ /* remove this if you want to embed libebb without GNUTLS */
11
+ //#define HAVE_GNUTLS 1
12
+
13
+ #include <sys/socket.h>
14
+ #include <netinet/in.h>
15
+ #include <ev.h>
16
+ #ifdef HAVE_GNUTLS
17
+ # include <gnutls/gnutls.h>
18
+ # include "rbtree.h" /* for ebb_server.session_cache */
19
+ #endif
20
+ #include "ebb_request_parser.h"
21
+
22
+ #define EBB_MAX_CONNECTIONS 1024
23
+ #define EBB_DEFAULT_TIMEOUT 30.0
24
+
25
+ #define EBB_AGAIN 0
26
+ #define EBB_STOP 1
27
+
28
+ typedef struct ebb_buf ebb_buf;
29
+ typedef struct ebb_server ebb_server;
30
+ typedef struct ebb_connection ebb_connection;
31
+ typedef void (*ebb_after_write_cb) (ebb_connection *connection);
32
+
33
+ typedef void (*ebb_connection_cb)(ebb_connection *connection, void *data);
34
+
35
+ struct ebb_buf {
36
+ size_t written; /* private */
37
+
38
+ /* public */
39
+ char *base;
40
+ size_t len;
41
+ void (*on_release)(ebb_buf*);
42
+ void *data;
43
+ };
44
+
45
+ struct ebb_server {
46
+ int fd; /* ro */
47
+ struct sockaddr_in sockaddr; /* ro */
48
+ socklen_t socklen; /* ro */
49
+ char port[6]; /* ro */
50
+ struct ev_loop *loop; /* ro */
51
+ unsigned listening:1; /* ro */
52
+ unsigned secure:1; /* ro */
53
+ #ifdef HAVE_GNUTLS
54
+ gnutls_certificate_credentials_t credentials; /* private */
55
+ struct rbtree_t session_cache; /* private */
56
+ #endif
57
+ ev_io connection_watcher; /* private */
58
+
59
+ /* Public */
60
+
61
+ /* Allocates and initializes an ebb_connection. NULL by default. */
62
+ ebb_connection* (*new_connection) (ebb_server*, struct sockaddr_in*);
63
+
64
+ void *data;
65
+ };
66
+
67
+ struct ebb_connection {
68
+ int fd; /* ro */
69
+ struct sockaddr_in sockaddr; /* ro */
70
+ socklen_t socklen; /* ro */
71
+ ebb_server *server; /* ro */
72
+ char *ip; /* ro */
73
+ unsigned open:1; /* ro */
74
+
75
+ const char *to_write; /* ro */
76
+ size_t to_write_len; /* ro */
77
+ size_t written; /* ro */
78
+ ebb_after_write_cb after_write_cb; /* ro */
79
+
80
+ ebb_request_parser parser; /* private */
81
+ ev_io write_watcher; /* private */
82
+ ev_io read_watcher; /* private */
83
+ ev_timer timeout_watcher; /* private */
84
+ ev_timer goodbye_watcher; /* private */
85
+ #ifdef HAVE_GNUTLS
86
+ ev_io handshake_watcher; /* private */
87
+ gnutls_session_t session; /* private */
88
+ ev_io goodbye_tls_watcher; /* private */
89
+ #endif
90
+
91
+ /* Public */
92
+
93
+ ebb_request* (*new_request) (ebb_connection*);
94
+
95
+ /* The new_buf callback allocates and initializes an ebb_buf structure.
96
+ * By default this is set to a simple malloc() based callback which always
97
+ * returns 4 kilobyte bufs. Write over it with your own to use your own
98
+ * custom allocation
99
+ *
100
+ * new_buf is called each time there is data from a client connection to
101
+ * be read. See on_readable() in server.c to see exactly how this is used.
102
+ */
103
+ ebb_buf* (*new_buf) (ebb_connection*);
104
+
105
+ /* Returns EBB_STOP or EBB_AGAIN
106
+ * NULL by default.
107
+ */
108
+ int (*on_timeout) (ebb_connection*);
109
+
110
+ /* The connection was closed */
111
+ void (*on_close) (ebb_connection*);
112
+
113
+ void *data;
114
+ };
115
+
116
+ void ebb_server_init (ebb_server *server, struct ev_loop *loop);
117
+ #ifdef HAVE_GNUTLS
118
+ int ebb_server_set_secure (ebb_server *server, const char *cert_file,
119
+ const char *key_file);
120
+ #endif
121
+ int ebb_server_listen_on_port (ebb_server *server, const int port);
122
+ int ebb_server_listen_on_fd (ebb_server *server, const int sfd);
123
+ void ebb_server_unlisten (ebb_server *server);
124
+
125
+ void ebb_connection_init (ebb_connection *connection);
126
+ void ebb_connection_schedule_close (ebb_connection *);
127
+ void ebb_connection_reset_timeout (ebb_connection *connection);
128
+ int ebb_connection_write (ebb_connection *connection, const char *buf, size_t len, ebb_after_write_cb);
129
+
130
+ #endif
@@ -0,0 +1,575 @@
1
+ /* A ruby binding to the ebb web server
2
+ * Copyright (c) 2008 Ry Dahl. This software is released under the MIT
3
+ * License. See README file for details.
4
+ */
5
+ #include <ruby.h>
6
+ #include <rubyio.h>
7
+ #include <rubysig.h>
8
+ #include <assert.h>
9
+ #include <fcntl.h>
10
+
11
+ #define EV_STANDALONE 1
12
+ #include <ev.c>
13
+ #include "ebb.h"
14
+
15
+ /* Variables with a leading underscore are C-level variables */
16
+
17
+ #ifndef RSTRING_PTR
18
+ # define RSTRING_PTR(s) (RSTRING(s)->ptr)
19
+ # define RSTRING_LEN(s) (RSTRING(s)->len)
20
+ #endif
21
+
22
+ static char upcase[] =
23
+ "\0______________________________"
24
+ "_________________0123456789_____"
25
+ "__ABCDEFGHIJKLMNOPQRSTUVWXYZ____"
26
+ "__ABCDEFGHIJKLMNOPQRSTUVWXYZ____"
27
+ "________________________________"
28
+ "________________________________"
29
+ "________________________________"
30
+ "________________________________";
31
+
32
+ static VALUE cRequest;
33
+ static VALUE cConnection;
34
+ static VALUE waiting_requests;
35
+
36
+ /* You don't want to run more than one server per Ruby VM. Really
37
+ * I'm making this explicit by not defining a Ebb::Server class but instead
38
+ * initializing a single server and single event loop on module load.
39
+ */
40
+ static ebb_server server;
41
+ static unsigned int nconnections;
42
+ struct ev_loop *loop;
43
+ struct ev_idle idle_watcher;
44
+
45
+ /* g is for global */
46
+ static VALUE g_fragment;
47
+ static VALUE g_path_info;
48
+ static VALUE g_query_string;
49
+ static VALUE g_request_body;
50
+ static VALUE g_request_method;
51
+ static VALUE g_request_path;
52
+ static VALUE g_request_uri;
53
+ static VALUE g_server_port;
54
+ static VALUE g_content_length;
55
+ static VALUE g_content_type;
56
+ static VALUE g_http_client_ip;
57
+ static VALUE g_http_prefix;
58
+ static VALUE g_http_version;
59
+ static VALUE g_empty_str;
60
+
61
+ static VALUE g_COPY;
62
+ static VALUE g_DELETE;
63
+ static VALUE g_GET;
64
+ static VALUE g_HEAD;
65
+ static VALUE g_LOCK;
66
+ static VALUE g_MKCOL;
67
+ static VALUE g_MOVE;
68
+ static VALUE g_OPTIONS;
69
+ static VALUE g_POST;
70
+ static VALUE g_PROPFIND;
71
+ static VALUE g_PROPPATCH;
72
+ static VALUE g_PUT;
73
+ static VALUE g_TRACE;
74
+ static VALUE g_UNLOCK;
75
+
76
+ static void attach_idle_watcher()
77
+ {
78
+ if(!ev_is_active(&idle_watcher)) {
79
+ ev_idle_start (loop, &idle_watcher);
80
+ }
81
+ }
82
+
83
+
84
+ static void detach_idle_watcher()
85
+ {
86
+ ev_idle_stop(loop, &idle_watcher);
87
+ }
88
+
89
+ static void
90
+ idle_cb (struct ev_loop *loop, struct ev_idle *w, int revents) {
91
+ /* Ryan's Hacky Ruby Thread Scheduler */
92
+
93
+ /* TODO: For Ruby 1.9 I should use rb_thread_blocking_region() instead of
94
+ * this hacky idle_cb. Of course then I couldn't use rb_funcall in the
95
+ * callbacks.. it might be easier to just use this!
96
+ */
97
+ if(rb_thread_alone()) {
98
+ /* best option: just stop calling idle_cb and wait for the
99
+ * server fd to wake up by sitting in the libev loop. This
100
+ * should be very fast.
101
+ */
102
+ detach_idle_watcher();
103
+ } else if(nconnections > 0) {
104
+ /* second best option: there are other threads running and there
105
+ * are clients to process. here we'll just call rb_thread_schedule()
106
+ * because it doesn't take much time and allows those workers to run.
107
+ */
108
+ rb_thread_schedule();
109
+ } else {
110
+ /* worst case: there are no clients to process but there is some long
111
+ * running thread. we use rb_thread_select() which is slow to wake up
112
+ * but allows us to wait until the server has a new connection.
113
+ * Try to avoid this - do not let threads run in the background of the
114
+ * server.
115
+ */
116
+ struct timeval idle_timeout = { 0, 50000 };
117
+ fd_set server_fd_set;
118
+ FD_ZERO(&server_fd_set);
119
+ FD_SET(server.fd, &server_fd_set);
120
+ rb_thread_select(server.fd+1, &server_fd_set, 0, 0, &idle_timeout);
121
+ }
122
+ }
123
+
124
+ static ebb_connection*
125
+ request_get_connection(ebb_request *request)
126
+ {
127
+ VALUE rb_request = (VALUE)request->data;
128
+ VALUE rb_connection = rb_iv_get(rb_request, "@connection");
129
+
130
+ if(rb_connection == Qnil) {
131
+ rb_raise(rb_eIOError, "read error - client connection closed");
132
+ return NULL;
133
+ }
134
+
135
+ ebb_connection *connection;
136
+ Data_Get_Struct(rb_connection, ebb_connection, connection);
137
+ return connection;
138
+ }
139
+
140
+ #define APPEND_ENV(NAME) \
141
+ VALUE rb_request = (VALUE)request->data; \
142
+ VALUE env = rb_iv_get(rb_request, "@env_ffi"); \
143
+ VALUE v = rb_hash_aref(env, g_##NAME); \
144
+ if(v == Qnil) \
145
+ rb_hash_aset(env, g_##NAME, rb_str_new(at, len)); \
146
+ else \
147
+ rb_str_cat(v, at, len);
148
+
149
+ static void
150
+ request_path(ebb_request *request, const char *at, size_t len)
151
+ {
152
+ APPEND_ENV(request_path);
153
+ }
154
+
155
+ static void
156
+ query_string(ebb_request *request, const char *at, size_t len)
157
+ {
158
+ APPEND_ENV(query_string);
159
+ }
160
+
161
+ static void
162
+ request_uri(ebb_request *request, const char *at, size_t len)
163
+ {
164
+ APPEND_ENV(request_uri);
165
+ }
166
+
167
+ static void
168
+ fragment(ebb_request *request, const char *at, size_t len)
169
+ {
170
+ APPEND_ENV(fragment);
171
+ }
172
+
173
+ /* very ugly... */
174
+ static void
175
+ header_field(ebb_request *request, const char *at, size_t len, int _)
176
+ {
177
+ VALUE rb_request = (VALUE)request->data;
178
+ VALUE field = rb_iv_get(rb_request, "@field_in_progress");
179
+ VALUE value = rb_iv_get(rb_request, "@value_in_progress");
180
+
181
+ if( (field == Qnil && value == Qnil) || (field != Qnil && value != Qnil)) {
182
+ if(field != Qnil) {
183
+ VALUE env = rb_iv_get(rb_request, "@env_ffi");
184
+ rb_hash_aset(env, field, value);
185
+ }
186
+
187
+ // prefix with HTTP_
188
+ VALUE f = rb_str_new(NULL, RSTRING_LEN(g_http_prefix) + len);
189
+ memcpy( RSTRING_PTR(f)
190
+ , RSTRING_PTR(g_http_prefix)
191
+ , RSTRING_LEN(g_http_prefix)
192
+ );
193
+ int i;
194
+ // normalize
195
+ for(i = 0; i < len; i++) {
196
+ char *ch = RSTRING_PTR(f) + RSTRING_LEN(g_http_prefix) + i;
197
+ *ch = upcase[(int)at[i]];
198
+ }
199
+ rb_iv_set(rb_request, "@field_in_progress", f);
200
+ rb_iv_set(rb_request, "@value_in_progress", Qnil);
201
+
202
+ } else if(field != Qnil) {
203
+ // nth pass n!= 1
204
+ rb_str_cat(field, at, len);
205
+
206
+ } else {
207
+ assert(0 && "field == Qnil && value != Qnil");
208
+ }
209
+ }
210
+
211
+ static void
212
+ header_value(ebb_request *request, const char *at, size_t len, int _)
213
+ {
214
+ VALUE rb_request = (VALUE)request->data;
215
+ VALUE v = rb_iv_get(rb_request, "@value_in_progress");
216
+ if(v == Qnil)
217
+ rb_iv_set(rb_request, "@value_in_progress", rb_str_new(at, len));
218
+ else
219
+ rb_str_cat(v, at, len);
220
+ }
221
+
222
+ static void
223
+ headers_complete(ebb_request *request)
224
+ {
225
+ VALUE rb_request = (VALUE)request->data;
226
+ ebb_connection *connection = request_get_connection(request);
227
+ if(connection == NULL)
228
+ return;
229
+
230
+ VALUE env = rb_iv_get(rb_request, "@env_ffi");
231
+
232
+ rb_iv_set(rb_request, "@content_length", INT2FIX(request->content_length));
233
+ rb_iv_set( rb_request
234
+ , "@chunked"
235
+ , request->transfer_encoding == EBB_CHUNKED ? Qtrue : Qfalse
236
+ );
237
+
238
+ /* set REQUEST_METHOD. yuck */
239
+ VALUE method = Qnil;
240
+ switch(request->method) {
241
+ case EBB_COPY : method = g_COPY ; break;
242
+ case EBB_DELETE : method = g_DELETE ; break;
243
+ case EBB_GET : method = g_GET ; break;
244
+ case EBB_HEAD : method = g_HEAD ; break;
245
+ case EBB_LOCK : method = g_LOCK ; break;
246
+ case EBB_MKCOL : method = g_MKCOL ; break;
247
+ case EBB_MOVE : method = g_MOVE ; break;
248
+ case EBB_OPTIONS : method = g_OPTIONS ; break;
249
+ case EBB_POST : method = g_POST ; break;
250
+ case EBB_PROPFIND : method = g_PROPFIND ; break;
251
+ case EBB_PROPPATCH : method = g_PROPPATCH ; break;
252
+ case EBB_PUT : method = g_PUT ; break;
253
+ case EBB_TRACE : method = g_TRACE ; break;
254
+ case EBB_UNLOCK : method = g_UNLOCK ; break;
255
+ }
256
+ rb_hash_aset(env, g_request_method, method);
257
+
258
+ /* set PATH_INFO */
259
+ rb_hash_aset(env, g_path_info, rb_hash_aref(env, g_request_path));
260
+
261
+ /* set SERVER_PORT */
262
+ char *server_port = connection->server->port;
263
+ if(server_port)
264
+ rb_hash_aset(env, g_server_port, rb_str_new2(server_port));
265
+
266
+ /* set HTTP_CLIENT_IP */
267
+ char *client_ip = connection->ip;
268
+ if(client_ip)
269
+ rb_hash_aset(env, g_http_client_ip, rb_str_new2(client_ip));
270
+
271
+ /* set HTTP_VERSION */
272
+ VALUE version = rb_str_buf_new(11);
273
+ sprintf(RSTRING_PTR(version), "HTTP/%d.%d", request->version_major, request->version_minor);
274
+ #if RUBY_VERSION_CODE < 187
275
+ RSTRING_LEN(version) = strlen(RSTRING_PTR(version));
276
+ #else
277
+ rb_str_set_len(version, strlen(RSTRING_PTR(version)));
278
+ #endif
279
+ rb_hash_aset(env, g_http_version, version);
280
+
281
+ /* make sure QUERY_STRING is set */
282
+ if(Qnil == rb_hash_aref(env, g_query_string)) {
283
+ rb_hash_aset(env, g_query_string, g_empty_str);
284
+ }
285
+
286
+ rb_ary_push(waiting_requests, rb_request);
287
+ attach_idle_watcher();
288
+ // TODO set to detached if it has body
289
+ }
290
+
291
+ static void
292
+ body_handler(ebb_request *request, const char *at, size_t length)
293
+ {
294
+ VALUE rb_request = (VALUE)request->data;
295
+ VALUE body, chunk;
296
+
297
+ if(length > 0) {
298
+ body = rb_iv_get(rb_request, "@body");
299
+ chunk = rb_str_new(at, length);
300
+ rb_ary_push(body, chunk);
301
+ }
302
+ }
303
+
304
+ static void
305
+ request_complete(ebb_request *request)
306
+ {
307
+ ;
308
+ }
309
+
310
+ static VALUE
311
+ request_read(VALUE x, VALUE rb_request, VALUE want)
312
+ {
313
+ ebb_request *request;
314
+ Data_Get_Struct(rb_request, ebb_request, request);
315
+ ebb_connection *connection = request_get_connection(request);
316
+ if(connection == NULL)
317
+ return Qnil;
318
+
319
+ VALUE body = rb_iv_get(rb_request, "@body");
320
+
321
+ if(RARRAY_LEN(body) > 0) {
322
+ /* ignore how much they want! haha */
323
+ return rb_ary_shift(body);
324
+
325
+ } if(ebb_request_has_body(request)) {
326
+ return Qnil;
327
+
328
+ } else {
329
+ fd_set set;
330
+ FD_ZERO(&set);
331
+ FD_SET(connection->fd, &set);
332
+ rb_thread_select(connection->fd+1, &set, 0, 0, NULL);
333
+ /* hairy! wait for read - invoke the ev callback inside libebb */
334
+ ev_invoke(loop, &connection->read_watcher, EV_READ);
335
+ /* return empty string, next read call will get data -
336
+ * if all goes okay ! :)
337
+ */
338
+ return g_empty_str;
339
+ }
340
+ }
341
+
342
+ static ebb_request*
343
+ new_request(ebb_connection *connection)
344
+ {
345
+ ebb_request *request = ALLOC(ebb_request);
346
+ ebb_request_init(request);
347
+ request->on_path = request_path;
348
+ request->on_query_string = query_string;
349
+ request->on_uri = request_uri;
350
+ request->on_fragment = fragment;
351
+ request->on_header_field = header_field;
352
+ request->on_header_value = header_value;
353
+ request->on_headers_complete = headers_complete;
354
+ request->on_body = body_handler;
355
+ request->on_complete = request_complete;
356
+
357
+ /*** NOTE ebb_request is freed with the rb_request is freed whic
358
+ * will happen from the ruby garbage collector
359
+ */
360
+ VALUE rb_request = Data_Wrap_Struct(cRequest, 0, xfree, request);
361
+ rb_iv_set(rb_request, "@env_ffi", rb_hash_new());
362
+ rb_iv_set(rb_request, "@body", rb_ary_new());
363
+ //rb_iv_set(rb_request, "@value_in_progress", Qnil);
364
+ //rb_iv_set(rb_request, "@field_in_progress", Qnil);
365
+ request->data = (void*)rb_request;
366
+ rb_obj_call_init(rb_request, 0, 0);
367
+
368
+ VALUE rb_connection = (VALUE)connection->data;
369
+ rb_iv_set(rb_connection, "@fd", INT2FIX(connection->fd));
370
+
371
+ rb_iv_set(rb_request, "@connection", (VALUE)connection->data);
372
+ rb_funcall(rb_connection, rb_intern("append_request"), 1, rb_request);
373
+
374
+ return request;
375
+ }
376
+
377
+ static void
378
+ on_close(ebb_connection *connection)
379
+ {
380
+ VALUE rb_connection = (VALUE)connection->data;
381
+ rb_funcall(rb_connection, rb_intern("on_close"), 0, 0);
382
+ xfree(connection);
383
+ nconnections -= 1;
384
+ }
385
+
386
+ static ebb_connection*
387
+ new_connection(ebb_server *server, struct sockaddr_in *addr)
388
+ {
389
+ ebb_connection *connection = ALLOC(ebb_connection);
390
+
391
+ ebb_connection_init(connection);
392
+ connection->new_request = new_request;
393
+ connection->on_close = on_close;
394
+
395
+ VALUE rb_connection = Data_Wrap_Struct(cConnection, 0, 0, connection);
396
+ //rb_iv_set(rb_connection, "@to_write", rb_ary_new());
397
+ connection->data = (void*)rb_connection;
398
+ rb_obj_call_init(rb_connection, 0, 0);
399
+ rb_funcall(rb_connection, rb_intern("on_open"), 0, 0);
400
+
401
+ nconnections += 1;
402
+
403
+ return connection;
404
+ }
405
+
406
+ static VALUE
407
+ server_listen_on_fd(VALUE _, VALUE sfd)
408
+ {
409
+ if(ebb_server_listen_on_fd(&server, FIX2INT(sfd)) < 0)
410
+ rb_sys_fail("Problem listening on FD");
411
+ return Qnil;
412
+ }
413
+
414
+ static VALUE
415
+ server_listen_on_port(VALUE _, VALUE port)
416
+ {
417
+ if(ebb_server_listen_on_port(&server, FIX2INT(port)) < 0)
418
+ rb_sys_fail("Problem listening on port");
419
+ return Qnil;
420
+ }
421
+
422
+ static VALUE
423
+ server_process_connections(VALUE _)
424
+ {
425
+ TRAP_BEG;
426
+ ev_loop(loop, EVLOOP_ONESHOT);
427
+ TRAP_END;
428
+ return Qnil;
429
+ }
430
+
431
+
432
+ static VALUE
433
+ server_unlisten(VALUE _)
434
+ {
435
+ ebb_server_unlisten(&server);
436
+ return Qnil;
437
+ }
438
+
439
+ static VALUE
440
+ server_open(VALUE _)
441
+ {
442
+ return server.listening ? Qtrue : Qfalse;
443
+ }
444
+
445
+ #ifdef HAVE_GNUTLS
446
+ static VALUE
447
+ server_set_secure(VALUE _, VALUE cert_file, VALUE key_file)
448
+ {
449
+ ebb_server_set_secure( &server
450
+ , RSTRING_PTR(cert_file)
451
+ , RSTRING_PTR(key_file)
452
+ );
453
+ return Qnil;
454
+ }
455
+ #endif /* HAVE_GNUTLS */
456
+
457
+ static VALUE
458
+ server_waiting_requests(VALUE _)
459
+ {
460
+ return waiting_requests;
461
+ }
462
+
463
+ static void
464
+ after_write(ebb_connection *connection)
465
+ {
466
+ VALUE rb_connection = (VALUE) connection->data;
467
+ rb_funcall(rb_connection, rb_intern("on_writable"), 0, 0);
468
+ }
469
+
470
+ static VALUE
471
+ connection_write(VALUE _, VALUE rb_connection, VALUE chunk)
472
+ {
473
+ ebb_connection *connection;
474
+ Data_Get_Struct(rb_connection, ebb_connection, connection);
475
+
476
+ int r =
477
+ ebb_connection_write( connection
478
+ , RSTRING_PTR(chunk)
479
+ , RSTRING_LEN(chunk)
480
+ , after_write
481
+ );
482
+ assert(r);
483
+ return chunk;
484
+ }
485
+
486
+ static VALUE
487
+ connection_schedule_close(VALUE _, VALUE rb_connection)
488
+ {
489
+ ebb_connection *connection;
490
+ Data_Get_Struct(rb_connection, ebb_connection, connection);
491
+ ebb_connection_schedule_close(connection);
492
+ return Qnil;
493
+ }
494
+
495
+ static VALUE
496
+ request_should_keep_alive(VALUE _, VALUE rb_request)
497
+ {
498
+ ebb_request *request;
499
+ Data_Get_Struct(rb_request, ebb_request, request);
500
+ return ebb_request_should_keep_alive(request) ? Qtrue : Qfalse;
501
+ }
502
+
503
+ void
504
+ Init_ebb_ffi()
505
+ {
506
+ VALUE mEbb = rb_define_module("Ebb");
507
+ VALUE mFFI = rb_define_module_under(mEbb, "FFI");
508
+
509
+ #define DEF_GLOBAL(N, val) g_##N = rb_obj_freeze(rb_str_new2(val)); rb_global_variable(&g_##N)
510
+ DEF_GLOBAL(content_length, "CONTENT_LENGTH");
511
+ DEF_GLOBAL(content_type, "CONTENT_TYPE");
512
+ DEF_GLOBAL(fragment, "FRAGMENT");
513
+ DEF_GLOBAL(path_info, "PATH_INFO");
514
+ DEF_GLOBAL(query_string, "QUERY_STRING");
515
+ DEF_GLOBAL(request_body, "REQUEST_BODY");
516
+ DEF_GLOBAL(request_method, "REQUEST_METHOD");
517
+ DEF_GLOBAL(request_path, "REQUEST_PATH");
518
+ DEF_GLOBAL(request_uri, "REQUEST_URI");
519
+ DEF_GLOBAL(server_port, "SERVER_PORT");
520
+ DEF_GLOBAL(http_client_ip, "HTTP_CLIENT_IP");
521
+ DEF_GLOBAL(http_prefix, "HTTP_");
522
+ DEF_GLOBAL(http_version, "HTTP_VERSION");
523
+ DEF_GLOBAL(empty_str, "");
524
+
525
+ DEF_GLOBAL(COPY, "COPY");
526
+ DEF_GLOBAL(DELETE, "DELETE");
527
+ DEF_GLOBAL(GET, "GET");
528
+ DEF_GLOBAL(HEAD, "HEAD");
529
+ DEF_GLOBAL(LOCK, "LOCK");
530
+ DEF_GLOBAL(MKCOL, "MKCOL");
531
+ DEF_GLOBAL(MOVE, "MOVE");
532
+ DEF_GLOBAL(OPTIONS, "OPTIONS");
533
+ DEF_GLOBAL(POST, "POST");
534
+ DEF_GLOBAL(PROPFIND, "PROPFIND");
535
+ DEF_GLOBAL(PROPPATCH, "PROPPATCH");
536
+ DEF_GLOBAL(PUT, "PUT");
537
+ DEF_GLOBAL(TRACE, "TRACE");
538
+ DEF_GLOBAL(UNLOCK, "UNLOCK");
539
+
540
+ rb_define_const(mFFI, "VERSION", rb_str_new2("BLANK"));
541
+
542
+ rb_define_singleton_method(mFFI, "server_process_connections", server_process_connections, 0);
543
+ rb_define_singleton_method(mFFI, "server_listen_on_fd", server_listen_on_fd, 1);
544
+ rb_define_singleton_method(mFFI, "server_listen_on_port", server_listen_on_port, 1);
545
+ rb_define_singleton_method(mFFI, "server_unlisten", server_unlisten, 0);
546
+ #ifdef HAVE_GNUTLS
547
+ rb_define_singleton_method(mFFI, "server_set_secure", server_set_secure, 2);
548
+ #endif
549
+ rb_define_singleton_method(mFFI, "server_open?", server_open, 0);
550
+ rb_define_singleton_method(mFFI, "server_waiting_requests", server_waiting_requests, 0);
551
+
552
+ rb_define_singleton_method(mFFI, "connection_schedule_close", connection_schedule_close, 1);
553
+ rb_define_singleton_method(mFFI, "connection_write", connection_write, 2);
554
+
555
+ rb_define_singleton_method(mFFI, "request_should_keep_alive?", request_should_keep_alive, 1);
556
+ rb_define_singleton_method(mFFI, "request_read", request_read, 2);
557
+
558
+ cRequest = rb_define_class_under(mEbb, "Request", rb_cObject);
559
+ cConnection = rb_define_class_under(mEbb, "Connection", rb_cObject);
560
+
561
+ /* initialize ebb_server */
562
+ loop = ev_default_loop (0);
563
+
564
+ ev_idle_init (&idle_watcher, idle_cb);
565
+ attach_idle_watcher();
566
+
567
+ nconnections = 0;
568
+
569
+ ebb_server_init(&server, loop);
570
+
571
+ waiting_requests = rb_ary_new();
572
+ rb_global_variable(&waiting_requests);
573
+
574
+ server.new_connection = new_connection;
575
+ }