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
Tests and a better FFI interface

The file benchmark/test.rb contains a handful of assertions to make very 
basic
demonstations that the server is doing what it intends to.

I've changed the FFI API so that it is more clear which functions are 
calling C
routines. Hopefully this will make ebb.rb a bit easier understand.

Large file uploads (greater than 16kb) are broken at the moment as is 
listening
from UNIX sockets.
Ryan Dahl (author)
2 months ago
commit  1fce3d5204583fde37459771e75d65a5f926db26
tree    d0274880200d318ad32cd266a4c40a7a7144cfc6
parent  3d4a4f70abc7eb097d4f5b8c6a4e4cdd52ae08f6
...
1
2
 
 
3
4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
6
7
8
9
10
11
12
13
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
15
16
17
18
19
20
21
 
22
...
1
 
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
 
 
 
 
 
 
 
 
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
 
 
 
 
 
 
 
105
106
0
@@ -1,21 +1,105 @@
0
 require File.dirname(__FILE__) + '/../ruby_lib/ebb'
0
-require File.dirname(__FILE__) + '/application'
0
+require 'test/unit'
0
+require 'net/http'
0
 
0
 
0
+class EbbTest < Test::Unit::TestCase
0
+ def setup
0
+ @pid = fork do
0
+ server = Ebb::Server.new(self, :port => 4044)
0
+ server.start
0
+ end
0
+ sleep 0.5
0
+ end
0
+
0
+ def teardown
0
+ Process.kill('KILL', @pid)
0
+ sleep 0.5
0
+ end
0
+
0
+ def get(path)
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
+
0
+ @@responses = {}
0
+ def call(env)
0
+ commands = env['PATH_INFO'].split('/')
0
+
0
+ if commands.include?('bytes')
0
+ n = commands.last.to_i
0
+ raise "bytes called with n <= 0" if n <= 0
0
+ body = @@responses[n] || "C"*n
0
+ status = 200
0
+
0
+ elsif commands.include?('test_post_length')
0
+ input_body = ""
0
+ while chunk = env['rack.input'].read(10)
0
+ input_body << chunk
0
+ end
0
+
0
+ content_length_header = env['HTTP_CONTENT_LENGTH'].to_i
0
+
0
+ if content_length_header == input_body.length
0
+ body = "Content-Length matches input length"
0
+ status = 200
0
+ else
0
+ body = "Content-Length header is #{content_length_header} but body length is #{input_body.length}"
0
+ # content_length = #{env['HTTP_CONTENT_LENGTH'].to_i}
0
+ # input_body.length = #{input_body.length}"
0
+ status = 500
0
+ end
0
+
0
+ else
0
+ status = 404
0
+ body = "Undefined url"
0
+ end
0
+
0
+ [status, {'Content-Type' => 'text/plain'}, body]
0
+ end
0
+
0
+ def test_get_bytes
0
+ [1,10,1000].each do |i|
0
+ response = get("/bytes/#{i}")
0
+ assert_equal "#{'C'*i.to_i}", response.body
0
+ end
0
+ end
0
+
0
+ def test_get_unknown
0
+ response = get('/blah')
0
+ assert_equal "Undefined url", response.body
0
+ end
0
+
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
+ end
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
-server = Ebb::Server.new(SimpleApp.new, {:Port => 4001})
0
-server.start
0
-
0
-
0
-# class EbbTest < Test::Unit::TestCase
0
-#
0
-# def get(path)
0
-# Net::HTTP.get_response(URI.parse("http://0.0.0.0:4001#{path}"))
0
+#
0
+# class SocketTest < Test::Unit::TestCase
0
+# def test_socket_creation
0
+# filename = '/tmp/ebb.socket'
0
+# @pid = fork do
0
+# server = Ebb::Server.new(TestApp.new, {:socket => filename})
0
+# server.start
0
+# end
0
+# sleep(1)
0
+# assert File.exists?(filename)
0
+#
0
+# Process.kill('KILL', @pid)
0
+#
0
+# assert !File.exists?(filename)
0
 # end
0
-#
0
-# def test_get
0
-# response = get('/hello')
0
-# eval response.body
0
-# end
0
-# end
0
-
0
+# end
0
\ No newline at end of file
...
25
26
27
28
 
29
30
31
32
 
 
 
 
 
 
 
 
33
34
35
...
39
40
41
42
 
43
44
45
...
61
62
63
64
65
66
67
...
73
74
75
76
 
77
78
79
80
 
 
 
 
 
 
 
