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
POST requests handled in Ruby

This commit fixes ebb_client_read. Now the ruby side will handle uploads.
Fixed a very dumb error in content_length_cb
Ryan Dahl (author)
2 months ago
commit  02b6f662e1a4fec2a15807ce1d76e48aa9067b0b
tree    7669437a22a55651241741dd0d3542976e0d5032
parent  5b6d59ac6933821590e887157931a712a88e95c4
...
2
3
4
 
 
 
 
 
 
5
 
 
 
 
 
6
7
 
8
9
10
...
17
18
19
20
 
21
22
23
...
32
33
34
35
36
37
...
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
 
18
19
20
21
...
28
29
30
 
31
32
33
34
...
43
44
45
 
46
47
0
@@ -2,9 +2,20 @@ require 'rake'
0
 require 'rake/gempackagetask'
0
 require 'rake/clean'
0
 
0
+def dir(path)
0
+ File.expand_path File.join(File.dirname(__FILE__), path)
0
+end
0
+
0
+task(:default => :compile)
0
+
0
 task(:package => 'src/parser.c')
0
+
0
+task(:compile => 'src/parser.c') do
0
+ sh "cd #{dir('src')} && ruby extconf.rb && make"
0
+end
0
+
0
 file('src/parser.c' => 'src/parser.rl') do
0
- sh 'ragel src/parser.rl | rlgen-cd -G2 -o src/parser.c'
0
+ sh "ragel #{dir('src/parser.rl')} | rlgen-cd -G2 -o #{dir('src/parser.c')}"
0
 end
0
 
0
 task(:wc) { sh "wc -l ruby_lib/*.rb src/ebb*.{c,h}" }
0
@@ -17,7 +28,7 @@ spec = Gem::Specification.new do |s|
0
   s.author = 'ry dahl'
0
   s.email = 'ry@tinyclouds.org'
0
   s.homepage = 'http://repo.or.cz/w/ebb.git'
0
- s.version = File.read(File.dirname(__FILE__) + "/VERSION").gsub(/\s/,'')
0
+ s.version = File.read(dir("VERSION")).gsub(/\s/,'')
0
   s.requirements << 'none'
0
   
0
   s.require_path = 'ruby_lib'
0
@@ -32,6 +43,5 @@ Rake::GemPackageTask.new(spec) do |pkg|
0
   pkg.need_zip = true
0
 end
0
 
0
-
0
 CLEAN.add ["**/*.{o,bundle,so,obj,pdb,lib,def,exp}", "benchmark/*.dump"]
0
 CLOBBER.add ['src/Makefile', 'src/parser.c', 'src/mkmf.log','doc', 'coverage']
...
49
50
51
52
 
53
54
55
...
49
50
51
 
52
53
54
55
0
@@ -49,7 +49,7 @@ class SimpleApp
0
       
0
     elsif commands.include?('test_post_length')
0
       input_body = ""
0
- while chunk = env['rack.input'].read(1024)
0
+ while chunk = env['rack.input'].read(512)
0
         input_body << chunk
0
       end
0
       if env['HTTP_CONTENT_LENGTH'].to_i == input_body.length
...
40
41
42
43
 
44
45
46
...
77
78
79
80
 
81
82
83
84
85
86
87
88
89
 
 
 
 
 
 
 
 
 
 
 
90
91
92
...
40
41
42
 
43
44
45
46
...
77
78
79
 
80
81
82
83
84
85
 
 
 
 
86
87
88
89
90
91
92
93
94
95
96
97
98
99
0
@@ -40,7 +40,7 @@ class EbbTest < Test::Unit::TestCase
0
       
0
     elsif commands.include?('test_post_length')
0
       input_body = ""
0
- while chunk = env['rack.input'].read(10)
0
+ while chunk = env['rack.input'].read(512)
0
         input_body << chunk
0
       end
0
       
0
@@ -77,16 +77,23 @@ class EbbTest < Test::Unit::TestCase
0
   end
0
   
0
   def test_small_posts
0
- [1,10,1000].each do |i|
0
+ [1,10,321,123,1000].each do |i|
0
       response = post("/test_post_length", 'C'*i)
0
       assert_equal 200, response.code.to_i, response.body
0
     end
0
   end
0
   
