ebb 0.0.1
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 +119 -0
- data/VERSION +1 -0
- data/benchmark/application.rb +84 -0
- data/benchmark/bench_results.rb +45 -0
- data/benchmark/server_test.rb +152 -0
- data/benchmark/test.rb +141 -0
- data/bin/ebb_rails +88 -0
- data/libev/ev.c +2440 -0
- data/libev/ev.h +551 -0
- data/libev/ev_epoll.c +174 -0
- data/libev/ev_kqueue.c +186 -0
- data/libev/ev_poll.c +127 -0
- data/libev/ev_port.c +155 -0
- data/libev/ev_select.c +236 -0
- data/libev/ev_vars.h +108 -0
- data/libev/ev_win32.c +117 -0
- data/libev/ev_wrap.h +132 -0
- data/ruby_lib/daemonizable.rb +134 -0
- data/ruby_lib/ebb.rb +179 -0
- data/ruby_lib/rack/adapter/rails.rb +152 -0
- data/src/ebb.c +709 -0
- data/src/ebb.h +102 -0
- data/src/ebb_ruby.c +256 -0
- data/src/extconf.rb +44 -0
- data/src/parser.c +1584 -0
- data/src/parser.h +50 -0
- metadata +80 -0
@@ -0,0 +1,152 @@
|
|
1
|
+
require 'cgi'
|
2
|
+
|
3
|
+
# Adapter to run a Rails app with any supported Rack handler.
|
4
|
+
# By default it will try to load the Rails application in the
|
5
|
+
# current directory in the development environment.
|
6
|
+
# Options:
|
7
|
+
# root: Root directory of the Rails app
|
8
|
+
# env: Rails environment to run in (development, production or test)
|
9
|
+
# Based on http://fuzed.rubyforge.org/ Rails adapter
|
10
|
+
module Rack
|
11
|
+
module Adapter
|
12
|
+
class Rails
|
13
|
+
def initialize(options={})
|
14
|
+
@root = options[:root] || Dir.pwd
|
15
|
+
@env = options[:environment] || 'development'
|
16
|
+
@prefix = options[:prefix]
|
17
|
+
|
18
|
+
load_application
|
19
|
+
|
20
|
+
@file_server = Rack::File.new(::File.join(RAILS_ROOT, "public"))
|
21
|
+
end
|
22
|
+
|
23
|
+
def load_application
|
24
|
+
ENV['RAILS_ENV'] = @env
|
25
|
+
|
26
|
+
require "#{@root}/config/environment"
|
27
|
+
require 'dispatcher'
|
28
|
+
|
29
|
+
ActionController::AbstractRequest.relative_url_root = @prefix if @prefix
|
30
|
+
end
|
31
|
+
|
32
|
+
# TODO refactor this in File#can_serve?(path) ??
|
33
|
+
def file_exist?(path)
|
34
|
+
full_path = ::File.join(@file_server.root, Utils.unescape(path))
|
35
|
+
::File.file?(full_path) && ::File.readable?(full_path)
|
36
|
+
end
|
37
|
+
|
38
|
+
def serve_file(env)
|
39
|
+
@file_server.call(env)
|
40
|
+
end
|
41
|
+
|
42
|
+
def serve_rails(env)
|
43
|
+
request = Request.new(env)
|
44
|
+
response = Response.new
|
45
|
+
|
46
|
+
session_options = ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS
|
47
|
+
cgi = CGIWrapper.new(request, response)
|
48
|
+
|
49
|
+
Dispatcher.dispatch(cgi, session_options, response)
|
50
|
+
|
51
|
+
response.finish
|
52
|
+
end
|
53
|
+
|
54
|
+
def call(env)
|
55
|
+
path = env['PATH_INFO'].chomp('/')
|
56
|
+
cached_path = (path.empty? ? 'index' : path) + ActionController::Base.page_cache_extension
|
57
|
+
|
58
|
+
if file_exist?(path) # Serve the file if it's there
|
59
|
+
serve_file(env)
|
60
|
+
elsif file_exist?(cached_path) # Serve the page cache if it's there
|
61
|
+
env['PATH_INFO'] = cached_path
|
62
|
+
serve_file(env)
|
63
|
+
else # No static file, let Rails handle it
|
64
|
+
serve_rails(env)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
protected
|
69
|
+
|
70
|
+
class CGIWrapper < ::CGI
|
71
|
+
def initialize(request, response, *args)
|
72
|
+
@request = request
|
73
|
+
@response = response
|
74
|
+
@args = *args
|
75
|
+
@input = request.body
|
76
|
+
|
77
|
+
super *args
|
78
|
+
end
|
79
|
+
|
80
|
+
def header(options = "text/html")
|
81
|
+
if options.is_a?(String)
|
82
|
+
@response['Content-Type'] = options unless @response['Content-Type']
|
83
|
+
else
|
84
|
+
@response['Content-Length'] = options.delete('Content-Length').to_s if options['Content-Length']
|
85
|
+
|
86
|
+
@response['Content-Type'] = options.delete('type') || "text/html"
|
87
|
+
@response['Content-Type'] += "; charset=" + options.delete('charset') if options['charset']
|
88
|
+
|
89
|
+
@response['Content-Language'] = options.delete('language') if options['language']
|
90
|
+
@response['Expires'] = options.delete('expires') if options['expires']
|
91
|
+
|
92
|
+
@response.status = options.delete('Status') if options['Status']
|
93
|
+
|
94
|
+
# Convert 'cookie' header to 'Set-Cookie' headers.
|
95
|
+
# Because Set-Cookie header can appear more the once in the response body,
|
96
|
+
# we store it in a line break seperated string that will be translated to
|
97
|
+
# multiple Set-Cookie header by the handler.
|
98
|
+
if cookie = options.delete('cookie')
|
99
|
+
cookies = []
|
100
|
+
|
101
|
+
case cookie
|
102
|
+
when Array then cookie.each { |c| cookies << c.to_s }
|
103
|
+
when Hash then cookie.each { |_, c| cookies << c.to_s }
|
104
|
+
else cookies << cookie.to_s
|
105
|
+
end
|
106
|
+
|
107
|
+
@output_cookies.each { |c| cookies << c.to_s } if @output_cookies
|
108
|
+
|
109
|
+
@response['Set-Cookie'] = [@response['Set-Cookie'], cookies].compact.join("\n")
|
110
|
+
end
|
111
|
+
|
112
|
+
options.each { |k,v| @response[k] = v }
|
113
|
+
end
|
114
|
+
|
115
|
+
""
|
116
|
+
end
|
117
|
+
|
118
|
+
def params
|
119
|
+
@params ||= @request.params
|
120
|
+
end
|
121
|
+
|
122
|
+
def cookies
|
123
|
+
@request.cookies
|
124
|
+
end
|
125
|
+
|
126
|
+
def query_string
|
127
|
+
@request.query_string
|
128
|
+
end
|
129
|
+
|
130
|
+
# Used to wrap the normal args variable used inside CGI.
|
131
|
+
def args
|
132
|
+
@args
|
133
|
+
end
|
134
|
+
|
135
|
+
# Used to wrap the normal env_table variable used inside CGI.
|
136
|
+
def env_table
|
137
|
+
@request.env
|
138
|
+
end
|
139
|
+
|
140
|
+
# Used to wrap the normal stdinput variable used inside CGI.
|
141
|
+
def stdinput
|
142
|
+
@input
|
143
|
+
end
|
144
|
+
|
145
|
+
def stdoutput
|
146
|
+
STDERR.puts "stdoutput should not be used."
|
147
|
+
@response.body
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
data/src/ebb.c
ADDED
@@ -0,0 +1,709 @@
|
|
1
|
+
/* Ebb Web Server
|
2
|
+
* Copyright (c) 2007 Ry Dahl
|
3
|
+
* This software is released under the "MIT License". See README file for details.
|
4
|
+
*/
|
5
|
+
#include <unistd.h>
|
6
|
+
#include <fcntl.h>
|
7
|
+
#include <sys/types.h>
|
8
|
+
#include <arpa/inet.h>
|
9
|
+
#include <netinet/tcp.h>
|
10
|
+
#include <sys/un.h>
|
11
|
+
#include <netdb.h>
|
12
|
+
|
13
|
+
#include <stdio.h>
|
14
|
+
#include <string.h>
|
15
|
+
#include <stdlib.h>
|
16
|
+
#include <errno.h>
|
17
|
+
#include <signal.h>
|
18
|
+
#include <assert.h>
|
19
|
+
|
20
|
+
#include <pthread.h>
|
21
|
+
#include <glib.h>
|
22
|
+
|
23
|
+
#define EV_STANDALONE 1
|
24
|
+
#include <ev.c>
|
25
|
+
|
26
|
+
#include "parser.h"
|
27
|
+
#include "ebb.h"
|
28
|
+
|
29
|
+
#define min(a,b) (a < b ? a : b)
|
30
|
+
#define ramp(a) (a > 0 ? a : 0)
|
31
|
+
|
32
|
+
static int server_socket(const int port);
|
33
|
+
static int server_socket_unix(const char *path, int access_mask);
|
34
|
+
|
35
|
+
#define env_add(client, field,flen,value,vlen) \
|
36
|
+
client->env_fields[client->env_size] = field; \
|
37
|
+
client->env_field_lengths[client->env_size] = flen; \
|
38
|
+
client->env_values[client->env_size] = value; \
|
39
|
+
client->env_value_lengths[client->env_size] = vlen; \
|
40
|
+
client->env_size += 1;
|
41
|
+
#define env_add_const(client,field,value,vlen) \
|
42
|
+
client->env_fields[client->env_size] = NULL; \
|
43
|
+
client->env_field_lengths[client->env_size] = field; \
|
44
|
+
client->env_values[client->env_size] = value; \
|
45
|
+
client->env_value_lengths[client->env_size] = vlen; \
|
46
|
+
client->env_size += 1;
|
47
|
+
#define env_error(client) \
|
48
|
+
client->env_fields[client->env_size] = NULL; \
|
49
|
+
client->env_field_lengths[client->env_size] = -1; \
|
50
|
+
client->env_values[client->env_size] = NULL; \
|
51
|
+
client->env_value_lengths[client->env_size] = -1; \
|
52
|
+
client->env_size += 1;
|
53
|
+
|
54
|
+
int env_has_error(ebb_client *client)
|
55
|
+
{
|
56
|
+
int i;
|
57
|
+
for(i = 0; i < client->env_size; i++)
|
58
|
+
if(client->env_field_lengths[i] < 0)
|
59
|
+
return TRUE;
|
60
|
+
return FALSE;
|
61
|
+
}
|
62
|
+
|
63
|
+
/** Defines common length and error messages for input length validation. */
|
64
|
+
#define DEF_MAX_LENGTH(N,length) const size_t MAX_##N##_LENGTH = length; const char *MAX_##N##_LENGTH_ERR = "HTTP Parse Error: HTTP element " # N " is longer than the " # length " allowed length."
|
65
|
+
|
66
|
+
/** Validates the max length of given input and throws an exception if over. */
|
67
|
+
#define VALIDATE_MAX_LENGTH(len, N) if(len > MAX_##N##_LENGTH) { env_error(client); g_message(MAX_##N##_LENGTH_ERR); }
|
68
|
+
|
69
|
+
/* Defines the maximum allowed lengths for various input elements.*/
|
70
|
+
DEF_MAX_LENGTH(FIELD_NAME, 256);
|
71
|
+
DEF_MAX_LENGTH(FIELD_VALUE, 80 * 1024);
|
72
|
+
DEF_MAX_LENGTH(REQUEST_URI, 1024 * 12);
|
73
|
+
DEF_MAX_LENGTH(FRAGMENT, 1024); /* Don't know if this length is specified somewhere or not */
|
74
|
+
DEF_MAX_LENGTH(REQUEST_PATH, 1024);
|
75
|
+
DEF_MAX_LENGTH(QUERY_STRING, (1024 * 10));
|
76
|
+
DEF_MAX_LENGTH(HEADER, (1024 * (80 + 32)));
|
77
|
+
|
78
|
+
void http_field_cb(void *data, const char *field, size_t flen, const char *value, size_t vlen)
|
79
|
+
{
|
80
|
+
ebb_client *client = (ebb_client*)(data);
|
81
|
+
VALIDATE_MAX_LENGTH(flen, FIELD_NAME);
|
82
|
+
VALIDATE_MAX_LENGTH(vlen, FIELD_VALUE);
|
83
|
+
env_add(client, field, flen, value, vlen);
|
84
|
+
}
|
85
|
+
|
86
|
+
|
87
|
+
void request_method_cb(void *data, const char *at, size_t length)
|
88
|
+
{
|
89
|
+
ebb_client *client = (ebb_client*)(data);
|
90
|
+
env_add_const(client, EBB_REQUEST_METHOD, at, length);
|
91
|
+
}
|
92
|
+
|
93
|
+
|
94
|
+
void request_uri_cb(void *data, const char *at, size_t length)
|
95
|
+
{
|
96
|
+
ebb_client *client = (ebb_client*)(data);
|
97
|
+
VALIDATE_MAX_LENGTH(length, REQUEST_URI);
|
98
|
+
env_add_const(client, EBB_REQUEST_URI, at, length);
|
99
|
+
}
|
100
|
+
|
101
|
+
|
102
|
+
void fragment_cb(void *data, const char *at, size_t length)
|
103
|
+
{
|
104
|
+
ebb_client *client = (ebb_client*)(data);
|
105
|
+
VALIDATE_MAX_LENGTH(length, FRAGMENT);
|
106
|
+
env_add_const(client, EBB_FRAGMENT, at, length);
|
107
|
+
}
|
108
|
+
|
109
|
+
|
110
|
+
void request_path_cb(void *data, const char *at, size_t length)
|
111
|
+
{
|
112
|
+
ebb_client *client = (ebb_client*)(data);
|
113
|
+
VALIDATE_MAX_LENGTH(length, REQUEST_PATH);
|
114
|
+
env_add_const(client, EBB_REQUEST_PATH, at, length);
|
115
|
+
}
|
116
|
+
|
117
|
+
|
118
|
+
void query_string_cb(void *data, const char *at, size_t length)
|
119
|
+
{
|
120
|
+
ebb_client *client = (ebb_client*)(data);
|
121
|
+
VALIDATE_MAX_LENGTH(length, QUERY_STRING);
|
122
|
+
env_add_const(client, EBB_QUERY_STRING, at, length);
|
123
|
+
}
|
124
|
+
|
125
|
+
|
126
|
+
void http_version_cb(void *data, const char *at, size_t length)
|
127
|
+
{
|
128
|
+
ebb_client *client = (ebb_client*)(data);
|
129
|
+
env_add_const(client, EBB_HTTP_VERSION, at, length);
|
130
|
+
}
|
131
|
+
|
132
|
+
|
133
|
+
void content_length_cb(void *data, const char *at, size_t length)
|
134
|
+
{
|
135
|
+
ebb_client *client = (ebb_client*)(data);
|
136
|
+
env_add_const(client, EBB_CONTENT_LENGTH, at, length);
|
137
|
+
|
138
|
+
/* i hate c. */
|
139
|
+
char buf[20];
|
140
|
+
strncpy(buf, at, length);
|
141
|
+
buf[length] = '\0';
|
142
|
+
client->content_length = atoi(buf);
|
143
|
+
}
|
144
|
+
|
145
|
+
|
146
|
+
const char* localhost_str = "0.0.0.0";
|
147
|
+
void dispatch(ebb_client *client)
|
148
|
+
{
|
149
|
+
ebb_server *server = client->server;
|
150
|
+
|
151
|
+
if(client->open == FALSE)
|
152
|
+
return;
|
153
|
+
|
154
|
+
/* Set the env variables */
|
155
|
+
if(server->port) {
|
156
|
+
env_add_const(client, EBB_SERVER_NAME
|
157
|
+
, localhost_str
|
158
|
+
, 7
|
159
|
+
);
|
160
|
+
env_add_const(client, EBB_SERVER_PORT
|
161
|
+
, server->port
|
162
|
+
, strlen(server->port)
|
163
|
+
);
|
164
|
+
}
|
165
|
+
server->request_cb(client, server->request_cb_data);
|
166
|
+
}
|
167
|
+
|
168
|
+
|
169
|
+
void on_timeout(struct ev_loop *loop, ev_timer *watcher, int revents)
|
170
|
+
{
|
171
|
+
ebb_client *client = (ebb_client*)(watcher->data);
|
172
|
+
|
173
|
+
assert(client->server->loop == loop);
|
174
|
+
assert(&(client->timeout_watcher) == watcher);
|
175
|
+
|
176
|
+
ebb_client_close(client);
|
177
|
+
#ifdef DEBUG
|
178
|
+
g_message("peer timed out");
|
179
|
+
#endif
|
180
|
+
}
|
181
|
+
|
182
|
+
#define client_finished_parsing http_parser_is_finished(&client->parser)
|
183
|
+
#define total_request_size (client->content_length + client->parser.nread)
|
184
|
+
|
185
|
+
void* read_body_into_file(void *_client)
|
186
|
+
{
|
187
|
+
ebb_client *client = (ebb_client*)_client;
|
188
|
+
static unsigned int id;
|
189
|
+
FILE *tmpfile;
|
190
|
+
|
191
|
+
assert(client->open);
|
192
|
+
assert(client->server->open);
|
193
|
+
assert(client->content_length > 0);
|
194
|
+
assert(client_finished_parsing);
|
195
|
+
|
196
|
+
/* set blocking socket */
|
197
|
+
int flags = fcntl(client->fd, F_GETFL, 0);
|
198
|
+
assert(0 <= fcntl(client->fd, F_SETFL, flags & ~O_NONBLOCK));
|
199
|
+
|
200
|
+
sprintf(client->upload_file_filename, "/tmp/ebb_upload_%010d", id++);
|
201
|
+
tmpfile = fopen(client->upload_file_filename, "w+");
|
202
|
+
if(tmpfile == NULL) g_message("Cannot open tmpfile %s", client->upload_file_filename);
|
203
|
+
client->upload_file = tmpfile;
|
204
|
+
|
205
|
+
size_t body_head_length = client->read - client->parser.nread;
|
206
|
+
size_t written = 0, r;
|
207
|
+
while(written < body_head_length) {
|
208
|
+
r = fwrite( client->request_buffer + sizeof(char)*(client->parser.nread + written)
|
209
|
+
, sizeof(char)
|
210
|
+
, body_head_length - written
|
211
|
+
, tmpfile
|
212
|
+
);
|
213
|
+
if(r <= 0) {
|
214
|
+
ebb_client_close(client);
|
215
|
+
return NULL;
|
216
|
+
}
|
217
|
+
written += r;
|
218
|
+
}
|
219
|
+
|
220
|
+
// g_debug("wrote request header to file. written: %d, content_length: %d", written, client->content_length);
|
221
|
+
|
222
|
+
int bufsize = 5*1024;
|
223
|
+
char buffer[bufsize];
|
224
|
+
size_t received;
|
225
|
+
while(written < client->content_length) {
|
226
|
+
received = recv(client->fd
|
227
|
+
, buffer
|
228
|
+
, min(client->content_length - written, bufsize)
|
229
|
+
, 0
|
230
|
+
);
|
231
|
+
if(received < 0) goto error;
|
232
|
+
client->read += received;
|
233
|
+
|
234
|
+
ssize_t w = 0;
|
235
|
+
int rv;
|
236
|
+
while(w < received) {
|
237
|
+
rv = fwrite( buffer + w*sizeof(char)
|
238
|
+
, sizeof(char)
|
239
|
+
, received - w
|
240
|
+
, tmpfile
|
241
|
+
);
|
242
|
+
if(rv <= 0) goto error;
|
243
|
+
w += rv;
|
244
|
+
}
|
245
|
+
written += received;
|
246
|
+
}
|
247
|
+
rewind(tmpfile);
|
248
|
+
// g_debug("%d bytes written to file %s", written, client->upload_file_filename);
|
249
|
+
return NULL;
|
250
|
+
error:
|
251
|
+
ebb_client_close(client);
|
252
|
+
return NULL;
|
253
|
+
}
|
254
|
+
|
255
|
+
|
256
|
+
void on_readable(struct ev_loop *loop, ev_io *watcher, int revents)
|
257
|
+
{
|
258
|
+
ebb_client *client = (ebb_client*)(watcher->data);
|
259
|
+
|
260
|
+
assert(client->open);
|
261
|
+
assert(client->server->open);
|
262
|
+
assert(client->server->loop == loop);
|
263
|
+
assert(&client->read_watcher == watcher);
|
264
|
+
|
265
|
+
ssize_t read = recv( client->fd
|
266
|
+
, client->request_buffer + client->read
|
267
|
+
, EBB_BUFFERSIZE - client->read - 1 /* -1 is for making ragel happy below */
|
268
|
+
, 0
|
269
|
+
);
|
270
|
+
if(read <= 0) goto error; /* XXX is this the right action to take for read==0 ? */
|
271
|
+
client->read += read;
|
272
|
+
ev_timer_again(loop, &client->timeout_watcher);
|
273
|
+
|
274
|
+
if(FALSE == client_finished_parsing) {
|
275
|
+
client->request_buffer[client->read] = '\0'; /* make ragel happy */
|
276
|
+
http_parser_execute( &client->parser
|
277
|
+
, client->request_buffer
|
278
|
+
, client->read
|
279
|
+
, client->parser.nread
|
280
|
+
);
|
281
|
+
if(http_parser_has_error(&client->parser)) goto error;
|
282
|
+
}
|
283
|
+
|
284
|
+
if(total_request_size == client->read) {
|
285
|
+
ev_io_stop(loop, watcher);
|
286
|
+
client->nread_from_body = 0;
|
287
|
+
dispatch(client);
|
288
|
+
return;
|
289
|
+
}
|
290
|
+
|
291
|
+
if(client_finished_parsing && total_request_size > EBB_BUFFERSIZE ) {
|
292
|
+
/* read body into file - in a thread */
|
293
|
+
pthread_t thread;
|
294
|
+
ev_io_stop(loop, watcher);
|
295
|
+
assert(0 <= pthread_create(&thread, NULL, read_body_into_file, client));
|
296
|
+
pthread_join(thread, NULL);
|
297
|
+
dispatch(client);
|
298
|
+
return;
|
299
|
+
}
|
300
|
+
return;
|
301
|
+
error:
|
302
|
+
if(read < 0) g_message("Error recving data: %s", strerror(errno));
|
303
|
+
ebb_client_close(client);
|
304
|
+
}
|
305
|
+
|
306
|
+
void on_request(struct ev_loop *loop, ev_io *watcher, int revents)
|
307
|
+
{
|
308
|
+
ebb_server *server = (ebb_server*)(watcher->data);
|
309
|
+
assert(server->open);
|
310
|
+
assert(server->loop == loop);
|
311
|
+
assert(&server->request_watcher == watcher);
|
312
|
+
|
313
|
+
if(EV_ERROR & revents) {
|
314
|
+
g_message("on_request() got error event, closing server.");
|
315
|
+
ebb_server_unlisten(server);
|
316
|
+
return;
|
317
|
+
}
|
318
|
+
/* Now we're going to initialize the client
|
319
|
+
* and set up her callbacks for read and write
|
320
|
+
* the client won't get passed back to the user, however,
|
321
|
+
* until the request is complete and parsed.
|
322
|
+
*/
|
323
|
+
int i;
|
324
|
+
ebb_client *client;
|
325
|
+
/* Get next availible peer */
|
326
|
+
for(i=0; i < EBB_MAX_CLIENTS; i++)
|
327
|
+
if(!server->clients[i].open) {
|
328
|
+
client = &(server->clients[i]);
|
329
|
+
break;
|
330
|
+
}
|
331
|
+
if(client == NULL) {
|
332
|
+
g_message("Too many peers. Refusing connections.");
|
333
|
+
return;
|
334
|
+
}
|
335
|
+
|
336
|
+
#ifdef DEBUG
|
337
|
+
int count = 0;
|
338
|
+
for(i = 0; i < EBB_MAX_CLIENTS; i++)
|
339
|
+
if(server->clients[i].open) count += 1;
|
340
|
+
g_debug("%d open connections", count);
|
341
|
+
#endif
|
342
|
+
client->open = TRUE;
|
343
|
+
client->server = server;
|
344
|
+
|
345
|
+
/* DO SOCKET STUFF */
|
346
|
+
socklen_t len;
|
347
|
+
client->fd = accept(server->fd, (struct sockaddr*)&(server->sockaddr), &len);
|
348
|
+
assert(client->fd >= 0);
|
349
|
+
int flags = fcntl(client->fd, F_GETFL, 0);
|
350
|
+
assert(0 <= fcntl(client->fd, F_SETFL, flags | O_NONBLOCK));
|
351
|
+
|
352
|
+
/* INITIALIZE http_parser */
|
353
|
+
http_parser_init(&(client->parser));
|
354
|
+
client->parser.data = client;
|
355
|
+
client->parser.http_field = http_field_cb;
|
356
|
+
client->parser.request_method = request_method_cb;
|
357
|
+
client->parser.request_uri = request_uri_cb;
|
358
|
+
client->parser.fragment = fragment_cb;
|
359
|
+
client->parser.request_path = request_path_cb;
|
360
|
+
client->parser.query_string = query_string_cb;
|
361
|
+
client->parser.http_version = http_version_cb;
|
362
|
+
client->parser.content_length = content_length_cb;
|
363
|
+
|
364
|
+
/* OTHER */
|
365
|
+
client->env_size = 0;
|
366
|
+
client->read = client->nread_from_body = 0;
|
367
|
+
client->response_buffer->len = 0; /* see note in ebb_client_close */
|
368
|
+
client->content_length = 0;
|
369
|
+
|
370
|
+
/* SETUP READ AND TIMEOUT WATCHERS */
|
371
|
+
client->read_watcher.data = client;
|
372
|
+
ev_init(&client->read_watcher, on_readable);
|
373
|
+
ev_io_set(&client->read_watcher, client->fd, EV_READ | EV_ERROR);
|
374
|
+
ev_io_start(server->loop, &client->read_watcher);
|
375
|
+
|
376
|
+
client->timeout_watcher.data = client;
|
377
|
+
ev_timer_init(&client->timeout_watcher, on_timeout, EBB_TIMEOUT, EBB_TIMEOUT);
|
378
|
+
ev_timer_start(server->loop, &client->timeout_watcher);
|
379
|
+
}
|
380
|
+
|
381
|
+
|
382
|
+
ebb_server* ebb_server_alloc()
|
383
|
+
{
|
384
|
+
ebb_server *server = g_new0(ebb_server, 1);
|
385
|
+
return server;
|
386
|
+
}
|
387
|
+
|
388
|
+
|
389
|
+
void ebb_server_init( ebb_server *server
|
390
|
+
, struct ev_loop *loop
|
391
|
+
, ebb_request_cb request_cb
|
392
|
+
, void *request_cb_data
|
393
|
+
)
|
394
|
+
{
|
395
|
+
int i;
|
396
|
+
for(i=0; i < EBB_MAX_CLIENTS; i++)
|
397
|
+
server->clients[i].response_buffer = g_string_new("");
|
398
|
+
|
399
|
+
server->request_cb = request_cb;
|
400
|
+
server->request_cb_data = request_cb_data;
|
401
|
+
server->loop = loop;
|
402
|
+
server->open = FALSE;
|
403
|
+
server->fd = -1;
|
404
|
+
return;
|
405
|
+
error:
|
406
|
+
ebb_server_free(server);
|
407
|
+
return;
|
408
|
+
}
|
409
|
+
|
410
|
+
|
411
|
+
void ebb_server_free(ebb_server *server)
|
412
|
+
{
|
413
|
+
ebb_server_unlisten(server);
|
414
|
+
|
415
|
+
int i;
|
416
|
+
for(i=0; i < EBB_MAX_CLIENTS; i++)
|
417
|
+
g_string_free(server->clients[i].response_buffer, TRUE);
|
418
|
+
if(server->port)
|
419
|
+
free(server->port);
|
420
|
+
if(server->socketpath)
|
421
|
+
free(server->socketpath);
|
422
|
+
free(server);
|
423
|
+
}
|
424
|
+
|
425
|
+
|
426
|
+
void ebb_server_unlisten(ebb_server *server)
|
427
|
+
{
|
428
|
+
if(server->open) {
|
429
|
+
//g_message("Stopping Ebb server");
|
430
|
+
int i;
|
431
|
+
ebb_client *client;
|
432
|
+
//for(i=0; i < EBB_MAX_CLIENTS; i++)
|
433
|
+
// ebb_client_close(client);
|
434
|
+
ev_io_stop(server->loop, &server->request_watcher);
|
435
|
+
close(server->fd);
|
436
|
+
if(server->socketpath)
|
437
|
+
unlink(server->socketpath);
|
438
|
+
server->open = FALSE;
|
439
|
+
}
|
440
|
+
}
|
441
|
+
|
442
|
+
|
443
|
+
void ebb_server_listen(ebb_server *server)
|
444
|
+
{
|
445
|
+
int r = listen(server->fd, EBB_MAX_CLIENTS);
|
446
|
+
assert(r >= 0);
|
447
|
+
assert(server->open == FALSE);
|
448
|
+
server->open = TRUE;
|
449
|
+
|
450
|
+
server->request_watcher.data = server;
|
451
|
+
ev_init (&server->request_watcher, on_request);
|
452
|
+
ev_io_set (&server->request_watcher, server->fd, EV_READ | EV_ERROR);
|
453
|
+
ev_io_start (server->loop, &server->request_watcher);
|
454
|
+
}
|
455
|
+
|
456
|
+
|
457
|
+
int ebb_server_listen_on_port(ebb_server *server, const int port)
|
458
|
+
{
|
459
|
+
int fd = server_socket(port);
|
460
|
+
if(fd < 0) return 0;
|
461
|
+
server->port = malloc(sizeof(char)*8); /* for easy access to the port */
|
462
|
+
sprintf(server->port, "%d", port);
|
463
|
+
server->fd = fd;
|
464
|
+
ebb_server_listen(server);
|
465
|
+
return fd;
|
466
|
+
}
|
467
|
+
|
468
|
+
|
469
|
+
int ebb_server_listen_on_socket(ebb_server *server, const char *socketpath)
|
470
|
+
{
|
471
|
+
int fd = server_socket_unix(socketpath, 0755);
|
472
|
+
if(fd < 0) return 0;
|
473
|
+
server->socketpath = strdup(socketpath);
|
474
|
+
server->fd = fd;
|
475
|
+
ebb_server_listen(server);
|
476
|
+
return fd;
|
477
|
+
}
|
478
|
+
|
479
|
+
|
480
|
+
void ebb_client_close(ebb_client *client)
|
481
|
+
{
|
482
|
+
if(client->open) {
|
483
|
+
ev_io_stop(client->server->loop, &client->read_watcher);
|
484
|
+
ev_io_stop(client->server->loop, &client->write_watcher);
|
485
|
+
ev_timer_stop(client->server->loop, &client->timeout_watcher);
|
486
|
+
|
487
|
+
if(client->upload_file) {
|
488
|
+
fclose(client->upload_file);
|
489
|
+
unlink(client->upload_file_filename);
|
490
|
+
}
|
491
|
+
|
492
|
+
/* here we do not free the already allocated GString client->response_buffer
|
493
|
+
* that we're holding the response in. we reuse it again -
|
494
|
+
* presumably because the backend is going to keep sending such long
|
495
|
+
* requests.
|
496
|
+
*/
|
497
|
+
client->response_buffer->len = 0;
|
498
|
+
|
499
|
+
close(client->fd);
|
500
|
+
client->open = FALSE;
|
501
|
+
}
|
502
|
+
}
|
503
|
+
|
504
|
+
|
505
|
+
void on_client_writable(struct ev_loop *loop, ev_io *watcher, int revents)
|
506
|
+
{
|
507
|
+
ebb_client *client = (ebb_client*)(watcher->data);
|
508
|
+
ssize_t sent;
|
509
|
+
|
510
|
+
if(EV_ERROR & revents) {
|
511
|
+
g_message("on_client_writable() got error event, closing peer");
|
512
|
+
return;
|
513
|
+
}
|
514
|
+
|
515
|
+
//if(client->written != 0)
|
516
|
+
// g_debug("total written: %d", (int)(client->written));
|
517
|
+
|
518
|
+
sent = send( client->fd
|
519
|
+
, client->response_buffer->str + sizeof(gchar)*(client->written)
|
520
|
+
, client->response_buffer->len - client->written
|
521
|
+
, 0
|
522
|
+
);
|
523
|
+
if(sent < 0) {
|
524
|
+
#ifdef DEBUG
|
525
|
+
g_message("Error writing: %s", strerror(errno));
|
526
|
+
#endif
|
527
|
+
ebb_client_close(client);
|
528
|
+
return;
|
529
|
+
}
|
530
|
+
client->written += sent;
|
531
|
+
|
532
|
+
assert(client->written <= client->response_buffer->len);
|
533
|
+
//g_message("wrote %d bytes. total: %d", (int)sent, (int)(client->written));
|
534
|
+
|
535
|
+
ev_timer_again(loop, &(client->timeout_watcher));
|
536
|
+
|
537
|
+
if(client->written == client->response_buffer->len)
|
538
|
+
ebb_client_close(client);
|
539
|
+
}
|
540
|
+
|
541
|
+
|
542
|
+
void ebb_client_write(ebb_client *client, const char *data, int length)
|
543
|
+
{
|
544
|
+
g_string_append_len(client->response_buffer, data, length);
|
545
|
+
}
|
546
|
+
|
547
|
+
|
548
|
+
void ebb_client_finished(ebb_client *client)
|
549
|
+
{
|
550
|
+
assert(client->open);
|
551
|
+
assert(FALSE == ev_is_active(&(client->write_watcher)));
|
552
|
+
|
553
|
+
/* assure the socket is still in non-blocking mode
|
554
|
+
* in the ruby binding, for example, i change this flag
|
555
|
+
*/
|
556
|
+
int flags = fcntl(client->fd, F_GETFL, 0);
|
557
|
+
if(0 > fcntl(client->fd, F_SETFL, flags | O_NONBLOCK)) {
|
558
|
+
perror("fcntl()");
|
559
|
+
ebb_client_close(client);
|
560
|
+
return;
|
561
|
+
}
|
562
|
+
|
563
|
+
client->written = 0;
|
564
|
+
client->write_watcher.data = client;
|
565
|
+
ev_init (&(client->write_watcher), on_client_writable);
|
566
|
+
ev_io_set (&(client->write_watcher), client->fd, EV_WRITE | EV_ERROR);
|
567
|
+
ev_io_start(client->server->loop, &(client->write_watcher));
|
568
|
+
}
|
569
|
+
|
570
|
+
|
571
|
+
/* pass an allocated buffer and the length to read. this function will try to
|
572
|
+
* fill the buffer with that length of data read from the body of the request.
|
573
|
+
* the return value says how much was actually written.
|
574
|
+
*/
|
575
|
+
int ebb_client_read(ebb_client *client, char *buffer, int length)
|
576
|
+
{
|
577
|
+
size_t read;
|
578
|
+
|
579
|
+
assert(client->open);
|
580
|
+
assert(client_finished_parsing);
|
581
|
+
|
582
|
+
if(client->upload_file) {
|
583
|
+
read = fread(buffer, 1, length, client->upload_file);
|
584
|
+
/* TODO error checking! */
|
585
|
+
return read;
|
586
|
+
} else {
|
587
|
+
char* request_body = client->request_buffer + client->parser.nread;
|
588
|
+
|
589
|
+
read = ramp(min(length, client->content_length - client->nread_from_body));
|
590
|
+
memcpy( buffer
|
591
|
+
, request_body + client->nread_from_body
|
592
|
+
, read
|
593
|
+
);
|
594
|
+
client->nread_from_body += read;
|
595
|
+
return read;
|
596
|
+
}
|
597
|
+
}
|
598
|
+
|
599
|
+
/* The following socket creation routines are modified and stolen from memcached */
|
600
|
+
|
601
|
+
static int server_socket(const int port) {
|
602
|
+
int sfd;
|
603
|
+
struct linger ling = {0, 0};
|
604
|
+
struct sockaddr_in addr;
|
605
|
+
int flags =1;
|
606
|
+
|
607
|
+
if ((sfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
|
608
|
+
perror("socket()");
|
609
|
+
return -1;
|
610
|
+
}
|
611
|
+
|
612
|
+
if ((flags = fcntl(sfd, F_GETFL, 0)) < 0 ||
|
613
|
+
fcntl(sfd, F_SETFL, flags | O_NONBLOCK) < 0) {
|
614
|
+
perror("setting O_NONBLOCK");
|
615
|
+
close(sfd);
|
616
|
+
return -1;
|
617
|
+
}
|
618
|
+
|
619
|
+
flags = 1;
|
620
|
+
setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, (void *)&flags, sizeof(flags));
|
621
|
+
setsockopt(sfd, SOL_SOCKET, SO_KEEPALIVE, (void *)&flags, sizeof(flags));
|
622
|
+
setsockopt(sfd, SOL_SOCKET, SO_LINGER, (void *)&ling, sizeof(ling));
|
623
|
+
setsockopt(sfd, IPPROTO_TCP, TCP_NODELAY, (void *)&flags, sizeof(flags));
|
624
|
+
|
625
|
+
/*
|
626
|
+
* the memset call clears nonstandard fields in some impementations
|
627
|
+
* that otherwise mess things up.
|
628
|
+
*/
|
629
|
+
memset(&addr, 0, sizeof(addr));
|
630
|
+
|
631
|
+
addr.sin_family = AF_INET;
|
632
|
+
addr.sin_port = htons(port);
|
633
|
+
addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
634
|
+
|
635
|
+
if (bind(sfd, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
|
636
|
+
perror("bind()");
|
637
|
+
close(sfd);
|
638
|
+
return -1;
|
639
|
+
}
|
640
|
+
if (listen(sfd, EBB_MAX_CLIENTS) == -1) {
|
641
|
+
perror("listen()");
|
642
|
+
close(sfd);
|
643
|
+
return -1;
|
644
|
+
}
|
645
|
+
return sfd;
|
646
|
+
}
|
647
|
+
|
648
|
+
|
649
|
+
static int server_socket_unix(const char *path, int access_mask) {
|
650
|
+
int sfd;
|
651
|
+
struct linger ling = {0, 0};
|
652
|
+
struct sockaddr_un addr;
|
653
|
+
struct stat tstat;
|
654
|
+
int flags =1;
|
655
|
+
int old_umask;
|
656
|
+
|
657
|
+
if (!path) {
|
658
|
+
return -1;
|
659
|
+
}
|
660
|
+
|
661
|
+
if ((sfd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
|
662
|
+
perror("socket()");
|
663
|
+
return -1;
|
664
|
+
}
|
665
|
+
|
666
|
+
if ((flags = fcntl(sfd, F_GETFL, 0)) < 0 ||
|
667
|
+
fcntl(sfd, F_SETFL, flags | O_NONBLOCK) < 0) {
|
668
|
+
perror("setting O_NONBLOCK");
|
669
|
+
close(sfd);
|
670
|
+
return -1;
|
671
|
+
}
|
672
|
+
|
673
|
+
/*
|
674
|
+
* Clean up a previous socket file if we left it around
|
675
|
+
*/
|
676
|
+
if (lstat(path, &tstat) == 0) {
|
677
|
+
if (S_ISSOCK(tstat.st_mode))
|
678
|
+
unlink(path);
|
679
|
+
}
|
680
|
+
|
681
|
+
flags = 1;
|
682
|
+
setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, (void *)&flags, sizeof(flags));
|
683
|
+
setsockopt(sfd, SOL_SOCKET, SO_KEEPALIVE, (void *)&flags, sizeof(flags));
|
684
|
+
setsockopt(sfd, SOL_SOCKET, SO_LINGER, (void *)&ling, sizeof(ling));
|
685
|
+
|
686
|
+
/*
|
687
|
+
* the memset call clears nonstandard fields in some impementations
|
688
|
+
* that otherwise mess things up.
|
689
|
+
*/
|
690
|
+
memset(&addr, 0, sizeof(addr));
|
691
|
+
|
692
|
+
addr.sun_family = AF_UNIX;
|
693
|
+
strcpy(addr.sun_path, path);
|
694
|
+
old_umask=umask( ~(access_mask&0777));
|
695
|
+
if (bind(sfd, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
|
696
|
+
perror("bind()");
|
697
|
+
close(sfd);
|
698
|
+
umask(old_umask);
|
699
|
+
return -1;
|
700
|
+
}
|
701
|
+
umask(old_umask);
|
702
|
+
if (listen(sfd, EBB_MAX_CLIENTS) == -1) {
|
703
|
+
perror("listen()");
|
704
|
+
close(sfd);
|
705
|
+
return -1;
|
706
|
+
}
|
707
|
+
return sfd;
|
708
|
+
}
|
709
|
+
|