81
82
83
...
104
105
106
107
108
 
109
110
111
 
112
113
114
115
116
 
117
118
119
...
122
123
124
125
 
126
127
128
...
164
165
166
167
 
168
169
170
...
25
26
27
 
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
...
47
48
49
 
50
51
52
53
...
69
70
71
 
72
73
74
...
80
81
82
 
83
84
85
86
 
87
88
89
90
91
92
93
94
95
96
...
117
118
119
 
 
120
121
 
 
122
123
124
125
126
 
127
128
129
130
...
133
134
135
 
136
137
138
139
...
175
176
177
 
178
179
180
181
0
@@ -25,11 +25,19 @@ module Ebb
0
     
0
     def env
0
       @env ||= begin
0
- env = @ebb_env.update(BASE_ENV)
0
+ env = FFI::client_env(self).update(BASE_ENV)
0
         env['rack.input'] = Input.new(self)
0
         env
0
       end
0
     end
0
+
0
+ def finished
0
+ FFI::client_finished(self)
0
+ end
0
+
0
+ def write(data)
0
+ FFI::client_write(self, data)
0
+ end
0
   end
0
   
0
   class Input
0
@@ -39,7 +47,7 @@ module Ebb
0
     end
0
     
0
     def read(len = 1)
0
- @client.read_input(len)
0
+ FFI::client_read_input(@client, len)
0
     end
0
     
0
     def gets
0
@@ -61,7 +69,6 @@ module Ebb
0
     end
0
     
0
     def initialize(app, options={})
0
- #@host = options[:host] || '0.0.0.0'
0
       @socket = options[:socket]
0
       @port = (options[:port] || 4001).to_i
0
       pid_file = options[:pid_file]
0
@@ -73,11 +80,17 @@ module Ebb
0
         daemonize
0
       end
0
       @app = app
0
- init
0
+ FFI::server_initialize(self)
0
     end
0
     
0
     def process_client(client)
0
- status, headers, body = @app.call(client.env)
0
+ begin
0
+ status, headers, body = @app.call(client.env)
0
+ rescue
0
+ status = 500
0
+ headers = {'Content-Type' => 'text/plain'}
0
+ body = HTTP_STATUS_CODES[status]
0
+ end
0
       
0
       client.write "HTTP/1.1 %d %s\r\n" % [status, HTTP_STATUS_CODES[status]]
0
       
0
@@ -104,16 +117,14 @@ module Ebb
0
       trap('INT') { @running = false }
0
       
0
       if @socket
0
- listen_on_socket(@socket) or raise "Problem listening on socket #{@socket}"
0
- puts "Listening on socket #{@socket}"
0
+ FFI::server_listen_on_socket(self, @socket) or raise "Problem listening on socket #{@socket}"
0
       else
0
- listen_on_port(@port) or raise "Problem listening on port #{@port}"
0
- puts "Listening on port #{@port}"
0
+ FFI::server_listen_on_port(self, @port) or raise "Problem listening on port #{@port}"
0
       end
0
       @waiting_clients = []
0
       
0
       @running = true
0
- while process_connections and @running
0
+ while FFI::server_process_connections(self) and @running
0
         unless @waiting_clients.empty?
0
           if $debug and @waiting_clients.length > 1
0
             puts "#{@waiting_clients.length} waiting clients"
0
@@ -122,7 +133,7 @@ module Ebb
0
           process_client(client)
0
         end
0
       end
0
- unlisten
0
+ FFI::server_unlisten(self)
0
     end
0
   end
0
   
0
@@ -164,7 +175,7 @@ module Ebb
0
     503 => 'Service Unavailable',
0
     504 => 'Gateway Time-out',
0
     505 => 'HTTP Version not supported'
0
- }
0
+ }.freeze
0
 end
0
 
0
 module Rack
...
141
142
143
 
144
145
146
...
164
165
166
167
168
169
170
 
171
172
173
...
216
217
218
 
 
219
220
221
...
242
243
244
245
 
246
247
248
...
382
383
384
385
 
386
387
388
...
440
441
442
443
 
444
 
 
 
445
446
447
...
475
476
477
478
 
479
480
481
...
512
513
514
515
516
517
518
 
519
520
521
522
523
524
 
525
526
527
...
557
558
559
 
560
561
562
...
567
568
569
570
 
571
572
573
...
141
142
143
144
145
146
147
...
165
166
167
 
 
 
 
168
169
170
171
...
214
215
216
217
218
219
220
221
...
242
243
244
 
