undoing even the last commit! it was too slow still!
[ebb.git] / src / ebb_ruby.c
blob039aabce6e49ca38fae174d83d723e7adb82e993
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 <assert.h>
7 #include <fcntl.h>
8 #include <ebb.h>
9 #include <ev.h>
11 static VALUE cClient;
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;
30 struct ev_loop *loop;
32 /* Variables with a leading underscore are C-level variables */
34 #define ASCII_UPPER(ch) ('a' <= ch && ch <= 'z' ? ch - 'a' + 'A' : ch)
35 #ifndef RSTRING_PTR
36 # define RSTRING_PTR(s) (RSTRING(s)->ptr)
37 # define RSTRING_LEN(s) (RSTRING(s)->len)
38 #endif
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");
51 return Qnil;
54 static void
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.
73 ev_timer timeout;
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? */
81 int i;
82 for(i = 0; i < EBB_MAX_CLIENTS; i++)
83 if(server->clients[i].in_use)
84 rb_thread_schedule();
86 return Qnil;
90 VALUE server_unlisten(VALUE _)
92 ebb_server_unlisten(server);
93 return Qnil;
96 VALUE server_open(VALUE _)
98 return server->open ? Qtrue : Qfalse;
101 VALUE env_field(struct ebb_env_item *item)
103 VALUE f;
104 switch(item->type) {
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)
111 int i;
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]);
116 return f;
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;
126 assert(FALSE);
127 return Qnil;
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);
135 else
136 return Qnil;
140 VALUE client_env(VALUE _, VALUE rb_client)
142 ebb_client *client;
143 VALUE field, value, hash = rb_hash_new();
144 int i;
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));
153 //printf("\n\n");
154 rb_hash_aset(hash, global_path_info, rb_hash_aref(hash, global_request_path));
155 return hash;
159 VALUE client_read_input(VALUE _, VALUE client, VALUE size)
161 ebb_client *_client;
162 GString *_string;
163 VALUE string;
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;
171 #else
172 rb_str_set_len(string, nread);
173 #endif
175 if(nread < 0)
176 rb_raise(rb_eRuntimeError,"There was a problem reading from input (bad tmp file?)");
177 if(nread == 0)
178 return Qnil;
179 return string;
182 VALUE client_write_status(VALUE _, VALUE client, VALUE status, VALUE human_status)
184 ebb_client *_client;
185 Data_Get_Struct(client, ebb_client, _client);
186 ebb_client_write_status(_client, FIX2INT(status), StringValuePtr(human_status));
187 return Qnil;
190 VALUE client_write_header(VALUE _, VALUE client, VALUE field, VALUE value)
192 ebb_client *_client;
193 Data_Get_Struct(client, ebb_client, _client);
194 ebb_client_write_header(_client, StringValuePtr(field), StringValuePtr(value));
195 return Qnil;
198 VALUE client_write(VALUE _, VALUE client, VALUE string)
200 ebb_client *_client;
201 Data_Get_Struct(client, ebb_client, _client);
202 ebb_client_write(_client, RSTRING_PTR(string), RSTRING_LEN(string));
203 return Qnil;
207 VALUE client_begin_transmission(VALUE _, VALUE rb_client)
209 ebb_client *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);
214 return Qnil;
217 VALUE client_release(VALUE _, VALUE rb_client)
219 ebb_client *client;
220 Data_Get_Struct(rb_client, ebb_client, client);
221 ebb_client_release(client);
222 return Qnil;
226 VALUE client_set_body_written(VALUE _, VALUE rb_client, VALUE v)
228 ebb_client *client;
229 Data_Get_Struct(rb_client, ebb_client, client);
230 client->body_written = RTEST(v);
231 return client->body_written ? Qtrue : Qfalse;
234 void Init_ebb_ext()
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);