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
Refactor POST interface

The C part of Ebb used to buffer the entire request (storing in a tempfile 
if
nessessary) before passing it off to Ruby. I've changed things around to 
allow
frameworks more control over uploads and also to simplify the C library.

Large file uploads still seem to be broken in the test case.
Ryan Dahl (author)
2 months ago
commit  5b6d59ac6933821590e887157931a712a88e95c4
tree    b8524885e855add8e5223e22b348d3803d4895e1
parent  1fce3d5204583fde37459771e75d65a5f926db26
...
49
50
51
52
 
53
54
55
...
70
71
72
 
 
 
 
 
 
 
 
 
 
 
 
73
...
49
50
51
 
52
53
54
55
...
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
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(10)
0
+ while chunk = env['rack.input'].read(1024)
0
         input_body << chunk
0
       end
0
       if env['HTTP_CONTENT_LENGTH'].to_i == input_body.length
0
@@ -70,3 +70,15 @@ class SimpleApp
0
     [status, {'Content-Type' => 'text/plain'}, body + "\r\n\r\n"]
0
   end
0
 end
0
+
0
+
0
+if $0 == __FILE__
0
+ require DIR + '/../ruby_lib/ebb'
0
+ require 'rubygems'
0
+ require 'ruby-debug'
0
+ Debugger.start
0
+
0
+ server = Ebb::Server.new(SimpleApp.new, :port => 4001)
0
+ puts "Ebb started on http://0.0.0.0:4001/"
0
+ server.start
0
+end
0
\ No newline at end of file
...
1
2
3
 
 
 
4
5
6
...
21
22
23
24
25
26
27
...
77
78
79
80
 
81
82
83
 
84
85
 
86
87
88
...
1
2
3
4
5
6
7
8
9
...
24
25
26
 
27
28
29
...
79
80
81
 
82
83
84
 
85
86
 
87
88
89
90
0
@@ -1,6 +1,9 @@
0
 require File.dirname(__FILE__) + '/../ruby_lib/ebb'
0
 require 'test/unit'
0
 require 'net/http'
0
+require 'rubygems'
0
+require 'ruby-debug'
0
+Debugger.start
0
 
0
 
0
 class EbbTest < Test::Unit::TestCase
0
@@ -21,7 +24,6 @@ class EbbTest < Test::Unit::TestCase
0
     Net::HTTP.get_response(URI.parse("http://0.0.0.0:4044#{path}"))
0
   end
0
   
0
-
0
   def post(path, data)
0
     Net::HTTP.post_form(URI.parse("http://0.0.0.0:4044#{path}"), data)
0
   end
0
@@ -77,12 +79,12 @@ class EbbTest < Test::Unit::TestCase
0
   def test_small_posts
0
     [1,10,1000].each do |i|
0
       response = post("/test_post_length", 'C'*i)
0
- assert_equal 200, response.code.to_i
0
+ assert_equal 200, response.code.to_i, response.body
0
     end
0
   end
0
- #
0
+
0
   # def test_large_post
0
- # response = post("/test_post_length", 'C'*1024*15)
0
+ # response = post("/test_post_length", 'C'*1024*10)
0
   # assert_equal 200, response.code.to_i, response.body
0
   # end
0
 end
...
26
27
28
29
 
30
31
32
...
40
41
42
43
44
 
45
46
47
48
49
50
 
 
51
52
53
54
 
55
56
57
58
 
59
60
61
...
87
88
89
 
90
91
92
 
93
94
95
...
126
127
128
129
 
130
131
132
...
26
27
28
 
29
30
31
32
...
40
41
42
 
 
43
44
45
46
47
 
 
48
49
50
51
52
 
53
54
55
56
 
57
58
59
60
...
86
87
88
89
90
91
 
92
93
94
95
...
126
127
128
 
129
130
131
132
0
@@ -26,7 +26,7 @@ module Ebb
0
     def env
0
       @env ||= begin
0
         env = FFI::client_env(self).update(BASE_ENV)
0
- env['rack.input'] = Input.new(self)
0
+ env['rack.input'] = RequestBody.new(self)
0
         env
0
       end
0
     end
0
@@ -40,22 +40,21 @@ module Ebb
0
     end
0
   end
0
   
0
- class Input
0
- CHUNKSIZE = 4*1024
0
+ class RequestBody
0
     def initialize(client)
0
       @client = client