245
246
247
248
...
382
383
384
 
385
386
387
388
...
440
441
442
 
443
444
445
446
447
448
449
450
...
478
479
480
 
481
482
483
484
...
515
516
517
 
 
 
 
518
519
520
521
522
523
 
524
525
526
527
...
557
558
559
560
561
562
563
...
568
569
570
 
571
572
573
574
0
@@ -141,6 +141,7 @@ void content_length_cb(void *data, const char *at, size_t length)
0
     client->content_length *= 10;
0
     client->content_length += at[i] - '0';
0
   }
0
+ // ebb_debug("content length read: %d", client->content_length);
0
 }
0
 
0
 
0
@@ -164,10 +165,7 @@ void dispatch(ebb_client *client)
0
 }
0
 
0
 
0
-void ebb_on_timeout( struct ev_loop *loop
0
- , ev_timer *watcher
0
- , int revents
0
- )
0
+void on_timeout(struct ev_loop *loop, ev_timer *watcher, int revents)
0
 {
0
   ebb_client *client = (ebb_client*)(watcher->data);
0
   
0
@@ -216,6 +214,8 @@ void* read_body_into_file(void *_client)
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
@@ -242,7 +242,7 @@ void* read_body_into_file(void *_client)
0
     written += received;
0
   }
0
   rewind(tmpfile);
0
-
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
@@ -382,7 +382,7 @@ void on_request( struct ev_loop *loop
0
   ev_io_start(server->loop, &client->read_watcher);
0
   
0
   client->timeout_watcher.data = client;
0
- ev_timer_init(&client->timeout_watcher, ebb_on_timeout, EBB_TIMEOUT, EBB_TIMEOUT);
0
+ ev_timer_init(&client->timeout_watcher, on_timeout, EBB_TIMEOUT, EBB_TIMEOUT);
0
   ev_timer_start(server->loop, &client->timeout_watcher);
0
 }
0
 
0
@@ -440,8 +440,11 @@ void ebb_server_unlisten(ebb_server *server)
0
     for(i=0; i < EBB_MAX_CLIENTS; i++)
0
       ebb_client_close(client);
0
     ev_io_stop(server->loop, &server->request_watcher);
0
- if(server->fd > 0)
0
+ if(server->fd > 0) {
0
       close(server->fd);
0
+ if(server->socketpath)
0
+ unlink(server->socketpath);
0
+ }
0
     server->open = FALSE;
0
   }
0
 }
0
@@ -475,7 +478,7 @@ int ebb_server_listen_on_port(ebb_server *server, const int port)
0
 
0
 int ebb_server_listen_on_socket(ebb_server *server, const char *socketpath)
0
 {
0
- int fd = server_socket_unix(socketpath, 0x700);
0
+ int fd = server_socket_unix(socketpath, 0755);
0
   if(fd < 0) return 0;
0
   server->socketpath = strdup(socketpath);
0
   server->fd = fd;
0
@@ -512,16 +515,13 @@ void ebb_client_close(ebb_client *client)
0
 }
0
 
0
 
0
-void ebb_on_writable( struct ev_loop *loop
0
- , ev_io *watcher
0
- , int revents
0
- )
0
+void on_client_writable(struct ev_loop *loop, ev_io *watcher, int revents)
0
 {
0
   ebb_client *client = (ebb_client*)(watcher->data);
0
   ssize_t sent;
0
   
0
   if(EV_ERROR & revents) {
0
- ebb_error("ebb_on_writable() got error event, closing peer");
0
+ ebb_error("on_client_writable() got error event, closing peer");
0
     return;
0
   }
0
   
0
@@ -557,6 +557,7 @@ void ebb_client_write(ebb_client *client, const char *data, int length)
0
   g_string_append_len(client->write_buffer, data, length);
0
 }
0
 
0
+
0
 void ebb_client_finished( ebb_client *client)
0
 {
0
   assert(client->open);
0
@@ -567,7 +568,7 @@ void ebb_client_finished( ebb_client *client)
0
   
0
   client->written = 0;
0
   client->write_watcher.data = client;
0
- ev_init (&(client->write_watcher), ebb_on_writable);
0
+ ev_init (&(client->write_watcher), on_client_writable);
0
   ev_io_set (&(client->write_watcher), client->fd, EV_WRITE | EV_ERROR);
0
   ev_io_start(client->server->loop, &(client->write_watcher));
0
 }
...
28
29
30
31
 
32
33
34
...
28
29
30
 
