ebb 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/README CHANGED
@@ -1,20 +1,11 @@
1
+ (this is only for HEAD - old docs are found in the git repo)
2
+
1
3
  # A Web Server Called *Ebb*
2
4
 
3
5
  Ebb aims to be a small and fast web server specifically for hosting
4
- dynamic web applications. It is not meant to be a full featured web server
5
- like Lighttpd, Apache, or Nginx. Rather it should be used in multiplicity
6
- behind a load balancer and a front-end server. It is not meant to serve static files in production.
7
-
8
- At one level Ebb is a minimalist C library that ties together the
9
- [Mongrel state machine](http://mongrel.rubyforge.org/browser/tags/rel_1-0-1/ext/http11/http11_parser.rl)
10
- and [libev](http://software.schmorp.de/pkg/libev.html) event loop. One can use
11
- this library to drive a web application written in C. (Perhaps for embedded
12
- devices?) However, most people will be interested in the binding of this
13
- library to the Ruby programming language. The binding provides a
14
- [Rack](http://rack.rubyforge.org/) server interface that allows it to host
15
- Rails, Merb, or other frameworks.
6
+ dynamic Ruby language web applications.
16
7
 
17
- A Python-WSGI binding is under development.
8
+ It is a binding to [libebb](http://tinyclouds.org/libebb)
18
9
 
19
10
  ## Install
20
11
 
@@ -22,37 +13,17 @@ The Ruby binding is available as a Ruby Gem. It can be install by executing
22
13
 
23
14
  gem install ebb
24
15
 
25
- Ebb depends on having glib2 headers and libraries installed. For example, in
26
- Macintosh if one is using Darwin ports then the following should do the trick
27
-
28
- port install glib2
29
-
30
- Downloads are available at
31
- the [RubyForge project page](http://rubyforge.org/frs/?group_id=5640).
16
+ If you want SSL support you must install GnuTLS.
17
+ Ebb has no other dependencies.
32
18
 
33
19
  ## Running
34
20
 
35
- Using the executable `ebb_rails` one can start Ebb with a Rails project. Use
36
- `ebb_rails -h` to see all of the options but to start one can try
37
-
38
- cd my_rails_project/
39
- ebb_rails start
40
-
41
- When using `ebb_rails` from monit, the monitrc entry might look like this:
42
-
43
- check process myApp4000
44
- with pidfile /home/webuser/myApp/current/tmp/ebb.4000.pid
45
- start program = "/usr/bin/ruby /usr/bin/ebb_rails start -d -e production -p 4000 -P /home/webuser/myApp/current/tmp/ebb.4000.pid -c /home/webuser/myApp/current" as uid webuser and gid webuser
46
- stop program = "/usr/bin/ruby /usr/bin/ebb_rails stop -P /home/webuser/myApp/current/tmp/ebb.4000.pid" as uid webuser and gid webuser
47
- if totalmem > 120.0 MB for 2 cycles then restart
48
- if loadavg(5min) greater than 10 for 8 cycles then restart
49
- group myApp
50
-
51
- To use Ebb with a different framework you will have to do a small amount of
52
- hacking at the moment! :)
21
+ Use Ebb.start_server()
53
22
 
54
23
  ## Speed
55
24
 
25
+ (these stats are out of date)
26
+
56
27
  Because Ebb handles most of the processing in C, it is able to do work
57
28
  often times more efficiently than other Ruby language web servers.
58
29
 
@@ -75,14 +46,9 @@ can be retrieved by executing
75
46
 
76
47
  git clone git://github.com/ry/ebb.git
77
48
 
78
- Here are some features that I would like to add:
79
- * HTTP 1.1 Expect/Continue (RFC 2616, sections 8.2.3 and 10.1.1)
80
- * A parser for multipart/form-data (only for optimization - this functionality is currently handled at the framework level)
81
- * Python binding
82
-
83
49
  ## (The MIT) License
84
50
 
85
- Copyright © 2008 [Ry Dahl](http://tinyclouds.org) (ry at tiny clouds dot org)
51
+ Copyright (c) 2008 [Ryah Dahl](http://tinyclouds.org) (ry at tiny clouds dot org)
86
52
 
87
53
  <div id="license">
88
54
  Permission is hereby granted, free of charge, to any person obtaining
@@ -0,0 +1,123 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rake/gempackagetask'
4
+ require 'rake/clean'
5
+
6
+ class String
7
+ def /(other)
8
+ File.join(self, other)
9
+ end
10
+ end
11
+
12
+ require 'lib/ebb/version'
13
+
14
+ libev_dist = "http://dist.schmorp.de/libev/Attic"
15
+ libev_release = "libev-3.43.tar.gz"
16
+ libev_url = File.join(libev_dist, libev_release)
17
+
18
+ LIBEBBFILES = ['ebb.c', 'ebb.h',
19
+ 'ebb_request_parser.rl', 'ebb_request_parser.c', 'ebb_request_parser.h',
20
+ 'rbtree.c', 'rbtree.h']
21
+ SRCEBBFILES = LIBEBBFILES.map { |f| "ext" / f }
22
+
23
+ DISTFILES = FileList.new('libev/*.{c,h}', 'lib/**/*.rb', 'ext/*.{rb,rl,c,h}', 'README', 'Rakefile') + SRCEBBFILES
24
+ CLEAN.add ["**/*.{o,bundle,so,obj,pdb,lib,def,exp}", "benchmark/*.dump", 'site/index.html']
25
+ CLOBBER.add ['ext/Makefile', 'ext/mkmf.log'] + SRCEBBFILES
26
+
27
+ Rake::TestTask.new do |t|
28
+ t.test_files = FileList.new("test/*.rb")
29
+ t.verbose = true
30
+ end
31
+
32
+ LIBEBBFILES.each do |f|
33
+ file(".libebb"/f => ".libebb")
34
+ file("ext"/f => ".libebb"/f) do |t|
35
+ sh "cp .libebb/#{f} ext/#{f}"
36
+ end
37
+ end
38
+
39
+ task(:default => [:compile])
40
+
41
+ task(:compile => ['ext/Makefile','libev'] + SRCEBBFILES) do
42
+ sh "cd ext && make"
43
+ end
44
+
45
+ file "libev" do
46
+ puts "downloading libev"
47
+ sh "wget #{libev_url}" do |ok, res|
48
+ if ! ok
49
+ puts "Couldn't download libev. Please put #{libev_url} in here and try again"
50
+ exit 1
51
+ end
52
+ end
53
+ sh "tar -zxf #{libev_release}"
54
+ sh "mv #{libev_release.sub('.tar.gz', '')} libev"
55
+ end
56
+
57
+ file ".libebb" do
58
+ sh "git clone git://github.com/ry/libebb.git .libebb"
59
+ end
60
+
61
+ file('ext/Makefile' => 'ext/extconf.rb') do
62
+ sh "cd ext && ruby extconf.rb"
63
+ end
64
+
65
+ file(".libebb/ebb_request_parser.c" => '.libebb/ebb_request_parser.rl') do
66
+ sh 'ragel -s -G2 .libebb/ebb_request_parser.rl'
67
+ end
68
+
69
+ task(:test => DISTFILES)
70
+ Rake::TestTask.new do |t|
71
+ t.test_files = 'test/basic_test.rb'
72
+ t.verbose = true
73
+ end
74
+
75
+ task(:site_upload => :site) do
76
+ sh 'scp -r site/* rydahl@rubyforge.org:/var/www/gforge-projects/ebb/'
77
+ end
78
+ task(:site => 'site/index.html')
79
+ file('site/index.html' => %w{README site/style.css}) do
80
+ require 'rubygems'
81
+ require 'bluecloth'
82
+ doc = BlueCloth.new(File.read('README'))
83
+ template = <<-HEREDOC
84
+ <html>
85
+ <head>
86
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
87
+ <title>Ebb</title>
88
+ <link rel="alternate" href="http://max.kanat.us/tag-syndicate/?user=four&tag=ebb" title="RSS Feed" type="application/rss+xml" />
89
+ <link type="text/css" rel="stylesheet" href="style.css" media="screen"/>
90
+ </head>
91
+ <body>
92
+ <div id="content">CONTENT</div>
93
+ </body>
94
+ </html>
95
+ HEREDOC
96
+
97
+ File.open('site/index.html', "w+") do |f|
98
+ f.write template.sub('CONTENT', doc.to_html)
99
+ end
100
+ end
101
+
102
+ spec = Gem::Specification.new do |s|
103
+ s.platform = Gem::Platform::RUBY
104
+ s.summary = "A Web Server"
105
+ s.description = ''
106
+ s.name = 'ebb'
107
+ s.author = 'ry dahl'
108
+ s.email = 'ry at tiny clouds dot org'
109
+ s.homepage = 'http://ebb.rubyforge.org'
110
+ s.version = Ebb::VERSION
111
+ s.rubyforge_project = 'ebb'
112
+
113
+ s.required_ruby_version = '>= 1.8.4'
114
+
115
+ s.require_path = 'lib'
116
+ s.extensions = 'ext/extconf.rb'
117
+
118
+ s.files = DISTFILES
119
+ end
120
+
121
+ Rake::GemPackageTask.new(spec) do |pkg|
122
+ pkg.need_zip = true
123
+ end
@@ -0,0 +1,794 @@
1
+ /* libebb web server library
2
+ * Copyright 2008 ryah dahl, ry at tiny clouds punkt org
3
+ *
4
+ * This software may be distributed under the "MIT" license included in the
5
+ * README
6
+ */
7
+
8
+ #include <assert.h>
9
+ #include <string.h>
10
+ #include <fcntl.h>
11
+ #include <sys/types.h>
12
+ #include <sys/socket.h>
13
+ #include <netinet/tcp.h> /* TCP_NODELAY */
14
+ #include <netinet/in.h> /* inet_ntoa */
15
+ #include <arpa/inet.h> /* inet_ntoa */
16
+ #include <unistd.h>
17
+ #include <stdio.h> /* perror */
18
+ #include <errno.h> /* perror */
19
+ #include <stdlib.h> /* for the default methods */
20
+ #include <ev.h>
21
+
22
+ #include "ebb.h"
23
+ #include "ebb_request_parser.h"
24
+ #ifdef HAVE_GNUTLS
25
+ # include <gnutls/gnutls.h>
26
+ # include "rbtree.h" /* for session_cache */
27
+ #endif
28
+
29
+ #ifndef TRUE
30
+ # define TRUE 1
31
+ #endif
32
+ #ifndef FALSE
33
+ # define FALSE 0
34
+ #endif
35
+ #ifndef MIN
36
+ # define MIN(a,b) (a < b ? a : b)
37
+ #endif
38
+
39
+ #define error(FORMAT, ...) fprintf(stderr, "error: " FORMAT "\n", ##__VA_ARGS__)
40
+
41
+ #define CONNECTION_HAS_SOMETHING_TO_WRITE (connection->to_write != NULL)
42
+
43
+ static void
44
+ set_nonblock (int fd)
45
+ {
46
+ int flags = fcntl(fd, F_GETFL, 0);
47
+ int r = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
48
+ assert(0 <= r && "Setting socket non-block failed!");
49
+ }
50
+
51
+ static ssize_t
52
+ nosigpipe_push(void *data, const void *buf, size_t len)
53
+ {
54
+ int fd = (int)data;
55
+ int flags = 0;
56
+ #ifdef MSG_NOSIGNAL
57
+ flags = MSG_NOSIGNAL;
58
+ #endif
59
+ return send(fd, buf, len, flags);
60
+ }
61
+
62
+ static void
63
+ close_connection(ebb_connection *connection)
64
+ {
65
+ #ifdef HAVE_GNUTLS
66
+ if(connection->server->secure)
67
+ ev_io_stop(connection->server->loop, &connection->handshake_watcher);
68
+ #endif
69
+ ev_io_stop(connection->server->loop, &connection->read_watcher);
70
+ ev_io_stop(connection->server->loop, &connection->write_watcher);
71
+ ev_timer_stop(connection->server->loop, &connection->timeout_watcher);
72
+
73
+ if(0 > close(connection->fd))
74
+ error("problem closing connection fd");
75
+
76
+ connection->open = FALSE;
77
+
78
+ if(connection->on_close)
79
+ connection->on_close(connection);
80
+ /* No access to the connection past this point!
81
+ * The user is allowed to free in the callback
82
+ */
83
+ }
84
+
85
+ #ifdef HAVE_GNUTLS
86
+ #define GNUTLS_NEED_WRITE (gnutls_record_get_direction(connection->session) == 1)
87
+ #define GNUTLS_NEED_READ (gnutls_record_get_direction(connection->session) == 0)
88
+
89
+ #define EBB_MAX_SESSION_KEY 32
90
+ #define EBB_MAX_SESSION_VALUE 512
91
+
92
+ struct session_cache {
93
+ struct rbtree_node_t node;
94
+
95
+ gnutls_datum_t key;
96
+ gnutls_datum_t value;
97
+
98
+ char key_storage[EBB_MAX_SESSION_KEY];
99
+ char value_storage[EBB_MAX_SESSION_VALUE];
100
+ };
101
+
102
+ static int
103
+ session_cache_compare (void *left, void *right)
104
+ {
105
+ gnutls_datum_t *left_key = left;
106
+ gnutls_datum_t *right_key = right;
107
+ if(left_key->size < right_key->size)
108
+ return -1;
109
+ else if(left_key->size > right_key->size)
110
+ return 1;
111
+ else
112
+ return memcmp( left_key->data
113
+ , right_key->data
114
+ , MIN(left_key->size, right_key->size)
115
+ );
116
+ }
117
+
118
+ static int
119
+ session_cache_store(void *data, gnutls_datum_t key, gnutls_datum_t value)
120
+ {
121
+ rbtree tree = data;
122
+
123
+ if( tree == NULL
124
+ || key.size > EBB_MAX_SESSION_KEY
125
+ || value.size > EBB_MAX_SESSION_VALUE
126
+ ) return -1;
127
+
128
+ struct session_cache *cache = gnutls_malloc(sizeof(struct session_cache));
129
+
130
+ memcpy (cache->key_storage, key.data, key.size);
131
+ cache->key.size = key.size;
132
+ cache->key.data = (void*)cache->key_storage;
133
+
134
+ memcpy (cache->value_storage, value.data, value.size);
135
+ cache->value.size = value.size;
136
+ cache->value.data = (void*)cache->value_storage;
137
+
138
+ cache->node.key = &cache->key;
139
+ cache->node.value = &cache;
140
+
141
+ rbtree_insert(tree, (rbtree_node)cache);
142
+
143
+ //printf("session_cache_store\n");
144
+
145
+ return 0;
146
+ }
147
+
148
+ static gnutls_datum_t
149
+ session_cache_retrieve (void *data, gnutls_datum_t key)
150
+ {
151
+ rbtree tree = data;
152
+ gnutls_datum_t res = { NULL, 0 };
153
+ struct session_cache *cache = rbtree_lookup(tree, &key);
154
+
155
+ if(cache == NULL)
156
+ return res;
157
+
158
+ res.size = cache->value.size;
159
+ res.data = gnutls_malloc (res.size);
160
+ if(res.data == NULL)
161
+ return res;
162
+
163
+ memcpy(res.data, cache->value.data, res.size);
164
+
165
+ //printf("session_cache_retrieve\n");
166
+
167
+ return res;
168
+ }
169
+
170
+ static int
171
+ session_cache_remove (void *data, gnutls_datum_t key)
172
+ {
173
+ rbtree tree = data;
174
+
175
+ if(tree == NULL)
176
+ return -1;
177
+
178
+ struct session_cache *cache = (struct session_cache *)rbtree_delete(tree, &key);
179
+ if(cache == NULL)
180
+ return -1;
181
+
182
+ gnutls_free(cache);
183
+
184
+ //printf("session_cache_remove\n");
185
+
186
+ return 0;
187
+ }
188
+
189
+ static void
190
+ on_handshake(struct ev_loop *loop ,ev_io *watcher, int revents)
191
+ {
192
+ ebb_connection *connection = watcher->data;
193
+
194
+ //printf("on_handshake\n");
195
+
196
+ assert(ev_is_active(&connection->timeout_watcher));
197
+ assert(!ev_is_active(&connection->read_watcher));
198
+ assert(!ev_is_active(&connection->write_watcher));
199
+
200
+ if(EV_ERROR & revents) {
201
+ error("on_handshake() got error event, closing connection.n");
202
+ goto error;
203
+ }
204
+
205
+ int r = gnutls_handshake(connection->session);
206
+ if(r < 0) {
207
+ if(gnutls_error_is_fatal(r)) goto error;
208
+ if(r == GNUTLS_E_INTERRUPTED || r == GNUTLS_E_AGAIN)
209
+ ev_io_set( watcher
210
+ , connection->fd
211
+ , EV_ERROR | (GNUTLS_NEED_WRITE ? EV_WRITE : EV_READ)
212
+ );
213
+ return;
214
+ }
215
+
216
+ ebb_connection_reset_timeout(connection);
217
+ ev_io_stop(loop, watcher);
218
+
219
+ ev_io_start(loop, &connection->read_watcher);
220
+ if(CONNECTION_HAS_SOMETHING_TO_WRITE)
221
+ ev_io_start(loop, &connection->write_watcher);
222
+
223
+ return;
224
+ error:
225
+ close_connection(connection);
226
+ }
227
+
228
+ #endif /* HAVE_GNUTLS */
229
+
230
+
231
+ /* Internal callback
232
+ * called by connection->timeout_watcher
233
+ */
234
+ static void
235
+ on_timeout(struct ev_loop *loop, ev_timer *watcher, int revents)
236
+ {
237
+ ebb_connection *connection = watcher->data;
238
+
239
+ assert(watcher == &connection->timeout_watcher);
240
+
241
+ //printf("on_timeout\n");
242
+
243
+ /* if on_timeout returns true, we don't time out */
244
+ if(connection->on_timeout) {
245
+ int r = connection->on_timeout(connection);
246
+
247
+ if(r == EBB_AGAIN) {
248
+ ebb_connection_reset_timeout(connection);
249
+ return;
250
+ }
251
+ }
252
+
253
+ ebb_connection_schedule_close(connection);
254
+ }
255
+
256
+ /* Internal callback
257
+ * called by connection->read_watcher
258
+ */
259
+ static void
260
+ on_readable(struct ev_loop *loop, ev_io *watcher, int revents)
261
+ {
262
+ ebb_connection *connection = watcher->data;
263
+ char base[TCP_MAXWIN];
264
+ char *recv_buffer = base;
265
+ size_t recv_buffer_size = TCP_MAXWIN;
266
+ ssize_t recved;
267
+
268
+ //printf("on_readable\n");
269
+
270
+ // TODO -- why is this broken?
271
+ //assert(ev_is_active(&connection->timeout_watcher));
272
+ assert(watcher == &connection->read_watcher);
273
+
274
+ if(EV_ERROR & revents) {
275
+ error("on_readable() got error event, closing connection.");
276
+ goto error;
277
+ }
278
+
279
+ ebb_buf *buf = NULL;
280
+ if(connection->new_buf) {
281
+ buf = connection->new_buf(connection);
282
+ if(buf == NULL) return;
283
+ recv_buffer = buf->base;
284
+ recv_buffer_size = buf->len;
285
+ }
286
+
287
+ #ifdef HAVE_GNUTLS
288
+ assert(!ev_is_active(&connection->handshake_watcher));
289
+
290
+ if(connection->server->secure) {
291
+ recved = gnutls_record_recv( connection->session
292
+ , recv_buffer
293
+ , recv_buffer_size
294
+ );
295
+ if(recved <= 0) {
296
+ if(gnutls_error_is_fatal(recved)) goto error;
297
+ if( (recved == GNUTLS_E_INTERRUPTED || recved == GNUTLS_E_AGAIN)
298
+ && GNUTLS_NEED_WRITE
299
+ ) ev_io_start(loop, &connection->write_watcher);
300
+ return;
301
+ }
302
+ } else {
303
+ #endif /* HAVE_GNUTLS */
304
+
305
+ recved = recv(connection->fd, recv_buffer, recv_buffer_size, 0);
306
+ if(recved < 0) goto error;
307
+ if(recved == 0) return;
308
+
309
+ #ifdef HAVE_GNUTLS
310
+ }
311
+ #endif /* HAVE_GNUTLS */
312
+
313
+ ebb_connection_reset_timeout(connection);
314
+
315
+ ebb_request_parser_execute(&connection->parser, recv_buffer, recved);
316
+
317
+ /* parse error? just drop the client. screw the 400 response */
318
+ if(ebb_request_parser_has_error(&connection->parser)) goto error;
319
+
320
+ if(buf && buf->on_release)
321
+ buf->on_release(buf);
322
+
323
+ return;
324
+ error:
325
+ ebb_connection_schedule_close(connection);
326
+ }
327
+
328
+ /* Internal callback
329
+ * called by connection->write_watcher
330
+ */
331
+ static void
332
+ on_writable(struct ev_loop *loop, ev_io *watcher, int revents)
333
+ {
334
+ ebb_connection *connection = watcher->data;
335
+ ssize_t sent;
336
+
337
+ //printf("on_writable\n");
338
+
339
+ assert(CONNECTION_HAS_SOMETHING_TO_WRITE);
340
+ assert(connection->written <= connection->to_write_len);
341
+ // TODO -- why is this broken?
342
+ //assert(ev_is_active(&connection->timeout_watcher));
343
+ assert(watcher == &connection->write_watcher);
344
+
345
+ #ifdef HAVE_GNUTLS
346
+ assert(!ev_is_active(&connection->handshake_watcher));
347
+
348
+ if(connection->server->secure) {
349
+ sent = gnutls_record_send( connection->session
350
+ , connection->to_write + connection->written
351
+ , connection->to_write_len - connection->written
352
+ );
353
+ if(sent <= 0) {
354
+ if(gnutls_error_is_fatal(sent)) goto error;
355
+ if( (sent == GNUTLS_E_INTERRUPTED || sent == GNUTLS_E_AGAIN)
356
+ && GNUTLS_NEED_READ
357
+ ) ev_io_stop(loop, watcher);
358
+ return;
359
+ }
360
+ } else {
361
+ #endif /* HAVE_GNUTLS */
362
+
363
+ sent = nosigpipe_push( (void*)connection->fd
364
+ , connection->to_write + connection->written
365
+ , connection->to_write_len - connection->written
366
+ );
367
+ if(sent < 0) goto error;
368
+ if(sent == 0) return;
369
+
370
+ #ifdef HAVE_GNUTLS
371
+ }
372
+ #endif /* HAVE_GNUTLS */
373
+
374
+ ebb_connection_reset_timeout(connection);
375
+
376
+ connection->written += sent;
377
+
378
+ if(connection->written == connection->to_write_len) {
379
+ ev_io_stop(loop, watcher);
380
+ connection->to_write = NULL;
381
+
382
+ if(connection->after_write_cb)
383
+ connection->after_write_cb(connection);
384
+ }
385
+ return;
386
+ error:
387
+ error("close connection on write.");
388
+ ebb_connection_schedule_close(connection);
389
+ }
390
+
391
+ #ifdef HAVE_GNUTLS
392
+
393
+ static void
394
+ on_goodbye_tls(struct ev_loop *loop, ev_io *watcher, int revents)
395
+ {
396
+ ebb_connection *connection = watcher->data;
397
+ assert(watcher == &connection->goodbye_tls_watcher);
398
+
399
+ if(EV_ERROR & revents) {
400
+ error("on_goodbye() got error event, closing connection.");
401
+ goto die;
402
+ }
403
+
404
+ int r = gnutls_bye(connection->session, GNUTLS_SHUT_RDWR);
405
+ if(r < 0) {
406
+ if(gnutls_error_is_fatal(r)) goto die;
407
+ if(r == GNUTLS_E_INTERRUPTED || r == GNUTLS_E_AGAIN)
408
+ ev_io_set( watcher
409
+ , connection->fd
410
+ , EV_ERROR | (GNUTLS_NEED_WRITE ? EV_WRITE : EV_READ)
411
+ );
412
+ return;
413
+ }
414
+
415
+ die:
416
+ ev_io_stop(loop, watcher);
417
+ if(connection->session)
418
+ gnutls_deinit(connection->session);
419
+ close_connection(connection);
420
+ }
421
+ #endif /* HAVE_GNUTLS*/
422
+
423
+ static void
424
+ on_goodbye(struct ev_loop *loop, ev_timer *watcher, int revents)
425
+ {
426
+ ebb_connection *connection = watcher->data;
427
+ assert(watcher == &connection->goodbye_watcher);
428
+
429
+ close_connection(connection);
430
+ }
431
+
432
+
433
+ static ebb_request*
434
+ new_request_wrapper(void *data)
435
+ {
436
+ ebb_connection *connection = data;
437
+ if(connection->new_request)
438
+ return connection->new_request(connection);
439
+ return NULL;
440
+ }
441
+
442
+ /* Internal callback
443
+ * Called by server->connection_watcher.
444
+ */
445
+ static void
446
+ on_connection(struct ev_loop *loop, ev_io *watcher, int revents)
447
+ {
448
+ ebb_server *server = watcher->data;
449
+
450
+ //printf("on connection!\n");
451
+
452
+ assert(server->listening);
453
+ assert(server->loop == loop);
454
+ assert(&server->connection_watcher == watcher);
455
+
456
+ if(EV_ERROR & revents) {
457
+ error("on_connection() got error event, closing server.");
458
+ ebb_server_unlisten(server);
459
+ return;
460
+ }
461
+
462
+
463
+ struct sockaddr_in addr; // connector's address information
464
+ socklen_t addr_len = sizeof(addr);
465
+ int fd = accept( server->fd
466
+ , (struct sockaddr*) & addr
467
+ , & addr_len
468
+ );
469
+ if(fd < 0) {
470
+ perror("accept()");
471
+ return;
472
+ }
473
+
474
+ ebb_connection *connection = NULL;
475
+ if(server->new_connection)
476
+ connection = server->new_connection(server, &addr);
477
+ if(connection == NULL) {
478
+ close(fd);
479
+ return;
480
+ }
481
+
482
+ set_nonblock(fd);
483
+ connection->fd = fd;
484
+ connection->open = TRUE;
485
+ connection->server = server;
486
+ memcpy(&connection->sockaddr, &addr, addr_len);
487
+ if(server->port[0] != '\0')
488
+ connection->ip = inet_ntoa(connection->sockaddr.sin_addr);
489
+
490
+ #ifdef SO_NOSIGPIPE
491
+ int arg = 1;
492
+ setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &arg, sizeof(int));
493
+ #endif
494
+
495
+ #ifdef HAVE_GNUTLS
496
+ if(server->secure) {
497
+ gnutls_init(&connection->session, GNUTLS_SERVER);
498
+ gnutls_transport_set_lowat(connection->session, 0);
499
+ gnutls_set_default_priority(connection->session);
500
+ gnutls_credentials_set(connection->session, GNUTLS_CRD_CERTIFICATE, connection->server->credentials);
501
+
502
+ gnutls_transport_set_ptr(connection->session, (gnutls_transport_ptr) fd);
503
+ gnutls_transport_set_push_function(connection->session, nosigpipe_push);
504
+
505
+ gnutls_db_set_ptr (connection->session, &server->session_cache);
506
+ gnutls_db_set_store_function (connection->session, session_cache_store);
507
+ gnutls_db_set_retrieve_function (connection->session, session_cache_retrieve);
508
+ gnutls_db_set_remove_function (connection->session, session_cache_remove);
509
+ }
510
+
511
+ ev_io_set(&connection->handshake_watcher, connection->fd, EV_READ | EV_WRITE | EV_ERROR);
512
+ #endif /* HAVE_GNUTLS */
513
+
514
+ /* Note: not starting the write watcher until there is data to be written */
515
+ ev_io_set(&connection->write_watcher, connection->fd, EV_WRITE);
516
+ ev_io_set(&connection->read_watcher, connection->fd, EV_READ | EV_ERROR);
517
+ /* XXX: seperate error watcher? */
518
+
519
+ ev_timer_start(loop, &connection->timeout_watcher);
520
+
521
+ #ifdef HAVE_GNUTLS
522
+ if(server->secure) {
523
+ ev_io_start(loop, &connection->handshake_watcher);
524
+ return;
525
+ }
526
+ #endif
527
+
528
+ ev_io_start(loop, &connection->read_watcher);
529
+ }
530
+
531
+ /**
532
+ * Begin the server listening on a file descriptor. This DOES NOT start the
533
+ * event loop. Start the event loop after making this call.
534
+ */
535
+ int
536
+ ebb_server_listen_on_fd(ebb_server *server, const int fd)
537
+ {
538
+ assert(server->listening == FALSE);
539
+
540
+ if (listen(fd, EBB_MAX_CONNECTIONS) < 0) {
541
+ perror("listen()");
542
+ return -1;
543
+ }
544
+
545
+ set_nonblock(fd); /* XXX superfluous? */
546
+
547
+ server->fd = fd;
548
+ server->listening = TRUE;
549
+
550
+ ev_io_set (&server->connection_watcher, server->fd, EV_READ | EV_ERROR);
551
+ ev_io_start (server->loop, &server->connection_watcher);
552
+
553
+ return server->fd;
554
+ }
555
+
556
+
557
+ /**
558
+ * Begin the server listening on a file descriptor This DOES NOT start the
559
+ * event loop. Start the event loop after making this call.
560
+ */
561
+ int
562
+ ebb_server_listen_on_port(ebb_server *server, const int port)
563
+ {
564
+ int fd = -1;
565
+ struct linger ling = {0, 0};
566
+ struct sockaddr_in addr;
567
+ int flags = 1;
568
+
569
+ if ((fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
570
+ perror("socket()");
571
+ goto error;
572
+ }
573
+
574
+ flags = 1;
575
+ setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *)&flags, sizeof(flags));
576
+ setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&flags, sizeof(flags));
577
+ setsockopt(fd, SOL_SOCKET, SO_LINGER, (void *)&ling, sizeof(ling));
578
+
579
+ /* XXX: Sending single byte chunks in a response body? Perhaps there is a
580
+ * need to enable the Nagel algorithm dynamically. For now disabling.
581
+ */
582
+ setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (void *)&flags, sizeof(flags));
583
+
584
+ /* the memset call clears nonstandard fields in some impementations that
585
+ * otherwise mess things up.
586
+ */
587
+ memset(&addr, 0, sizeof(addr));
588
+
589
+ addr.sin_family = AF_INET;
590
+ addr.sin_port = htons(port);
591
+ addr.sin_addr.s_addr = htonl(INADDR_ANY);
592
+
593
+ if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
594
+ perror("bind()");
595
+ goto error;
596
+ }
597
+
598
+ int ret = ebb_server_listen_on_fd(server, fd);
599
+ if (ret >= 0) {
600
+ sprintf(server->port, "%d", port);
601
+ }
602
+ return ret;
603
+ error:
604
+ if(fd > 0) close(fd);
605
+ return -1;
606
+ }
607
+
608
+ /**
609
+ * Stops the server. Will not accept new connections. Does not drop
610
+ * existing connections.
611
+ */
612
+ void
613
+ ebb_server_unlisten(ebb_server *server)
614
+ {
615
+ if(server->listening) {
616
+ ev_io_stop(server->loop, &server->connection_watcher);
617
+ close(server->fd);
618
+ server->port[0] = '\0';
619
+ server->listening = FALSE;
620
+ }
621
+ }
622
+
623
+ /**
624
+ * Initialize an ebb_server structure. After calling ebb_server_init set
625
+ * the callback server->new_connection and, optionally, callback data
626
+ * server->data. The new connection MUST be initialized with
627
+ * ebb_connection_init before returning it to the server.
628
+ *
629
+ * @param server the server to initialize
630
+ * @param loop a libev loop
631
+ */
632
+ void
633
+ ebb_server_init(ebb_server *server, struct ev_loop *loop)
634
+ {
635
+ server->loop = loop;
636
+ server->listening = FALSE;
637
+ server->port[0] = '\0';
638
+ server->fd = -1;
639
+ server->connection_watcher.data = server;
640
+ ev_init (&server->connection_watcher, on_connection);
641
+ server->secure = FALSE;
642
+
643
+ #ifdef HAVE_GNUTLS
644
+ rbtree_init(&server->session_cache, session_cache_compare);
645
+ server->credentials = NULL;
646
+ #endif
647
+
648
+ server->new_connection = NULL;
649
+ server->data = NULL;
650
+ }
651
+
652
+
653
+ #ifdef HAVE_GNUTLS
654
+ /* similar to server_init.
655
+ *
656
+ * the user of secure server might want to set additional callbacks from
657
+ * GNUTLS. In particular
658
+ * gnutls_global_set_mem_functions()
659
+ * gnutls_global_set_log_function()
660
+ * Also see the note above ebb_connection_init() about setting gnutls cache
661
+ * access functions
662
+ *
663
+ * cert_file: the filename of a PEM certificate file
664
+ *
665
+ * key_file: the filename of a private key. Currently only PKCS-1 encoded
666
+ * RSA and DSA private keys are accepted.
667
+ */
668
+ int
669
+ ebb_server_set_secure (ebb_server *server, const char *cert_file, const char *key_file)
670
+ {
671
+ server->secure = TRUE;
672
+ gnutls_global_init();
673
+ gnutls_certificate_allocate_credentials(&server->credentials);
674
+ /* todo gnutls_certificate_free_credentials */
675
+ int r = gnutls_certificate_set_x509_key_file( server->credentials
676
+ , cert_file
677
+ , key_file
678
+ , GNUTLS_X509_FMT_PEM
679
+ );
680
+ if(r < 0) {
681
+ error("loading certificates");
682
+ return -1;
683
+ }
684
+ return 1;
685
+ }
686
+ #endif /* HAVE_GNUTLS */
687
+
688
+ /**
689
+ * Initialize an ebb_connection structure. After calling this function you
690
+ * must setup callbacks for the different actions the server can take. See
691
+ * server.h for which callbacks are availible.
692
+ *
693
+ * This should be called immediately after allocating space for a new
694
+ * ebb_connection structure. Most likely, this will only be called within
695
+ * the ebb_server->new_connection callback which you supply.
696
+ *
697
+ * If using SSL do consider setting
698
+ * gnutls_db_set_retrieve_function (connection->session, _);
699
+ * gnutls_db_set_remove_function (connection->session, _);
700
+ * gnutls_db_set_store_function (connection->session, _);
701
+ * gnutls_db_set_ptr (connection->session, _);
702
+ * To provide a better means of storing SSL session caches. libebb provides
703
+ * only a simple default implementation.
704
+ *
705
+ * @param connection the connection to initialize
706
+ * @param timeout the timeout in seconds
707
+ */
708
+ void
709
+ ebb_connection_init(ebb_connection *connection)
710
+ {
711
+ connection->fd = -1;
712
+ connection->server = NULL;
713
+ connection->ip = NULL;
714
+ connection->open = FALSE;
715
+
716
+ ebb_request_parser_init( &connection->parser );
717
+ connection->parser.data = connection;
718
+ connection->parser.new_request = new_request_wrapper;
719
+
720
+ ev_init (&connection->write_watcher, on_writable);
721
+ connection->write_watcher.data = connection;
722
+ connection->to_write = NULL;
723
+
724
+ ev_init(&connection->read_watcher, on_readable);
725
+ connection->read_watcher.data = connection;
726
+
727
+ #ifdef HAVE_GNUTLS
728
+ connection->handshake_watcher.data = connection;
729
+ ev_init(&connection->handshake_watcher, on_handshake);
730
+
731
+ ev_init(&connection->goodbye_tls_watcher, on_goodbye_tls);
732
+ connection->goodbye_tls_watcher.data = connection;
733
+
734
+ connection->session = NULL;
735
+ #endif /* HAVE_GNUTLS */
736
+
737
+ ev_timer_init(&connection->goodbye_watcher, on_goodbye, 0., 0.);
738
+ connection->goodbye_watcher.data = connection;
739
+
740
+ ev_timer_init(&connection->timeout_watcher, on_timeout, EBB_DEFAULT_TIMEOUT, 0.);
741
+ connection->timeout_watcher.data = connection;
742
+
743
+ connection->new_buf = NULL;
744
+ connection->new_request = NULL;
745
+ connection->on_timeout = NULL;
746
+ connection->on_close = NULL;
747
+ connection->data = NULL;
748
+ }
749
+
750
+ void
751
+ ebb_connection_schedule_close (ebb_connection *connection)
752
+ {
753
+ #ifdef HAVE_GNUTLS
754
+ if(connection->server->secure) {
755
+ ev_io_set(&connection->goodbye_tls_watcher, connection->fd, EV_ERROR | EV_READ | EV_WRITE);
756
+ ev_io_start(connection->server->loop, &connection->goodbye_tls_watcher);
757
+ return;
758
+ }
759
+ #endif
760
+ ev_timer_start(connection->server->loop, &connection->goodbye_watcher);
761
+ }
762
+
763
+ /*
764
+ * Resets the timeout to stay alive for another connection->timeout seconds
765
+ */
766
+ void
767
+ ebb_connection_reset_timeout(ebb_connection *connection)
768
+ {
769
+ ev_timer_again(connection->server->loop, &connection->timeout_watcher);
770
+ }
771
+
772
+ /**
773
+ * Writes a string to the socket. This is actually sets a watcher
774
+ * which may take multiple iterations to write the entire string.
775
+ *
776
+ * The buf->on_release() callback will be made when the operation is complete.
777
+ *
778
+ * This can only be called once at a time. If you call it again
779
+ * while the connection is writing another buffer the ebb_connection_write
780
+ * will return FALSE and ignore the request.
781
+ */
782
+ int
783
+ ebb_connection_write (ebb_connection *connection, const char *buf, size_t len, ebb_after_write_cb cb)
784
+ {
785
+ if(ev_is_active(&connection->write_watcher))
786
+ return FALSE;
787
+ assert(!CONNECTION_HAS_SOMETHING_TO_WRITE);
788
+ connection->to_write = buf;
789
+ connection->to_write_len = len;
790
+ connection->written = 0;
791
+ connection->after_write_cb = cb;
792
+ ev_io_start(connection->server->loop, &connection->write_watcher);
793
+ return TRUE;
794
+ }