fix -e option
[ebb.git] / src / ebb_ruby.c
blob37d19bbce895a7a5fd42782c94c3125c9660fe89
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 cServer;
12 static VALUE cClient;
13 static VALUE global_http_prefix;
14 static VALUE global_request_method;
15 static VALUE global_request_uri;
16 static VALUE global_fragment;
17 static VALUE global_request_path;
18 static VALUE global_query_string;
19 static VALUE global_http_version;
20 static VALUE global_request_body;
21 static VALUE global_server_name;
22 static VALUE global_server_port;
23 static VALUE global_path_info;
24 static VALUE global_content_length;
25 static VALUE global_http_host;
27 /* Variables with a leading underscore are C-level variables */
29 #define ASCII_UPPER(ch) ('a' <= ch && ch <= 'z' ? ch - 'a' + 'A' : ch)
30 #ifndef RSTRING_PTR
31 # define RSTRING_PTR(s) (RSTRING(s)->ptr)
32 # define RSTRING_LEN(s) (RSTRING(s)->len)
33 #endif
35 VALUE client_new(ebb_client *_client)
37 VALUE client = Data_Wrap_Struct(cClient, 0, 0, _client);
38 return client;
42 void request_cb(ebb_client *_client, void *data)
44 VALUE server = (VALUE)data;
45 VALUE waiting_clients;
46 VALUE client = client_new(_client);
48 waiting_clients = rb_iv_get(server, "@waiting_clients");
49 rb_ary_push(waiting_clients, client);
53 VALUE server_alloc(VALUE self)
55 struct ev_loop *loop = ev_default_loop (0);
56 ebb_server *_server = ebb_server_alloc();
57 VALUE server = Qnil;
58 server = Data_Wrap_Struct(cServer, 0, ebb_server_free, _server);
59 ebb_server_init(_server, loop, request_cb, (void*)server);
60 return server;
64 VALUE server_listen_on_port(VALUE x, VALUE server, VALUE port)
66 ebb_server *_server;
67 Data_Get_Struct(server, ebb_server, _server);
68 int r = ebb_server_listen_on_port(_server, FIX2INT(port));
69 return r < 0 ? Qfalse : Qtrue;
73 VALUE server_listen_on_socket(VALUE x, VALUE server, VALUE socketpath)
75 ebb_server *_server;
76 Data_Get_Struct(server, ebb_server, _server);
77 int r = ebb_server_listen_on_socket(_server, StringValuePtr(socketpath));
78 return r < 0 ? Qfalse : Qtrue;
82 static void
83 oneshot_timeout (struct ev_loop *loop, struct ev_timer *w, int revents) {;}
86 VALUE server_process_connections(VALUE x, VALUE server)
88 ebb_server *_server;
89 VALUE host, port;
91 Data_Get_Struct(server, ebb_server, _server);
93 ev_timer timeout;
94 ev_timer_init (&timeout, oneshot_timeout, 0.5, 0.);
95 ev_timer_start (_server->loop, &timeout);
97 ev_loop(_server->loop, EVLOOP_ONESHOT);
98 /* XXX: Need way to know when the loop is finished...
99 * should return true or false */
101 ev_timer_stop(_server->loop, &timeout);
103 if(_server->open)
104 return Qtrue;
105 else
106 return Qfalse;
110 VALUE server_unlisten(VALUE x, VALUE server)
112 ebb_server *_server;
113 Data_Get_Struct(server, ebb_server, _server);
114 ebb_server_unlisten(_server);
115 return Qnil;
119 VALUE env_field(struct ebb_env_item *item)
121 VALUE f;
122 switch(item->type) {
123 case EBB_FIELD_VALUE_PAIR:
124 f = rb_str_new(NULL, RSTRING_LEN(global_http_prefix) + item->field_length);
125 memcpy( RSTRING_PTR(f)
126 , RSTRING_PTR(global_http_prefix)
127 , RSTRING_LEN(global_http_prefix)
129 int i;
130 for(i = 0; i < item->field_length; i++) {
131 char *ch = RSTRING_PTR(f) + RSTRING_LEN(global_http_prefix) + i;
132 *ch = item->field[i] == '-' ? '_' : ASCII_UPPER(item->field[i]);
134 return f;
135 case EBB_REQUEST_METHOD: return global_request_method;
136 case EBB_REQUEST_URI: return global_request_uri;
137 case EBB_FRAGMENT: return global_fragment;
138 case EBB_REQUEST_PATH: return global_request_path;
139 case EBB_QUERY_STRING: return global_query_string;
140 case EBB_HTTP_VERSION: return global_http_version;
141 case EBB_SERVER_NAME: return global_server_name;
142 case EBB_SERVER_PORT: return global_server_port;
143 case EBB_CONTENT_LENGTH: return global_content_length;
145 assert(FALSE);
146 return Qnil;
150 VALUE env_value(struct ebb_env_item *item)
152 if(item->value_length > 0)
153 return rb_str_new(item->value, item->value_length);
154 else
155 return Qnil;
159 VALUE client_env(VALUE x, VALUE client)
161 ebb_client *_client;
162 VALUE hash = rb_hash_new();
163 int i;
164 Data_Get_Struct(client, ebb_client, _client);
166 for(i=0; i < _client->env_size; i++) {
167 rb_hash_aset(hash, env_field(&_client->env[i])
168 , env_value(&_client->env[i])
171 rb_hash_aset(hash, global_path_info, rb_hash_aref(hash, global_request_path));
172 return hash;
176 VALUE client_read_input(VALUE x, VALUE client, VALUE size)
178 ebb_client *_client;
179 GString *_string;
180 VALUE string;
181 int _size = FIX2INT(size);
182 Data_Get_Struct(client, ebb_client, _client);
184 string = rb_str_buf_new( _size );
185 int nread = ebb_client_read(_client, RSTRING_PTR(string), _size);
186 #if RUBY_VERSION_CODE < 190
187 RSTRING(string)->len = nread;
188 #else
189 rb_str_set_len(string, nread);
190 #endif
192 if(nread < 0)
193 rb_raise(rb_eRuntimeError,"There was a problem reading from input (bad tmp file?)");
194 if(nread == 0)
195 return Qnil;
196 return string;
199 VALUE client_write_status(VALUE x, VALUE client, VALUE status, VALUE human_status)
201 ebb_client *_client;
202 Data_Get_Struct(client, ebb_client, _client);
203 ebb_client_write_status(_client, FIX2INT(status), StringValuePtr(human_status));
204 return Qnil;
207 VALUE client_write_header(VALUE x, VALUE client, VALUE field, VALUE value)
209 ebb_client *_client;
210 Data_Get_Struct(client, ebb_client, _client);
211 ebb_client_write_header(_client, StringValuePtr(field), StringValuePtr(value));
212 return Qnil;
215 VALUE client_write(VALUE x, VALUE client, VALUE string)
217 ebb_client *_client;
218 Data_Get_Struct(client, ebb_client, _client);
219 ebb_client_write(_client, RSTRING_PTR(string), RSTRING_LEN(string));
220 return Qnil;
224 VALUE client_finished(VALUE x, VALUE client)
226 ebb_client *_client;
227 Data_Get_Struct(client, ebb_client, _client);
228 ebb_client_finished(_client);
229 return Qnil;
233 void Init_ebb_ext()
235 VALUE mEbb = rb_define_module("Ebb");
236 VALUE mFFI = rb_define_module_under(mEbb, "FFI");
238 /** Defines global strings in the init method. */
239 #define DEF_GLOBAL(N, val) global_##N = rb_obj_freeze(rb_str_new2(val)); rb_global_variable(&global_##N)
240 DEF_GLOBAL(http_prefix, "HTTP_");
241 DEF_GLOBAL(request_method, "REQUEST_METHOD");
242 DEF_GLOBAL(request_uri, "REQUEST_URI");
243 DEF_GLOBAL(fragment, "FRAGMENT");
244 DEF_GLOBAL(request_path, "REQUEST_PATH");
245 DEF_GLOBAL(query_string, "QUERY_STRING");
246 DEF_GLOBAL(http_version, "HTTP_VERSION");
247 DEF_GLOBAL(request_body, "REQUEST_BODY");
248 DEF_GLOBAL(server_name, "SERVER_NAME");
249 DEF_GLOBAL(server_port, "SERVER_PORT");
250 DEF_GLOBAL(path_info, "PATH_INFO");
251 DEF_GLOBAL(content_length, "CONTENT_LENGTH");
252 DEF_GLOBAL(http_host, "HTTP_HOST");
254 cServer = rb_define_class_under(mEbb, "Server", rb_cObject);
255 rb_define_alloc_func(cServer, server_alloc);
256 rb_define_singleton_method(mFFI, "server_process_connections", server_process_connections, 1);
257 rb_define_singleton_method(mFFI, "server_listen_on_port", server_listen_on_port, 2);
258 rb_define_singleton_method(mFFI, "server_listen_on_socket", server_listen_on_socket, 2);
259 rb_define_singleton_method(mFFI, "server_unlisten", server_unlisten, 1);
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_finished", client_finished, 1);
267 rb_define_singleton_method(mFFI, "client_env", client_env, 1);