31
32
33
34
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 (16*1024)
0
+#define EBB_BUFFERSIZE (14*1024)
0
 #define EBB_MAX_CLIENTS 200
0
 #define EBB_TIMEOUT 30.0
0
 #define EBB_MAX_ENV 100
...
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
...
110
111
112
113
 
114
115
116
...
121
122
123
124
 
125
126
127
...
130
131
132
133
 
134
135
136
...
143
144
145
146
 
147
148
149
...
167
168
169
170
 
171
172
173
...
175
176
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
203
204
205
 
206
207
208
...
229
230
231
232
233
234
235
236
237
238
 
239
240
241
...
253
254
255
 
256
257
258
259
260
261
262
263
264
265
266
267
268
 
 
 
 
 
269
 
 
 
 
 
 
270
...
63
64
65
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
66
67
68
69
70
71
 
72
73
74
...
91
92
93
 
94
95
96
97
...
102
103
104
 
105
106
107
108
...
111
112
113
 
114
115
116
117
...
124
125
126
 
127
128
129
130
...
148
149
150
 
151
152
153
154
...
156
157
158
 
159
160
161
162
 
 
163
164
165
 
 
166
167
168
169
170
171
172
173
174
175
176
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
...
223
224
225
 
226
227
228
229
 
 
230
231
232
233
...
245
246
247
248
249
 
 
 
 
 
 
 
 
 
 
 
 
250
251
252
253
254
255
256
257
258
259
260
261
262
0
@@ -63,31 +63,12 @@ VALUE env_field(const char *field, int length)
0
   return Qnil;
0
 }
0
 
0
-VALUE client_env(ebb_client *_client)
0
-{
0
- VALUE hash = rb_hash_new();
0
- int i;
0
- /* This client->env_fields, client->env_value structure is pretty hacky
0
- * and a bit hard to follow. Look at the #defines at the top of ebb.c to
0
- * see what they are doing. Basically it's a list of (ptr,length) pairs
0
- * for both a field and value
0
- */
0
- for(i=0; i < _client->env_size; i++) {
0
- rb_hash_aset(hash, env_field(_client->env_fields[i], _client->env_field_lengths[i])
0
- , rb_str_new(_client->env_values[i], _client->env_value_lengths[i])
0
- );
0
- }
0
- rb_hash_aset(hash, global_path_info, rb_hash_aref(hash, global_request_path));
0
- return hash;
0
-}
0
-
0
 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, "@ebb_env", client_env(_client));
0
   return client;
0
 }
0
 
0
@@ -110,7 +91,7 @@ VALUE server_alloc(VALUE self)
0
 }
0
 
0
 
0
-VALUE server_init(VALUE server)
0
+VALUE server_initialize(VALUE x, VALUE server)
0
 {
0
   struct ev_loop *loop = ev_default_loop (0);
0
   ebb_server *_server;
0
@@ -121,7 +102,7 @@ VALUE server_init(VALUE server)
0
 }
0
 
0
 
0
-VALUE server_listen_on_port(VALUE server, VALUE port)
0
+VALUE server_listen_on_port(VALUE x, VALUE server, VALUE port)
0
 {
0
   ebb_server *_server;
0
   Data_Get_Struct(server, ebb_server, _server);
0
@@ -130,7 +111,7 @@ VALUE server_listen_on_port(VALUE server, VALUE port)
0
 }
0
 
0
 
