ry / ebb fork watch download tarball
public this repo is viewable by everyone
Description: web server
Homepage: http://ebb.rubyforge.org
Clone URL: git://github.com/ry/ebb.git
revert: handle uploads in c

on second thought - i want ebb handling uploads. tests pass.
Ryan Dahl (author)
2 months ago
commit  bdee84b3f95a6ac6e9632090f7095bd655af3288
tree    cbdacdbe44ff0ce7c99ec84ebe61a2cd82b90bf7
parent  02b6f662e1a4fec2a15807ce1d76e48aa9067b0b
...
15
16
17
18
 
19
20
21
...
36
37
38
39
 
 
 
 
 
 
 
40
41
42
...
15
16
17
 
18
19
20
21
...
36
37
38
 
39
40
41
42
43
44
45
46
47
48
0
@@ -15,7 +15,7 @@ task(:compile => 'src/parser.c') do
0
 end
0
 
0
 file('src/parser.c' => 'src/parser.rl') do
0
- sh "ragel #{dir('src/parser.rl')} | rlgen-cd -G2 -o #{dir('src/parser.c')}"
0
+ sh "ragel src/parser.rl | rlgen-cd -G2 -o src/parser.c"
0
 end
0
 
0
 task(:wc) { sh "wc -l ruby_lib/*.rb src/ebb*.{c,h}" }
0
@@ -36,7 +36,13 @@ spec = Gem::Specification.new do |s|
0
   s.bindir = 'bin'
0
   s.executables = %w(ebb_rails)
0
   
0
- s.files = FileList.new ['{src,libev,benchmark,ruby_lib}/*.(rb|c|h)', 'bin/ebb_rails','README']
0
+ s.files = FileList.new('src/*.{c,h}',
0
+ 'src/extconf.rb',
0
+ 'libev/*',
0
+ 'ruby_lib/*',
0
+ 'benchmark/*.rb',
0
+ 'bin/ebb_rails',
0
+ 'README')
0
 end
0
 
0
 Rake::GemPackageTask.new(spec) do |pkg|
...
83
84
85
86
 
87
88
89
...
83
84
85
 
86
87
88
89
0
@@ -83,7 +83,7 @@ class EbbTest < Test::Unit::TestCase
0
     end
0
   end
0
   
0
- # this is rough but does detect errors
0
+ # this is rough but does detect major problems
0
   def test_ab
0
     r = %x{ab -n 1000 -c 50 -q http://0.0.0.0:4044/bytes/123}
0
     assert r =~ /Requests per second:\s*(\d+)/, r
...
46
47
48
49
 
50
51
52
...
46
47
48
 
49
50
51
52
0
@@ -46,7 +46,7 @@ module Ebb
0
     end
0
     
0
     def read(len)
0
- FFI::client_read(@client, len)
0
+ FFI::client_read_input(@client, len)
0
     end
0
     
0
     def gets
...
180
181
182
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
183
184
185
...
189
190
191
192
193
194
195
196
 
 
197
198
199
200
 
201
202
203
204
205
206
207
208
209
210
 
 
 
 
 
 
 
 
 
 
 
211
 
212
 
 
 
 
 
 
 
 
 
 
 
213
214
215
...
217
218
219
220
221
222
223
224
 
225
226
227
...
281
282
283
284
 
285
286
 
287
288
289
...
404
405
406
407
 
 
 
 
408
409
410
...
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
...
538
539
540
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
541
542
543
...
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
...
261
262
263
 
264
265
 
 
266
267
268
269
270
 
271
272
273
 
 
 
 
 
 
 
 
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
...
303
304
305
 
 
 
 
 
306
307
308
309
...
363
364
365
 
366
367
 
368
369
370
371
...
486
487
488
 
489
490
491
492
493
494
495
...
547
548
549
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
550
551
552
...
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
0
@@ -180,6 +180,78 @@ void on_timeout(struct ev_loop *loop, ev_timer *watcher, int revents)
0
 }
0
 
0
 #define client_finished_parsing http_parser_is_finished(&client->parser)
