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.
- 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
|
+
}
|