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.
14 static VALUE global_fragment
;
15 static VALUE global_path_info
;
16 static VALUE global_query_string
;
17 static VALUE global_request_body
;
18 static VALUE global_request_method
;
19 static VALUE global_request_path
;
20 static VALUE global_request_uri
;
21 static VALUE global_server_port
;
22 static VALUE global_content_length
;
23 static VALUE global_content_type
;
24 static VALUE global_http_client_ip
;
25 static VALUE global_http_prefix
;
26 static VALUE global_http_version
;
29 /* You don't want to run more than one server per Ruby VM. Really
30 * I'm making this explicit by not defining a Ebb::Server class but instead
31 * initializing a single server and single event loop on module load.
33 static ebb_server
*server
;
35 struct ev_idle idle_watcher
;
37 /* Variables with a leading underscore are C-level variables */
39 #define ASCII_UPPER(ch) ('a' <= ch && ch <= 'z' ? ch - 'a' + 'A' : ch)
41 # define RSTRING_PTR(s) (RSTRING(s)->ptr)
42 # define RSTRING_LEN(s) (RSTRING(s)->len)
45 static void attach_idle_watcher()
47 if(!ev_is_active(&idle_watcher
)) {
48 ev_idle_start (loop
, &idle_watcher
);
53 static void detach_idle_watcher()
55 ev_idle_stop(loop
, &idle_watcher
);
58 static int clients_in_use_p()
61 for(i
= 0; i
< EBB_MAX_CLIENTS
; i
++)
62 if(server
->clients
[i
].in_use
) return TRUE
;
66 void request_cb(ebb_client
*client
, void *data
)
68 VALUE waiting_clients
= (VALUE
)data
;
69 VALUE rb_client
= Data_Wrap_Struct(cClient
, 0, 0, client
);
70 rb_ary_push(waiting_clients
, rb_client
);
71 attach_idle_watcher();
74 VALUE
server_listen_on_fd(VALUE _
, VALUE sfd
)
76 if(ebb_server_listen_on_fd(server
, FIX2INT(sfd
)) < 0)
77 rb_sys_fail("Problem listening on FD");
81 VALUE
server_listen_on_port(VALUE _
, VALUE port
)
83 if(ebb_server_listen_on_port(server
, FIX2INT(port
)) < 0)
84 rb_sys_fail("Problem listening on port");
89 idle_cb (struct ev_loop
*loop
, struct ev_idle
*w
, int revents
) {
90 if(clients_in_use_p()) {
92 } else if(!rb_thread_alone()) {
93 /* if you have another long running thread running besides the ones used
94 * for the webapp's requests you will run into performance problems in
95 * ruby 1.8.x because rb_thread_select is slow.
96 * (Don't worry - you're probably not doing this.)
98 struct timeval select_timeout
= { tv_sec
: 0, tv_usec
: 50000 };
100 FD_ZERO(&server_fd_set
);
101 FD_SET(server
->fd
, &server_fd_set
);
102 rb_thread_select(server
->fd
+1, &server_fd_set
, 0, 0, &select_timeout
);
104 detach_idle_watcher();
108 VALUE
server_process_connections(VALUE _
)
111 ev_loop(loop
, EVLOOP_ONESHOT
);
117 VALUE
server_unlisten(VALUE _
)
119 ebb_server_unlisten(server
);
123 VALUE
server_open(VALUE _
)
125 return server
->open
? Qtrue
: Qfalse
;
128 VALUE
env_field(struct ebb_env_item
*item
)
131 VALUE f
= rb_str_new(NULL
, RSTRING_LEN(global_http_prefix
) + item
->field_length
);
132 memcpy( RSTRING_PTR(f
)
133 , RSTRING_PTR(global_http_prefix
)
134 , RSTRING_LEN(global_http_prefix
)
137 for(i
= 0; i
< item
->field_length
; i
++) {
138 char *ch
= RSTRING_PTR(f
) + RSTRING_LEN(global_http_prefix
) + i
;
139 *ch
= item
->field
[i
] == '-' ? '_' : ASCII_UPPER(item
->field
[i
]);
144 case MONGREL_CONTENT_LENGTH
: return global_content_length
;
145 case MONGREL_CONTENT_TYPE
: return global_content_type
;
146 case MONGREL_FRAGMENT
: return global_fragment
;
147 case MONGREL_HTTP_VERSION
: return global_http_version
;
148 case MONGREL_QUERY_STRING
: return global_query_string
;
149 case MONGREL_REQUEST_METHOD
: return global_request_method
;
150 case MONGREL_REQUEST_PATH
: return global_request_path
;
151 case MONGREL_REQUEST_URI
: return global_request_uri
;
153 fprintf(stderr
, "Unknown environ type: %d", item
->type
);
159 VALUE
env_value(struct ebb_env_item
*item
)
161 if(item
->value_length
> 0)
162 return rb_str_new(item
->value
, item
->value_length
);
168 VALUE
client_env(VALUE _
, VALUE rb_client
)
171 VALUE field
, value
, env
= rb_hash_new();
174 Data_Get_Struct(rb_client
, ebb_client
, client
);
175 for(i
=0; i
< client
->env_size
; i
++) {
176 field
= env_field(&client
->env
[i
]);
177 value
= env_value(&client
->env
[i
]);
178 rb_hash_aset(env
, field
, value
);
181 if(client
->server
->port
)
182 rb_hash_aset(env
, global_server_port
, rb_str_new2(client
->server
->port
));
185 rb_hash_aset(env
, global_http_client_ip
, rb_str_new2(client
->ip
));
187 rb_hash_aset(env
, global_path_info
, rb_hash_aref(env
, global_request_path
));
192 VALUE
client_read_input(VALUE _
, VALUE client
, VALUE size
)
197 int _size
= FIX2INT(size
);
198 Data_Get_Struct(client
, ebb_client
, _client
);
200 string
= rb_str_buf_new( _size
);
201 int nread
= ebb_client_read(_client
, RSTRING_PTR(string
), _size
);
202 #if RUBY_VERSION_CODE < 190
203 RSTRING(string
)->len
= nread
;
205 rb_str_set_len(string
, nread
);
209 rb_raise(rb_eRuntimeError
,"There was a problem reading from input (bad tmp file?)");
215 VALUE
client_write_status(VALUE _
, VALUE client
, VALUE status
, VALUE human_status
)
218 Data_Get_Struct(client
, ebb_client
, _client
);
219 ebb_client_write_status(_client
, FIX2INT(status
), StringValuePtr(human_status
));
223 VALUE
client_write_header(VALUE _
, VALUE client
, VALUE field
, VALUE value
)
226 Data_Get_Struct(client
, ebb_client
, _client
);
227 ebb_client_write_header(_client
, StringValuePtr(field
), StringValuePtr(value
));
231 VALUE
client_write_body(VALUE _
, VALUE client
, VALUE string
)
234 Data_Get_Struct(client
, ebb_client
, _client
);
235 ebb_client_write_body(_client
, RSTRING_PTR(string
), RSTRING_LEN(string
));
240 VALUE
client_release(VALUE _
, VALUE rb_client
)
243 Data_Get_Struct(rb_client
, ebb_client
, client
);
244 ebb_client_release(client
);
250 VALUE mEbb
= rb_define_module("Ebb");
251 VALUE mFFI
= rb_define_module_under(mEbb
, "FFI");
253 rb_define_const(mEbb
, "VERSION", rb_str_new2(EBB_VERSION
));
255 /** Defines global strings in the init method. */
256 #define DEF_GLOBAL(N, val) global_##N = rb_obj_freeze(rb_str_new2(val)); rb_global_variable(&global_##N)
257 DEF_GLOBAL(content_length
, "CONTENT_LENGTH");
258 DEF_GLOBAL(content_type
, "CONTENT_TYPE");
259 DEF_GLOBAL(fragment
, "FRAGMENT");
260 DEF_GLOBAL(path_info
, "PATH_INFO");
261 DEF_GLOBAL(query_string
, "QUERY_STRING");
262 DEF_GLOBAL(request_body
, "REQUEST_BODY");
263 DEF_GLOBAL(request_method
, "REQUEST_METHOD");
264 DEF_GLOBAL(request_path
, "REQUEST_PATH");
265 DEF_GLOBAL(request_uri
, "REQUEST_URI");
266 DEF_GLOBAL(server_port
, "SERVER_PORT");
267 DEF_GLOBAL(http_client_ip
, "HTTP_CLIENT_IP");
268 DEF_GLOBAL(http_prefix
, "HTTP_");
269 DEF_GLOBAL(http_version
, "HTTP_VERSION");
271 rb_define_singleton_method(mFFI
, "server_process_connections", server_process_connections
, 0);
272 rb_define_singleton_method(mFFI
, "server_listen_on_fd", server_listen_on_fd
, 1);
273 rb_define_singleton_method(mFFI
, "server_listen_on_port", server_listen_on_port
, 1);
274 rb_define_singleton_method(mFFI
, "server_unlisten", server_unlisten
, 0);
275 rb_define_singleton_method(mFFI
, "server_open?", server_open
, 0);
277 cClient
= rb_define_class_under(mEbb
, "Client", rb_cObject
);
278 rb_define_singleton_method(mFFI
, "client_read_input", client_read_input
, 2);
279 rb_define_singleton_method(mFFI
, "client_write_status", client_write_status
, 3);
280 rb_define_singleton_method(mFFI
, "client_write_header", client_write_header
, 3);
281 rb_define_singleton_method(mFFI
, "client_write_body", client_write_body
, 2);
282 rb_define_singleton_method(mFFI
, "client_env", client_env
, 1);
283 rb_define_singleton_method(mFFI
, "client_release", client_release
, 1);
285 /* initialize ebb_server */
286 loop
= ev_default_loop (0);
288 ev_idle_init (&idle_watcher
, idle_cb
);
289 attach_idle_watcher();
291 server
= ebb_server_alloc();
292 VALUE waiting_clients
= rb_ary_new();
293 rb_iv_set(mFFI
, "@waiting_clients", waiting_clients
);
294 ebb_server_init(server
, loop
, request_cb
, (void*)waiting_clients
);