0
+#define total_request_size (client->content_length + client->parser.nread)
0
+
0
+void* read_body_into_file(void *_client)
0
+{
0
+ ebb_client *client = (ebb_client*)_client;
0
+ static unsigned int id;
0
+ FILE *tmpfile;
0
+
0
+ assert(client->open);
0
+ assert(client->server->open);
0
+ assert(client->content_length > 0);
0
+ assert(client_finished_parsing);
0
+
0
+ /* set blocking socket */
0
+ int flags = fcntl(client->fd, F_GETFL, 0);
0
+ assert(0 <= fcntl(client->fd, F_SETFL, flags & ~O_NONBLOCK));
0
+
0
+ sprintf(client->upload_file_filename, "/tmp/ebb_upload_%010d", id++);
0
+ tmpfile = fopen(client->upload_file_filename, "w+");
0
+ if(tmpfile == NULL) ebb_error("Cannot open tmpfile %s", client->upload_file_filename);
0
+ client->upload_file = tmpfile;
0
+
0
+ size_t body_head_length = client->read - client->parser.nread;
0
+ size_t written = 0, r;
0
+ while(written < body_head_length) {
0
+ r = fwrite( client->request_buffer + sizeof(char)*(client->parser.nread + written)
0
+ , sizeof(char)
0
+ , body_head_length - written
0
+ , tmpfile
0
+ );
0
+ if(r <= 0) {
0
+ ebb_client_close(client);
0
+ return NULL;
0
+ }
0
+ written += r;
0
+ }
0
+
0
+ // ebb_debug("wrote request header to file. written: %d, content_length: %d", written, client->content_length);
0
+
0
+ int bufsize = 5*1024;
0
+ char buffer[bufsize];
0
+ size_t received;
0
+ while(written < client->content_length) {
0
+ received = recv(client->fd
0
+ , buffer
0
+ , min(client->content_length - written, bufsize)
0
+ , 0
0
+ );
0
+ if(received < 0) goto error;
0
+ client->read += received;
0
+
0
+ ssize_t w = 0;
0
+ int rv;
0
+ while(w < received) {
0
+ rv = fwrite( buffer + w*sizeof(char)
0
+ , sizeof(char)
0
+ , received - w
0
+ , tmpfile
0
+ );
0
+ if(rv <= 0) goto error;
0
+ w += rv;
0
+ }
0
+ written += received;
0
+ }
0
+ rewind(tmpfile);
0
+ // ebb_debug("%d bytes written to file %s", written, client->upload_file_filename);
0
+ return NULL;
0
+error:
0
+ ebb_client_close(client);
0
+ return NULL;
0
+}
0
+
0
 
0
 void on_readable(struct ev_loop *loop, ev_io *watcher, int revents)
