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.
12 static VALUE global_http_prefix
;
13 static VALUE global_request_method
;
14 static VALUE global_request_uri
;
15 static VALUE global_fragment
;
16 static VALUE global_request_path
;
17 static VALUE global_query_string
;
18 static VALUE global_http_version
;
19 static VALUE global_request_body
;
20 static VALUE global_server_port
;
21 static VALUE global_path_info
;
22 static VALUE global_content_length
;
23 static VALUE global_http_host
;
25 /* You don't want to run more than one server per Ruby VM. Really
26 * I'm making this explicit by not defining a Ebb::Server class but instead
27 * initializing a single server and single event loop on module load.
29 static ebb_server
*server
;
32 /* Variables with a leading underscore are C-level variables */
34 #define ASCII_UPPER(ch) ('a' <= ch && ch <= 'z' ? ch - 'a' + 'A' : ch)
36 # define RSTRING_PTR(s) (RSTRING(s)->ptr)
37 # define RSTRING_LEN(s) (RSTRING(s)->len)
40 void request_cb(ebb_client
*client
, void *data
)
42 VALUE waiting_clients
= (VALUE
)data
;
43 VALUE rb_client
= Data_Wrap_Struct(cClient
, 0, 0, client
);
44 rb_ary_push(waiting_clients
, rb_client
);
47 VALUE
server_listen_on_port(VALUE _
, VALUE port
)
49 if(ebb_server_listen_on_port(server
, FIX2INT(port
)) < 0)
50 rb_sys_fail("Problem listening on port");
55 oneshot_timeout (struct ev_loop
*loop
, struct ev_timer
*w
, int revents
) {;}
57 VALUE
server_process_connections(VALUE _
)
59 /* This function is super hacky. The libev loop is called for one iteration
60 * this means that any pending events are handled. If no events exist then
61 * the function blocks. We want blocking so that the while loop in ruby
62 * doesn't race away - however there is a need to continue to process other
63 * ruby threads which are running. While this function is being called
64 * other ruby threads cannot execute.
65 * So we set this timeout event which breaks the block after 0.1 seconds.
66 * Additionally we make sure that other threads get enough processing time
67 * by calling rb_thread_schedule() many times.
69 * Instead we should probably use rb_thread_select on server->fd when no
70 * clients are in_use? Whatever happens here, one should make sure the
71 * 'wait' benchmark is running as quickly with Ebb as it does with mongrel.
74 ev_timer_init (&timeout
, oneshot_timeout
, 0.1, 0.);
75 ev_timer_start (loop
, &timeout
);
76 ev_loop(loop
, EVLOOP_ONESHOT
);
77 ev_timer_stop(loop
, &timeout
);
79 /* Call rb_thread_schedule() proportional to the number of rb threads running */
80 /* SO HACKY! Anyone have a better way to do this? */
82 for(i
= 0; i
< EBB_MAX_CLIENTS
; i
++)
83 if(server
->clients
[i
].in_use
)
90 VALUE
server_unlisten(VALUE _
)
92 ebb_server_unlisten(server
);
96 VALUE
server_open(VALUE _
)
98 return server
->open
? Qtrue
: Qfalse
;
101 VALUE
env_field(struct ebb_env_item
*item
)
105 case EBB_FIELD_VALUE_PAIR
:
106 f
= rb_str_new(NULL
, RSTRING_LEN(global_http_prefix
) + item
->field_length
);
107 memcpy( RSTRING_PTR(f
)
108 , RSTRING_PTR(global_http_prefix
)
109 , RSTRING_LEN(global_http_prefix
)
112 for(i
= 0; i
< item
->field_length
; i
++) {
113 char *ch
= RSTRING_PTR(f
) + RSTRING_LEN(global_http_prefix
) + i
;
114 *ch
= item
->field
[i
] == '-' ? '_' : ASCII_UPPER(item
->field
[i
]);
117 case EBB_REQUEST_METHOD
: return global_request_method
;
118 case EBB_REQUEST_URI
: return global_request_uri
;
119 case EBB_FRAGMENT
: return global_fragment
;
120 case EBB_REQUEST_PATH
: return global_request_path
;
121 case EBB_QUERY_STRING
: return global_query_string
;
122 case EBB_HTTP_VERSION
: return global_http_version
;
123 case EBB_SERVER_PORT
: return global_server_port
;
124 case EBB_CONTENT_LENGTH
: return global_content_length
;
131 VALUE
env_value(struct ebb_env_item
*item
)
133 if(item
->value_length
> 0)
134 return rb_str_new(item
->value
, item
->value_length
);
140 VALUE
client_env(VALUE _
, VALUE rb_client
)
143 VALUE field
, value
, hash
= rb_hash_new();
146 Data_Get_Struct(rb_client
, ebb_client
, client
);
147 for(i
=0; i
< client
->env_size
; i
++) {
148 field
= env_field(&client
->env
[i
]);
149 value
= env_value(&client
->env
[i
]);
150 rb_hash_aset(hash
, field
, value
);
151 //printf("(%s, %s)\n", StringValuePtr(field), StringValuePtr(value));
154 rb_hash_aset(hash
, global_path_info
, rb_hash_aref(hash
, global_request_path
));
159 VALUE
client_read_input(VALUE _
, VALUE client
, VALUE size
)
164 int _size
= FIX2INT(size
);
165 Data_Get_Struct(client
, ebb_client
, _client
);
167 string
= rb_str_buf_new( _size
);
168 int nread
= ebb_client_read(_client
, RSTRING_PTR(string
), _size
);
169 #if RUBY_VERSION_CODE < 190
170 RSTRING(string
)->len
= nread
;
172 rb_str_set_len(string
, nread
);
176 rb_raise(rb_eRuntimeError
,"There was a problem reading from input (bad tmp file?)");
182 VALUE
client_write_status(VALUE _
, VALUE client
, VALUE status
, VALUE human_status
)
185 Data_Get_Struct(client
, ebb_client
, _client
);
186 ebb_client_write_status(_client
, FIX2INT(status
), StringValuePtr(human_status
));
190 VALUE
client_write_header(VALUE _
, VALUE client
, VALUE field
, VALUE value
)
193 Data_Get_Struct(client
, ebb_client
, _client
);
194 ebb_client_write_header(_client
, StringValuePtr(field
), StringValuePtr(value
));
198 VALUE
client_write(VALUE _
, VALUE client
, VALUE string
)
201 Data_Get_Struct(client
, ebb_client
, _client
);
202 ebb_client_write(_client
, RSTRING_PTR(string
), RSTRING_LEN(string
));
207 VALUE
client_begin_transmission(VALUE _
, VALUE rb_client
)
210 Data_Get_Struct(rb_client
, ebb_client
, client
);
211 client
->status_written
= TRUE
;
212 client
->headers_written
= TRUE
;
213 ebb_client_begin_transmission(client
);
217 VALUE
client_release(VALUE _
, VALUE rb_client
)
220 Data_Get_Struct(rb_client
, ebb_client
, client
);
221 ebb_client_release(client
);
226 VALUE
client_set_body_written(VALUE _
, VALUE rb_client
, VALUE v
)
229 Data_Get_Struct(rb_client
, ebb_client
, client
);
230 client
->body_written
= RTEST(v
);
231 return client
->body_written
? Qtrue
: Qfalse
;
236 VALUE mEbb
= rb_define_module("Ebb");
237 VALUE mFFI
= rb_define_module_under(mEbb
, "FFI");
239 rb_define_const(mEbb
, "VERSION", rb_str_new2(EBB_VERSION
));
241 /** Defines global strings in the init method. */
242 #define DEF_GLOBAL(N, val) global_##N = rb_obj_freeze(rb_str_new2(val)); rb_global_variable(&global_##N)
243 DEF_GLOBAL(http_prefix
, "HTTP_");
244 DEF_GLOBAL(request_method
, "REQUEST_METHOD");
245 DEF_GLOBAL(request_uri
, "REQUEST_URI");
246 DEF_GLOBAL(fragment
, "FRAGMENT");
247 DEF_GLOBAL(request_path
, "REQUEST_PATH");
248 DEF_GLOBAL(query_string
, "QUERY_STRING");
249 DEF_GLOBAL(http_version
, "HTTP_VERSION");
250 DEF_GLOBAL(request_body
, "REQUEST_BODY");
251 DEF_GLOBAL(server_port
, "SERVER_PORT");
252 DEF_GLOBAL(path_info
, "PATH_INFO");
253 DEF_GLOBAL(content_length
, "HTTP_CONTENT_LENGTH");
254 DEF_GLOBAL(http_host
, "HTTP_HOST");
256 rb_define_singleton_method(mFFI
, "server_process_connections", server_process_connections
, 0);
257 rb_define_singleton_method(mFFI
, "server_listen_on_port", server_listen_on_port
, 1);
258 rb_define_singleton_method(mFFI
, "server_unlisten", server_unlisten
, 0);
259 rb_define_singleton_method(mFFI
, "server_open?", server_open
, 0);
261 cClient
= rb_define_class_under(mEbb
, "Client", rb_cObject
);
262 rb_define_singleton_method(mFFI
, "client_read_input", client_read_input
, 2);
263 rb_define_singleton_method(mFFI
, "client_write_status", client_write_status
, 3);
264 rb_define_singleton_method(mFFI
, "client_write_header", client_write_header
, 3);
265 rb_define_singleton_method(mFFI
, "client_write", client_write
, 2);
266 rb_define_singleton_method(mFFI
, "client_begin_transmission", client_begin_transmission
, 1);
267 rb_define_singleton_method(mFFI
, "client_set_body_written", client_set_body_written
, 2);
268 rb_define_singleton_method(mFFI
, "client_env", client_env
, 1);
269 rb_define_singleton_method(mFFI
, "client_release", client_release
, 1);
271 /* initialize ebb_server */
272 loop
= ev_default_loop (0);
273 server
= ebb_server_alloc();
274 VALUE waiting_clients
= rb_ary_new();
275 rb_iv_set(mFFI
, "@waiting_clients", waiting_clients
);
276 ebb_server_init(server
, loop
, request_cb
, (void*)waiting_clients
);