bossan 0.1.6 → 0.1.7
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/.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
|