Revision 40c0f755
configure | ||
---|---|---|
4 | 4 |
|
5 | 5 |
# Fancy colors used to beautify the output a bit. |
6 | 6 |
# |
7 |
if [ "$NOCOLOR" ] ; then |
|
8 |
NORMAL="" |
|
9 |
BOLD="" |
|
10 |
RED="" |
|
11 |
YELLOW="" |
|
12 |
GREEN="" |
|
13 |
else |
|
14 |
NORMAL='\\033[0m' |
|
15 |
BOLD='\\033[01;1m' |
|
16 |
RED='\\033[01;91m' |
|
17 |
YELLOW='\\033[00;33m' |
|
18 |
GREEN='\\033[01;92m' |
|
19 |
fi |
|
7 |
NORMAL="" |
|
8 |
BOLD="" |
|
9 |
RED="" |
|
10 |
YELLOW="" |
|
11 |
GREEN="" |
|
20 | 12 |
|
21 | 13 |
EXIT_SUCCESS=0 |
22 | 14 |
EXIT_FAILURE=1 |
deps/libebb/.gitignore | ||
---|---|---|
1 |
*.o |
|
2 |
examples/hello_world |
|
3 |
test_request_parser |
|
4 |
ebb_request_parser.c |
|
5 |
tags |
deps/libebb/LICENSE | ||
---|---|---|
1 |
Copyright (c) 2008 Ryan Dahl (ry@ndahl.us) |
|
2 |
|
|
3 |
Permission is hereby granted, free of charge, to any person obtaining |
|
4 |
a copy of this software and associated documentation files (the |
|
5 |
"Software"), to deal in the Software without restriction, including |
|
6 |
without limitation the rights to use, copy, modify, merge, publish, |
|
7 |
distribute, sublicense, and/or sell copies of the Software, and to |
|
8 |
permit persons to whom the Software is furnished to do so, subject to |
|
9 |
the following conditions: |
|
10 |
|
|
11 |
The above copyright notice and this permission notice shall be |
|
12 |
included in all copies or substantial portions of the Software. |
|
13 |
|
|
14 |
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
|
15 |
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
|
16 |
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
|
17 |
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE |
|
18 |
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION |
|
19 |
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION |
|
20 |
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
|
21 |
|
deps/libebb/README | ||
---|---|---|
1 |
see doc/index.html and examples/hello_world.c for explanation |
|
2 |
|
|
3 |
webpage: http://tinyclouds.org/libebb/ |
|
4 |
git repository: http://github.com/ry/libebb/tree/master |
|
5 |
|
|
6 |
To build libebb please edit config.mk to reflect your system's parameters. |
|
7 |
|
deps/libebb/config.mk | ||
---|---|---|
1 |
PREFIX = $(HOME)/local/libebb |
|
2 |
|
|
3 |
# libev |
|
4 |
EVINC = $(HOME)/local/libev/include |
|
5 |
EVLIB = $(HOME)/local/libev/lib |
|
6 |
EVLIBS = -L${EVLIB} -lev |
|
7 |
|
|
8 |
# GnuTLS, comment if you don't want it (necessary for HTTPS) |
|
9 |
GNUTLSLIB = /usr/lib |
|
10 |
GNUTLSINC = /usr/include |
|
11 |
GNUTLSLIBS = -L${GNUTLSLIB} -lgnutls |
|
12 |
GNUTLSFLAGS = -DHAVE_GNUTLS |
|
13 |
|
|
14 |
# includes and libs |
|
15 |
INCS = -I${EVINC} -I${GNUTLSINC} |
|
16 |
LIBS = ${EVLIBS} ${GNUTLSLIBS} -lefence |
|
17 |
|
|
18 |
# flags |
|
19 |
CPPFLAGS = -DVERSION=\"$(VERSION)\" ${GNUTLSFLAGS} |
|
20 |
CFLAGS = -O2 -g -Wall ${INCS} ${CPPFLAGS} -fPIC |
|
21 |
LDFLAGS = -s ${LIBS} |
|
22 |
LDOPT = -shared |
|
23 |
SUFFIX = so |
|
24 |
SONAME = -Wl,-soname,$(OUTPUT_LIB) |
|
25 |
|
|
26 |
# Solaris |
|
27 |
#CFLAGS = -fast ${INCS} -DVERSION=\"$(VERSION)\" -fPIC |
|
28 |
#LDFLAGS = ${LIBS} |
|
29 |
#SONAME = |
|
30 |
|
|
31 |
# Darwin |
|
32 |
# LDOPT = -dynamiclib |
|
33 |
# SUFFIX = dylib |
|
34 |
# SONAME = -current_version $(VERSION) -compatibility_version $(VERSION) |
|
35 |
|
|
36 |
# compiler and linker |
|
37 |
CC = cc |
|
38 |
RANLIB = ranlib |
deps/libebb/doc/index.html | ||
---|---|---|
1 |
|
|
2 |
<html> |
|
3 |
<style> |
|
4 |
body { |
|
5 |
background: #fff; |
|
6 |
color: #2e3436; |
|
7 |
font-size: 12pt; |
|
8 |
line-height: 16pt; |
|
9 |
/* font-family: Palatino; */ |
|
10 |
margin: 3em 0 3em 3em; |
|
11 |
} |
|
12 |
|
|
13 |
code, pre { |
|
14 |
} |
|
15 |
|
|
16 |
#contents { |
|
17 |
max-width: 40em; |
|
18 |
} |
|
19 |
|
|
20 |
ul { |
|
21 |
padding-left: 0; |
|
22 |
} |
|
23 |
|
|
24 |
li { |
|
25 |
margin-top: 0.5em; |
|
26 |
margin-bottom: 0.5em; |
|
27 |
} |
|
28 |
|
|
29 |
p { |
|
30 |
text-align: left; |
|
31 |
} |
|
32 |
|
|
33 |
|
|
34 |
img { |
|
35 |
float: left; |
|
36 |
margin: 0 1em 1em 0; |
|
37 |
} |
|
38 |
|
|
39 |
p { clear: both; } |
|
40 |
</style> |
|
41 |
<body> <div id="contents"> |
|
42 |
<img src="icon.png"/> |
|
43 |
<h1>libebb</h1> |
|
44 |
|
|
45 |
<p> |
|
46 |
libebb is a lightweight HTTP server library for C. It lays the |
|
47 |
foundation for writing a web server by providing the socket juggling |
|
48 |
and request parsing. By implementing the HTTP/1.1 grammar provided in |
|
49 |
RFC2612, libebb understands most most valid HTTP/1.1 connections |
|
50 |
(persistent, pipelined, and chunked requests included) and rejects |
|
51 |
invalid or malicious requests. libebb supports SSL over HTTP. |
|
52 |
</p> |
|
53 |
|
|
54 |
<p> |
|
55 |
The library embraces a minimalistic single-threaded evented design. |
|
56 |
No control is removed from the user. For example, all allocations are |
|
57 |
done through callbacks so that the user might implement in optimal |
|
58 |
ways for their specific application. By design libebb is not |
|
59 |
thread-safe and all provided callbacks must not block. libebb uses |
|
60 |
the <a href="http://libev.schmorp.de/bench.html">high-performance</a> |
|
61 |
libev event loop, but does not control it. The user of the library may |
|
62 |
start and stop the loop at will, they may attach thier own watchers. |
|
63 |
</p> |
|
64 |
|
|
65 |
<p> |
|
66 |
libebb depends on POSIX sockets, libev, and optionally GnuTLS. |
|
67 |
</p> |
|
68 |
|
|
69 |
<p> |
|
70 |
libebb is in the early stages of development and probably contains |
|
71 |
many bugs. The API is subject to radical changes. If you're |
|
72 |
interested <a href="http://github.com/ry/libebb/tree/master">checkout |
|
73 |
the source code</a> and <a |
|
74 |
href="http://groups.google.com/group/ebbebb">join the mailing |
|
75 |
list</a>. A release will be made when it proves stable. |
|
76 |
</p> |
|
77 |
|
|
78 |
<p>libebb is released under <a |
|
79 |
href="http://www.gnu.org/licenses/license-list.html#X11License">the |
|
80 |
X11 license</a>.</p> |
|
81 |
|
|
82 |
<h2>Usage</h2> |
|
83 |
|
|
84 |
<p> |
|
85 |
libebb is a simple API, mostly it is providing callbacks. There are |
|
86 |
two types of callbacks that one will work with: |
|
87 |
</p> |
|
88 |
|
|
89 |
<ul> |
|
90 |
<li>callbacks to allocate and initialize data for libebb. These are |
|
91 |
named <code>new_*</code> like <code>new_connection</code> and |
|
92 |
<code>new_request</code></li> |
|
93 |
<li>callbacks which happen on an event and might provide a pointer to |
|
94 |
a chunk of data. These are named <code>on_*</code> like |
|
95 |
<code>on_body</code> and <code>on_close</code>.</li> |
|
96 |
</ul> |
|
97 |
|
|
98 |
<p> |
|
99 |
In libebb there are three important classes: <code>ebb_server</code>, |
|
100 |
<code>ebb_connection</code>, and <code>ebb_request</code>. |
|
101 |
Each server has many peer connections. Each peer connection may have many |
|
102 |
requests. |
|
103 |
There are two additional classes <code>ebb_buf</code> and <code>ebb_request_parser</code> |
|
104 |
which may or may not be useful. |
|
105 |
</p> |
|
106 |
|
|
107 |
<h3><code>ebb_server</code></h3> |
|
108 |
<p> |
|
109 |
<code>ebb_server</code> represents a single web server listening on a |
|
110 |
single port. The user must allocate the structure themselves, then |
|
111 |
call <code>ebb_server_init()</code> and provide a libev event loop. |
|
112 |
<code>ebb_server_set_secure()</code> will make the server understand |
|
113 |
HTTPS connections. |
|
114 |
</p> |
|
115 |
|
|
116 |
<p> |
|
117 |
After initialized the <code>ebb_server_listen_on_port()</code> can be |
|
118 |
called to open the server up to new connections. libebb does not |
|
119 |
control the event loop, it is the user's responsibility to start the |
|
120 |
event loop (using <code>ev_loop()</code>) after |
|
121 |
<code>ebb_server_listen_on_port()</code> is called. |
|
122 |
</p> |
|
123 |
|
|
124 |
<p> |
|
125 |
To accept connections you must provide the new server with a callback |
|
126 |
called <code>new_connection</code>. This callback must return an allocated |
|
127 |
and initialized <code>ebb_connection</code> structure. |
|
128 |
To set this callback do |
|
129 |
</p> |
|
130 |
|
|
131 |
<pre>my_server->new_connection = my_new_connection_callback;</pre> |
|
132 |
|
|
133 |
<p> |
|
134 |
Additional documentation can be found in <code>ebb.h</code> |
|
135 |
</p> |
|
136 |
|
|
137 |
<h3><code>ebb_connection</code></h3> |
|
138 |
|
|
139 |
<p> |
|
140 |
This structure contains information and callbacks for a single client |
|
141 |
connection. It is allocated and initialized through the |
|
142 |
<code>new_connection</code> callback in <code>ebb_server</code>. |
|
143 |
To initialize a newly allocated <code>ebb_connection</code> use |
|
144 |
<code>ebb_connection_init()</code>. |
|
145 |
</p> |
|
146 |
|
|
147 |
<p> |
|
148 |
After <code>ebb_connection_init()</code> is called a number of |
|
149 |
callbacks can be set: <code>new_request</code>, <code>new_buf</code>, |
|
150 |
<code>on_timeout</code>, and <code>on_close</code>. |
|
151 |
</p> |
|
152 |
|
|
153 |
<p> |
|
154 |
When an <code>ebb_connection</code> is returned to an |
|
155 |
<code>ebb_server</code>, data is immediately data is read from the |
|
156 |
socket. This data must be stored somewhere. Because libebb is |
|
157 |
agnostic about allocation decisions, it passes this off to the user in |
|
158 |
the form of a callback: <code>connection->new_buf</code>. This |
|
159 |
callback returns a newly allocated and initialized |
|
160 |
<code>ebb_buf</code> structure. How much libebb attempts to read from |
|
161 |
the socket is determined by how large the returned |
|
162 |
<code>ebb_buf</code> structure is. Using <code>new_buf</code> is |
|
163 |
optional. By default libebb reads data into a static buffer |
|
164 |
(allocated at compile time), writing over it on each read. In many |
|
165 |
web server using the static buffer will be sufficent because callbacks |
|
166 |
made during the parsing will buffer the data elsewhere. Providing a |
|
167 |
<code>new_buf</code> callback is necessary only if you want to save |
|
168 |
the raw data coming from the socket. |
|
169 |
</p> |
|
170 |
|
|
171 |
<p> |
|
172 |
The <code>new_request</code> callback is called at the beginning of a |
|
173 |
request. It must return a newly allocated and initialized |
|
174 |
<code>ebb_request</code> structure. Because HTTP/1.1 supports <a |
|
175 |
href="http://en.wikipedia.org/wiki/HTTP_persistent_connection">peristant</a> |
|
176 |
connections, there may be many requests per connection. |
|
177 |
</p> |
|
178 |
|
|
179 |
<p> |
|
180 |
You may access the file descriptor for the client socket inside the |
|
181 |
<code>ebb_connection</code> structure. Writing the response, in valid |
|
182 |
HTTP, is the user's responsibility. Remember, requests must be |
|
183 |
returned to client in the same order that they were received. |
|
184 |
</p> |
|
185 |
|
|
186 |
<p> |
|
187 |
A convience function, <coe>ebb_connection_write</code>, is provided |
|
188 |
which will write a single string to the peer. You may use |
|
189 |
this function or you may write to the file descriptor directly. |
|
190 |
</p> |
|
191 |
|
|
192 |
<p> |
|
193 |
To close a peer connection use |
|
194 |
<code>ebb_connnection_schedule_close()</code>. Because SSL may require |
|
195 |
some additional communication to close the connection properly, the |
|
196 |
file descriptor cannot be closed immediately. The |
|
197 |
<code>on_close</code> callback will be made when the peer socket is |
|
198 |
finally closed. |
|
199 |
<em>Only once <code>on_close</code> is called may the |
|
200 |
user free the <code>ebb_connection</code> structure.</em> |
|
201 |
</p> |
|
202 |
|
|
203 |
|
|
204 |
<h3><code>ebb_request</code></h3> |
|
205 |
|
|
206 |
<p> |
|
207 |
This structure provides information about a request. For example, |
|
208 |
<code>request->method == EBB_POST</code> would mean the method of |
|
209 |
the request is <code>POST</code>. There are also many callbacks |
|
210 |
which can be set to handle data from a request as it is parsed. |
|
211 |
</p> |
|
212 |
|
|
213 |
<p> |
|
214 |
The <code>on_uri</code> callback and all other |
|
215 |
<code>ebb_element_cb</code> callbacks provide pointers to request |
|
216 |
data. The annoying thing is they do not necessarily provide a |
|
217 |
complete string. This is because the reads from the socket may not |
|
218 |
contain an entire request and for efficency libebb does not attempt to |
|
219 |
buffer the data. Theoretically, one might receive an |
|
220 |
<code>on_uri</code> callback 10 times, each providing just a single |
|
221 |
character of the request's URI. See <code>ebb_request_parser.h</code> for |
|
222 |
a full list of callbacks that you may provide. (If you don't set them, |
|
223 |
they are ignored.) |
|
224 |
</p> |
|
225 |
|
|
226 |
<p> |
|
227 |
The <code>on_complete</code> callback is called at the end of |
|
228 |
each request. |
|
229 |
<em>Only once <code>on_complete</code> is called may the |
|
230 |
user free the <code>ebb_request</code> structure.</em> |
|
231 |
</p> |
|
232 |
|
|
233 |
<h2>Example</h2> |
|
234 |
|
|
235 |
<p> |
|
236 |
A simple example is provided in <code>examples/hello_world.c</code>. |
|
237 |
</p> |
|
238 |
|
|
239 |
</div></body> |
|
240 |
</html> |
deps/libebb/ebb.c | ||
---|---|---|
1 |
/* This file is part of libebb. |
|
2 |
* |
|
3 |
* Copyright (c) 2008 Ryan Dahl (ry@ndahl.us) |
|
4 |
* All rights reserved. |
|
5 |
* |
|
6 |
* Permission is hereby granted, free of charge, to any person obtaining |
|
7 |
* a copy of this software and associated documentation files (the |
|
8 |
* "Software"), to deal in the Software without restriction, including |
|
9 |
* without limitation the rights to use, copy, modify, merge, publish, |
|
10 |
* distribute, sublicense, and/or sell copies of the Software, and to |
|
11 |
* permit persons to whom the Software is furnished to do so, subject to |
|
12 |
* the following conditions: |
|
13 |
* |
|
14 |
* The above copyright notice and this permission notice shall be |
|
15 |
* included in all copies or substantial portions of the Software. |
|
16 |
* |
|
17 |
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
|
18 |
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
|
19 |
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
|
20 |
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE |
|
21 |
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION |
|
22 |
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION |
|
23 |
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
|
24 |
*/ |
|
25 |
#include <assert.h> |
|
26 |
#include <string.h> |
|
27 |
#include <fcntl.h> |
|
28 |
#include <sys/types.h> |
|
29 |
#include <sys/socket.h> |
|
30 |
#include <netinet/tcp.h> /* TCP_NODELAY */ |
|
31 |
#include <netinet/in.h> /* inet_ntoa */ |
|
32 |
#include <arpa/inet.h> /* inet_ntoa */ |
|
33 |
#include <unistd.h> |
|
34 |
#include <stdio.h> /* perror */ |
|
35 |
#include <errno.h> /* perror */ |
|
36 |
#include <stdlib.h> /* for the default methods */ |
|
37 |
#include <ev.h> |
|
38 |
|
|
39 |
#include "ebb.h" |
|
40 |
#include "ebb_request_parser.h" |
|
41 |
#ifdef HAVE_GNUTLS |
|
42 |
# include <gnutls/gnutls.h> |
|
43 |
# include "rbtree.h" /* for session_cache */ |
|
44 |
#endif |
|
45 |
|
|
46 |
#ifndef TRUE |
|
47 |
# define TRUE 1 |
|
48 |
#endif |
|
49 |
#ifndef FALSE |
|
50 |
# define FALSE 0 |
|
51 |
#endif |
|
52 |
#ifndef MIN |
|
53 |
# define MIN(a,b) (a < b ? a : b) |
|
54 |
#endif |
|
55 |
|
|
56 |
#define error(FORMAT, ...) fprintf(stderr, "error: " FORMAT "\n", ##__VA_ARGS__) |
|
57 |
|
|
58 |
#define CONNECTION_HAS_SOMETHING_TO_WRITE (connection->to_write != NULL) |
|
59 |
|
|
60 |
static void |
|
61 |
set_nonblock (int fd) |
|
62 |
{ |
|
63 |
int flags = fcntl(fd, F_GETFL, 0); |
|
64 |
int r = fcntl(fd, F_SETFL, flags | O_NONBLOCK); |
|
65 |
assert(0 <= r && "Setting socket non-block failed!"); |
|
66 |
} |
|
67 |
|
|
68 |
static ssize_t |
|
69 |
nosigpipe_push(void *data, const void *buf, size_t len) |
|
70 |
{ |
|
71 |
int fd = (int)data; |
|
72 |
int flags = 0; |
|
73 |
#ifdef MSG_NOSIGNAL |
|
74 |
flags = MSG_NOSIGNAL; |
|
75 |
#endif |
|
76 |
return send(fd, buf, len, flags); |
|
77 |
} |
|
78 |
|
|
79 |
static void |
|
80 |
close_connection(ebb_connection *connection) |
|
81 |
{ |
|
82 |
#ifdef HAVE_GNUTLS |
|
83 |
if(connection->server->secure) |
|
84 |
ev_io_stop(connection->server->loop, &connection->handshake_watcher); |
|
85 |
#endif |
|
86 |
ev_io_stop(connection->server->loop, &connection->read_watcher); |
|
87 |
ev_io_stop(connection->server->loop, &connection->write_watcher); |
|
88 |
ev_timer_stop(connection->server->loop, &connection->timeout_watcher); |
|
89 |
|
|
90 |
if(0 > close(connection->fd)) |
|
91 |
error("problem closing connection fd"); |
|
92 |
|
|
93 |
connection->open = FALSE; |
|
94 |
|
|
95 |
if(connection->on_close) |
|
96 |
connection->on_close(connection); |
|
97 |
/* No access to the connection past this point! |
|
98 |
* The user is allowed to free in the callback |
|
99 |
*/ |
|
100 |
} |
|
101 |
|
|
102 |
#ifdef HAVE_GNUTLS |
|
103 |
#define GNUTLS_NEED_WRITE (gnutls_record_get_direction(connection->session) == 1) |
|
104 |
#define GNUTLS_NEED_READ (gnutls_record_get_direction(connection->session) == 0) |
|
105 |
|
|
106 |
#define EBB_MAX_SESSION_KEY 32 |
|
107 |
#define EBB_MAX_SESSION_VALUE 512 |
|
108 |
|
|
109 |
struct session_cache { |
|
110 |
struct rbtree_node_t node; |
|
111 |
|
|
112 |
gnutls_datum_t key; |
|
113 |
gnutls_datum_t value; |
|
114 |
|
|
115 |
char key_storage[EBB_MAX_SESSION_KEY]; |
|
116 |
char value_storage[EBB_MAX_SESSION_VALUE]; |
|
117 |
}; |
|
118 |
|
|
119 |
static int |
|
120 |
session_cache_compare (void *left, void *right) |
|
121 |
{ |
|
122 |
gnutls_datum_t *left_key = left; |
|
123 |
gnutls_datum_t *right_key = right; |
|
124 |
if(left_key->size < right_key->size) |
|
125 |
return -1; |
|
126 |
else if(left_key->size > right_key->size) |
|
127 |
return 1; |
|
128 |
else |
|
129 |
return memcmp( left_key->data |
|
130 |
, right_key->data |
|
131 |
, MIN(left_key->size, right_key->size) |
|
132 |
); |
|
133 |
} |
|
134 |
|
|
135 |
static int |
|
136 |
session_cache_store(void *data, gnutls_datum_t key, gnutls_datum_t value) |
|
137 |
{ |
|
138 |
rbtree tree = data; |
|
139 |
|
|
140 |
if( tree == NULL |
|
141 |
|| key.size > EBB_MAX_SESSION_KEY |
|
142 |
|| value.size > EBB_MAX_SESSION_VALUE |
|
143 |
) return -1; |
|
144 |
|
|
145 |
struct session_cache *cache = gnutls_malloc(sizeof(struct session_cache)); |
|
146 |
|
|
147 |
memcpy (cache->key_storage, key.data, key.size); |
|
148 |
cache->key.size = key.size; |
|
149 |
cache->key.data = (void*)cache->key_storage; |
|
150 |
|
|
151 |
memcpy (cache->value_storage, value.data, value.size); |
|
152 |
cache->value.size = value.size; |
|
153 |
cache->value.data = (void*)cache->value_storage; |
|
154 |
|
|
155 |
cache->node.key = &cache->key; |
|
156 |
cache->node.value = &cache; |
|
157 |
|
|
158 |
rbtree_insert(tree, (rbtree_node)cache); |
|
159 |
|
|
160 |
//printf("session_cache_store\n"); |
|
161 |
|
|
162 |
return 0; |
|
163 |
} |
|
164 |
|
|
165 |
static gnutls_datum_t |
|
166 |
session_cache_retrieve (void *data, gnutls_datum_t key) |
|
167 |
{ |
|
168 |
rbtree tree = data; |
|
169 |
gnutls_datum_t res = { NULL, 0 }; |
|
170 |
struct session_cache *cache = rbtree_lookup(tree, &key); |
|
171 |
|
|
172 |
if(cache == NULL) |
|
173 |
return res; |
|
174 |
|
|
175 |
res.size = cache->value.size; |
|
176 |
res.data = gnutls_malloc (res.size); |
|
177 |
if(res.data == NULL) |
|
178 |
return res; |
|
179 |
|
|
180 |
memcpy(res.data, cache->value.data, res.size); |
|
181 |
|
|
182 |
//printf("session_cache_retrieve\n"); |
|
183 |
|
|
184 |
return res; |
|
185 |
} |
|
186 |
|
|
187 |
static int |
|
188 |
session_cache_remove (void *data, gnutls_datum_t key) |
|
189 |
{ |
|
190 |
rbtree tree = data; |
|
191 |
|
|
192 |
if(tree == NULL) |
|
193 |
return -1; |
|
194 |
|
|
195 |
struct session_cache *cache = (struct session_cache *)rbtree_delete(tree, &key); |
|
196 |
if(cache == NULL) |
|
197 |
return -1; |
|
198 |
|
|
199 |
gnutls_free(cache); |
|
200 |
|
|
201 |
//printf("session_cache_remove\n"); |
|
202 |
|
|
203 |
return 0; |
|
204 |
} |
|
205 |
|
|
206 |
static void |
|
207 |
on_handshake(struct ev_loop *loop ,ev_io *watcher, int revents) |
|
208 |
{ |
|
209 |
ebb_connection *connection = watcher->data; |
|
210 |
|
|
211 |
//printf("on_handshake\n"); |
|
212 |
|
|
213 |
assert(ev_is_active(&connection->timeout_watcher)); |
|
214 |
assert(!ev_is_active(&connection->read_watcher)); |
|
215 |
assert(!ev_is_active(&connection->write_watcher)); |
|
216 |
|
|
217 |
if(EV_ERROR & revents) { |
|
218 |
error("on_handshake() got error event, closing connection.n"); |
|
219 |
goto error; |
|
220 |
} |
|
221 |
|
|
222 |
int r = gnutls_handshake(connection->session); |
|
223 |
if(r < 0) { |
|
224 |
if(gnutls_error_is_fatal(r)) goto error; |
|
225 |
if(r == GNUTLS_E_INTERRUPTED || r == GNUTLS_E_AGAIN) |
|
226 |
ev_io_set( watcher |
|
227 |
, connection->fd |
|
228 |
, (GNUTLS_NEED_WRITE ? EV_WRITE : EV_READ) |
|
229 |
); |
|
230 |
return; |
|
231 |
} |
|
232 |
|
|
233 |
ebb_connection_reset_timeout(connection); |
|
234 |
ev_io_stop(loop, watcher); |
|
235 |
|
|
236 |
ev_io_start(loop, &connection->read_watcher); |
|
237 |
if(CONNECTION_HAS_SOMETHING_TO_WRITE) |
|
238 |
ev_io_start(loop, &connection->write_watcher); |
|
239 |
|
|
240 |
return; |
|
241 |
error: |
|
242 |
close_connection(connection); |
|
243 |
} |
|
244 |
|
|
245 |
#endif /* HAVE_GNUTLS */ |
|
246 |
|
|
247 |
|
|
248 |
/* Internal callback |
|
249 |
* called by connection->timeout_watcher |
|
250 |
*/ |
|
251 |
static void |
|
252 |
on_timeout(struct ev_loop *loop, ev_timer *watcher, int revents) |
|
253 |
{ |
|
254 |
ebb_connection *connection = watcher->data; |
|
255 |
|
|
256 |
assert(watcher == &connection->timeout_watcher); |
|
257 |
|
|
258 |
//printf("on_timeout\n"); |
|
259 |
|
|
260 |
/* if on_timeout returns true, we don't time out */ |
|
261 |
if(connection->on_timeout) { |
|
262 |
int r = connection->on_timeout(connection); |
|
263 |
|
|
264 |
if(r == EBB_AGAIN) { |
|
265 |
ebb_connection_reset_timeout(connection); |
|
266 |
return; |
|
267 |
} |
|
268 |
} |
|
269 |
|
|
270 |
ebb_connection_schedule_close(connection); |
|
271 |
} |
|
272 |
|
|
273 |
/* Internal callback |
|
274 |
* called by connection->read_watcher |
|
275 |
*/ |
|
276 |
static void |
|
277 |
on_readable(struct ev_loop *loop, ev_io *watcher, int revents) |
|
278 |
{ |
|
279 |
ebb_connection *connection = watcher->data; |
|
280 |
char recv_buffer[TCP_MAXWIN]; |
|
281 |
ssize_t recved; |
|
282 |
|
|
283 |
//printf("on_readable\n"); |
|
284 |
// TODO -- why is this broken? |
|
285 |
//assert(ev_is_active(&connection->timeout_watcher)); |
|
286 |
assert(watcher == &connection->read_watcher); |
|
287 |
|
|
288 |
if(EV_ERROR & revents) { |
|
289 |
error("on_readable() got error event, closing connection."); |
|
290 |
goto error; |
|
291 |
} |
|
292 |
|
|
293 |
#ifdef HAVE_GNUTLS |
|
294 |
assert(!ev_is_active(&connection->handshake_watcher)); |
|
295 |
|
|
296 |
if(connection->server->secure) { |
|
297 |
recved = gnutls_record_recv( connection->session |
|
298 |
, recv_buffer |
|
299 |
, TCP_MAXWIN |
|
300 |
); |
|
301 |
if(recved <= 0) { |
|
302 |
if(gnutls_error_is_fatal(recved)) goto error; |
|
303 |
if( (recved == GNUTLS_E_INTERRUPTED || recved == GNUTLS_E_AGAIN) |
|
304 |
&& GNUTLS_NEED_WRITE |
|
305 |
) ev_io_start(loop, &connection->write_watcher); |
|
306 |
return; |
|
307 |
} |
|
308 |
} else { |
|
309 |
#endif /* HAVE_GNUTLS */ |
|
310 |
|
|
311 |
recved = recv(connection->fd, recv_buffer, TCP_MAXWIN, 0); |
|
312 |
if(recved <= 0) goto error; |
|
313 |
|
|
314 |
#ifdef HAVE_GNUTLS |
|
315 |
} |
|
316 |
#endif /* HAVE_GNUTLS */ |
|
317 |
|
|
318 |
ebb_connection_reset_timeout(connection); |
|
319 |
|
|
320 |
ebb_request_parser_execute(&connection->parser, recv_buffer, recved); |
|
321 |
|
|
322 |
/* parse error? just drop the client. screw the 400 response */ |
|
323 |
if(ebb_request_parser_has_error(&connection->parser)) goto error; |
|
324 |
return; |
|
325 |
error: |
|
326 |
ebb_connection_schedule_close(connection); |
|
327 |
} |
|
328 |
|
|
329 |
/* Internal callback |
|
330 |
* called by connection->write_watcher |
|
331 |
*/ |
|
332 |
static void |
|
333 |
on_writable(struct ev_loop *loop, ev_io *watcher, int revents) |
|
334 |
{ |
|
335 |
ebb_connection *connection = watcher->data; |
|
336 |
ssize_t sent; |
|
337 |
|
|
338 |
//printf("on_writable\n"); |
|
339 |
|
|
340 |
assert(CONNECTION_HAS_SOMETHING_TO_WRITE); |
|
341 |
assert(connection->written <= connection->to_write_len); |
|
342 |
// TODO -- why is this broken? |
|
343 |
//assert(ev_is_active(&connection->timeout_watcher)); |
|
344 |
assert(watcher == &connection->write_watcher); |
|
345 |
|
|
346 |
if(connection->to_write == 0) |
|
347 |
goto stop_writing; |
|
348 |
|
|
349 |
#ifdef HAVE_GNUTLS |
|
350 |
assert(!ev_is_active(&connection->handshake_watcher)); |
|
351 |
|
|
352 |
if(connection->server->secure) { |
|
353 |
sent = gnutls_record_send( connection->session |
|
354 |
, connection->to_write + connection->written |
|
355 |
, connection->to_write_len - connection->written |
|
356 |
); |
|
357 |
if(sent < 0) { |
|
358 |
if(gnutls_error_is_fatal(sent)) goto error; |
|
359 |
if( (sent == GNUTLS_E_INTERRUPTED || sent == GNUTLS_E_AGAIN) |
|
360 |
&& GNUTLS_NEED_READ |
|
361 |
) ev_io_stop(loop, watcher); |
|
362 |
return; |
|
363 |
} |
|
364 |
} else { |
|
365 |
#endif /* HAVE_GNUTLS */ |
|
366 |
|
|
367 |
sent = nosigpipe_push( (void*)connection->fd |
|
368 |
, connection->to_write + connection->written |
|
369 |
, connection->to_write_len - connection->written |
|
370 |
); |
|
371 |
if(sent < 0) goto error; |
|
372 |
if(sent == 0) return; |
|
373 |
|
|
374 |
#ifdef HAVE_GNUTLS |
|
375 |
} |
|
376 |
#endif /* HAVE_GNUTLS */ |
|
377 |
|
|
378 |
ebb_connection_reset_timeout(connection); |
|
379 |
|
|
380 |
connection->written += sent; |
|
381 |
|
|
382 |
if(connection->written == connection->to_write_len) { |
|
383 |
goto stop_writing; |
|
384 |
} |
|
385 |
return; |
|
386 |
stop_writing: |
|
387 |
ev_io_stop(loop, watcher); |
|
388 |
connection->to_write = NULL; |
|
389 |
|
|
390 |
if(connection->after_write_cb) |
|
391 |
connection->after_write_cb(connection); |
|
392 |
return; |
|
393 |
error: |
|
394 |
error("close connection on write."); |
|
395 |
ebb_connection_schedule_close(connection); |
|
396 |
} |
|
397 |
|
|
398 |
#ifdef HAVE_GNUTLS |
|
399 |
|
|
400 |
static void |
|
401 |
on_goodbye_tls(struct ev_loop *loop, ev_io *watcher, int revents) |
|
402 |
{ |
|
403 |
ebb_connection *connection = watcher->data; |
|
404 |
assert(watcher == &connection->goodbye_tls_watcher); |
|
405 |
|
|
406 |
if(EV_ERROR & revents) { |
|
407 |
error("on_goodbye() got error event, closing connection."); |
|
408 |
goto die; |
|
409 |
} |
|
410 |
|
|
411 |
int r = gnutls_bye(connection->session, GNUTLS_SHUT_RDWR); |
|
412 |
if(r < 0) { |
|
413 |
if(gnutls_error_is_fatal(r)) goto die; |
|
414 |
if(r == GNUTLS_E_INTERRUPTED || r == GNUTLS_E_AGAIN) |
|
415 |
ev_io_set( watcher |
|
416 |
, connection->fd |
|
417 |
, (GNUTLS_NEED_WRITE ? EV_WRITE : EV_READ) |
|
418 |
); |
|
419 |
return; |
|
420 |
} |
|
421 |
|
|
422 |
die: |
|
423 |
ev_io_stop(loop, watcher); |
|
424 |
if(connection->session) |
|
425 |
gnutls_deinit(connection->session); |
|
426 |
close_connection(connection); |
|
427 |
} |
|
428 |
#endif /* HAVE_GNUTLS*/ |
|
429 |
|
|
430 |
static void |
|
431 |
on_goodbye(struct ev_loop *loop, ev_timer *watcher, int revents) |
|
432 |
{ |
|
433 |
ebb_connection *connection = watcher->data; |
|
434 |
assert(watcher == &connection->goodbye_watcher); |
|
435 |
|
|
436 |
close_connection(connection); |
|
437 |
} |
|
438 |
|
|
439 |
|
|
440 |
static ebb_request* |
|
441 |
new_request_wrapper(void *data) |
|
442 |
{ |
|
443 |
ebb_connection *connection = data; |
|
444 |
if(connection->new_request) |
|
445 |
return connection->new_request(connection); |
|
446 |
return NULL; |
|
447 |
} |
|
448 |
|
|
449 |
/* Internal callback |
|
450 |
* Called by server->connection_watcher. |
|
451 |
*/ |
|
452 |
static void |
|
453 |
on_connection(struct ev_loop *loop, ev_io *watcher, int revents) |
|
454 |
{ |
|
455 |
ebb_server *server = watcher->data; |
|
456 |
|
|
457 |
//printf("on connection!\n"); |
|
458 |
|
|
459 |
assert(server->listening); |
|
460 |
assert(server->loop == loop); |
|
461 |
assert(&server->connection_watcher == watcher); |
|
462 |
|
|
463 |
if(EV_ERROR & revents) { |
|
464 |
error("on_connection() got error event, closing server."); |
|
465 |
ebb_server_unlisten(server); |
|
466 |
return; |
|
467 |
} |
|
468 |
|
|
469 |
struct sockaddr_in addr; // connector's address information |
|
470 |
socklen_t addr_len = sizeof(addr); |
|
471 |
int fd = accept( server->fd |
|
472 |
, (struct sockaddr*) & addr |
|
473 |
, & addr_len |
|
474 |
); |
|
475 |
if(fd < 0) { |
|
476 |
perror("accept()"); |
|
477 |
return; |
|
478 |
} |
|
479 |
|
|
480 |
ebb_connection *connection = NULL; |
|
481 |
if(server->new_connection) |
|
482 |
connection = server->new_connection(server, &addr); |
|
483 |
if(connection == NULL) { |
|
484 |
close(fd); |
|
485 |
return; |
|
486 |
} |
|
487 |
|
|
488 |
set_nonblock(fd); |
|
489 |
connection->fd = fd; |
|
490 |
connection->open = TRUE; |
|
491 |
connection->server = server; |
|
492 |
memcpy(&connection->sockaddr, &addr, addr_len); |
|
493 |
if(server->port[0] != '\0') |
|
494 |
connection->ip = inet_ntoa(connection->sockaddr.sin_addr); |
|
495 |
|
|
496 |
#ifdef SO_NOSIGPIPE |
|
497 |
int arg = 1; |
|
498 |
setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &arg, sizeof(int)); |
|
499 |
#endif |
|
500 |
|
|
501 |
#ifdef HAVE_GNUTLS |
|
502 |
if(server->secure) { |
|
503 |
gnutls_init(&connection->session, GNUTLS_SERVER); |
|
504 |
gnutls_transport_set_lowat(connection->session, 0); |
|
505 |
gnutls_set_default_priority(connection->session); |
|
506 |
gnutls_credentials_set(connection->session, GNUTLS_CRD_CERTIFICATE, connection->server->credentials); |
|
507 |
|
|
508 |
gnutls_transport_set_ptr(connection->session, (gnutls_transport_ptr) fd); |
|
509 |
gnutls_transport_set_push_function(connection->session, nosigpipe_push); |
|
510 |
|
|
511 |
gnutls_db_set_ptr (connection->session, &server->session_cache); |
|
512 |
gnutls_db_set_store_function (connection->session, session_cache_store); |
|
513 |
gnutls_db_set_retrieve_function (connection->session, session_cache_retrieve); |
|
514 |
gnutls_db_set_remove_function (connection->session, session_cache_remove); |
|
515 |
} |
|
516 |
|
|
517 |
ev_io_set(&connection->handshake_watcher, connection->fd, EV_READ | EV_WRITE); |
|
518 |
#endif /* HAVE_GNUTLS */ |
|
519 |
|
|
520 |
/* Note: not starting the write watcher until there is data to be written */ |
|
521 |
ev_io_set(&connection->write_watcher, connection->fd, EV_WRITE); |
|
522 |
ev_io_set(&connection->read_watcher, connection->fd, EV_READ); |
|
523 |
/* XXX: seperate error watcher? */ |
|
524 |
|
|
525 |
ev_timer_again(loop, &connection->timeout_watcher); |
|
526 |
|
|
527 |
#ifdef HAVE_GNUTLS |
|
528 |
if(server->secure) { |
|
529 |
ev_io_start(loop, &connection->handshake_watcher); |
|
530 |
return; |
|
531 |
} |
|
532 |
#endif |
|
533 |
|
|
534 |
ev_io_start(loop, &connection->read_watcher); |
|
535 |
} |
|
536 |
|
|
537 |
/** |
|
538 |
* Begin the server listening on a file descriptor. This DOES NOT start the |
|
539 |
* event loop. Start the event loop after making this call. |
|
540 |
*/ |
|
541 |
int |
|
542 |
ebb_server_listen_on_fd(ebb_server *server, const int fd) |
|
543 |
{ |
|
544 |
assert(server->listening == FALSE); |
|
545 |
|
|
546 |
if (listen(fd, EBB_MAX_CONNECTIONS) < 0) { |
|
547 |
perror("listen()"); |
|
548 |
return -1; |
|
549 |
} |
|
550 |
|
|
551 |
set_nonblock(fd); /* XXX superfluous? */ |
|
552 |
|
|
553 |
server->fd = fd; |
|
554 |
server->listening = TRUE; |
|
555 |
|
|
556 |
ev_io_set (&server->connection_watcher, server->fd, EV_READ); |
|
557 |
ev_io_start (server->loop, &server->connection_watcher); |
|
558 |
|
|
559 |
return server->fd; |
|
560 |
} |
|
561 |
|
|
562 |
|
|
563 |
/** |
|
564 |
* Begin the server listening on a file descriptor This DOES NOT start the |
|
565 |
* event loop. Start the event loop after making this call. |
|
566 |
*/ |
|
567 |
int |
|
568 |
ebb_server_listen_on_port(ebb_server *server, const int port) |
|
569 |
{ |
|
570 |
int fd = -1; |
|
571 |
struct linger ling = {0, 0}; |
|
572 |
struct sockaddr_in addr; |
|
573 |
int flags = 1; |
|
574 |
|
|
575 |
if ((fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { |
|
576 |
perror("socket()"); |
|
577 |
goto error; |
|
578 |
} |
|
579 |
|
|
580 |
flags = 1; |
|
581 |
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *)&flags, sizeof(flags)); |
|
582 |
setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&flags, sizeof(flags)); |
|
583 |
setsockopt(fd, SOL_SOCKET, SO_LINGER, (void *)&ling, sizeof(ling)); |
|
584 |
|
|
585 |
/* XXX: Sending single byte chunks in a response body? Perhaps there is a |
|
586 |
* need to enable the Nagel algorithm dynamically. For now disabling. |
|
587 |
*/ |
|
588 |
setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (void *)&flags, sizeof(flags)); |
|
589 |
|
|
590 |
/* the memset call clears nonstandard fields in some impementations that |
|
591 |
* otherwise mess things up. |
|
592 |
*/ |
|
593 |
memset(&addr, 0, sizeof(addr)); |
|
594 |
|
|
595 |
addr.sin_family = AF_INET; |
|
596 |
addr.sin_port = htons(port); |
|
597 |
addr.sin_addr.s_addr = htonl(INADDR_ANY); |
|
598 |
|
|
599 |
if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { |
|
600 |
perror("bind()"); |
|
601 |
goto error; |
|
602 |
} |
|
603 |
|
|
604 |
int ret = ebb_server_listen_on_fd(server, fd); |
|
605 |
if (ret >= 0) { |
|
606 |
sprintf(server->port, "%d", port); |
|
607 |
} |
|
608 |
return ret; |
|
609 |
error: |
|
610 |
if(fd > 0) close(fd); |
|
611 |
return -1; |
|
612 |
} |
|
613 |
|
|
614 |
/** |
|
615 |
* Stops the server. Will not accept new connections. Does not drop |
|
616 |
* existing connections. |
|
617 |
*/ |
|
618 |
void |
|
619 |
ebb_server_unlisten(ebb_server *server) |
|
620 |
{ |
|
621 |
if(server->listening) { |
|
622 |
ev_io_stop(server->loop, &server->connection_watcher); |
|
623 |
close(server->fd); |
|
624 |
server->port[0] = '\0'; |
|
625 |
server->listening = FALSE; |
|
626 |
} |
|
627 |
} |
|
628 |
|
|
629 |
/** |
|
630 |
* Initialize an ebb_server structure. After calling ebb_server_init set |
|
631 |
* the callback server->new_connection and, optionally, callback data |
|
632 |
* server->data. The new connection MUST be initialized with |
|
633 |
* ebb_connection_init before returning it to the server. |
|
634 |
* |
|
635 |
* @param server the server to initialize |
|
636 |
* @param loop a libev loop |
|
637 |
*/ |
|
638 |
void |
|
639 |
ebb_server_init(ebb_server *server, struct ev_loop *loop) |
|
640 |
{ |
|
641 |
server->loop = loop; |
|
642 |
server->listening = FALSE; |
|
643 |
server->port[0] = '\0'; |
|
644 |
server->fd = -1; |
|
645 |
server->connection_watcher.data = server; |
|
646 |
ev_init (&server->connection_watcher, on_connection); |
|
647 |
server->secure = FALSE; |
|
648 |
|
|
649 |
#ifdef HAVE_GNUTLS |
|
650 |
rbtree_init(&server->session_cache, session_cache_compare); |
|
651 |
server->credentials = NULL; |
|
652 |
#endif |
|
653 |
|
|
654 |
server->new_connection = NULL; |
|
655 |
server->data = NULL; |
|
656 |
} |
|
657 |
|
|
658 |
|
|
659 |
#ifdef HAVE_GNUTLS |
|
660 |
/* similar to server_init. |
|
661 |
* |
|
662 |
* the user of secure server might want to set additional callbacks from |
|
663 |
* GNUTLS. In particular |
|
664 |
* gnutls_global_set_mem_functions() |
|
665 |
* gnutls_global_set_log_function() |
|
666 |
* Also see the note above ebb_connection_init() about setting gnutls cache |
|
667 |
* access functions |
|
668 |
* |
|
669 |
* cert_file: the filename of a PEM certificate file |
|
670 |
* |
|
671 |
* key_file: the filename of a private key. Currently only PKCS-1 encoded |
|
672 |
* RSA and DSA private keys are accepted. |
|
673 |
*/ |
|
674 |
int |
|
675 |
ebb_server_set_secure (ebb_server *server, const char *cert_file, const char *key_file) |
|
676 |
{ |
|
677 |
server->secure = TRUE; |
|
678 |
gnutls_global_init(); |
|
679 |
gnutls_certificate_allocate_credentials(&server->credentials); |
|
680 |
/* todo gnutls_certificate_free_credentials */ |
|
681 |
int r = gnutls_certificate_set_x509_key_file( server->credentials |
|
682 |
, cert_file |
|
683 |
, key_file |
|
684 |
, GNUTLS_X509_FMT_PEM |
|
685 |
); |
|
686 |
if(r < 0) { |
|
687 |
error("loading certificates"); |
|
688 |
return -1; |
|
689 |
} |
|
690 |
return 1; |
|
691 |
} |
|
692 |
#endif /* HAVE_GNUTLS */ |
|
693 |
|
|
694 |
/** |
|
695 |
* Initialize an ebb_connection structure. After calling this function you |
|
696 |
* must setup callbacks for the different actions the server can take. See |
|
697 |
* server.h for which callbacks are availible. |
|
698 |
* |
|
699 |
* This should be called immediately after allocating space for a new |
|
700 |
* ebb_connection structure. Most likely, this will only be called within |
|
701 |
* the ebb_server->new_connection callback which you supply. |
|
702 |
* |
|
703 |
* If using SSL do consider setting |
|
704 |
* gnutls_db_set_retrieve_function (connection->session, _); |
|
705 |
* gnutls_db_set_remove_function (connection->session, _); |
|
706 |
* gnutls_db_set_store_function (connection->session, _); |
|
707 |
* gnutls_db_set_ptr (connection->session, _); |
|
708 |
* To provide a better means of storing SSL session caches. libebb provides |
|
709 |
* only a simple default implementation. |
|
710 |
* |
|
711 |
* @param connection the connection to initialize |
|
712 |
* @param timeout the timeout in seconds |
|
713 |
*/ |
|
714 |
void |
|
715 |
ebb_connection_init(ebb_connection *connection) |
|
716 |
{ |
|
717 |
connection->fd = -1; |
|
718 |
connection->server = NULL; |
|
719 |
connection->ip = NULL; |
|
720 |
connection->open = FALSE; |
|
721 |
|
|
722 |
ebb_request_parser_init( &connection->parser ); |
|
723 |
connection->parser.data = connection; |
|
724 |
connection->parser.new_request = new_request_wrapper; |
|
725 |
|
|
726 |
ev_init (&connection->write_watcher, on_writable); |
|
727 |
connection->write_watcher.data = connection; |
|
728 |
connection->to_write = NULL; |
|
729 |
|
|
730 |
ev_init(&connection->read_watcher, on_readable); |
|
731 |
connection->read_watcher.data = connection; |
|
732 |
|
|
733 |
#ifdef HAVE_GNUTLS |
|
734 |
connection->handshake_watcher.data = connection; |
|
735 |
ev_init(&connection->handshake_watcher, on_handshake); |
|
736 |
|
|
737 |
ev_init(&connection->goodbye_tls_watcher, on_goodbye_tls); |
|
738 |
connection->goodbye_tls_watcher.data = connection; |
|
739 |
|
|
740 |
connection->session = NULL; |
|
741 |
#endif /* HAVE_GNUTLS */ |
|
742 |
|
|
743 |
ev_timer_init(&connection->goodbye_watcher, on_goodbye, 0., 0.); |
|
744 |
connection->goodbye_watcher.data = connection; |
|
745 |
|
|
746 |
ev_timer_init(&connection->timeout_watcher, on_timeout, 0., EBB_DEFAULT_TIMEOUT); |
|
747 |
connection->timeout_watcher.data = connection; |
|
748 |
|
|
749 |
connection->new_request = NULL; |
|
750 |
connection->on_timeout = NULL; |
|
751 |
connection->on_close = NULL; |
|
752 |
connection->data = NULL; |
|
753 |
} |
|
754 |
|
|
755 |
void |
|
756 |
ebb_connection_schedule_close (ebb_connection *connection) |
|
757 |
{ |
|
758 |
#ifdef HAVE_GNUTLS |
|
759 |
if(connection->server->secure) { |
|
760 |
ev_io_set(&connection->goodbye_tls_watcher, connection->fd, EV_READ | EV_WRITE); |
|
761 |
ev_io_start(connection->server->loop, &connection->goodbye_tls_watcher); |
|
762 |
return; |
|
763 |
} |
|
764 |
#endif |
|
765 |
ev_timer_start(connection->server->loop, &connection->goodbye_watcher); |
|
766 |
} |
|
767 |
|
|
768 |
/* |
|
769 |
* Resets the timeout to stay alive for another connection->timeout seconds |
|
770 |
*/ |
|
771 |
void |
|
772 |
ebb_connection_reset_timeout(ebb_connection *connection) |
|
773 |
{ |
|
774 |
ev_timer_again(connection->server->loop, &connection->timeout_watcher); |
|
775 |
} |
|
776 |
|
|
777 |
/** |
|
778 |
* Writes a string to the socket. This is actually sets a watcher |
|
779 |
* which may take multiple iterations to write the entire string. |
|
780 |
* |
|
781 |
* This can only be called once at a time. If you call it again |
|
782 |
* while the connection is writing another buffer the ebb_connection_write |
|
783 |
* will return FALSE and ignore the request. |
|
784 |
*/ |
|
785 |
int |
|
786 |
ebb_connection_write (ebb_connection *connection, const char *buf, size_t len, ebb_after_write_cb cb) |
|
787 |
{ |
|
788 |
if(ev_is_active(&connection->write_watcher)) |
|
789 |
return FALSE; |
|
790 |
assert(!CONNECTION_HAS_SOMETHING_TO_WRITE); |
|
791 |
connection->to_write = buf; |
|
792 |
connection->to_write_len = len; |
|
793 |
connection->written = 0; |
|
794 |
connection->after_write_cb = cb; |
|
795 |
ev_io_start(connection->server->loop, &connection->write_watcher); |
|
796 |
return TRUE; |
|
797 |
} |
|
798 |
|
deps/libebb/ebb.h | ||
---|---|---|
1 |
/* This file is part of libebb. |
|
2 |
* |
|
3 |
* Copyright (c) 2008 Ryan Dahl (ry@ndahl.us) |
|
4 |
* All rights reserved. |
|
5 |
* |
|
6 |
* Permission is hereby granted, free of charge, to any person obtaining |
|
7 |
* a copy of this software and associated documentation files (the |
|
8 |
* "Software"), to deal in the Software without restriction, including |
|
9 |
* without limitation the rights to use, copy, modify, merge, publish, |
|
10 |
* distribute, sublicense, and/or sell copies of the Software, and to |
|
11 |
* permit persons to whom the Software is furnished to do so, subject to |
|
12 |
* the following conditions: |
|
13 |
* |
|
14 |
* The above copyright notice and this permission notice shall be |
|
15 |
* included in all copies or substantial portions of the Software. |
|
16 |
* |
|
17 |
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
|
18 |
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
|
19 |
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
|
20 |
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE |
|
21 |
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION |
|
22 |
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION |
|
23 |
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
|
24 |
*/ |
|
25 |
#ifndef EBB_H |
|
26 |
#define EBB_H |
|
27 |
|
|
28 |
#include <sys/socket.h> |
|
29 |
#include <netinet/in.h> |
|
30 |
#include <ev.h> |
|
31 |
#ifdef HAVE_GNUTLS |
|
32 |
# include <gnutls/gnutls.h> |
|
33 |
# include "rbtree.h" /* for ebb_server.session_cache */ |
|
34 |
#endif |
|
35 |
#include "ebb_request_parser.h" |
|
36 |
|
|
37 |
#define EBB_MAX_CONNECTIONS 1024 |
|
38 |
#define EBB_DEFAULT_TIMEOUT 30.0 |
|
39 |
|
|
40 |
#define EBB_AGAIN 0 |
|
41 |
#define EBB_STOP 1 |
|
42 |
|
|
43 |
typedef struct ebb_server ebb_server; |
|
44 |
typedef struct ebb_connection ebb_connection; |
|
45 |
typedef void (*ebb_after_write_cb) (ebb_connection *connection); |
|
46 |
typedef void (*ebb_connection_cb)(ebb_connection *connection, void *data); |
|
47 |
|
|
48 |
struct ebb_server { |
|
49 |
int fd; /* ro */ |
|
50 |
struct sockaddr_in sockaddr; /* ro */ |
|
51 |
socklen_t socklen; /* ro */ |
|
52 |
char port[6]; /* ro */ |
|
53 |
struct ev_loop *loop; /* ro */ |
|
54 |
unsigned listening:1; /* ro */ |
|
55 |
unsigned secure:1; /* ro */ |
|
56 |
#ifdef HAVE_GNUTLS |
|
57 |
gnutls_certificate_credentials_t credentials; /* private */ |
|
58 |
struct rbtree_t session_cache; /* private */ |
|
59 |
#endif |
|
60 |
ev_io connection_watcher; /* private */ |
|
61 |
|
|
62 |
/* Public */ |
|
63 |
|
|
64 |
/* Allocates and initializes an ebb_connection. NULL by default. */ |
|
65 |
ebb_connection* (*new_connection) (ebb_server*, struct sockaddr_in*); |
|
66 |
|
|
67 |
void *data; |
|
68 |
}; |
|
69 |
|
|
70 |
struct ebb_connection { |
|
71 |
int fd; /* ro */ |
|
72 |
struct sockaddr_in sockaddr; /* ro */ |
|
73 |
socklen_t socklen; /* ro */ |
|
74 |
ebb_server *server; /* ro */ |
|
75 |
char *ip; /* ro */ |
|
76 |
unsigned open:1; /* ro */ |
|
77 |
|
|
78 |
const char *to_write; /* ro */ |
|
79 |
size_t to_write_len; /* ro */ |
|
80 |
size_t written; /* ro */ |
|
81 |
ebb_after_write_cb after_write_cb; /* ro */ |
|
82 |
|
|
83 |
ebb_request_parser parser; /* private */ |
|
84 |
ev_io write_watcher; /* private */ |
|
85 |
ev_io read_watcher; /* private */ |
|
86 |
ev_timer timeout_watcher; /* private */ |
|
87 |
ev_timer goodbye_watcher; /* private */ |
|
88 |
#ifdef HAVE_GNUTLS |
|
89 |
ev_io handshake_watcher; /* private */ |
|
90 |
gnutls_session_t session; /* private */ |
|
91 |
ev_io goodbye_tls_watcher; /* private */ |
|
92 |
#endif |
|
93 |
|
|
94 |
/* Public */ |
|
95 |
|
|
96 |
ebb_request* (*new_request) (ebb_connection*); |
|
97 |
|
|
98 |
/* Returns EBB_STOP or EBB_AGAIN. NULL by default. */ |
|
99 |
int (*on_timeout) (ebb_connection*); |
|
100 |
|
|
101 |
void (*on_close) (ebb_connection*); |
|
102 |
|
|
103 |
void *data; |
|
104 |
}; |
|
105 |
|
|
106 |
void ebb_server_init (ebb_server *server, struct ev_loop *loop); |
|
107 |
#ifdef HAVE_GNUTLS |
|
108 |
int ebb_server_set_secure (ebb_server *server, const char *cert_file, |
|
109 |
const char *key_file); |
|
110 |
#endif |
|
111 |
int ebb_server_listen_on_port (ebb_server *server, const int port); |
|
112 |
int ebb_server_listen_on_fd (ebb_server *server, const int sfd); |
|
113 |
void ebb_server_unlisten (ebb_server *server); |
|
114 |
|
|
115 |
void ebb_connection_init (ebb_connection *); |
|
116 |
void ebb_connection_schedule_close (ebb_connection *); |
|
117 |
void ebb_connection_reset_timeout (ebb_connection *); |
|
118 |
int ebb_connection_write (ebb_connection *, const char *buf, size_t len, ebb_after_write_cb); |
|
119 |
|
|
120 |
#endif |
deps/libebb/ebb_request_parser.h | ||
---|---|---|
1 |
/* This file is part of the libebb web server library |
|
2 |
* |
|
3 |
* Copyright (c) 2008 Ryan Dahl (ry@ndahl.us) |
|
4 |
* All rights reserved. |
|
5 |
* |
|
6 |
* This parser is based on code from Zed Shaw's Mongrel. |
|
7 |
* Copyright (c) 2005 Zed A. Shaw |
|
8 |
* |
|
9 |
* Permission is hereby granted, free of charge, to any person obtaining |
|
10 |
* a copy of this software and associated documentation files (the |
|
11 |
* "Software"), to deal in the Software without restriction, including |
|
12 |
* without limitation the rights to use, copy, modify, merge, publish, |
|
13 |
* distribute, sublicense, and/or sell copies of the Software, and to |
|
14 |
* permit persons to whom the Software is furnished to do so, subject to |
|
15 |
* the following conditions: |
|
16 |
* |
|
17 |
* The above copyright notice and this permission notice shall be |
|
18 |
* included in all copies or substantial portions of the Software. |
|
19 |
* |
|
20 |
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
|
21 |
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
|
22 |
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
|
23 |
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE |
|
24 |
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION |
|
25 |
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION |
|
26 |
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
|
27 |
*/ |
|
28 |
#ifndef ebb_request_parser_h |
|
29 |
#define ebb_request_parser_h |
|
30 |
#ifdef __cplusplus |
|
31 |
extern "C" { |
|
32 |
#endif |
|
33 |
|
|
34 |
|
|
35 |
#include <sys/types.h> |
|
36 |
|
|
37 |
typedef struct ebb_request ebb_request; |
|
38 |
typedef struct ebb_request_parser ebb_request_parser; |
|
39 |
typedef void (*ebb_header_cb)(ebb_request*, const char *at, size_t length, int header_index); |
|
40 |
typedef void (*ebb_element_cb)(ebb_request*, const char *at, size_t length); |
|
41 |
|
|
42 |
#define EBB_MAX_MULTIPART_BOUNDARY_LEN 20 |
|
43 |
|
|
44 |
/* HTTP Methods */ |
|
45 |
#define EBB_COPY 0x00000001 |
|
46 |
#define EBB_DELETE 0x00000002 |
|
47 |
#define EBB_GET 0x00000004 |
|
48 |
#define EBB_HEAD 0x00000008 |
|
49 |
#define EBB_LOCK 0x00000010 |
|
50 |
#define EBB_MKCOL 0x00000020 |
|
51 |
#define EBB_MOVE 0x00000040 |
|
52 |
#define EBB_OPTIONS 0x00000080 |
|
53 |
#define EBB_POST 0x00000100 |
|
54 |
#define EBB_PROPFIND 0x00000200 |
|
55 |
#define EBB_PROPPATCH 0x00000400 |
|
56 |
#define EBB_PUT 0x00000800 |
|
57 |
#define EBB_TRACE 0x00001000 |
|
58 |
#define EBB_UNLOCK 0x00002000 |
|
59 |
|
|
60 |
/* Transfer Encodings */ |
|
61 |
#define EBB_IDENTITY 0x00000001 |
|
62 |
#define EBB_CHUNKED 0x00000002 |
|
63 |
|
|
64 |
struct ebb_request { |
|
65 |
int method; |
|
66 |
int transfer_encoding; /* ro */ |
|
67 |
int expect_continue; /* ro */ |
|
68 |
unsigned int version_major; /* ro */ |
|
69 |
unsigned int version_minor; /* ro */ |
|
70 |
int number_of_headers; /* ro */ |
|
71 |
int keep_alive; /* private - use ebb_request_should_keep_alive */ |
|
72 |
size_t content_length; /* ro - 0 if unknown */ |
|
73 |
size_t body_read; /* ro */ |
|
74 |
|
|
75 |
/* Public - ordered list of callbacks */ |
|
76 |
ebb_element_cb on_path; |
|
77 |
ebb_element_cb on_query_string; |
|
78 |
ebb_element_cb on_uri; |
|
79 |
ebb_element_cb on_fragment; |
|
80 |
ebb_header_cb on_header_field; |
|
81 |
ebb_header_cb on_header_value; |
|
82 |
void (*on_headers_complete)(ebb_request *); |
|
83 |
ebb_element_cb on_body; |
|
84 |
void (*on_complete)(ebb_request *); |
|
85 |
void *data; |
|
86 |
}; |
|
87 |
|
|
88 |
struct ebb_request_parser { |
|
89 |
int cs; /* private */ |
|
90 |
size_t chunk_size; /* private */ |
|
91 |
unsigned eating:1; /* private */ |
|
92 |
ebb_request *current_request; /* ro */ |
|
93 |
const char *header_field_mark; |
|
94 |
const char *header_value_mark; |
|
95 |
const char *query_string_mark; |
|
96 |
const char *path_mark; |
|
97 |
const char *uri_mark; |
|
98 |
const char *fragment_mark; |
|
99 |
|
|
100 |
/* Public */ |
|
101 |
ebb_request* (*new_request)(void*); |
|
102 |
void *data; |
|
103 |
}; |
|
104 |
|
|
105 |
void ebb_request_parser_init(ebb_request_parser *parser); |
|
106 |
size_t ebb_request_parser_execute(ebb_request_parser *parser, const char *data, size_t len); |
|
107 |
int ebb_request_parser_has_error(ebb_request_parser *parser); |
|
108 |
int ebb_request_parser_is_finished(ebb_request_parser *parser); |
|
109 |
void ebb_request_init(ebb_request *); |
|
110 |
int ebb_request_should_keep_alive(ebb_request *request); |
|
111 |
#define ebb_request_has_body(request) \ |
|
112 |
(request->transfer_encoding == EBB_CHUNKED || request->content_length > 0 ) |
|
113 |
|
|
114 |
#ifdef __cplusplus |
|
115 |
} |
|
116 |
#endif |
|
117 |
#endif |
deps/libebb/ebb_request_parser.rl | ||
---|---|---|
1 |
/* This file is part of the libebb web server library |
|
2 |
* |
|
3 |
* Copyright (c) 2008 Ryan Dahl (ry@ndahl.us) |
|
4 |
* All rights reserved. |
|
5 |
* |
|
6 |
* This parser is based on code from Zed Shaw's Mongrel. |
|
7 |
* Copyright (c) 2005 Zed A. Shaw |
|
8 |
* |
|
9 |
* Permission is hereby granted, free of charge, to any person obtaining |
|
10 |
* a copy of this software and associated documentation files (the |
|
11 |
* "Software"), to deal in the Software without restriction, including |
|
12 |
* without limitation the rights to use, copy, modify, merge, publish, |
|
13 |
* distribute, sublicense, and/or sell copies of the Software, and to |
|
14 |
* permit persons to whom the Software is furnished to do so, subject to |
|
15 |
* the following conditions: |
|
16 |
* |
|
17 |
* The above copyright notice and this permission notice shall be |
|
18 |
* included in all copies or substantial portions of the Software. |
|
19 |
* |
|
20 |
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
|
21 |
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
|
22 |
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
|
23 |
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE |
|
24 |
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION |
|
25 |
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION |
|
26 |
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
|
27 |
*/ |
|
28 |
#include "ebb_request_parser.h" |
|
29 |
|
|
30 |
#include <stdio.h> |
|
31 |
#include <assert.h> |
|
32 |
|
|
33 |
static int unhex[] = {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 |
|
34 |
,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 |
|
35 |
,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 |
|
36 |
, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1 |
|
37 |
,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1 |
|
38 |
,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 |
|
39 |
,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1 |
|
40 |
,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 |
|
41 |
}; |
|
42 |
#define TRUE 1 |
|
43 |
#define FALSE 0 |
|
44 |
#define MIN(a,b) (a < b ? a : b) |
|
45 |
|
|
46 |
#define REMAINING (pe - p) |
|
47 |
#define CURRENT (parser->current_request) |
|
48 |
#define CONTENT_LENGTH (parser->current_request->content_length) |
|
49 |
#define CALLBACK(FOR) \ |
|
50 |
if(parser->FOR##_mark && CURRENT->on_##FOR) { \ |
|
51 |
CURRENT->on_##FOR( CURRENT \ |
|
52 |
, parser->FOR##_mark \ |
|
53 |
, p - parser->FOR##_mark \ |
|
54 |
); \ |
|
55 |
} |
|
56 |
#define HEADER_CALLBACK(FOR) \ |
|
57 |
if(parser->FOR##_mark && CURRENT->on_##FOR) { \ |
|
58 |
CURRENT->on_##FOR( CURRENT \ |
|
59 |
, parser->FOR##_mark \ |
|
60 |
, p - parser->FOR##_mark \ |
|
61 |
, CURRENT->number_of_headers \ |
|
62 |
); \ |
Also available in: Unified diff