0
- # def test_large_post
0
- # response = post("/test_post_length", 'C'*1024*10)
0
- # assert_equal 200, response.code.to_i, response.body
0
- # end
0
+ # this is rough but does detect errors
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
0
+ assert $1.to_i > 100, r
0
+ end
0
+
0
+ def test_large_post
0
+ response = post("/test_post_length", 'C'*1024*15)
0
+ assert_equal 200, response.code.to_i, response.body
0
+ end
0
 end
0
 
0
 #
...
135
136
137
138
139
140
141
142
143
144
 
 
 
 
 
145
146
147
...
181
182
183
 
184
185
186
187
188
 
189
190
191
...
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
...
288
289
290
291
292
293
294
295
 
 
 
296
297
298
...
321
322
323
324
 
325
326
327
...
341
342
343
344
 
345
346
347
...
409
410
411
412
413
414
 
 
 
415
416
417
 
418
419
420
421
 
 
422
423
424
425
 
426
427
428
...
444
445
446
447
448
 
 
449
450
451
...
457
458
459
460
 
461
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
...
549
550
551
552
553
554
 
 
 
555
556
557
...
135
136
137
 
 
 
 
 
 
 
138
139
140
141
142
143
144
145
...
179
180
181
182
183
 
 
 
 
184
185
186
187
...
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
...
281
282
283
 
 
 
 
 
284
285
286
287
288
289
...
312
313
314
 
315
316
317
318
...
332
333
334
 
335
336
337
338
...
400
401
402
 
 
 
403
404
405
406
 
 
407
408
 
 
 
409
410
411
412
413
 
414
415
416
417
...
433
434
435
 
 
436
437
438
439
440
...
446
447
448
 
449
450
451
452
453
 
454
455
456
457
458
459
460
 
461
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
521
...
560
561
562
 
 
 
563
564
565
566
567
568
0
@@ -135,13 +135,11 @@ void content_length_cb(void *data, const char *at, size_t length)
0
   ebb_client *client = (ebb_client*)(data);
0
   env_add_const(client, EBB_CONTENT_LENGTH, at, length);
0
   
0
- client->content_length = 0;
0
- int i;
0
- for(i = length-1; 0 <= i; i--) { /* i hate c. */
0
- client->content_length *= 10;
0
- client->content_length += at[i] - '0';
0
- }
0
- // ebb_debug("content length read: %d", client->content_length);
0
+ /* i hate c. */
0
+ char buf[20];
0
+ strncpy(buf, at, length);
0
+ buf[length] = '\0';
0
+ client->content_length = atoi(buf);
0
 }
0
 
0
 
0
@@ -181,11 +179,9 @@ void on_timeout(struct ev_loop *loop, ev_timer *watcher, int revents)
0
 #endif
0
 }
0
 
0
+#define client_finished_parsing http_parser_is_finished(&client->parser)
0
 
