ebb 0.2.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+ }