ebb 0.0.1

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.
@@ -0,0 +1,152 @@
1
+ require 'cgi'
2
+
3
+ # Adapter to run a Rails app with any supported Rack handler.
4
+ # By default it will try to load the Rails application in the
5
+ # current directory in the development environment.
6
+ # Options:
7
+ # root: Root directory of the Rails app
8
+ # env: Rails environment to run in (development, production or test)
9
+ # Based on http://fuzed.rubyforge.org/ Rails adapter
10
+ module Rack
11
+ module Adapter
12
+ class Rails
13
+ def initialize(options={})
14
+ @root = options[:root] || Dir.pwd
15
+ @env = options[:environment] || 'development'
16
+ @prefix = options[:prefix]
17
+
18
+ load_application
19
+
20
+ @file_server = Rack::File.new(::File.join(RAILS_ROOT, "public"))
21
+ end
22
+
23
+ def load_application
24
+ ENV['RAILS_ENV'] = @env
25
+
26
+ require "#{@root}/config/environment"
27
+ require 'dispatcher'
28
+
29
+ ActionController::AbstractRequest.relative_url_root = @prefix if @prefix
30
+ end
31
+
32
+ # TODO refactor this in File#can_serve?(path) ??
33
+ def file_exist?(path)
34
+ full_path = ::File.join(@file_server.root, Utils.unescape(path))
35
+ ::File.file?(full_path) && ::File.readable?(full_path)
36
+ end
37
+
38
+ def serve_file(env)
39
+ @file_server.call(env)
40
+ end
41
+
42
+ def serve_rails(env)
43
+ request = Request.new(env)
44
+ response = Response.new
45
+
46
+ session_options = ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS
47
+ cgi = CGIWrapper.new(request, response)
48
+
49
+ Dispatcher.dispatch(cgi, session_options, response)
50
+
51
+ response.finish
52
+ end
53
+
54
+ def call(env)
55
+ path = env['PATH_INFO'].chomp('/')
56
+ cached_path = (path.empty? ? 'index' : path) + ActionController::Base.page_cache_extension
57
+
58
+ if file_exist?(path) # Serve the file if it's there
59
+ serve_file(env)
60
+ elsif file_exist?(cached_path) # Serve the page cache if it's there
61
+ env['PATH_INFO'] = cached_path
62
+ serve_file(env)
63
+ else # No static file, let Rails handle it
64
+ serve_rails(env)
65
+ end
66
+ end
67
+
68
+ protected
69
+
70
+ class CGIWrapper < ::CGI
71
+ def initialize(request, response, *args)
72
+ @request = request
73
+ @response = response
74
+ @args = *args
75
+ @input = request.body
76
+
77
+ super *args
78
+ end
79
+
80
+ def header(options = "text/html")
81
+ if options.is_a?(String)
82
+ @response['Content-Type'] = options unless @response['Content-Type']
83
+ else
84
+ @response['Content-Length'] = options.delete('Content-Length').to_s if options['Content-Length']
85
+
86
+ @response['Content-Type'] = options.delete('type') || "text/html"
87
+ @response['Content-Type'] += "; charset=" + options.delete('charset') if options['charset']
88
+
89
+ @response['Content-Language'] = options.delete('language') if options['language']
90
+ @response['Expires'] = options.delete('expires') if options['expires']
91
+
92
+ @response.status = options.delete('Status') if options['Status']
93
+
94
+ # Convert 'cookie' header to 'Set-Cookie' headers.
95
+ # Because Set-Cookie header can appear more the once in the response body,
96
+ # we store it in a line break seperated string that will be translated to
97
+ # multiple Set-Cookie header by the handler.
98
+ if cookie = options.delete('cookie')
99
+ cookies = []
100
+
101
+ case cookie
102
+ when Array then cookie.each { |c| cookies << c.to_s }
103
+ when Hash then cookie.each { |_, c| cookies << c.to_s }
104
+ else cookies << cookie.to_s
105
+ end
106
+
107
+ @output_cookies.each { |c| cookies << c.to_s } if @output_cookies
108
+
109
+ @response['Set-Cookie'] = [@response['Set-Cookie'], cookies].compact.join("\n")
110
+ end
111
+
112
+ options.each { |k,v| @response[k] = v }
113
+ end
114
+
115
+ ""
116
+ end
117
+
118
+ def params
119
+ @params ||= @request.params
120
+ end
121
+
122
+ def cookies
123
+ @request.cookies
124
+ end
125
+
126
+ def query_string
127
+ @request.query_string
128
+ end
129
+
130
+ # Used to wrap the normal args variable used inside CGI.
131
+ def args
132
+ @args
133
+ end
134
+
135
+ # Used to wrap the normal env_table variable used inside CGI.
136
+ def env_table
137
+ @request.env
138
+ end
139
+
140
+ # Used to wrap the normal stdinput variable used inside CGI.
141
+ def stdinput
142
+ @input
143
+ end
144
+
145
+ def stdoutput
146
+ STDERR.puts "stdoutput should not be used."
147
+ @response.body
148
+ end
149
+ end
150
+ end
151
+ end
152
+ end
data/src/ebb.c ADDED
@@ -0,0 +1,709 @@
1
+ /* Ebb Web Server
2
+ * Copyright (c) 2007 Ry Dahl
3
+ * This software is released under the "MIT License". See README file for details.
4
+ */
5
+ #include <unistd.h>
6
+ #include <fcntl.h>
7
+ #include <sys/types.h>
8
+ #include <arpa/inet.h>
9
+ #include <netinet/tcp.h>
10
+ #include <sys/un.h>
11
+ #include <netdb.h>
12
+
13
+ #include <stdio.h>
14
+ #include <string.h>
15
+ #include <stdlib.h>
16
+ #include <errno.h>
17
+ #include <signal.h>
18
+ #include <assert.h>
19
+
20
+ #include <pthread.h>
21
+ #include <glib.h>
22
+
23
+ #define EV_STANDALONE 1
24
+ #include <ev.c>
25
+
26
+ #include "parser.h"
27
+ #include "ebb.h"
28
+
29
+ #define min(a,b) (a < b ? a : b)
30
+ #define ramp(a) (a > 0 ? a : 0)
31
+
32
+ static int server_socket(const int port);
33
+ static int server_socket_unix(const char *path, int access_mask);
34
+
35
+ #define env_add(client, field,flen,value,vlen) \
36
+ client->env_fields[client->env_size] = field; \
37
+ client->env_field_lengths[client->env_size] = flen; \
38
+ client->env_values[client->env_size] = value; \
39
+ client->env_value_lengths[client->env_size] = vlen; \
40
+ client->env_size += 1;
41
+ #define env_add_const(client,field,value,vlen) \
42
+ client->env_fields[client->env_size] = NULL; \
43
+ client->env_field_lengths[client->env_size] = field; \
44
+ client->env_values[client->env_size] = value; \
45
+ client->env_value_lengths[client->env_size] = vlen; \
46
+ client->env_size += 1;
47
+ #define env_error(client) \
48
+ client->env_fields[client->env_size] = NULL; \
49
+ client->env_field_lengths[client->env_size] = -1; \
50
+ client->env_values[client->env_size] = NULL; \
51
+ client->env_value_lengths[client->env_size] = -1; \
52
+ client->env_size += 1;
53
+
54
+ int env_has_error(ebb_client *client)
55
+ {
56
+ int i;
57
+ for(i = 0; i < client->env_size; i++)
58
+ if(client->env_field_lengths[i] < 0)
59
+ return TRUE;
60
+ return FALSE;
61
+ }
62
+
63
+ /** Defines common length and error messages for input length validation. */
64
+ #define DEF_MAX_LENGTH(N,length) const size_t MAX_##N##_LENGTH = length; const char *MAX_##N##_LENGTH_ERR = "HTTP Parse Error: HTTP element " # N " is longer than the " # length " allowed length."
65
+
66
+ /** Validates the max length of given input and throws an exception if over. */
67
+ #define VALIDATE_MAX_LENGTH(len, N) if(len > MAX_##N##_LENGTH) { env_error(client); g_message(MAX_##N##_LENGTH_ERR); }
68
+
69
+ /* Defines the maximum allowed lengths for various input elements.*/
70
+ DEF_MAX_LENGTH(FIELD_NAME, 256);
71
+ DEF_MAX_LENGTH(FIELD_VALUE, 80 * 1024);
72
+ DEF_MAX_LENGTH(REQUEST_URI, 1024 * 12);
73
+ DEF_MAX_LENGTH(FRAGMENT, 1024); /* Don't know if this length is specified somewhere or not */
74
+ DEF_MAX_LENGTH(REQUEST_PATH, 1024);
75
+ DEF_MAX_LENGTH(QUERY_STRING, (1024 * 10));
76
+ DEF_MAX_LENGTH(HEADER, (1024 * (80 + 32)));
77
+
78
+ void http_field_cb(void *data, const char *field, size_t flen, const char *value, size_t vlen)
79
+ {
80
+ ebb_client *client = (ebb_client*)(data);
81
+ VALIDATE_MAX_LENGTH(flen, FIELD_NAME);
82
+ VALIDATE_MAX_LENGTH(vlen, FIELD_VALUE);
83
+ env_add(client, field, flen, value, vlen);
84
+ }
85
+
86
+
87
+ void request_method_cb(void *data, const char *at, size_t length)
88
+ {
89
+ ebb_client *client = (ebb_client*)(data);
90
+ env_add_const(client, EBB_REQUEST_METHOD, at, length);
91
+ }
92
+
93
+
94
+ void request_uri_cb(void *data, const char *at, size_t length)
95
+ {
96
+ ebb_client *client = (ebb_client*)(data);
97
+ VALIDATE_MAX_LENGTH(length, REQUEST_URI);
98
+ env_add_const(client, EBB_REQUEST_URI, at, length);
99
+ }
100
+
101
+
102
+ void fragment_cb(void *data, const char *at, size_t length)
103
+ {
104
+ ebb_client *client = (ebb_client*)(data);
105
+ VALIDATE_MAX_LENGTH(length, FRAGMENT);
106
+ env_add_const(client, EBB_FRAGMENT, at, length);
107
+ }
108
+
109
+
110
+ void request_path_cb(void *data, const char *at, size_t length)
111
+ {
112
+ ebb_client *client = (ebb_client*)(data);
113
+ VALIDATE_MAX_LENGTH(length, REQUEST_PATH);
114
+ env_add_const(client, EBB_REQUEST_PATH, at, length);
115
+ }
116
+
117
+
118
+ void query_string_cb(void *data, const char *at, size_t length)
119
+ {
120
+ ebb_client *client = (ebb_client*)(data);
121
+ VALIDATE_MAX_LENGTH(length, QUERY_STRING);
122
+ env_add_const(client, EBB_QUERY_STRING, at, length);
123
+ }
124
+
125
+
126
+ void http_version_cb(void *data, const char *at, size_t length)
127
+ {
128
+ ebb_client *client = (ebb_client*)(data);
129
+ env_add_const(client, EBB_HTTP_VERSION, at, length);
130
+ }
131
+
132
+
133
+ void content_length_cb(void *data, const char *at, size_t length)
134
+ {
135
+ ebb_client *client = (ebb_client*)(data);
136
+ env_add_const(client, EBB_CONTENT_LENGTH, at, length);
137
+
138
+ /* i hate c. */
139
+ char buf[20];
140
+ strncpy(buf, at, length);
141
+ buf[length] = '\0';
142
+ client->content_length = atoi(buf);
143
+ }
144
+
145
+
146
+ const char* localhost_str = "0.0.0.0";
147
+ void dispatch(ebb_client *client)
148
+ {
149
+ ebb_server *server = client->server;
150
+
151
+ if(client->open == FALSE)
152
+ return;
153
+
154
+ /* Set the env variables */
155
+ if(server->port) {
156
+ env_add_const(client, EBB_SERVER_NAME
157
+ , localhost_str
158
+ , 7
159
+ );
160
+ env_add_const(client, EBB_SERVER_PORT
161
+ , server->port
162
+ , strlen(server->port)
163
+ );
164
+ }
165
+ server->request_cb(client, server->request_cb_data);
166
+ }
167
+
168
+
169
+ void on_timeout(struct ev_loop *loop, ev_timer *watcher, int revents)
170
+ {
171
+ ebb_client *client = (ebb_client*)(watcher->data);
172
+
173
+ assert(client->server->loop == loop);
174
+ assert(&(client->timeout_watcher) == watcher);
175
+
176
+ ebb_client_close(client);
177
+ #ifdef DEBUG
178
+ g_message("peer timed out");
179
+ #endif
180
+ }
181
+
182
+ #define client_finished_parsing http_parser_is_finished(&client->parser)
183
+ #define total_request_size (client->content_length + client->parser.nread)
184
+
185
+ void* read_body_into_file(void *_client)
186
+ {
187
+ ebb_client *client = (ebb_client*)_client;
188
+ static unsigned int id;
189
+ FILE *tmpfile;
190
+
191
+ assert(client->open);
192
+ assert(client->server->open);
193
+ assert(client->content_length > 0);
194
+ assert(client_finished_parsing);
195
+
196
+ /* set blocking socket */
197
+ int flags = fcntl(client->fd, F_GETFL, 0);
198
+ assert(0 <= fcntl(client->fd, F_SETFL, flags & ~O_NONBLOCK));
199
+
200
+ sprintf(client->upload_file_filename, "/tmp/ebb_upload_%010d", id++);
201
+ tmpfile = fopen(client->upload_file_filename, "w+");
202
+ if(tmpfile == NULL) g_message("Cannot open tmpfile %s", client->upload_file_filename);
203
+ client->upload_file = tmpfile;
204
+
205
+ size_t body_head_length = client->read - client->parser.nread;
206
+ size_t written = 0, r;
207
+ while(written < body_head_length) {
208
+ r = fwrite( client->request_buffer + sizeof(char)*(client->parser.nread + written)
209
+ , sizeof(char)
210
+ , body_head_length - written
211
+ , tmpfile
212
+ );
213
+ if(r <= 0) {
214
+ ebb_client_close(client);
215
+ return NULL;
216
+ }
217
+ written += r;
218
+ }
219
+
220
+ // g_debug("wrote request header to file. written: %d, content_length: %d", written, client->content_length);
221
+
222
+ int bufsize = 5*1024;
223
+ char buffer[bufsize];
224
+ size_t received;
225
+ while(written < client->content_length) {
226
+ received = recv(client->fd
227
+ , buffer
228
+ , min(client->content_length - written, bufsize)
229
+ , 0
230
+ );
231
+ if(received < 0) goto error;
232
+ client->read += received;
233
+
234
+ ssize_t w = 0;
235
+ int rv;
236
+ while(w < received) {
237
+ rv = fwrite( buffer + w*sizeof(char)
238
+ , sizeof(char)
239
+ , received - w
240
+ , tmpfile
241
+ );
242
+ if(rv <= 0) goto error;
243
+ w += rv;
244
+ }
245
+ written += received;
246
+ }
247
+ rewind(tmpfile);
248
+ // g_debug("%d bytes written to file %s", written, client->upload_file_filename);
249
+ return NULL;
250
+ error:
251
+ ebb_client_close(client);
252
+ return NULL;
253
+ }
254
+
255
+
256
+ void on_readable(struct ev_loop *loop, ev_io *watcher, int revents)
257
+ {
258
+ ebb_client *client = (ebb_client*)(watcher->data);
259
+
260
+ assert(client->open);
261
+ assert(client->server->open);
262
+ assert(client->server->loop == loop);
263
+ assert(&client->read_watcher == watcher);
264
+
265
+ ssize_t read = recv( client->fd
266
+ , client->request_buffer + client->read
267
+ , EBB_BUFFERSIZE - client->read - 1 /* -1 is for making ragel happy below */
268
+ , 0
269
+ );
270
+ if(read <= 0) goto error; /* XXX is this the right action to take for read==0 ? */
271
+ client->read += read;
272
+ ev_timer_again(loop, &client->timeout_watcher);
273
+
274
+ if(FALSE == client_finished_parsing) {
275
+ client->request_buffer[client->read] = '\0'; /* make ragel happy */
276
+ http_parser_execute( &client->parser
277
+ , client->request_buffer
278
+ , client->read
279
+ , client->parser.nread
280
+ );
281
+ if(http_parser_has_error(&client->parser)) goto error;
282
+ }
283
+
284
+ if(total_request_size == client->read) {
285
+ ev_io_stop(loop, watcher);
286
+ client->nread_from_body = 0;
287
+ dispatch(client);
288
+ return;
289
+ }
290
+
291
+ if(client_finished_parsing && total_request_size > EBB_BUFFERSIZE ) {
292
+ /* read body into file - in a thread */
293
+ pthread_t thread;
294
+ ev_io_stop(loop, watcher);
295
+ assert(0 <= pthread_create(&thread, NULL, read_body_into_file, client));
296
+ pthread_join(thread, NULL);
297
+ dispatch(client);
298
+ return;
299
+ }
300
+ return;
301
+ error:
302
+ if(read < 0) g_message("Error recving data: %s", strerror(errno));
303
+ ebb_client_close(client);
304
+ }
305
+
306
+ void on_request(struct ev_loop *loop, ev_io *watcher, int revents)
307
+ {
308
+ ebb_server *server = (ebb_server*)(watcher->data);
309
+ assert(server->open);
310
+ assert(server->loop == loop);
311
+ assert(&server->request_watcher == watcher);
312
+
313
+ if(EV_ERROR & revents) {
314
+ g_message("on_request() got error event, closing server.");
315
+ ebb_server_unlisten(server);
316
+ return;
317
+ }
318
+ /* Now we're going to initialize the client
319
+ * and set up her callbacks for read and write
320
+ * the client won't get passed back to the user, however,
321
+ * until the request is complete and parsed.
322
+ */
323
+ int i;
324
+ ebb_client *client;
325
+ /* Get next availible peer */
326
+ for(i=0; i < EBB_MAX_CLIENTS; i++)
327
+ if(!server->clients[i].open) {
328
+ client = &(server->clients[i]);
329
+ break;
330
+ }
331
+ if(client == NULL) {
332
+ g_message("Too many peers. Refusing connections.");
333
+ return;
334
+ }
335
+
336
+ #ifdef DEBUG
337
+ int count = 0;
338
+ for(i = 0; i < EBB_MAX_CLIENTS; i++)
339
+ if(server->clients[i].open) count += 1;
340
+ g_debug("%d open connections", count);
341
+ #endif
342
+ client->open = TRUE;
343
+ client->server = server;
344
+
345
+ /* DO SOCKET STUFF */
346
+ socklen_t len;
347
+ client->fd = accept(server->fd, (struct sockaddr*)&(server->sockaddr), &len);
348
+ assert(client->fd >= 0);
349
+ int flags = fcntl(client->fd, F_GETFL, 0);
350
+ assert(0 <= fcntl(client->fd, F_SETFL, flags | O_NONBLOCK));
351
+
352
+ /* INITIALIZE http_parser */
353
+ http_parser_init(&(client->parser));
354
+ client->parser.data = client;
355
+ client->parser.http_field = http_field_cb;
356
+ client->parser.request_method = request_method_cb;
357
+ client->parser.request_uri = request_uri_cb;
358
+ client->parser.fragment = fragment_cb;
359
+ client->parser.request_path = request_path_cb;
360
+ client->parser.query_string = query_string_cb;
361
+ client->parser.http_version = http_version_cb;
362
+ client->parser.content_length = content_length_cb;
363
+
364
+ /* OTHER */
365
+ client->env_size = 0;
366
+ client->read = client->nread_from_body = 0;
367
+ client->response_buffer->len = 0; /* see note in ebb_client_close */
368
+ client->content_length = 0;
369
+
370
+ /* SETUP READ AND TIMEOUT WATCHERS */
371
+ client->read_watcher.data = client;
372
+ ev_init(&client->read_watcher, on_readable);
373
+ ev_io_set(&client->read_watcher, client->fd, EV_READ | EV_ERROR);
374
+ ev_io_start(server->loop, &client->read_watcher);
375
+
376
+ client->timeout_watcher.data = client;
377
+ ev_timer_init(&client->timeout_watcher, on_timeout, EBB_TIMEOUT, EBB_TIMEOUT);
378
+ ev_timer_start(server->loop, &client->timeout_watcher);
379
+ }
380
+
381
+
382
+ ebb_server* ebb_server_alloc()
383
+ {
384
+ ebb_server *server = g_new0(ebb_server, 1);
385
+ return server;
386
+ }
387
+
388
+
389
+ void ebb_server_init( ebb_server *server
390
+ , struct ev_loop *loop
391
+ , ebb_request_cb request_cb
392
+ , void *request_cb_data
393
+ )
394
+ {
395
+ int i;
396
+ for(i=0; i < EBB_MAX_CLIENTS; i++)
397
+ server->clients[i].response_buffer = g_string_new("");
398
+
399
+ server->request_cb = request_cb;
400
+ server->request_cb_data = request_cb_data;
401
+ server->loop = loop;
402
+ server->open = FALSE;
403
+ server->fd = -1;
404
+ return;
405
+ error:
406
+ ebb_server_free(server);
407
+ return;
408
+ }
409
+
410
+
411
+ void ebb_server_free(ebb_server *server)
412
+ {
413
+ ebb_server_unlisten(server);
414
+
415
+ int i;
416
+ for(i=0; i < EBB_MAX_CLIENTS; i++)
417
+ g_string_free(server->clients[i].response_buffer, TRUE);
418
+ if(server->port)
419
+ free(server->port);
420
+ if(server->socketpath)
421
+ free(server->socketpath);
422
+ free(server);
423
+ }
424
+
425
+
426
+ void ebb_server_unlisten(ebb_server *server)
427
+ {
428
+ if(server->open) {
429
+ //g_message("Stopping Ebb server");
430
+ int i;
431
+ ebb_client *client;
432
+ //for(i=0; i < EBB_MAX_CLIENTS; i++)
433
+ // ebb_client_close(client);
434
+ ev_io_stop(server->loop, &server->request_watcher);
435
+ close(server->fd);
436
+ if(server->socketpath)
437
+ unlink(server->socketpath);
438
+ server->open = FALSE;
439
+ }
440
+ }
441
+
442
+
443
+ void ebb_server_listen(ebb_server *server)
444
+ {
445
+ int r = listen(server->fd, EBB_MAX_CLIENTS);
446
+ assert(r >= 0);
447
+ assert(server->open == FALSE);
448
+ server->open = TRUE;
449
+
450
+ server->request_watcher.data = server;
451
+ ev_init (&server->request_watcher, on_request);
452
+ ev_io_set (&server->request_watcher, server->fd, EV_READ | EV_ERROR);
453
+ ev_io_start (server->loop, &server->request_watcher);
454
+ }
455
+
456
+
457
+ int ebb_server_listen_on_port(ebb_server *server, const int port)
458
+ {
459
+ int fd = server_socket(port);
460
+ if(fd < 0) return 0;
461
+ server->port = malloc(sizeof(char)*8); /* for easy access to the port */
462
+ sprintf(server->port, "%d", port);
463
+ server->fd = fd;
464
+ ebb_server_listen(server);
465
+ return fd;
466
+ }
467
+
468
+
469
+ int ebb_server_listen_on_socket(ebb_server *server, const char *socketpath)
470
+ {
471
+ int fd = server_socket_unix(socketpath, 0755);
472
+ if(fd < 0) return 0;
473
+ server->socketpath = strdup(socketpath);
474
+ server->fd = fd;
475
+ ebb_server_listen(server);
476
+ return fd;
477
+ }
478
+
479
+
480
+ void ebb_client_close(ebb_client *client)
481
+ {
482
+ if(client->open) {
483
+ ev_io_stop(client->server->loop, &client->read_watcher);
484
+ ev_io_stop(client->server->loop, &client->write_watcher);
485
+ ev_timer_stop(client->server->loop, &client->timeout_watcher);
486
+
487
+ if(client->upload_file) {
488
+ fclose(client->upload_file);
489
+ unlink(client->upload_file_filename);
490
+ }
491
+
492
+ /* here we do not free the already allocated GString client->response_buffer
493
+ * that we're holding the response in. we reuse it again -
494
+ * presumably because the backend is going to keep sending such long
495
+ * requests.
496
+ */
497
+ client->response_buffer->len = 0;
498
+
499
+ close(client->fd);
500
+ client->open = FALSE;
501
+ }
502
+ }
503
+
504
+
505
+ void on_client_writable(struct ev_loop *loop, ev_io *watcher, int revents)
506
+ {
507
+ ebb_client *client = (ebb_client*)(watcher->data);
508
+ ssize_t sent;
509
+
510
+ if(EV_ERROR & revents) {
511
+ g_message("on_client_writable() got error event, closing peer");
512
+ return;
513
+ }
514
+
515
+ //if(client->written != 0)
516
+ // g_debug("total written: %d", (int)(client->written));
517
+
518
+ sent = send( client->fd
519
+ , client->response_buffer->str + sizeof(gchar)*(client->written)
520
+ , client->response_buffer->len - client->written
521
+ , 0
522
+ );
523
+ if(sent < 0) {
524
+ #ifdef DEBUG
525
+ g_message("Error writing: %s", strerror(errno));
526
+ #endif
527
+ ebb_client_close(client);
528
+ return;
529
+ }
530
+ client->written += sent;
531
+
532
+ assert(client->written <= client->response_buffer->len);
533
+ //g_message("wrote %d bytes. total: %d", (int)sent, (int)(client->written));
534
+
535
+ ev_timer_again(loop, &(client->timeout_watcher));
536
+
537
+ if(client->written == client->response_buffer->len)
538
+ ebb_client_close(client);
539
+ }
540
+
541
+
542
+ void ebb_client_write(ebb_client *client, const char *data, int length)
543
+ {
544
+ g_string_append_len(client->response_buffer, data, length);
545
+ }
546
+
547
+
548
+ void ebb_client_finished(ebb_client *client)
549
+ {
550
+ assert(client->open);
551
+ assert(FALSE == ev_is_active(&(client->write_watcher)));
552
+
553
+ /* assure the socket is still in non-blocking mode
554
+ * in the ruby binding, for example, i change this flag
555
+ */
556
+ int flags = fcntl(client->fd, F_GETFL, 0);
557
+ if(0 > fcntl(client->fd, F_SETFL, flags | O_NONBLOCK)) {
558
+ perror("fcntl()");
559
+ ebb_client_close(client);
560
+ return;
561
+ }
562
+
563
+ client->written = 0;
564
+ client->write_watcher.data = client;
565
+ ev_init (&(client->write_watcher), on_client_writable);
566
+ ev_io_set (&(client->write_watcher), client->fd, EV_WRITE | EV_ERROR);
567
+ ev_io_start(client->server->loop, &(client->write_watcher));
568
+ }
569
+
570
+
571
+ /* pass an allocated buffer and the length to read. this function will try to
572
+ * fill the buffer with that length of data read from the body of the request.
573
+ * the return value says how much was actually written.
574
+ */
575
+ int ebb_client_read(ebb_client *client, char *buffer, int length)
576
+ {
577
+ size_t read;
578
+
579
+ assert(client->open);
580
+ assert(client_finished_parsing);
581
+
582
+ if(client->upload_file) {
583
+ read = fread(buffer, 1, length, client->upload_file);
584
+ /* TODO error checking! */
585
+ return read;
586
+ } else {
587
+ char* request_body = client->request_buffer + client->parser.nread;
588
+
589
+ read = ramp(min(length, client->content_length - client->nread_from_body));
590
+ memcpy( buffer
591
+ , request_body + client->nread_from_body
592
+ , read
593
+ );
594
+ client->nread_from_body += read;
595
+ return read;
596
+ }
597
+ }
598
+
599
+ /* The following socket creation routines are modified and stolen from memcached */
600
+
601
+ static int server_socket(const int port) {
602
+ int sfd;
603
+ struct linger ling = {0, 0};
604
+ struct sockaddr_in addr;
605
+ int flags =1;
606
+
607
+ if ((sfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
608
+ perror("socket()");
609
+ return -1;
610
+ }
611
+
612
+ if ((flags = fcntl(sfd, F_GETFL, 0)) < 0 ||
613
+ fcntl(sfd, F_SETFL, flags | O_NONBLOCK) < 0) {
614
+ perror("setting O_NONBLOCK");
615
+ close(sfd);
616
+ return -1;
617
+ }
618
+
619
+ flags = 1;
620
+ setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, (void *)&flags, sizeof(flags));
621
+ setsockopt(sfd, SOL_SOCKET, SO_KEEPALIVE, (void *)&flags, sizeof(flags));
622
+ setsockopt(sfd, SOL_SOCKET, SO_LINGER, (void *)&ling, sizeof(ling));
623
+ setsockopt(sfd, IPPROTO_TCP, TCP_NODELAY, (void *)&flags, sizeof(flags));
624
+
625
+ /*
626
+ * the memset call clears nonstandard fields in some impementations
627
+ * that otherwise mess things up.
628
+ */
629
+ memset(&addr, 0, sizeof(addr));
630
+
631
+ addr.sin_family = AF_INET;
632
+ addr.sin_port = htons(port);
633
+ addr.sin_addr.s_addr = htonl(INADDR_ANY);
634
+
635
+ if (bind(sfd, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
636
+ perror("bind()");
637
+ close(sfd);
638
+ return -1;
639
+ }
640
+ if (listen(sfd, EBB_MAX_CLIENTS) == -1) {
641
+ perror("listen()");
642
+ close(sfd);
643
+ return -1;
644
+ }
645
+ return sfd;
646
+ }
647
+
648
+
649
+ static int server_socket_unix(const char *path, int access_mask) {
650
+ int sfd;
651
+ struct linger ling = {0, 0};
652
+ struct sockaddr_un addr;
653
+ struct stat tstat;
654
+ int flags =1;
655
+ int old_umask;
656
+
657
+ if (!path) {
658
+ return -1;
659
+ }
660
+
661
+ if ((sfd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
662
+ perror("socket()");
663
+ return -1;
664
+ }
665
+
666
+ if ((flags = fcntl(sfd, F_GETFL, 0)) < 0 ||
667
+ fcntl(sfd, F_SETFL, flags | O_NONBLOCK) < 0) {
668
+ perror("setting O_NONBLOCK");
669
+ close(sfd);
670
+ return -1;
671
+ }
672
+
673
+ /*
674
+ * Clean up a previous socket file if we left it around
675
+ */
676
+ if (lstat(path, &tstat) == 0) {
677
+ if (S_ISSOCK(tstat.st_mode))
678
+ unlink(path);
679
+ }
680
+
681
+ flags = 1;
682
+ setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, (void *)&flags, sizeof(flags));
683
+ setsockopt(sfd, SOL_SOCKET, SO_KEEPALIVE, (void *)&flags, sizeof(flags));
684
+ setsockopt(sfd, SOL_SOCKET, SO_LINGER, (void *)&ling, sizeof(ling));
685
+
686
+ /*
687
+ * the memset call clears nonstandard fields in some impementations
688
+ * that otherwise mess things up.
689
+ */
690
+ memset(&addr, 0, sizeof(addr));
691
+
692
+ addr.sun_family = AF_UNIX;
693
+ strcpy(addr.sun_path, path);
694
+ old_umask=umask( ~(access_mask&0777));
695
+ if (bind(sfd, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
696
+ perror("bind()");
697
+ close(sfd);
698
+ umask(old_umask);
699
+ return -1;
700
+ }
701
+ umask(old_umask);
702
+ if (listen(sfd, EBB_MAX_CLIENTS) == -1) {
703
+ perror("listen()");
704
+ close(sfd);
705
+ return -1;
706
+ }
707
+ return sfd;
708
+ }
709
+