0
 {
0
@@ -189,27 +261,41 @@ void on_readable(struct ev_loop *loop, ev_io *watcher, int revents)
0
   assert(client->server->open);
0
   assert(client->server->loop == loop);
0
   assert(&client->read_watcher == watcher);
0
- assert(FALSE == client_finished_parsing);
0
   
0
   ssize_t read = recv( client->fd
0
- , client->request_buffer + client->nread_head
0
- , EBB_BUFFERSIZE - client->nread_head - 1
0
+ , client->request_buffer + client->read
0
+ , EBB_BUFFERSIZE - client->read - 1 /* -1 is for making ragel happy below */
0
                      , 0
0
                      );
0
   if(read <= 0) goto error; /* XXX is this the right action to take for read==0 ? */
0
- client->nread_head += read;
0
+ client->read += read;
0
   ev_timer_again(loop, &client->timeout_watcher);
0
   
0
- client->request_buffer[client->nread_head] = '\0'; /* make ragel happy */
0
- http_parser_execute( &client->parser
0
- , client->request_buffer
0
- , client->nread_head
0
- , client->parser.nread
0
- );
0
- if(http_parser_has_error(&client->parser)) goto error;
0
- if(client_finished_parsing) {
0
+ if(FALSE == client_finished_parsing) {
0
+ client->request_buffer[client->read] = '\0'; /* make ragel happy */
0
+ http_parser_execute( &client->parser
0
+ , client->request_buffer
0
+ , client->read
0
+ , client->parser.nread
0
+ );
0
+ if(http_parser_has_error(&client->parser)) goto error;
0
+ }
0
+
0
+ if(total_request_size == client->read) {
0
     ev_io_stop(loop, watcher);
0
+ client->nread_from_body = 0;
0
     dispatch(client);
0
+ return;
0
+ }
0
+
0
+ if(client_finished_parsing && total_request_size > EBB_BUFFERSIZE ) {
0
+ /* read body into file - in a thread */
0
+ pthread_t thread;
0
+ ev_io_stop(loop, watcher);
0
+ assert(0 <= pthread_create(&thread, NULL, read_body_into_file, client));
0
+ pthread_join(thread, NULL);
0
+ dispatch(client);
0
+ return;
0
   }
0
   return;
0
 error:
0
@@ -217,11 +303,7 @@ error:
0
   ebb_client_close(client);
0
 }
0
 
0
-
0
-void on_request( struct ev_loop *loop
0
- , ev_io *watcher
0
- , int revents
0
- )
0
+void on_request(struct ev_loop *loop, ev_io *watcher, int revents)
0
 {
0
   ebb_server *server = (ebb_server*)(watcher->data);
0
   assert(server->open);
0
@@ -281,9 +363,9 @@ void on_request( struct ev_loop *loop
0
   
0
   /* OTHER */
0
   client->env_size = 0;
0
- client->nread_head = client->nread_body = 0;
0
+ client->read = client->nread_from_body = 0;
0
   client->response_buffer->len = 0; /* see note in ebb_client_close */
0
- client->content_length = -1;
0
+ client->content_length = 0;
0
   
0
   /* SETUP READ AND TIMEOUT WATCHERS */
0
   client->read_watcher.data = client;
0
@@ -404,7 +486,10 @@ void ebb_client_close(ebb_client *client)
0
     ev_io_stop(client->server->loop, &client->write_watcher);
0
     ev_timer_stop(client->server->loop, &client->timeout_watcher);
0
     
0
- http_parser_finish(&client->parser);
0
+ if(client->upload_file) {
0
+ fclose(client->upload_file);
0
+ unlink(client->upload_file_filename);
0
+ }
0
     
0
     /* here we do not free the already allocated GString client->response_buffer
0
      * that we're holding the response in. we reuse it again -
0
@@ -462,59 +547,6 @@ void ebb_client_write(ebb_client *client, const char *data, int length)
0
 }
0
 
0
 
0
-/* pass an allocated buffer and the length to read. this function will try to
0
- * fill the buffer with that length of data read from the body of the request.
0
- * the return value says how much was actually written.
0
- *
0
- * return length read on successful read
0
- * returns -1 on error
0
- * returns -2 if the nonblocking socket is not yet availible for reading
0
- * returns -3 if content_length is present and the request is finished.
0
- */
0
-int ebb_client_read(ebb_client *client, char *buffer, int length)
0
-{
0
- int to_read, read;
0
- char* body_beginning = client->request_buffer + client->parser.nread;
0
- int body_beginning_size = client->nread_head - client->parser.nread;
0
-
0
- if(!client->open) return -1;
0
-
0
- assert(client->nread_body >= 0);
0
-
0
- assert(client_finished_parsing);
0
- assert(client->parser.nread + body_beginning_size < EBB_BUFFERSIZE);
0
- assert(client->parser.nread + body_beginning_size == client->nread_head);
0
-
0
- if(client->nread_body == client->content_length) return(-3);
0
-
0
- if(client->nread_body < body_beginning_size) {
0
- to_read = min(length, body_beginning_size - client->nread_body);
0
- assert(0 <= to_read && to_read <= length);
0
- memcpy( buffer
0
- , body_beginning + client->nread_body
0
- , to_read
0
- );
0
- client->nread_body += to_read;
0
- return(to_read);
0
-
0
- } else {
0
- /* allow for requests where the content length is not mentioned. e.g.
0
- * streaming posts
0
- */
0
- read = recv(client->fd, buffer, length, 0);
0
- //ebb_info("recv(%d, buffer, %d, 0) -> %d (%s)", client->fd, length, read, strerror(errno));
0
- if(read < 0) {
0
- if(errno == EAGAIN) return -2;
0
- ebb_warning("closing client. ebb_client_read: %s", strerror(errno));
0
- ebb_client_close(client);
0
- return -1;
0
- }
0
- client->nread_body += read;
0
- return(read);
0
- }
0
-}
0
-
0
-
0
 void ebb_client_finished(ebb_client *client)
0
 {
0
   assert(client->open);
0
@@ -538,6 +570,34 @@ void ebb_client_finished(ebb_client *client)
0
 }
0
 
0
 
0
+/* pass an allocated buffer and the length to read. this function will try to
0
+ * fill the buffer with that length of data read from the body of the request.
0
+ * the return value says how much was actually written.
0
+ */
0
+int ebb_client_read(ebb_client *client, char *buffer, int length)
0
+{
0
+ size_t read;
0
+
0
+ assert(client->open);
0
+ assert(client_finished_parsing);
0
+
0
+ if(client->upload_file) {
0
+ read = fread(buffer, 1, length, client->upload_file);
0
+ /* TODO error checking! */
0
+ return read;
0
+ } else {
0
+ char* request_body = client->request_buffer + client->parser.nread;
0
+
0
+ read = ramp(min(length, client->content_length - client->nread_from_body));
0
+ memcpy( buffer
0
+ , request_body + client->nread_from_body
0
+ , read
0
+ );
0
+ client->nread_from_body += read;
0
+ return read;
0
+ }
0
+}
0
+
0
 /* The following socket creation routines are modified and stolen from memcached */
0
 
0
 static int server_socket(const int port) {
...
62
63
64
65
 
 
 
 
66
67
68
...
70
71
72
73
74
75
76
77
78
...
62
63
64
 
65
66
67
68
69
70
71
...
73
74
75
 
 
 
76
77
78
0
@@ -62,7 +62,10 @@ struct ebb_client {
0
   
0
   char request_buffer[EBB_BUFFERSIZE];
0
   ev_io read_watcher;
0
- size_t nread_head, nread_body;
0
+ size_t read, nread_from_body;
0
+
0
+ char upload_file_filename[200];
0
+ FILE *upload_file;
0
   
0
   int content_length;
0
   
0
@@ -70,9 +73,6 @@ struct ebb_client {
0
   GString *response_buffer;
0
   size_t written;
0
   
0
- void *data;
0
-
0
-
0
   ev_timer timeout_watcher;
0
   
0
   /* the ENV structure */
...
67
68
69
70
71
 
72
73
74
...
178
179
180
181
 
182
183
 
184
185
 
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
 
201
202
203
204
205
 
 
 
 
 
206
207
208
...
255
256
257
258
 
259
260
261
...
67
68
69
 
 
70
71
72
73
...
177
178
179
 
180
181
182
183
184
 
185
186
187
188
 
 
 
 
 
 
 
 
 
 
 
 
189
190
191
192
193
194
195
196
197
198
199
200
201
202
...
249
250
251
 
252
253
254
255
0
@@ -67,8 +67,7 @@ VALUE env_field(const char *field, int length)
0
 VALUE client_new(ebb_client *_client)
0
 {
0
   VALUE client = Data_Wrap_Struct(cClient, 0, 0, _client);
0
- _client->data = (void*)client;
0
- rb_iv_set(client, "@content_length", INT2FIX(_client->content_length));
0
+ // rb_iv_set(client, "@content_length", INT2FIX(_client->content_length));
0
   return client;
0
 }
0
 
0
@@ -178,31 +177,26 @@ VALUE client_env(VALUE x, VALUE client)
0
 }
0
 
0
 
0
-VALUE client_read(VALUE x, VALUE client, VALUE size)
0
+VALUE client_read_input(VALUE x, VALUE client, VALUE size)
0
 {
0
   ebb_client *_client;
0
+ GString *_string;
0
   VALUE string;
0
- int nread, _size = FIX2INT(size);
0
+ int _size = FIX2INT(size);
0
   Data_Get_Struct(client, ebb_client, _client);
0
   
0
   string = rb_str_buf_new( _size );
0
-
0
- do {
0
- nread = ebb_client_read(_client, RSTRING_PTR(string), _size);
0
- } while(nread == -2 && rb_io_wait_readable(_client->fd) == Qtrue);
0
-
0
- if(nread == -1) {
0
- rb_raise(rb_eRuntimeError,"There was a problem reading from request input");
0
- return Qnil;
0
- } else if(nread == -3) {
0
- return Qnil;
0
- }
0
-
0
+ int nread = ebb_client_read(_client, RSTRING_PTR(string), _size);
0
 #if RUBY_VERSION_CODE < 190
0
   RSTRING(string)->len = nread;
0
 #else
0
   rb_str_set_len(string, nread);
0
 #endif
0
+
0
+ if(nread < 0)
0
+ rb_raise(rb_eRuntimeError,"There was a problem reading from input (bad tmp file?)");
0
+ if(nread == 0)
0
+ return Qnil;
0
   return string;
0
 }
0
 
0
@@ -255,7 +249,7 @@ void Init_ebb_ext()
0
   rb_define_singleton_method(mFFI, "server_unlisten", server_unlisten, 1);
0
   
0
   cClient = rb_define_class_under(mEbb, "Client", rb_cObject);
0
- rb_define_singleton_method(mFFI, "client_read", client_read, 2);
0
+ rb_define_singleton_method(mFFI, "client_read_input", client_read_input, 2);
0
   rb_define_singleton_method(mFFI, "client_write", client_write, 2);
0
   rb_define_singleton_method(mFFI, "client_finished", client_finished, 1);
0
   rb_define_singleton_method(mFFI, "client_env", client_env, 1);

Comments

    No one has commented yet.