0
-void on_readable( struct ev_loop *loop
0
- , ev_io *watcher
0
- , int revents
0
- )
0
+void on_readable(struct ev_loop *loop, ev_io *watcher, int revents)
0
 {
0
   ebb_client *client = (ebb_client*)(watcher->data);
0
   
0
@@ -193,29 +189,26 @@ void on_readable( struct ev_loop *loop
0
   assert(client->server->open);
0
   assert(client->server->loop == loop);
0
   assert(&client->read_watcher == watcher);
0
- assert(FALSE == http_parser_is_finished(&client->parser));
0
+ assert(FALSE == client_finished_parsing);
0
   
0
   ssize_t read = recv( client->fd
0
- , client->request_buffer + client->read
0
- , EBB_BUFFERSIZE - client->read - 1
0
+ , client->request_buffer + client->nread_head
0
+ , EBB_BUFFERSIZE - client->nread_head - 1
0
                      , 0
0
                      );
0
   if(read <= 0) goto error; /* XXX is this the right action to take for read==0 ? */
0
- client->read += read;
0
+ client->nread_head += read;
0
   ev_timer_again(loop, &client->timeout_watcher);
0
   
0
- client->request_buffer[client->read] = '\0'; /* make ragel happy */
0
+ client->request_buffer[client->nread_head] = '\0'; /* make ragel happy */
0
   http_parser_execute( &client->parser
0
                      , client->request_buffer
0
- , client->read
0
+ , client->nread_head
0
                      , client->parser.nread
0
                      );
0
   if(http_parser_has_error(&client->parser)) goto error;
0
- if(http_parser_is_finished(&client->parser)) {
0
+ if(client_finished_parsing) {
0
     ev_io_stop(loop, watcher);
0
- client->read_from_body = 0;
0
- client->request_body_head = client->request_buffer + client->parser.nread;
0
- client->request_body_head_size = client->read - client->parser.nread;
0
     dispatch(client);
0
   }
0
   return;
0
@@ -288,11 +281,9 @@ void on_request( struct ev_loop *loop
0
   
0
   /* OTHER */
0
   client->env_size = 0;
0
- client->read = 0;
0
- client->write_buffer->len = 0; // see note in ebb_client_close
0
- client->content_length = 0;
0
- client->request_body_head = NULL;
0
- client->request_body_head_size = 0;
0
+ client->nread_head = client->nread_body = 0;
0
+ client->response_buffer->len = 0; /* see note in ebb_client_close */
0
+ client->content_length = -1;
0
   
0
   /* SETUP READ AND TIMEOUT WATCHERS */
0
   client->read_watcher.data = client;
0
@@ -321,7 +312,7 @@ void ebb_server_init( ebb_server *server
0
 {
0
   int i;
0
   for(i=0; i < EBB_MAX_CLIENTS; i++)
0
- server->clients[i].write_buffer = g_string_new("");
0
+ server->clients[i].response_buffer = g_string_new("");
0
   
0
   server->request_cb = request_cb;
0
   server->request_cb_data = request_cb_data;
0
@@ -341,7 +332,7 @@ void ebb_server_free(ebb_server *server)
0
   
0
   int i;
0
   for(i=0; i < EBB_MAX_CLIENTS; i++)
0
- g_string_free(server->clients[i].write_buffer, TRUE);
0
+ g_string_free(server->clients[i].response_buffer, TRUE);
0
   if(server->port)
0
     free(server->port);
0
   if(server->socketpath)
0
@@ -409,20 +400,18 @@ int ebb_server_listen_on_socket(ebb_server *server, const char *socketpath)
0
 void ebb_client_close(ebb_client *client)
0
 {
0
   if(client->open) {
0
- ev_io_stop(client->server->loop, &(client->read_watcher));
0
- ev_io_stop(client->server->loop, &(client->write_watcher));
0
- ev_timer_stop(client->server->loop, &(client->timeout_watcher));
0
+ ev_io_stop(client->server->loop, &client->read_watcher);
0
+ ev_io_stop(client->server->loop, &client->write_watcher);
0
+ ev_timer_stop(client->server->loop, &client->timeout_watcher);
0
     
0
- /* http_parser */
0
- http_parser_finish(&(client->parser));
0
+ http_parser_finish(&client->parser);
0
     
0
- /* buffer */
0
- /* the theory here is that we do not free the already allocated
0
- * strings that we're holding the response in. we reuse it again -
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
      * presumably because the backend is going to keep sending such long
0
      * requests.
0
      */
0
- client->write_buffer->len = 0;
0
+ client->response_buffer->len = 0;
0
     
0
     close(client->fd);
0
     client->open = FALSE;
0
@@ -444,8 +433,8 @@ void on_client_writable(struct ev_loop *loop, ev_io *watcher, int revents)
0
   // ebb_debug("total written: %d", (int)(client->written));
0
   
0
   sent = send( client->fd
0
- , client->write_buffer->str + sizeof(gchar)*(client->written)
0
- , client->write_buffer->len - client->written
0
+ , client->response_buffer->str + sizeof(gchar)*(client->written)
0
+ , client->response_buffer->len - client->written
0
              , 0
0
              );
0
   if(sent < 0) {
0
@@ -457,54 +446,76 @@ void on_client_writable(struct ev_loop *loop, ev_io *watcher, int revents)
0
   }
0
   client->written += sent;
0
   
0
- assert(client->written <= client->write_buffer->len);
0
+ assert(client->written <= client->response_buffer->len);
0
   //ebb_info("wrote %d bytes. total: %d", (int)sent, (int)(client->written));
0
   
0
   ev_timer_again(loop, &(client->timeout_watcher));
0
   
0
- if(client->written == client->write_buffer->len)
0
+ if(client->written == client->response_buffer->len)
0
     ebb_client_close(client);
0
 }
0
 
0
 
0
 void ebb_client_write(ebb_client *client, const char *data, int length)
0
 {
0
- g_string_append_len(client->write_buffer, data, length);
0
+ g_string_append_len(client->response_buffer, data, 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
-size_t ebb_client_read(ebb_client *client, char *buffer, int length)
0
+int ebb_client_read(ebb_client *client, char *buffer, int length)
0
 {
0
- assert(client->open);
0
- assert(TRUE == http_parser_is_finished(&client->parser));
0
- assert(client->read_from_body <= client->content_length);
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
- size_t to_read = ramp(min(length, client->content_length - client->read_from_body));
0
+ assert(client->nread_body >= 0);
0
   
0
- if(client->read_from_body < client->request_body_head_size) {
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
- , client->request_body_head + client->read_from_body
0
+ , body_beginning + client->nread_body
0
           , to_read
0
           );
0
- client->read_from_body += to_read;
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
- ssize_t read = recv(client->fd, buffer, to_read, 0);
0
- if(read < 0) {
0
- ebb_warning("closing client. ebb_client_read: %s", strerror(errno));
0
- ebb_client_close(client);
0
- return -1;
0
- }
0
- client->read_from_body += read;
0
- return(read);
0
 }
0
 
0
 
0
-void ebb_client_finished( ebb_client *client)
0
+void ebb_client_finished(ebb_client *client)
0
 {
0
   assert(client->open);
0
   assert(FALSE == ev_is_active(&(client->write_watcher)));
0
@@ -549,9 +560,9 @@ static int server_socket(const int port) {
0
     
0
     flags = 1;
0
     setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, (void *)&flags, sizeof(flags));
0
- setsockopt(sfd, SOL_SOCKET, SO_KEEPALIVE, (void *)&flags, sizeof(flags));
0
- setsockopt(sfd, SOL_SOCKET, SO_LINGER, (void *)&ling, sizeof(ling));
0
- setsockopt(sfd, IPPROTO_TCP, TCP_NODELAY, (void *)&flags, sizeof(flags));
0
+ // setsockopt(sfd, SOL_SOCKET, SO_KEEPALIVE, (void *)&flags, sizeof(flags));
0
+ // setsockopt(sfd, SOL_SOCKET, SO_LINGER, (void *)&ling, sizeof(ling));
0
+ // setsockopt(sfd, IPPROTO_TCP, TCP_NODELAY, (void *)&flags, sizeof(flags));
0
     
0
     /*
0
      * the memset call clears nonstandard fields in some impementations
...
39
40
41
42
 
43
44
45
...
61
62
63
64
65
66
67
68
 
69
70
71
72
73
 
74
75
76
...
39
40
41
 
42
43
44
45
...
61
62
63
 
64
 
 
 
65
66
67
68
69
 
70
71
72
73
0
@@ -39,7 +39,7 @@ typedef struct ebb_client ebb_client;
0
 
0
 /*** Ebb Client ***/
0
 void ebb_client_close(ebb_client*);
0
-size_t ebb_client_read_request(ebb_client *client, char *buffer, int length);
0
+int ebb_client_read(ebb_client *client, char *buffer, int length);
0
 void ebb_client_write(ebb_client*, const char *data, int length);
0
 void ebb_client_finished( ebb_client *client);
0
 
0
@@ -61,16 +61,13 @@ struct ebb_client {
0
   http_parser parser;
0
   
0
   char request_buffer[EBB_BUFFERSIZE];
0
- size_t read;
0
   ev_io read_watcher;
0
- char *request_body_head;
0
- int request_body_head_size;
0
- size_t read_from_body;
0
+ size_t nread_head, nread_body;
0
   
0
   int content_length;
0
   
0
   ev_io write_watcher;
0
- GString *write_buffer;
0
+ GString *response_buffer;
0
   size_t written;
0
   
0
   void *data;
...
182
183
184
185
186
 
187
188
189
190
 
 
 
 
 
 
 
 
 
 
 
 
191
192
193
194
195
196
197
198
199
200
201
202
...
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
0
@@ -182,21 +182,27 @@ VALUE client_read(VALUE x, VALUE client, VALUE size)
0
 {
0
   ebb_client *_client;
0
   VALUE string;
0
- int _size = FIX2INT(size);
0
-
0
+ int nread, _size = FIX2INT(size);
0
   Data_Get_Struct(client, ebb_client, _client);
0
   
0
   string = rb_str_buf_new( _size );
0
- int nread = ebb_client_read(_client, RSTRING_PTR(string), _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
 #if RUBY_VERSION_CODE < 190
0
   RSTRING(string)->len = nread;
0
 #else
0
   rb_str_set_len(string, nread);
0
 #endif
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
 

Comments

    No one has commented yet.