0
     end
0
     
0
- def read(len = 1)
0
- FFI::client_read_input(@client, len)
0
+ def read(len)
0
+ FFI::client_read(@client, len)
0
     end
0
     
0
     def gets
0
- raise NotImplementedError, "Fix me, please. Yes, you!"
0
+ raise NotImplementedError
0
     end
0
     
0
     def each
0
- raise NotImplementedError, "Fix me, please Yes, you!"
0
+ raise NotImplementedError
0
     end
0
   end
0
   
0
@@ -87,9 +86,10 @@ module Ebb
0
       begin
0
         status, headers, body = @app.call(client.env)
0
       rescue
0
+ raise if $DEBUG
0
         status = 500
0
         headers = {'Content-Type' => 'text/plain'}
0
- body = HTTP_STATUS_CODES[status]
0
+ body = "Internal Server Error\n"
0
       end
0
       
0
       client.write "HTTP/1.1 %d %s\r\n" % [status, HTTP_STATUS_CODES[status]]
0
@@ -126,7 +126,7 @@ module Ebb
0
       @running = true
0
       while FFI::server_process_connections(self) and @running
0
         unless @waiting_clients.empty?
0
- if $debug and @waiting_clients.length > 1
0
+ if $DEBUG and @waiting_clients.length > 1
0
             puts "#{@waiting_clients.length} waiting clients"
0
           end
0
           client = @waiting_clients.shift
...
145
146
147
 
148
149
150
...
153
154
155
156
157
158
159
160
161
162
163
 
 
 
 
 
 
 
 
 
 
164
165
166
...
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
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
...
262
263
264
 
265
266
267
 
268
269
270
...
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
302
303
...
372
373
374
375
376
 
 
377
378
379
...
494
495
496
497
498
499
500
501
502
503
...
558
559
560
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
561
562
563
564
565
 
 
 
566
567
 
 
 
 
 
568
569
570
...
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
604
...
145
146
147
148
149
150
151
...
154
155
156
 
 
 
 
 
 
 
 
157
158
159
160
161
162
163
164
165
166
167
168
169
...
182
183
184
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
185
186
187
...
193
194
195
196
197
198
 
199
200
201
202
...
204
205
206
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
207
208
209
210
211
212
213
214
215
 
 
216
217
218
219
 
220
221
222
...
291
292
293
 
 
294
295
296
297
298
...
413
414
415
 
 
 
 
416
417
418
...
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
522
523
...
527
528
529
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
530
531
532
0
@@ -145,6 +145,7 @@ void content_length_cb(void *data, const char *at, size_t length)
0
 }
0
 
0
 
0
+const char* localhost_str = "0.0.0.0";
0
 void dispatch(ebb_client *client)
0
 {
0
   ebb_server *server = client->server;
0
@@ -153,14 +154,16 @@ void dispatch(ebb_client *client)
0
     return;
0
   
0
   /* Set the env variables */
0
- // env_add_const(client, EBB_SERVER_NAME
0
- // , server->address
0
- // , strlen(server->address)
0
- // );
0
- // env_add_const(client, EBB_SERVER_PORT
0
- // , server->port
0
- // , strlen(server->port)
0
- // );
0
+ if(server->port) {
0
+ env_add_const(client, EBB_SERVER_NAME
0
+ , localhost_str
0
+ , 8
0
+ );
0
+ env_add_const(client, EBB_SERVER_PORT
0
+ , server->port
0
+ , strlen(server->port)
0
+ );
0
+ }
0
   server->request_cb(client, server->request_cb_data);
0
 }
0
 
0
@@ -179,78 +182,6 @@ void on_timeout(struct ev_loop *loop, ev_timer *watcher, int revents)
0
 }
