ebb 0.2.1 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README +10 -44
- data/Rakefile +123 -0
- data/ext/ebb.c +794 -0
- data/ext/ebb.h +130 -0
- data/ext/ebb_ffi.c +575 -0
- data/ext/ebb_request_parser.c +5339 -0
- data/ext/ebb_request_parser.h +97 -0
- data/ext/ebb_request_parser.rl +513 -0
- data/{src → ext}/extconf.rb +12 -8
- data/ext/rbtree.c +408 -0
- data/ext/rbtree.h +54 -0
- data/lib/ebb.rb +311 -0
- data/lib/ebb/version.rb +4 -0
- data/libev/ev++.h +803 -0
- data/libev/ev.c +24 -6
- data/libev/ev.h +4 -0
- data/libev/ev_select.c +50 -15
- data/libev/ev_vars.h +3 -0
- data/libev/ev_win32.c +3 -0
- data/libev/ev_wrap.h +2 -0
- data/libev/event.c +403 -0
- data/libev/event.h +152 -0
- metadata +26 -40
- data/benchmark/application.rb +0 -93
- data/benchmark/server_test.rb +0 -193
- data/bin/ebb_rails +0 -4
- data/ruby_lib/ebb.rb +0 -257
- data/ruby_lib/ebb/runner.rb +0 -134
- data/ruby_lib/ebb/runner/rails.rb +0 -31
- data/ruby_lib/rack/adapter/rails.rb +0 -159
- data/src/ebb.c +0 -627
- data/src/ebb.h +0 -102
- data/src/ebb_ruby.c +0 -306
- data/src/parser.c +0 -2860
- data/src/parser.h +0 -53
- data/test/basic_test.rb +0 -46
- data/test/ebb_rails_test.rb +0 -34
- data/test/env_test.rb +0 -110
- data/test/helper.rb +0 -138
data/ext/ebb.h
ADDED
@@ -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
|
data/ext/ebb_ffi.c
ADDED
@@ -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
|
+
}
|