ebb 0.2.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
+ }