0
 
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(http_parser_is_finished(&client->parser));
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->read_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
-#define total_request_size (client->content_length + client->parser.nread)
0
 void on_readable( struct ev_loop *loop
0
                 , ev_io *watcher
0
                 , int revents
0
@@ -262,9 +193,10 @@ 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
   
0
   ssize_t read = recv( client->fd
0
- , client->read_buffer + client->read
0
+ , client->request_buffer + client->read
0
                      , EBB_BUFFERSIZE - client->read - 1
0
                      , 0
0
                      );
0
@@ -272,32 +204,19 @@ void on_readable( struct ev_loop *loop
0
   client->read += read;
0
   ev_timer_again(loop, &client->timeout_watcher);
0
   
0
- if(FALSE == http_parser_is_finished(&client->parser)) {
0
- client->read_buffer[client->read] = '\0'; /* make ragel happy */
0
- http_parser_execute( &client->parser
0
- , client->read_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->request_body = client->read_buffer + client->parser.nread;
0
- client->nread_from_body = 0;
0
- dispatch(client);
0
- return;
0
- }
0
-
0
- if(http_parser_is_finished(&client->parser) && total_request_size > EBB_BUFFERSIZE ) {
0
- /* read body into file - in a thread */
0
- pthread_t thread;
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
+ if(http_parser_is_finished(&client->parser)) {
0
     ev_io_stop(loop, watcher);
0
- assert(0 <= pthread_create(&thread, NULL, read_body_into_file, client));
0
- pthread_join(thread, NULL);
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
- return;
0
   }
0
   return;
0
 error:
0
@@ -372,8 +291,8 @@ void on_request( struct ev_loop *loop
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 = NULL;
0
- client->nread_from_body = -1;
0
+ client->request_body_head = NULL;
0
+ client->request_body_head_size = 0;
0
   
0
   /* SETUP READ AND TIMEOUT WATCHERS */
0
   client->read_watcher.data = client;
0
@@ -494,10 +413,6 @@ 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
- if(client->upload_file) {
0
- fclose(client->upload_file);
0
- unlink(client->upload_file_filename);
0
- }
0
     /* http_parser */
0
     http_parser_finish(&(client->parser));
0
     
0
@@ -558,13 +473,51 @@ 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
+size_t 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
+
0
+ size_t to_read = ramp(min(length, client->content_length - client->read_from_body));
0
+
0
+ if(client->read_from_body < client->request_body_head_size) {
0
+ memcpy( buffer
0
+ , client->request_body_head + client->read_from_body
0
+ , to_read
0
+ );
0
+ client->read_from_body += to_read;
0
+ return(to_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
 {
0
   assert(client->open);
0
   assert(FALSE == ev_is_active(&(client->write_watcher)));
0
   
0
+ /* assure the socket is still in non-blocking mode
0
+ * in the ruby binding, for example, i change this flag
0
+ */
0
   int flags = fcntl(client->fd, F_GETFL, 0);
0
- assert(0 <= fcntl(client->fd, F_SETFL, flags | O_NONBLOCK));
0
+ if(0 > fcntl(client->fd, F_SETFL, flags | O_NONBLOCK)) {
0
+ perror("fcntl()");
0
+ ebb_client_close(client);
0
+ return;
0
+ }
0
   
0
   client->written = 0;
0
   client->write_watcher.data = client;
0
@@ -574,31 +527,6 @@ 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
-size_t ebb_client_read(ebb_client *client, char *buffer, int length)
0
-{
0
- size_t read;
0
-
0
- assert(client->open);
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
- read = ramp(min(length, client->content_length - client->nread_from_body));
0
- memcpy( buffer
0
- , client->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) {
...
28
29
30
31
 
32
33
34
...
39
40
41
42
 
43
44
45
46
47
48
49
...
62
63
64
65
 
66
67
 
 
 
 
 
68
69
70
...
72
73
74
75
76
77
78
79
80
81
82
83
...
28
29
30
 
31
32
33
34
...
39
40
41
 
42
43
44
 
 
45
46
47
...
60
61
62
 
63
64
65
66
67
68
69
70
71
72
73
...
75
76
77
 
 
 
 
 
 
78
79
80
0
@@ -28,7 +28,7 @@ typedef struct ebb_client ebb_client;
0
 #define ebb_debug(str, ...) \
0
   g_log(EBB_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, str, ## __VA_ARGS__);
0
 
0
-#define EBB_BUFFERSIZE (14*1024)
0
+#define EBB_BUFFERSIZE (2*1024)
0
 #define EBB_MAX_CLIENTS 200
0
 #define EBB_TIMEOUT 30.0
0
 #define EBB_MAX_ENV 100
0
@@ -39,11 +39,9 @@ typedef struct ebb_client ebb_client;
0
 
0
 /*** Ebb Client ***/
0
 void ebb_client_close(ebb_client*);
0
-size_t ebb_client_read(ebb_client*, char *buffer, int length);
0
+size_t ebb_client_read_request(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
-/* User must free the GString returned from ebb_client_read_input */
0
-GString* ebb_client_read_input(ebb_client *client, ssize_t size);
0
 
0
 enum { EBB_REQUEST_METHOD
0
      , EBB_REQUEST_URI
0
@@ -62,9 +60,14 @@ struct ebb_client {
0
   ebb_server *server;
0
   http_parser parser;
0
   
0
- char read_buffer[EBB_BUFFERSIZE];
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
+
0
+ int content_length;
0
   
0
   ev_io write_watcher;
0
   GString *write_buffer;
0
@@ -72,12 +75,6 @@ struct ebb_client {
0
   
0
   void *data;
0
   
0
- char upload_file_filename[200];
0
- FILE *upload_file;
0
- int content_length;
0
-
0
- char *request_body;
0
- int nread_from_body;
0
   
0
   ev_timer timeout_watcher;
0
   
...
5
6
7
 
8
9
10
...
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
209
210
 
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
227
228
229
...
254
255
256
 
257
258
259
260
261
...
5
6
7
8
9
10
11
...
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
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
...
249
250
251
252
253
 
254
255
256
0
@@ -5,6 +5,7 @@
0
 
0
 #include <ruby.h>
0
 #include <assert.h>
0
+#include <fcntl.h>
0
 #include <ebb.h>
0
 #include <ev.h>
0
 
0
@@ -67,8 +68,7 @@ VALUE client_new(ebb_client *_client)
0
 {
0
   VALUE client = Data_Wrap_Struct(cClient, 0, 0, _client);
0
   _client->data = (void*)client;
0
- // if(_client->upload_file_filename)
0
- // rb_iv_set(client, "@upload_filename", rb_str_new2(_client->upload_file_filename));
0
+ rb_iv_set(client, "@content_length", INT2FIX(_client->content_length));
0
   return client;
0
 }
0
 
0
@@ -178,52 +178,47 @@ VALUE client_env(VALUE x, VALUE client)
0
 }
0
 
0
 
0
-VALUE client_write(VALUE x, VALUE client, VALUE string)
0
-{
0
- ebb_client *_client;
0
- int written;
0
-
0
- Data_Get_Struct(client, ebb_client, _client);
0
- ebb_client_write(_client, RSTRING_PTR(string), RSTRING_LEN(string));
0
- return Qnil;
0
-}
0
-
0
-VALUE client_finished(VALUE x, VALUE client)
0
+VALUE client_read(VALUE x, VALUE client, VALUE size)
0
 {
0
   ebb_client *_client;
0
- Data_Get_Struct(client, ebb_client, _client);
0
- ebb_client_finished(_client);
0
- return Qnil;
0
-}
0
-
0
-VALUE client_read_input(VALUE x, VALUE client, VALUE size)
0
-{
0
- ebb_client *_client;
0
- GString *_string;
0
   VALUE string;
0
   int _size = FIX2INT(size);
0
- int nread;
0
   
0
   Data_Get_Struct(client, ebb_client, _client);
0
   
0
   string = rb_str_buf_new( _size );
0
- nread = ebb_client_read(_client, RSTRING_PTR(string), _size);
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
-
0
   if(nread == 0)
0
     return Qnil;
0
-
0
   return string;
0
 }
0
 
0
 
0
+VALUE client_write(VALUE x, VALUE client, VALUE string)
0
+{
0
+ ebb_client *_client;
0
+ Data_Get_Struct(client, ebb_client, _client);
0
+ ebb_client_write(_client, RSTRING_PTR(string), RSTRING_LEN(string));
0
+ return Qnil;
0
+}
0
+
0
+
0
+VALUE client_finished(VALUE x, VALUE client)
0
+{
0
+ ebb_client *_client;
0
+ Data_Get_Struct(client, ebb_client, _client);
0
+ ebb_client_finished(_client);
0
+ return Qnil;
0
+}
0
+
0
+
0
 void Init_ebb_ext()
0
 {
0
   VALUE mEbb = rb_define_module("Ebb");
0
@@ -254,8 +249,8 @@ 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_write", client_write, 2);
0
- rb_define_singleton_method(mFFI, "client_read_input", client_read_input, 2);
0
   rb_define_singleton_method(mFFI, "client_finished", client_finished, 1);
0
   rb_define_singleton_method(mFFI, "client_env", client_env, 1);
0
 

Comments

    No one has commented yet.