bossan 0.1.6 → 0.1.7
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +23 -0
- data/.travis.yml +4 -0
- data/Gemfile +6 -0
- data/README.md +52 -0
- data/Rakefile +22 -0
- data/bossan.gemspec +20 -0
- data/examples/blog/README.md +19 -0
- data/examples/blog/app.rb +77 -0
- data/examples/blog/config.ru +4 -0
- data/examples/blog/public/style.css +18 -0
- data/examples/blog/views/login.erb +23 -0
- data/examples/blog/views/show_entries.erb +38 -0
- data/examples/config.ru +19 -0
- data/examples/hello.rb +13 -0
- data/examples/sinatra_app.rb +11 -0
- data/examples/views/index.haml +10 -0
- data/examples/views_sample.rb +11 -0
- data/ext/bossan/bossan_ext.c +20 -222
- data/ext/bossan/buffer.c +74 -0
- data/ext/bossan/buffer.h +43 -0
- data/ext/bossan/client.h +35 -0
- data/ext/bossan/extconf.rb +11 -0
- data/ext/bossan/picoev_kqueue.c +209 -0
- data/ext/bossan/request.c +62 -0
- data/ext/bossan/request.h +54 -0
- data/lib/bossan/version.rb +1 -1
- data/lib/rack/handler/bossan.rb +1 -1
- data/test/test_rack_env_query.rb +57 -0
- data/test/test_rack_env_simple.rb +60 -0
- data/test/test_rack_spec.rb +65 -0
- metadata +35 -9
data/ext/bossan/buffer.c
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
#include "buffer.h"
|
2
|
+
|
3
|
+
#define LIMIT_MAX 1024 * 1024 * 1024
|
4
|
+
|
5
|
+
buffer *
|
6
|
+
new_buffer(size_t buf_size, size_t limit)
|
7
|
+
{
|
8
|
+
buffer *buf;
|
9
|
+
buf = ruby_xmalloc(sizeof(buffer));
|
10
|
+
memset(buf, 0, sizeof(buffer));
|
11
|
+
buf->buf = ruby_xmalloc(sizeof(char) * buf_size);
|
12
|
+
buf->buf_size = buf_size;
|
13
|
+
if(limit){
|
14
|
+
buf->limit = limit;
|
15
|
+
}else{
|
16
|
+
buf->limit = LIMIT_MAX;
|
17
|
+
}
|
18
|
+
return buf;
|
19
|
+
}
|
20
|
+
|
21
|
+
|
22
|
+
buffer_result
|
23
|
+
write2buf(buffer *buf, const char *c, size_t l)
|
24
|
+
{
|
25
|
+
size_t newl;
|
26
|
+
char *newbuf;
|
27
|
+
buffer_result ret = WRITE_OK;
|
28
|
+
newl = buf->len + l;
|
29
|
+
|
30
|
+
if (newl >= buf->buf_size) {
|
31
|
+
buf->buf_size *= 2;
|
32
|
+
if(buf->buf_size <= newl) {
|
33
|
+
buf->buf_size = (int)(newl + 1);
|
34
|
+
}
|
35
|
+
if(buf->buf_size > buf->limit){
|
36
|
+
buf->buf_size = buf->limit + 1;
|
37
|
+
}
|
38
|
+
newbuf = (char*)ruby_xrealloc(buf->buf, buf->buf_size);
|
39
|
+
buf->buf = newbuf;
|
40
|
+
}
|
41
|
+
if(newl >= buf->buf_size){
|
42
|
+
l = buf->buf_size - buf->len -1;
|
43
|
+
ret = LIMIT_OVER;
|
44
|
+
}
|
45
|
+
memcpy(buf->buf + buf->len, c , l);
|
46
|
+
buf->len += (int)l;
|
47
|
+
return ret;
|
48
|
+
}
|
49
|
+
|
50
|
+
|
51
|
+
void
|
52
|
+
free_buffer(buffer *buf)
|
53
|
+
{
|
54
|
+
ruby_xfree(buf->buf);
|
55
|
+
ruby_xfree(buf);
|
56
|
+
}
|
57
|
+
|
58
|
+
|
59
|
+
VALUE
|
60
|
+
getRbString(buffer *buf)
|
61
|
+
{
|
62
|
+
VALUE o;
|
63
|
+
o = rb_str_new(buf->buf, buf->len);
|
64
|
+
free_buffer(buf);
|
65
|
+
return o;
|
66
|
+
}
|
67
|
+
|
68
|
+
|
69
|
+
char *
|
70
|
+
getString(buffer *buf)
|
71
|
+
{
|
72
|
+
buf->buf[buf->len] = '\0';
|
73
|
+
return buf->buf;
|
74
|
+
}
|
data/ext/bossan/buffer.h
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
#ifndef BUFFER_H
|
2
|
+
#define BUFFER_H
|
3
|
+
|
4
|
+
#include "ruby.h"
|
5
|
+
#include <stdio.h>
|
6
|
+
#include <stdlib.h>
|
7
|
+
#include <string.h>
|
8
|
+
#include <inttypes.h>
|
9
|
+
|
10
|
+
typedef enum {
|
11
|
+
WRITE_OK,
|
12
|
+
MEMORY_ERROR,
|
13
|
+
LIMIT_OVER,
|
14
|
+
} buffer_result;
|
15
|
+
|
16
|
+
typedef struct {
|
17
|
+
char *buf;
|
18
|
+
size_t buf_size;
|
19
|
+
size_t len;
|
20
|
+
size_t limit;
|
21
|
+
} buffer;
|
22
|
+
|
23
|
+
buffer *
|
24
|
+
new_buffer(size_t buf_size, size_t limit);
|
25
|
+
|
26
|
+
buffer_result
|
27
|
+
write2buf(buffer *buf, const char *c, size_t l);
|
28
|
+
|
29
|
+
void
|
30
|
+
free_buffer(buffer *buf);
|
31
|
+
|
32
|
+
VALUE
|
33
|
+
getRbString(buffer *buf);
|
34
|
+
|
35
|
+
char *
|
36
|
+
getString(buffer *buf);
|
37
|
+
|
38
|
+
|
39
|
+
#endif
|
40
|
+
|
41
|
+
|
42
|
+
|
43
|
+
|
data/ext/bossan/client.h
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
#ifndef CLIENT_H
|
2
|
+
#define CLIENT_H
|
3
|
+
|
4
|
+
#include "request.h"
|
5
|
+
|
6
|
+
typedef struct _client {
|
7
|
+
int fd;
|
8
|
+
char *remote_addr;
|
9
|
+
uint32_t remote_port;
|
10
|
+
uint8_t keep_alive;
|
11
|
+
request *req;
|
12
|
+
uint32_t body_length;
|
13
|
+
int body_readed;
|
14
|
+
void *body;
|
15
|
+
int bad_request_code;
|
16
|
+
request_body_type body_type;
|
17
|
+
uint8_t complete;
|
18
|
+
|
19
|
+
http_parser *http; // http req parser
|
20
|
+
VALUE environ; // rack environ
|
21
|
+
int status_code; // response status code
|
22
|
+
|
23
|
+
VALUE http_status; // response status line
|
24
|
+
VALUE headers; // http response headers
|
25
|
+
uint8_t header_done; // header write status
|
26
|
+
VALUE response; // rack response object
|
27
|
+
VALUE response_iter; // rack response object
|
28
|
+
uint8_t content_length_set; // content_length_set flag
|
29
|
+
uint32_t content_length; // content_length
|
30
|
+
uint32_t write_bytes; // send body length
|
31
|
+
void *bucket; //write_data
|
32
|
+
uint8_t response_closed; //response closed flag
|
33
|
+
} client_t;
|
34
|
+
|
35
|
+
#endif
|
data/ext/bossan/extconf.rb
CHANGED
@@ -10,4 +10,15 @@ Dir.chdir bossan_dir
|
|
10
10
|
|
11
11
|
dir_config bossan_dir
|
12
12
|
|
13
|
+
srcs = Dir.glob("*.c")
|
14
|
+
if have_header("sys/epoll.h")
|
15
|
+
srcs.delete("picoev_kqueue.c")
|
16
|
+
elsif have_header("sys/event.h")
|
17
|
+
srcs.delete("picoev_epoll.c")
|
18
|
+
else
|
19
|
+
$stderr.puts "error: not found kqueue or epoll on your system."
|
20
|
+
exit 1
|
21
|
+
end
|
22
|
+
$srcs = srcs
|
23
|
+
|
13
24
|
create_makefile "bossan/bossan_ext"
|
@@ -0,0 +1,209 @@
|
|
1
|
+
/*
|
2
|
+
* Copyright (c) 2009, Cybozu Labs, Inc.
|
3
|
+
* All rights reserved.
|
4
|
+
*
|
5
|
+
* Redistribution and use in source and binary forms, with or without
|
6
|
+
* modification, are permitted provided that the following conditions are met:
|
7
|
+
*
|
8
|
+
* * Redistributions of source code must retain the above copyright notice,
|
9
|
+
* this list of conditions and the following disclaimer.
|
10
|
+
* * Redistributions in binary form must reproduce the above copyright notice,
|
11
|
+
* this list of conditions and the following disclaimer in the documentation
|
12
|
+
* and/or other materials provided with the distribution.
|
13
|
+
* * Neither the name of the <ORGANIZATION> nor the names of its contributors
|
14
|
+
* may be used to endorse or promote products derived from this software
|
15
|
+
* without specific prior written permission.
|
16
|
+
*
|
17
|
+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
18
|
+
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
19
|
+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
20
|
+
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
21
|
+
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
22
|
+
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
23
|
+
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
24
|
+
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
25
|
+
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
26
|
+
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
27
|
+
* POSSIBILITY OF SUCH DAMAGE.
|
28
|
+
*/
|
29
|
+
|
30
|
+
#include <errno.h>
|
31
|
+
#include <sys/types.h>
|
32
|
+
#include <sys/event.h>
|
33
|
+
#include <sys/time.h>
|
34
|
+
#include <unistd.h>
|
35
|
+
#include "picoev.h"
|
36
|
+
|
37
|
+
#define EV_QUEUE_SZ 128
|
38
|
+
|
39
|
+
#define BACKEND_BUILD(next_fd, events) \
|
40
|
+
((unsigned)((next_fd << 8) | (events & 0xff)))
|
41
|
+
#define BACKEND_GET_NEXT_FD(backend) ((int)(backend) >> 8)
|
42
|
+
#define BACKEND_GET_OLD_EVENTS(backend) ((int)(backend) & 0xff)
|
43
|
+
|
44
|
+
typedef struct picoev_loop_kqueue_st {
|
45
|
+
picoev_loop loop;
|
46
|
+
int kq;
|
47
|
+
int changed_fds; /* link list using picoev_fd::_backend, -1 if not changed */
|
48
|
+
struct kevent events[1024];
|
49
|
+
struct kevent changelist[256];
|
50
|
+
} picoev_loop_kqueue;
|
51
|
+
|
52
|
+
picoev_globals picoev;
|
53
|
+
|
54
|
+
static int apply_pending_changes(picoev_loop_kqueue* loop, int apply_all)
|
55
|
+
{
|
56
|
+
#define SET(op, events) \
|
57
|
+
EV_SET(loop->changelist + cl_off++, loop->changed_fds, \
|
58
|
+
(((events) & PICOEV_READ) != 0 ? EVFILT_READ : 0) \
|
59
|
+
| (((events) & PICOEV_WRITE) != 0 ? EVFILT_WRITE : 0), \
|
60
|
+
(op), 0, 0, NULL)
|
61
|
+
|
62
|
+
int cl_off = 0, nevents;
|
63
|
+
|
64
|
+
while (loop->changed_fds != -1) {
|
65
|
+
picoev_fd* changed = picoev.fds + loop->changed_fds;
|
66
|
+
int old_events = BACKEND_GET_OLD_EVENTS(changed->_backend);
|
67
|
+
if (changed->events != old_events) {
|
68
|
+
if (old_events != 0) {
|
69
|
+
SET(EV_DISABLE, old_events);
|
70
|
+
}
|
71
|
+
if (changed->events != 0) {
|
72
|
+
SET(EV_ADD | EV_ENABLE, changed->events);
|
73
|
+
}
|
74
|
+
if ((size_t)cl_off + 1
|
75
|
+
>= sizeof(loop->changelist) / sizeof(loop->changelist[0])) {
|
76
|
+
nevents = kevent(loop->kq, loop->changelist, cl_off, NULL, 0, NULL);
|
77
|
+
assert(nevents == 0);
|
78
|
+
cl_off = 0;
|
79
|
+
}
|
80
|
+
}
|
81
|
+
loop->changed_fds = BACKEND_GET_NEXT_FD(changed->_backend);
|
82
|
+
changed->_backend = -1;
|
83
|
+
}
|
84
|
+
|
85
|
+
if (apply_all && cl_off != 0) {
|
86
|
+
nevents = kevent(loop->kq, loop->changelist, cl_off, NULL, 0, NULL);
|
87
|
+
assert(nevents == 0);
|
88
|
+
cl_off = 0;
|
89
|
+
}
|
90
|
+
|
91
|
+
return cl_off;
|
92
|
+
|
93
|
+
#undef SET
|
94
|
+
}
|
95
|
+
|
96
|
+
picoev_loop* picoev_create_loop(int max_timeout)
|
97
|
+
{
|
98
|
+
picoev_loop_kqueue* loop;
|
99
|
+
|
100
|
+
/* init parent */
|
101
|
+
assert(PICOEV_IS_INITED);
|
102
|
+
if ((loop = (picoev_loop_kqueue*)malloc(sizeof(picoev_loop_kqueue)))
|
103
|
+
== NULL) {
|
104
|
+
return NULL;
|
105
|
+
}
|
106
|
+
if (picoev_init_loop_internal(&loop->loop, max_timeout) != 0) {
|
107
|
+
free(loop);
|
108
|
+
return NULL;
|
109
|
+
}
|
110
|
+
|
111
|
+
/* init kqueue */
|
112
|
+
if ((loop->kq = kqueue()) == -1) {
|
113
|
+
picoev_deinit_loop_internal(&loop->loop);
|
114
|
+
free(loop);
|
115
|
+
return NULL;
|
116
|
+
}
|
117
|
+
loop->changed_fds = -1;
|
118
|
+
|
119
|
+
loop->loop.now = time(NULL);
|
120
|
+
return &loop->loop;
|
121
|
+
}
|
122
|
+
|
123
|
+
int picoev_destroy_loop(picoev_loop* _loop)
|
124
|
+
{
|
125
|
+
picoev_loop_kqueue* loop = (picoev_loop_kqueue*)_loop;
|
126
|
+
|
127
|
+
if (close(loop->kq) != 0) {
|
128
|
+
return -1;
|
129
|
+
}
|
130
|
+
picoev_deinit_loop_internal(&loop->loop);
|
131
|
+
free(loop);
|
132
|
+
return 0;
|
133
|
+
}
|
134
|
+
|
135
|
+
int picoev_update_events_internal(picoev_loop* _loop, int fd, int events)
|
136
|
+
{
|
137
|
+
picoev_loop_kqueue* loop = (picoev_loop_kqueue*)_loop;
|
138
|
+
picoev_fd* target = picoev.fds + fd;
|
139
|
+
|
140
|
+
assert(PICOEV_FD_BELONGS_TO_LOOP(&loop->loop, fd));
|
141
|
+
|
142
|
+
/* initialize if adding the fd */
|
143
|
+
if ((events & PICOEV_ADD) != 0) {
|
144
|
+
target->_backend = -1;
|
145
|
+
}
|
146
|
+
/* return if nothing to do */
|
147
|
+
if (events == PICOEV_DEL
|
148
|
+
? target->_backend == -1
|
149
|
+
: (events & PICOEV_READWRITE) == target->events) {
|
150
|
+
return 0;
|
151
|
+
}
|
152
|
+
/* add to changed list if not yet being done */
|
153
|
+
if (target->_backend == -1) {
|
154
|
+
target->_backend = BACKEND_BUILD(loop->changed_fds, target->events);
|
155
|
+
loop->changed_fds = fd;
|
156
|
+
}
|
157
|
+
/* update events */
|
158
|
+
target->events = events & PICOEV_READWRITE;
|
159
|
+
/* apply immediately if is a DELETE */
|
160
|
+
if ((events & PICOEV_DEL) != 0) {
|
161
|
+
apply_pending_changes(loop, 1);
|
162
|
+
}
|
163
|
+
|
164
|
+
return 0;
|
165
|
+
}
|
166
|
+
|
167
|
+
int picoev_poll_once_internal(picoev_loop* _loop, int max_wait)
|
168
|
+
{
|
169
|
+
picoev_loop_kqueue* loop = (picoev_loop_kqueue*)_loop;
|
170
|
+
struct timespec ts;
|
171
|
+
int cl_off = 0, nevents, i;
|
172
|
+
|
173
|
+
/* apply pending changes, with last changes stored to loop->changelist */
|
174
|
+
cl_off = apply_pending_changes(loop, 0);
|
175
|
+
|
176
|
+
ts.tv_sec = max_wait;
|
177
|
+
ts.tv_nsec = 0;
|
178
|
+
nevents = kevent(loop->kq, loop->changelist, cl_off, loop->events,
|
179
|
+
sizeof(loop->events) / sizeof(loop->events[0]), &ts);
|
180
|
+
if (nevents == -1) {
|
181
|
+
/* the errors we can only rescue */
|
182
|
+
assert(errno == EACCES || errno == EFAULT || errno == EINTR);
|
183
|
+
return -1;
|
184
|
+
}
|
185
|
+
for (i = 0; i < nevents; ++i) {
|
186
|
+
struct kevent* event = loop->events + i;
|
187
|
+
picoev_fd* target = picoev.fds + event->ident;
|
188
|
+
assert((event->flags & EV_ERROR) == 0); /* changelist errors are fatal */
|
189
|
+
if (loop->loop.loop_id == target->loop_id
|
190
|
+
&& (event->filter & (EVFILT_READ | EVFILT_WRITE)) != 0) {
|
191
|
+
int revents;
|
192
|
+
switch (event->filter) {
|
193
|
+
case EVFILT_READ:
|
194
|
+
revents = PICOEV_READ;
|
195
|
+
break;
|
196
|
+
case EVFILT_WRITE:
|
197
|
+
revents = PICOEV_WRITE;
|
198
|
+
break;
|
199
|
+
default:
|
200
|
+
assert(0);
|
201
|
+
revents = 0; // suppress compiler warning
|
202
|
+
break;
|
203
|
+
}
|
204
|
+
(*target->callback)(&loop->loop, event->ident, revents, target->cb_arg);
|
205
|
+
}
|
206
|
+
}
|
207
|
+
|
208
|
+
return 0;
|
209
|
+
}
|
@@ -0,0 +1,62 @@
|
|
1
|
+
#include "request.h"
|
2
|
+
|
3
|
+
|
4
|
+
request *
|
5
|
+
new_request(void)
|
6
|
+
{
|
7
|
+
request *req = (request *)ruby_xmalloc(sizeof(request));
|
8
|
+
memset(req, 0, sizeof(request));
|
9
|
+
return req;
|
10
|
+
}
|
11
|
+
|
12
|
+
|
13
|
+
header *
|
14
|
+
new_header(size_t fsize, size_t flimit, size_t vsize, size_t vlimit)
|
15
|
+
{
|
16
|
+
header *h;
|
17
|
+
h = ruby_xmalloc(sizeof(header));
|
18
|
+
h->field = new_buffer(fsize, flimit);
|
19
|
+
h->value = new_buffer(vsize, vlimit);
|
20
|
+
return h;
|
21
|
+
}
|
22
|
+
|
23
|
+
|
24
|
+
void
|
25
|
+
free_header(header *h)
|
26
|
+
{
|
27
|
+
ruby_xfree(h);
|
28
|
+
}
|
29
|
+
|
30
|
+
|
31
|
+
void
|
32
|
+
free_request(request *req)
|
33
|
+
{
|
34
|
+
uint32_t i;
|
35
|
+
header *h;
|
36
|
+
if(req->path){
|
37
|
+
free_buffer(req->path);
|
38
|
+
req->path = NULL;
|
39
|
+
}
|
40
|
+
if(req->uri){
|
41
|
+
free_buffer(req->uri);
|
42
|
+
req->uri = NULL;
|
43
|
+
}
|
44
|
+
if(req->query_string){
|
45
|
+
free_buffer(req->query_string);
|
46
|
+
req->query_string = NULL;
|
47
|
+
}
|
48
|
+
if(req->fragment){
|
49
|
+
free_buffer(req->fragment);
|
50
|
+
req->fragment = NULL;
|
51
|
+
}
|
52
|
+
for(i = 0; i < req->num_headers+1; i++){
|
53
|
+
h = req->headers[i];
|
54
|
+
if(h){
|
55
|
+
free_buffer(h->field);
|
56
|
+
free_buffer(h->value);
|
57
|
+
free_header(h);
|
58
|
+
req->headers[i] = NULL;
|
59
|
+
}
|
60
|
+
}
|
61
|
+
ruby_xfree(req);
|
62
|
+
}
|
@@ -0,0 +1,54 @@
|
|
1
|
+
#ifndef REQUEST_H
|
2
|
+
#define REQUEST_H
|
3
|
+
|
4
|
+
#include <inttypes.h>
|
5
|
+
#include "buffer.h"
|
6
|
+
|
7
|
+
#define LIMIT_PATH 1024 * 4
|
8
|
+
#define LIMIT_FRAGMENT 1024
|
9
|
+
#define LIMIT_URI 1024 * 4
|
10
|
+
#define LIMIT_QUERY_STRING 1024 * 8
|
11
|
+
|
12
|
+
#define LIMIT_REQUEST_FIELDS 30
|
13
|
+
#define LIMIT_REQUEST_FIELD_SIZE 1024 * 8
|
14
|
+
|
15
|
+
typedef enum {
|
16
|
+
BODY_TYPE_NONE,
|
17
|
+
BODY_TYPE_TMPFILE,
|
18
|
+
BODY_TYPE_BUFFER
|
19
|
+
} request_body_type;
|
20
|
+
|
21
|
+
typedef enum {
|
22
|
+
FIELD,
|
23
|
+
VAL,
|
24
|
+
} field_type;
|
25
|
+
|
26
|
+
typedef struct {
|
27
|
+
buffer *field;
|
28
|
+
buffer *value;
|
29
|
+
} header;
|
30
|
+
|
31
|
+
typedef struct {
|
32
|
+
buffer *path;
|
33
|
+
buffer *uri;
|
34
|
+
buffer *query_string;
|
35
|
+
buffer *fragment;
|
36
|
+
header *headers[LIMIT_REQUEST_FIELDS];
|
37
|
+
uint32_t num_headers;
|
38
|
+
field_type last_header_element;
|
39
|
+
} request;
|
40
|
+
|
41
|
+
|
42
|
+
request *
|
43
|
+
new_request(void);
|
44
|
+
|
45
|
+
header *
|
46
|
+
new_header(size_t fsize, size_t flimit, size_t vsize, size_t vlimit);
|
47
|
+
|
48
|
+
void
|
49
|
+
free_header(header *h);
|
50
|
+
|
51
|
+
void
|
52
|
+
free_request(request *req);
|
53
|
+
|
54
|
+
#endif
|