0
-VALUE server_listen_on_socket(VALUE server, VALUE socketpath)
0
+VALUE server_listen_on_socket(VALUE x, VALUE server, VALUE socketpath)
0
 {
0
   ebb_server *_server;
0
   Data_Get_Struct(server, ebb_server, _server);
0
@@ -143,7 +124,7 @@ static void
0
 oneshot_timeout (struct ev_loop *loop, struct ev_timer *w, int revents) {;}
0
 
0
 
0
-VALUE server_process_connections(VALUE server)
0
+VALUE server_process_connections(VALUE x, VALUE server)
0
 {
0
   ebb_server *_server;
0
   VALUE host, port;
0
@@ -167,7 +148,7 @@ VALUE server_process_connections(VALUE server)
0
     return Qfalse;
0
 }
0
 
0
-VALUE server_unlisten(VALUE server)
0
+VALUE server_unlisten(VALUE x, VALUE server)
0
 {
0
   ebb_server *_server;
0
   Data_Get_Struct(server, ebb_server, _server);
0
@@ -175,34 +156,47 @@ VALUE server_unlisten(VALUE server)
0
   return Qnil;
0
 }
0
 
0
-VALUE client_write(VALUE client, VALUE string)
0
+
0
+VALUE client_env(VALUE x, VALUE client)
0
 {
0
   ebb_client *_client;
0
- int written;
0
-
0
+ VALUE hash = rb_hash_new();
0
+ int i;
0
   Data_Get_Struct(client, ebb_client, _client);
0
- ebb_client_write(_client, RSTRING_PTR(string), RSTRING_LEN(string));
0
- return Qnil;
0
+ /* This client->env_fields, client->env_value structure is pretty hacky
0
+ * and a bit hard to follow. Look at the #defines at the top of ebb.c to
0
+ * see what they are doing. Basically it's a list of (ptr,length) pairs
0
+ * for both a field and value
0
+ */
0
+ for(i=0; i < _client->env_size; i++) {
0
+ rb_hash_aset(hash, env_field(_client->env_fields[i], _client->env_field_lengths[i])
0
+ , rb_str_new(_client->env_values[i], _client->env_value_lengths[i])
0
+ );
0
+ }
0
+ rb_hash_aset(hash, global_path_info, rb_hash_aref(hash, global_request_path));
0
+ return hash;
0
 }
0
 
0
-VALUE client_finished(VALUE client)
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_finished(_client);
0
+ ebb_client_write(_client, RSTRING_PTR(string), RSTRING_LEN(string));
0
   return Qnil;
0
 }
0
 
0
-VALUE client_close(VALUE client)
0
+VALUE client_finished(VALUE x, VALUE client)
0
 {
0
   ebb_client *_client;
0
-
0
   Data_Get_Struct(client, ebb_client, _client);
0
- ebb_client_close(_client);
0
+ ebb_client_finished(_client);
0
   return Qnil;
0
 }
0
 
0
-VALUE client_read_input(VALUE client, VALUE size)
0
+VALUE client_read_input(VALUE x, VALUE client, VALUE size)
0
 {
0
   ebb_client *_client;
0
   GString *_string;
0
@@ -229,13 +223,11 @@ VALUE client_read_input(VALUE client, VALUE size)
0
   return string;
0
 }
0
 
0
-VALUE client_init(VALUE self, VALUE something) {return self;}
0
 
0
 void Init_ebb_ext()
0
 {
0
   VALUE mEbb = rb_define_module("Ebb");
0
- cServer = rb_define_class_under(mEbb, "Server", rb_cObject);
0
- cClient = rb_define_class_under(mEbb, "Client", rb_cObject);
0
+ VALUE mFFI = rb_define_module_under(mEbb, "FFI");
0
   
0
   /** Defines global strings in the init method. */
0
 #define DEF_GLOBAL(N, val) global_##N = rb_obj_freeze(rb_str_new2(val)); rb_global_variable(&global_##N)
0
@@ -253,18 +245,18 @@ void Init_ebb_ext()
0
   DEF_GLOBAL(content_length, "HTTP_CONTENT_LENGTH");
0
   DEF_GLOBAL(http_host, "HTTP_HOST");
0
   
0
+ cServer = rb_define_class_under(mEbb, "Server", rb_cObject);
0
   rb_define_alloc_func(cServer, server_alloc);
0
- rb_define_method(cServer, "init", server_init, 0);
0
- rb_define_method(cServer, "process_connections", server_process_connections, 0);
0
- rb_define_method(cServer, "listen_on_port", server_listen_on_port, 1);
0
- rb_define_method(cServer, "listen_on_socket", server_listen_on_socket, 1);
0
- rb_define_method(cServer, "unlisten", server_unlisten, 0);
0
-
0
- rb_define_method(cClient, "initialize", client_init, 1);
0
- rb_define_method(cClient, "write", client_write, 1);
0
- rb_define_method(cClient, "read_input", client_read_input, 1);
0
- rb_define_method(cClient, "finished", client_finished, 0);
0
-
0
- rb_define_method(cClient, "read_input", client_read_input, 1);
0
+ rb_define_singleton_method(mFFI, "server_initialize", server_initialize, 1);
0
+ rb_define_singleton_method(mFFI, "server_process_connections", server_process_connections, 1);
0
+ rb_define_singleton_method(mFFI, "server_listen_on_port", server_listen_on_port, 2);
0
+ rb_define_singleton_method(mFFI, "server_listen_on_socket", server_listen_on_socket, 2);
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_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
+
0
 }

Comments

    No one has commented yet.