bossan 0.1.8 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +15 -0
- data/.gitignore +1 -0
- data/CHANGELOG.md +13 -0
- data/README.md +2 -2
- data/Rakefile +1 -1
- data/TODO +6 -0
- data/examples/hello.rb +1 -1
- data/examples/sinatra_app.rb +1 -1
- data/examples/views_sample.rb +1 -1
- data/ext/bossan/bossan.h +75 -0
- data/ext/bossan/bossan_ext.c +221 -178
- data/ext/bossan/picoev_epoll.c +0 -2
- data/lib/bossan/version.rb +1 -1
- data/test/test_rack_spec.rb +2 -1
- metadata +8 -9
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
NTI5NjNkNTJkYmMwYmY0NDk5OGFkZDIyYzU1NzQxYWU1NGM3ODcxYw==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
NjBmZmUxYjczNTM0YzllYjRmMmI4MzM3Mzc0MjkyZmQ1MDU2MmI4Ng==
|
7
|
+
!binary "U0hBNTEy":
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
NjJjMzk1YTlhMjM4OTgxYjU4MWUwODY1ZmZhNWQzY2M3YzU4ZjA0N2U0ODIx
|
10
|
+
MzUzODU0MDYxZmM2YTBhMzlkZjVjNGMyOTU0YjY2MGJiYzlmYTI1YWZlZTZh
|
11
|
+
M2ZjNjBiZWMxZmRlZjE0YzNlNjNlNjE2ODhkZjMxMzM4MjE1MGE=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
NWQ4YTdkNmNjODVkZTgyZGQxNzdhMjk5NjViYzk1ZjliYjQwODY2OGI1MTI0
|
14
|
+
MTgxMjlkNDA2MzRkNzJlNDY4MDNmMTViODJmM2ZmNDY4MTFiNjYyNzIyYzlk
|
15
|
+
MThmNWY4Mjg4NGI5YzgyOWY1OGFhMDlhOGEzYTE2OTRkNzA1ZGE=
|
data/.gitignore
CHANGED
data/CHANGELOG.md
ADDED
data/README.md
CHANGED
@@ -8,7 +8,7 @@ Bossan is a high performance asynchronous ruby's rack-compliant web server.
|
|
8
8
|
|
9
9
|
Bossan requires Ruby 1.9.2 or later.
|
10
10
|
|
11
|
-
Bossan supports Linux, FreeBSD and
|
11
|
+
Bossan supports Linux, FreeBSD and MacOSX(need gcc>=4.2).
|
12
12
|
|
13
13
|
## Installation
|
14
14
|
|
@@ -16,7 +16,7 @@ from rubygems
|
|
16
16
|
|
17
17
|
`gem install bossan`
|
18
18
|
|
19
|
-
|
19
|
+
from source(github)
|
20
20
|
|
21
21
|
```
|
22
22
|
git clone git://github.com/kubo39/bossan.git
|
data/Rakefile
CHANGED
data/TODO
ADDED
data/examples/hello.rb
CHANGED
data/examples/sinatra_app.rb
CHANGED
data/examples/views_sample.rb
CHANGED
data/ext/bossan/bossan.h
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
#ifndef BOSSAN_H
|
2
|
+
#define BOSSAN_H
|
3
|
+
|
4
|
+
#include "ruby.h"
|
5
|
+
#include <assert.h>
|
6
|
+
#include <fcntl.h>
|
7
|
+
#include <stddef.h>
|
8
|
+
#include <unistd.h>
|
9
|
+
#include <errno.h>
|
10
|
+
#include <arpa/inet.h>
|
11
|
+
#include <signal.h>
|
12
|
+
#include <sys/socket.h>
|
13
|
+
#include <sys/types.h>
|
14
|
+
#ifdef linux
|
15
|
+
#include <sys/prctl.h>
|
16
|
+
#elif defined(__APPLE__) || defined(__FreeBSD__)
|
17
|
+
#include <sys/uio.h>
|
18
|
+
#endif
|
19
|
+
#include <sys/un.h>
|
20
|
+
#include <sys/stat.h>
|
21
|
+
#include <sys/file.h>
|
22
|
+
#include <netinet/in.h>
|
23
|
+
#include <netinet/tcp.h>
|
24
|
+
#include <netdb.h>
|
25
|
+
|
26
|
+
/* #define DEVELOP */
|
27
|
+
|
28
|
+
#ifdef DEVELOP
|
29
|
+
#define DEBUG(...) \
|
30
|
+
do { \
|
31
|
+
printf("%-40s %-26s%4u: ", __FILE__, __func__, __LINE__); \
|
32
|
+
printf(__VA_ARGS__); \
|
33
|
+
printf("\n"); \
|
34
|
+
} while(0)
|
35
|
+
#define RDEBUG(...) \
|
36
|
+
do { \
|
37
|
+
printf("\x1B[31m%-40s %-26s%4u: ", __FILE__, __func__, __LINE__); \
|
38
|
+
printf(__VA_ARGS__); \
|
39
|
+
printf("\x1B[0m\n"); \
|
40
|
+
} while(0)
|
41
|
+
#define GDEBUG(...) \
|
42
|
+
do { \
|
43
|
+
printf("\x1B[32m%-40s %-26s%4u: ", __FILE__, __func__, __LINE__); \
|
44
|
+
printf(__VA_ARGS__); \
|
45
|
+
printf("\x1B[0m\n"); \
|
46
|
+
} while(0)
|
47
|
+
#define BDEBUG(...) \
|
48
|
+
do { \
|
49
|
+
printf("\x1B[1;34m%-40s %-26s%4u: ", __FILE__, __func__, __LINE__); \
|
50
|
+
printf(__VA_ARGS__); \
|
51
|
+
printf("\x1B[0m\n"); \
|
52
|
+
} while(0)
|
53
|
+
#define YDEBUG(...) \
|
54
|
+
do { \
|
55
|
+
printf("\x1B[1;33m%-40s %-26s%4u: ", __FILE__, __func__, __LINE__); \
|
56
|
+
printf(__VA_ARGS__); \
|
57
|
+
printf("\x1B[0m\n"); \
|
58
|
+
} while(0)
|
59
|
+
#else
|
60
|
+
#define DEBUG(...) do{}while(0)
|
61
|
+
#define RDEBUG(...) do{}while(0)
|
62
|
+
#define GDEBUG(...) do{}while(0)
|
63
|
+
#define BDEBUG(...) do{}while(0)
|
64
|
+
#define YDEBUG(...) do{}while(0)
|
65
|
+
#endif
|
66
|
+
|
67
|
+
#if __GNUC__ >= 3
|
68
|
+
# define likely(x) __builtin_expect(!!(x), 1)
|
69
|
+
# define unlikely(x) __builtin_expect(!!(x), 0)
|
70
|
+
#else
|
71
|
+
# define likely(x) (x)
|
72
|
+
# define unlikely(x) (x)
|
73
|
+
#endif
|
74
|
+
|
75
|
+
#endif
|
data/ext/bossan/bossan_ext.c
CHANGED
@@ -1,25 +1,4 @@
|
|
1
|
-
#include "
|
2
|
-
#include <assert.h>
|
3
|
-
#include <fcntl.h>
|
4
|
-
#include <stddef.h>
|
5
|
-
#include <unistd.h>
|
6
|
-
#include <errno.h>
|
7
|
-
#include <arpa/inet.h>
|
8
|
-
#include <signal.h>
|
9
|
-
#include <sys/socket.h>
|
10
|
-
#include <sys/types.h>
|
11
|
-
#ifdef linux
|
12
|
-
#include <sys/sendfile.h>
|
13
|
-
#include <sys/prctl.h>
|
14
|
-
#elif defined(__APPLE__) || defined(__FreeBSD__)
|
15
|
-
#include <sys/uio.h>
|
16
|
-
#endif
|
17
|
-
#include <sys/un.h>
|
18
|
-
#include <sys/stat.h>
|
19
|
-
#include <sys/file.h>
|
20
|
-
#include <netinet/in.h>
|
21
|
-
#include <netinet/tcp.h>
|
22
|
-
#include <netdb.h>
|
1
|
+
#include "bossan.h"
|
23
2
|
|
24
3
|
#include "time_cache.h"
|
25
4
|
#include "http_parser.h"
|
@@ -27,13 +6,11 @@
|
|
27
6
|
#include "buffer.h"
|
28
7
|
#include "client.h"
|
29
8
|
|
30
|
-
#define MAX_FDS 1024 * 8
|
31
9
|
#define ACCEPT_TIMEOUT_SECS 1
|
32
10
|
#define SHORT_TIMEOUT_SECS 2
|
33
11
|
#define WRITE_TIMEOUT_SECS 5
|
34
12
|
#define READ_LONG_TIMEOUT_SECS 5
|
35
13
|
|
36
|
-
#define BACKLOG 1024
|
37
14
|
#define MAX_BUFSIZE 1024 * 8
|
38
15
|
#define INPUT_BUF_SIZE 1024 * 8
|
39
16
|
|
@@ -50,7 +27,7 @@
|
|
50
27
|
|
51
28
|
#define MSG_413 ("HTTP/1.0 413 Request Entity Too Large\r\nContent-Type: text/html\r\nServer: " SERVER "\r\n\r\n<html><head><title>Request Entity Too Large</title></head><body><p>Request Entity Too Large.</p></body></html>")
|
52
29
|
|
53
|
-
#define SERVER "bossan/0.
|
30
|
+
#define SERVER "bossan/0.2.0"
|
54
31
|
|
55
32
|
VALUE server; // Bossan
|
56
33
|
|
@@ -116,6 +93,10 @@ static int log_fd = -1; //access log
|
|
116
93
|
/* static int err_log_fd = -1; //error log */
|
117
94
|
|
118
95
|
static int is_keep_alive = 0; //keep alive support
|
96
|
+
static int keep_alive_timeout = 5;
|
97
|
+
|
98
|
+
static int max_fd = 1024 * 4; // picoev max_fd size
|
99
|
+
static int backlog = 1024; // backlog size
|
119
100
|
int max_content_length = 1024 * 1024 * 16; //max_content_length
|
120
101
|
|
121
102
|
static VALUE StringIO;
|
@@ -132,6 +113,13 @@ typedef struct {
|
|
132
113
|
} write_bucket;
|
133
114
|
|
134
115
|
|
116
|
+
static void
|
117
|
+
r_callback(picoev_loop* loop, int fd, int events, void* cb_arg);
|
118
|
+
|
119
|
+
static void
|
120
|
+
w_callback(picoev_loop* loop, int fd, int events, void* cb_arg);
|
121
|
+
|
122
|
+
|
135
123
|
int
|
136
124
|
open_log_file(const char *path)
|
137
125
|
{
|
@@ -139,6 +127,27 @@ open_log_file(const char *path)
|
|
139
127
|
}
|
140
128
|
|
141
129
|
|
130
|
+
void
|
131
|
+
write_error_log(char *file_name, int line)
|
132
|
+
{
|
133
|
+
char buf[64];
|
134
|
+
FILE *fp = stderr;
|
135
|
+
int fd = fileno(fp);
|
136
|
+
|
137
|
+
flock(fd, LOCK_EX);
|
138
|
+
|
139
|
+
cache_time_update();
|
140
|
+
fputs((char *)err_log_time, fp);
|
141
|
+
fputs(" [error] ", fp);
|
142
|
+
|
143
|
+
sprintf(buf, "pid %d, File \"%s\", line %d :", getpid(), file_name, line);
|
144
|
+
fputs(buf, fp);
|
145
|
+
fflush(fp);
|
146
|
+
|
147
|
+
flock(fd, LOCK_UN);
|
148
|
+
}
|
149
|
+
|
150
|
+
|
142
151
|
static int
|
143
152
|
write_log(const char *new_path, int fd, const char *data, size_t len)
|
144
153
|
{
|
@@ -261,7 +270,7 @@ blocking_write(client_t *client, char *data, size_t len)
|
|
261
270
|
// TODO:
|
262
271
|
// raise exception from errno
|
263
272
|
|
264
|
-
|
273
|
+
write_error_log(__FILE__, __LINE__);
|
265
274
|
client->keep_alive = 0;
|
266
275
|
}
|
267
276
|
return -1;
|
@@ -309,15 +318,6 @@ send_error_page(client_t *client)
|
|
309
318
|
}
|
310
319
|
|
311
320
|
|
312
|
-
static void
|
313
|
-
extent_sndbuf(client_t *client)
|
314
|
-
{
|
315
|
-
int bufsize = 1024 * 1024 * 2, r;
|
316
|
-
r = setsockopt(client->fd, SOL_SOCKET, SO_SNDBUF, &bufsize, sizeof(bufsize));
|
317
|
-
assert(r == 0);
|
318
|
-
}
|
319
|
-
|
320
|
-
|
321
321
|
static void
|
322
322
|
enable_cork(client_t *client)
|
323
323
|
{
|
@@ -391,7 +391,7 @@ writev_bucket(write_bucket *data)
|
|
391
391
|
// TODO:
|
392
392
|
// raise exception from errno
|
393
393
|
/* rb_raise(rb_eIOError); */
|
394
|
-
|
394
|
+
write_error_log(__FILE__, __LINE__);
|
395
395
|
return -1;
|
396
396
|
}
|
397
397
|
}if(w == 0){
|
@@ -410,9 +410,7 @@ writev_bucket(write_bucket *data)
|
|
410
410
|
}
|
411
411
|
}
|
412
412
|
data->total = data->total -w;
|
413
|
-
|
414
|
-
printf("writev_bucket write %d progeress %d/%d \n", w, data->total, data->total_size);
|
415
|
-
#endif
|
413
|
+
DEBUG("writev_bucket write %d progeress %d/%d \n", w, data->total, data->total_size);
|
416
414
|
//resume
|
417
415
|
// again later
|
418
416
|
return writev_bucket(data);
|
@@ -531,7 +529,7 @@ write_headers(client_t *client)
|
|
531
529
|
}
|
532
530
|
return ret;
|
533
531
|
error:
|
534
|
-
|
532
|
+
write_error_log(__FILE__, __LINE__);
|
535
533
|
if(bucket){
|
536
534
|
free_write_bucket(bucket);
|
537
535
|
client->bucket = NULL;
|
@@ -662,9 +660,7 @@ start_response_write(client_t *client)
|
|
662
660
|
return -1;
|
663
661
|
}
|
664
662
|
|
665
|
-
|
666
|
-
printf("start_response_write buflen %d \n", buflen);
|
667
|
-
#endif
|
663
|
+
/* DEBUG("start_response_write buflen %d \n", buflen); */
|
668
664
|
return write_headers(client);
|
669
665
|
}
|
670
666
|
|
@@ -678,9 +674,7 @@ response_start(client_t *client)
|
|
678
674
|
return write_headers(client);
|
679
675
|
}
|
680
676
|
ret = start_response_write(client);
|
681
|
-
|
682
|
-
printf("start_response_write ret = %d \n", ret);
|
683
|
-
#endif
|
677
|
+
DEBUG("start_response_write ret = %d \n", ret);
|
684
678
|
if(ret > 0){
|
685
679
|
// sended header
|
686
680
|
ret = processs_write(client);
|
@@ -693,7 +687,7 @@ static void
|
|
693
687
|
key_upper(char *s, const char *key, size_t len)
|
694
688
|
{
|
695
689
|
int i = 0;
|
696
|
-
|
690
|
+
int c;
|
697
691
|
for (i = 0; i < len; i++) {
|
698
692
|
c = key[i];
|
699
693
|
if(c == '-'){
|
@@ -709,30 +703,14 @@ key_upper(char *s, const char *key, size_t len)
|
|
709
703
|
}
|
710
704
|
|
711
705
|
|
712
|
-
static int
|
713
|
-
write_body2file(client_t *client, const char *buffer, size_t buffer_len)
|
714
|
-
{
|
715
|
-
FILE *tmp = (FILE *)client->body;
|
716
|
-
fwrite(buffer, 1, buffer_len, tmp);
|
717
|
-
client->body_readed += buffer_len;
|
718
|
-
#ifdef DEBUG
|
719
|
-
printf("write_body2file %d bytes \n", buffer_len);
|
720
|
-
#endif
|
721
|
-
return client->body_readed;
|
722
|
-
}
|
723
|
-
|
724
|
-
|
725
706
|
static int
|
726
707
|
write_body2mem(client_t *client, const char *buffer, size_t buffer_len)
|
727
708
|
{
|
728
709
|
VALUE obj;
|
729
|
-
/* printf("body2mem called\n"); */
|
730
710
|
|
731
711
|
rb_funcall((VALUE)client->body, i_write, 1, rb_str_new(buffer, buffer_len));
|
732
712
|
client->body_readed += buffer_len;
|
733
|
-
|
734
|
-
printf("write_body2mem %d bytes \n", buffer_len);
|
735
|
-
#endif
|
713
|
+
DEBUG("write_body2mem %d bytes \n", buffer_len);
|
736
714
|
return client->body_readed;
|
737
715
|
}
|
738
716
|
|
@@ -910,7 +888,7 @@ int
|
|
910
888
|
request_uri_cb (http_parser *p, const char *buf, size_t len, char partial)
|
911
889
|
{
|
912
890
|
client_t *client = get_client(p);
|
913
|
-
|
891
|
+
request *req = client->req;
|
914
892
|
buffer_result ret = MEMORY_ERROR;
|
915
893
|
|
916
894
|
if(req->uri){
|
@@ -964,7 +942,7 @@ int
|
|
964
942
|
fragment_cb (http_parser *p, const char *buf, size_t len, char partial)
|
965
943
|
{
|
966
944
|
client_t *client = get_client(p);
|
967
|
-
|
945
|
+
request *req = client->req;
|
968
946
|
buffer_result ret = MEMORY_ERROR;
|
969
947
|
|
970
948
|
if(req->fragment){
|
@@ -1002,14 +980,10 @@ body_cb (http_parser *p, const char *buf, size_t len, char partial)
|
|
1002
980
|
return -1;
|
1003
981
|
}
|
1004
982
|
//default memory stream
|
1005
|
-
|
1006
|
-
printf("client->body_length %d \n", client->body_length);
|
1007
|
-
#endif
|
983
|
+
DEBUG("client->body_length %d \n", client->body_length);
|
1008
984
|
client->body = rb_funcall(StringIO, i_new, 1, rb_str_new2(""));
|
1009
985
|
client->body_type = BODY_TYPE_BUFFER;
|
1010
|
-
|
1011
|
-
printf("BODY_TYPE_BUFFER \n");
|
1012
|
-
#endif
|
986
|
+
DEBUG("BODY_TYPE_BUFFER \n");
|
1013
987
|
}
|
1014
988
|
write_body(client, buf, len);
|
1015
989
|
return 0;
|
@@ -1276,12 +1250,35 @@ setsig(int sig, void* handler)
|
|
1276
1250
|
}
|
1277
1251
|
|
1278
1252
|
|
1253
|
+
void
|
1254
|
+
setup_listen_sock(int fd)
|
1255
|
+
{
|
1256
|
+
int on = 1, r;
|
1257
|
+
r = setsockopt(fd, IPPROTO_TCP, TCP_DEFER_ACCEPT, &on, sizeof(on));
|
1258
|
+
assert(r == 0);
|
1259
|
+
r = fcntl(fd, F_SETFL, O_NONBLOCK);
|
1260
|
+
assert(r == 0);
|
1261
|
+
}
|
1262
|
+
|
1263
|
+
|
1279
1264
|
static void
|
1280
1265
|
setup_sock(int fd)
|
1281
1266
|
{
|
1282
1267
|
int on = 1, r;
|
1283
1268
|
r = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on));
|
1284
1269
|
assert(r == 0);
|
1270
|
+
|
1271
|
+
// 60 + 30 * 4
|
1272
|
+
on = 300;
|
1273
|
+
r = setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &on, sizeof(on));
|
1274
|
+
assert(r == 0);
|
1275
|
+
on = 30;
|
1276
|
+
r = setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &on, sizeof(on));
|
1277
|
+
assert(r == 0);
|
1278
|
+
on = 4;
|
1279
|
+
r = setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &on, sizeof(on));
|
1280
|
+
assert(r == 0);
|
1281
|
+
|
1285
1282
|
r = fcntl(fd, F_SETFL, O_NONBLOCK);
|
1286
1283
|
assert(r == 0);
|
1287
1284
|
}
|
@@ -1304,21 +1301,18 @@ disable_cork(client_t *client)
|
|
1304
1301
|
|
1305
1302
|
|
1306
1303
|
static client_t *
|
1307
|
-
new_client_t(int client_fd,
|
1304
|
+
new_client_t(int client_fd, char *remote_addr, uint32_t remote_port)
|
1305
|
+
{
|
1308
1306
|
client_t *client;
|
1309
1307
|
|
1310
1308
|
client = ruby_xmalloc(sizeof(client_t));
|
1311
1309
|
memset(client, 0, sizeof(client_t));
|
1312
1310
|
|
1313
|
-
/* printf("size %d\n", sizeof(client_t)); */
|
1314
|
-
|
1315
1311
|
client->fd = client_fd;
|
1316
|
-
|
1317
|
-
client->
|
1318
|
-
client->remote_port = ntohs(client_addr.sin_port);
|
1312
|
+
client->remote_addr = remote_addr;
|
1313
|
+
client->remote_port = remote_port;
|
1319
1314
|
client->req = new_request();
|
1320
1315
|
client->body_type = BODY_TYPE_NONE;
|
1321
|
-
//printf("input_buf_size %d\n", client->input_buf_size);
|
1322
1316
|
return client;
|
1323
1317
|
}
|
1324
1318
|
|
@@ -1331,9 +1325,7 @@ clean_cli(client_t *client)
|
|
1331
1325
|
free_request(client->req);
|
1332
1326
|
client->req = NULL;
|
1333
1327
|
}
|
1334
|
-
|
1335
|
-
printf("close environ %p \n", client->environ);
|
1336
|
-
#endif
|
1328
|
+
DEBUG("close environ %p \n", client->environ);
|
1337
1329
|
|
1338
1330
|
// force clear
|
1339
1331
|
client->environ = rb_hash_new();
|
@@ -1348,32 +1340,26 @@ clean_cli(client_t *client)
|
|
1348
1340
|
static void
|
1349
1341
|
close_conn(client_t *cli, picoev_loop* loop)
|
1350
1342
|
{
|
1343
|
+
client_t *new_client;
|
1344
|
+
if(!cli->response_closed){
|
1345
|
+
close_response(cli);
|
1346
|
+
}
|
1347
|
+
picoev_del(loop, cli->fd);
|
1348
|
+
|
1349
|
+
DEBUG("picoev_del client:%p fd:%d \n", cli, cli->fd);
|
1350
|
+
clean_cli(cli);
|
1351
1351
|
if(!cli->keep_alive){
|
1352
|
-
picoev_del(loop, cli->fd);
|
1353
|
-
clean_cli(cli);
|
1354
1352
|
close(cli->fd);
|
1355
|
-
|
1356
|
-
printf("close fd %d \n", cli->fd);
|
1357
|
-
#endif
|
1358
|
-
ruby_xfree(cli);
|
1353
|
+
DEBUG("close client:%p fd:%d \n", cli, cli->fd);
|
1359
1354
|
}else{
|
1360
|
-
clean_cli(cli);
|
1361
1355
|
disable_cork(cli);
|
1362
|
-
cli->
|
1363
|
-
|
1364
|
-
|
1365
|
-
|
1366
|
-
|
1367
|
-
cli->body_type = BODY_TYPE_NONE;
|
1368
|
-
cli->status_code = 0;
|
1369
|
-
cli->response = Qnil;
|
1370
|
-
cli->content_length_set = 0;
|
1371
|
-
cli->content_length = 0;
|
1372
|
-
cli->write_bytes = 0;
|
1373
|
-
cli->response_closed = 0;
|
1374
|
-
cli->bad_request_code = 0;
|
1375
|
-
init_parser(cli, server_name, server_port);
|
1356
|
+
new_client = new_client_t(cli->fd, cli->remote_addr, cli->remote_port);
|
1357
|
+
rb_gc_register_address(&new_client->environ);
|
1358
|
+
new_client->keep_alive = 1;
|
1359
|
+
init_parser(new_client, server_name, server_port);
|
1360
|
+
picoev_add(main_loop, new_client->fd, PICOEV_READ, keep_alive_timeout, r_callback, (void *)new_client);
|
1376
1361
|
}
|
1362
|
+
ruby_xfree(cli);
|
1377
1363
|
}
|
1378
1364
|
|
1379
1365
|
|
@@ -1413,7 +1399,7 @@ process_rack_app(client_t *cli)
|
|
1413
1399
|
|
1414
1400
|
//check response
|
1415
1401
|
if(cli->response && cli->response == Qnil){
|
1416
|
-
|
1402
|
+
write_error_log(__FILE__, __LINE__);
|
1417
1403
|
rb_raise(rb_eException, "response must be a iter or sequence object");
|
1418
1404
|
}
|
1419
1405
|
|
@@ -1426,13 +1412,9 @@ w_callback(picoev_loop* loop, int fd, int events, void* cb_arg)
|
|
1426
1412
|
{
|
1427
1413
|
client_t *client = ( client_t *)(cb_arg);
|
1428
1414
|
int ret;
|
1429
|
-
|
1430
|
-
printf("call w_callback \n");
|
1431
|
-
#endif
|
1415
|
+
DEBUG("call w_callback \n");
|
1432
1416
|
if ((events & PICOEV_TIMEOUT) != 0) {
|
1433
|
-
|
1434
|
-
printf("** w_callback timeout ** \n");
|
1435
|
-
#endif
|
1417
|
+
YDEBUG("** w_callback timeout ** \n");
|
1436
1418
|
//timeout
|
1437
1419
|
client->keep_alive = 0;
|
1438
1420
|
close_conn(client, loop);
|
@@ -1440,9 +1422,7 @@ w_callback(picoev_loop* loop, int fd, int events, void* cb_arg)
|
|
1440
1422
|
} else if ((events & PICOEV_WRITE) != 0) {
|
1441
1423
|
ret = process_body(client);
|
1442
1424
|
picoev_set_timeout(loop, client->fd, WRITE_TIMEOUT_SECS);
|
1443
|
-
|
1444
|
-
printf("process_body ret %d \n", ret);
|
1445
|
-
#endif
|
1425
|
+
DEBUG("process_body ret %d \n", ret);
|
1446
1426
|
if(ret != 0){
|
1447
1427
|
//ok or die
|
1448
1428
|
close_conn(client, loop);
|
@@ -1464,7 +1444,6 @@ call_rack_app(client_t *client, picoev_loop* loop)
|
|
1464
1444
|
}
|
1465
1445
|
|
1466
1446
|
ret = response_start(client);
|
1467
|
-
/* printf("response_start done: %d\n", ret); */
|
1468
1447
|
switch(ret){
|
1469
1448
|
case -1:
|
1470
1449
|
// Internal Server Error
|
@@ -1475,9 +1454,7 @@ call_rack_app(client_t *client, picoev_loop* loop)
|
|
1475
1454
|
case 0:
|
1476
1455
|
// continue
|
1477
1456
|
// set callback
|
1478
|
-
|
1479
|
-
printf("set write callback %d \n", ret);
|
1480
|
-
#endif
|
1457
|
+
DEBUG("set write callback %d \n", ret);
|
1481
1458
|
//clear event
|
1482
1459
|
picoev_del(loop, client->fd);
|
1483
1460
|
picoev_add(loop, client->fd, PICOEV_WRITE, WRITE_TIMEOUT_SECS, w_callback, (void *)client);
|
@@ -1528,16 +1505,12 @@ r_callback(picoev_loop* loop, int fd, int events, void* cb_arg)
|
|
1528
1505
|
{
|
1529
1506
|
client_t *cli = ( client_t *)(cb_arg);
|
1530
1507
|
if ((events & PICOEV_TIMEOUT) != 0) {
|
1531
|
-
|
1532
|
-
printf("** r_callback timeout ** \n");
|
1533
|
-
#endif
|
1508
|
+
YDEBUG("** r_callback timeout ** \n");
|
1534
1509
|
//timeout
|
1535
1510
|
cli->keep_alive = 0;
|
1536
1511
|
close_conn(cli, loop);
|
1537
1512
|
} else if ((events & PICOEV_READ) != 0) {
|
1538
|
-
|
1539
|
-
printf("ready read \n");
|
1540
|
-
#endif
|
1513
|
+
RDEBUG("ready read \n");
|
1541
1514
|
/* update timeout, and read */
|
1542
1515
|
int finish = 0, nread;
|
1543
1516
|
char buf[INPUT_BUF_SIZE];
|
@@ -1548,50 +1521,56 @@ r_callback(picoev_loop* loop, int fd, int events, void* cb_arg)
|
|
1548
1521
|
r = read(cli->fd, buf, sizeof(buf));
|
1549
1522
|
switch (r) {
|
1550
1523
|
case 0:
|
1551
|
-
|
1552
|
-
|
1524
|
+
cli->keep_alive = 0;
|
1525
|
+
cli->status_code = 500; // ??? 503 ??
|
1526
|
+
send_error_page(cli);
|
1527
|
+
close_conn(cli, loop);
|
1528
|
+
return;
|
1553
1529
|
case -1: /* error */
|
1554
1530
|
if (errno == EAGAIN || errno == EWOULDBLOCK) { /* try again later */
|
1555
1531
|
break;
|
1556
1532
|
} else { /* fatal error */
|
1557
|
-
|
1558
|
-
|
1559
|
-
|
1560
|
-
|
1561
|
-
|
1562
|
-
|
1563
|
-
|
1533
|
+
if (cli->keep_alive && errno == ECONNRESET) {
|
1534
|
+
cli->keep_alive = 0;
|
1535
|
+
cli->status_code = 500;
|
1536
|
+
cli->header_done = 1;
|
1537
|
+
cli->response_closed = 1;
|
1538
|
+
} else {
|
1539
|
+
/* rb_raise(rb_eException, "fatal error"); */
|
1540
|
+
write_error_log(__FILE__, __LINE__);
|
1541
|
+
cli->keep_alive = 0;
|
1542
|
+
cli->status_code = 500;
|
1543
|
+
if(errno != ECONNRESET){
|
1544
|
+
send_error_page(cli);
|
1545
|
+
}else{
|
1546
|
+
cli->header_done = 1;
|
1547
|
+
cli->response_closed = 1;
|
1548
|
+
}
|
1549
|
+
}
|
1564
1550
|
close_conn(cli, loop);
|
1565
1551
|
return;
|
1566
1552
|
}
|
1567
1553
|
break;
|
1568
1554
|
default:
|
1569
|
-
|
1570
|
-
printf("read request fd %d bufsize %d \n", cli->fd, r);
|
1571
|
-
#endif
|
1555
|
+
RDEBUG("read request fd %d bufsize %d \n", cli->fd, r);
|
1572
1556
|
nread = execute_parse(cli, buf, r);
|
1573
|
-
|
1557
|
+
|
1574
1558
|
if(cli->bad_request_code > 0){
|
1575
|
-
|
1576
|
-
printf("fd %d bad_request code %d \n", cli->fd, cli->bad_request_code);
|
1577
|
-
#endif
|
1559
|
+
RDEBUG("fd %d bad_request code %d \n", cli->fd, cli->bad_request_code);
|
1578
1560
|
send_error_page(cli);
|
1579
1561
|
close_conn(cli, loop);
|
1580
1562
|
return;
|
1581
1563
|
}
|
1582
1564
|
if( nread != r ){
|
1583
1565
|
// parse error
|
1584
|
-
|
1585
|
-
printf("fd %d parse error %d \n", cli->fd, cli->bad_request_code);
|
1586
|
-
#endif
|
1566
|
+
DEBUG("fd %d parse error %d \n", cli->fd, cli->bad_request_code);
|
1587
1567
|
cli->bad_request_code = 400;
|
1588
1568
|
send_error_page(cli);
|
1589
1569
|
close_conn(cli, loop);
|
1590
1570
|
return;
|
1591
1571
|
}
|
1592
|
-
|
1593
|
-
|
1594
|
-
#endif
|
1572
|
+
RDEBUG("parse ok, fd %d %d nread \n", cli->fd, nread);
|
1573
|
+
|
1595
1574
|
if(parser_finish(cli) > 0){
|
1596
1575
|
finish = 1;
|
1597
1576
|
}
|
@@ -1599,6 +1578,7 @@ r_callback(picoev_loop* loop, int fd, int events, void* cb_arg)
|
|
1599
1578
|
}
|
1600
1579
|
|
1601
1580
|
if(finish == 1){
|
1581
|
+
/* picoev_del(loop, cli->fd); */
|
1602
1582
|
prepare_call_rack(cli);
|
1603
1583
|
call_rack_app(cli, loop);
|
1604
1584
|
return;
|
@@ -1613,6 +1593,9 @@ accept_callback(picoev_loop* loop, int fd, int events, void* cb_arg)
|
|
1613
1593
|
int client_fd;
|
1614
1594
|
client_t *client;
|
1615
1595
|
struct sockaddr_in client_addr;
|
1596
|
+
char *remote_addr;
|
1597
|
+
uint32_t remote_port;
|
1598
|
+
|
1616
1599
|
if ((events & PICOEV_TIMEOUT) != 0) {
|
1617
1600
|
// time out
|
1618
1601
|
// next turn or other process
|
@@ -1625,24 +1608,17 @@ accept_callback(picoev_loop* loop, int fd, int events, void* cb_arg)
|
|
1625
1608
|
client_fd = accept(fd, (struct sockaddr *)&client_addr, &client_len);
|
1626
1609
|
#endif
|
1627
1610
|
if (client_fd != -1) {
|
1628
|
-
|
1629
|
-
printf("accept fd %d \n", client_fd);
|
1630
|
-
#endif
|
1611
|
+
DEBUG("accept fd %d \n", client_fd);
|
1631
1612
|
setup_sock(client_fd);
|
1632
|
-
|
1633
|
-
|
1634
|
-
client
|
1613
|
+
remote_addr = inet_ntoa(client_addr.sin_addr);
|
1614
|
+
remote_port = ntohs(client_addr.sin_port);
|
1615
|
+
client = new_client_t(client_fd, remote_addr, remote_port);
|
1635
1616
|
rb_gc_register_address(&client->environ);
|
1636
|
-
|
1637
1617
|
init_parser(client, server_name, server_port);
|
1638
|
-
|
1639
|
-
picoev_add(loop, client_fd, PICOEV_READ, READ_LONG_TIMEOUT_SECS, r_callback, (void *)client);
|
1618
|
+
picoev_add(loop, client_fd, PICOEV_READ, keep_alive_timeout, r_callback, (void *)client);
|
1640
1619
|
}else{
|
1641
1620
|
if (errno != EAGAIN && errno != EWOULDBLOCK) {
|
1642
|
-
|
1643
|
-
// raise exception from errno
|
1644
|
-
/* rb_raise(); */
|
1645
|
-
/* write_error_log(__FILE__, __LINE__); */
|
1621
|
+
write_error_log(__FILE__, __LINE__);
|
1646
1622
|
// die
|
1647
1623
|
loop_done = 0;
|
1648
1624
|
}
|
@@ -1654,9 +1630,9 @@ accept_callback(picoev_loop* loop, int fd, int events, void* cb_arg)
|
|
1654
1630
|
static void
|
1655
1631
|
setup_server_env(void)
|
1656
1632
|
{
|
1633
|
+
setup_listen_sock(listen_sock);
|
1657
1634
|
setup_sock(listen_sock);
|
1658
1635
|
cache_time_init();
|
1659
|
-
|
1660
1636
|
setup_static_env(server_name, server_port);
|
1661
1637
|
}
|
1662
1638
|
|
@@ -1677,9 +1653,6 @@ inet_listen(void)
|
|
1677
1653
|
snprintf(strport, sizeof (strport), "%d", server_port);
|
1678
1654
|
|
1679
1655
|
if ((rv = getaddrinfo(server_name, strport, &hints, &servinfo)) == -1) {
|
1680
|
-
// TODO:
|
1681
|
-
// raise exception from errno
|
1682
|
-
/* rb_raise(); */
|
1683
1656
|
return -1;
|
1684
1657
|
}
|
1685
1658
|
|
@@ -1694,17 +1667,11 @@ inet_listen(void)
|
|
1694
1667
|
if (setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &flag,
|
1695
1668
|
sizeof(int)) == -1) {
|
1696
1669
|
close(listen_sock);
|
1697
|
-
// TODO:
|
1698
|
-
// raise exception from errno
|
1699
|
-
/* rb_raise(); */
|
1700
1670
|
return -1;
|
1701
1671
|
}
|
1702
1672
|
|
1703
1673
|
if (bind(listen_sock, p->ai_addr, p->ai_addrlen) == -1) {
|
1704
1674
|
close(listen_sock);
|
1705
|
-
// TODO:
|
1706
|
-
// raise exception from errno
|
1707
|
-
/* rb_raise(); */
|
1708
1675
|
return -1;
|
1709
1676
|
}
|
1710
1677
|
break;
|
@@ -1719,11 +1686,8 @@ inet_listen(void)
|
|
1719
1686
|
freeaddrinfo(servinfo); // all done with this structure
|
1720
1687
|
|
1721
1688
|
// BACKLOG 1024
|
1722
|
-
if (listen(listen_sock,
|
1689
|
+
if (listen(listen_sock, backlog) == -1) {
|
1723
1690
|
close(listen_sock);
|
1724
|
-
// TODO:
|
1725
|
-
// raise exception from errno
|
1726
|
-
/* rb_raise(); */
|
1727
1691
|
return -1;
|
1728
1692
|
}
|
1729
1693
|
setup_server_env();
|
@@ -1750,9 +1714,7 @@ unix_listen(char *sock_name)
|
|
1750
1714
|
struct sockaddr_un saddr;
|
1751
1715
|
mode_t old_umask;
|
1752
1716
|
|
1753
|
-
|
1754
|
-
printf("unix domain socket %s\n", sock_name);
|
1755
|
-
#endif
|
1717
|
+
DEBUG("unix domain socket %s\n", sock_name);
|
1756
1718
|
memset(&saddr, 0, sizeof(saddr));
|
1757
1719
|
check_unix_sockpath(sock_name);
|
1758
1720
|
|
@@ -1778,7 +1740,7 @@ unix_listen(char *sock_name)
|
|
1778
1740
|
umask(old_umask);
|
1779
1741
|
|
1780
1742
|
// BACKLOG 1024
|
1781
|
-
if (listen(listen_sock,
|
1743
|
+
if (listen(listen_sock, backlog) == -1) {
|
1782
1744
|
close(listen_sock);
|
1783
1745
|
rb_raise(rb_eIOError, "server: failed to set backlog\n");
|
1784
1746
|
}
|
@@ -1792,7 +1754,6 @@ static void
|
|
1792
1754
|
sigint_cb(int signum)
|
1793
1755
|
{
|
1794
1756
|
loop_done = 0;
|
1795
|
-
/* rb_interrupt(); */
|
1796
1757
|
}
|
1797
1758
|
|
1798
1759
|
|
@@ -1881,7 +1842,7 @@ bossan_run_loop(int argc, VALUE *argv, VALUE self)
|
|
1881
1842
|
}
|
1882
1843
|
|
1883
1844
|
/* init picoev */
|
1884
|
-
picoev_init(
|
1845
|
+
picoev_init(max_fd);
|
1885
1846
|
/* create loop */
|
1886
1847
|
main_loop = picoev_create_loop(60);
|
1887
1848
|
loop_done = 1;
|
@@ -1899,6 +1860,9 @@ bossan_run_loop(int argc, VALUE *argv, VALUE self)
|
|
1899
1860
|
picoev_destroy_loop(main_loop);
|
1900
1861
|
picoev_deinit();
|
1901
1862
|
|
1863
|
+
if(unix_sock_name){
|
1864
|
+
unlink(unix_sock_name);
|
1865
|
+
}
|
1902
1866
|
printf("Bye.\n");
|
1903
1867
|
return Qnil;
|
1904
1868
|
}
|
@@ -1919,6 +1883,75 @@ bossan_get_max_content_length(VALUE self)
|
|
1919
1883
|
}
|
1920
1884
|
|
1921
1885
|
|
1886
|
+
VALUE
|
1887
|
+
bossan_set_keepalive(VALUE self, VALUE args)
|
1888
|
+
{
|
1889
|
+
int on;
|
1890
|
+
|
1891
|
+
on = NUM2INT(args);
|
1892
|
+
if(on < 0){
|
1893
|
+
rb_p("keep alive value out of range.\n");
|
1894
|
+
return Qfalse;
|
1895
|
+
}
|
1896
|
+
|
1897
|
+
is_keep_alive = on;
|
1898
|
+
|
1899
|
+
if(is_keep_alive){
|
1900
|
+
keep_alive_timeout = on;
|
1901
|
+
}else{
|
1902
|
+
keep_alive_timeout = 2;
|
1903
|
+
}
|
1904
|
+
return Qnil;
|
1905
|
+
}
|
1906
|
+
|
1907
|
+
|
1908
|
+
VALUE
|
1909
|
+
bossan_get_keepalive(VALUE self)
|
1910
|
+
{
|
1911
|
+
return INT2NUM(is_keep_alive);
|
1912
|
+
}
|
1913
|
+
|
1914
|
+
|
1915
|
+
VALUE
|
1916
|
+
bossan_set_picoev_max_fd(VALUE self, VALUE args)
|
1917
|
+
{
|
1918
|
+
int temp;
|
1919
|
+
temp = NUM2INT(args);
|
1920
|
+
if (temp <= 0) {
|
1921
|
+
rb_raise(rb_eException, "max_fd value out of range ");
|
1922
|
+
}
|
1923
|
+
max_fd = temp;
|
1924
|
+
return Qnil;
|
1925
|
+
}
|
1926
|
+
|
1927
|
+
|
1928
|
+
VALUE
|
1929
|
+
bossan_get_picoev_max_fd(VALUE self)
|
1930
|
+
{
|
1931
|
+
return INT2NUM(max_fd);
|
1932
|
+
}
|
1933
|
+
|
1934
|
+
|
1935
|
+
VALUE
|
1936
|
+
bossan_set_backlog(VALUE self, VALUE args)
|
1937
|
+
{
|
1938
|
+
int temp;
|
1939
|
+
temp = NUM2INT(args);
|
1940
|
+
if (temp <= 0) {
|
1941
|
+
rb_raise(rb_eException, "backlog value out of range ");
|
1942
|
+
}
|
1943
|
+
backlog = temp;
|
1944
|
+
return Qnil;
|
1945
|
+
}
|
1946
|
+
|
1947
|
+
|
1948
|
+
VALUE
|
1949
|
+
bossan_get_backlog(VALUE self)
|
1950
|
+
{
|
1951
|
+
return INT2NUM(backlog);
|
1952
|
+
}
|
1953
|
+
|
1954
|
+
|
1922
1955
|
void
|
1923
1956
|
Init_bossan_ext(void)
|
1924
1957
|
{
|
@@ -1984,11 +2017,21 @@ Init_bossan_ext(void)
|
|
1984
2017
|
|
1985
2018
|
rb_define_module_function(server, "run", bossan_run_loop, -1);
|
1986
2019
|
rb_define_module_function(server, "stop", bossan_stop, 0);
|
2020
|
+
rb_define_module_function(server, "shutdown", bossan_stop, 0);
|
1987
2021
|
|
1988
2022
|
rb_define_module_function(server, "access_log", bossan_access_log, 1);
|
1989
2023
|
rb_define_module_function(server, "set_max_content_length", bossan_set_max_content_length, 1);
|
1990
2024
|
rb_define_module_function(server, "get_max_content_length", bossan_get_max_content_length, 0);
|
1991
2025
|
|
2026
|
+
rb_define_module_function(server, "set_keepalive", bossan_set_keepalive, 1);
|
2027
|
+
rb_define_module_function(server, "get_keepalive", bossan_get_keepalive, 0);
|
2028
|
+
|
2029
|
+
rb_define_module_function(server, "set_picoev_max_fd", bossan_set_picoev_max_fd, 1);
|
2030
|
+
rb_define_module_function(server, "get_picoev_max_fd", bossan_get_picoev_max_fd, 0);
|
2031
|
+
|
2032
|
+
rb_define_module_function(server, "set_backlog", bossan_set_backlog, 1);
|
2033
|
+
rb_define_module_function(server, "get_backlog", bossan_get_backlog, 0);
|
2034
|
+
|
1992
2035
|
rb_require("stringio");
|
1993
2036
|
StringIO = rb_const_get(rb_cObject, rb_intern("StringIO"));
|
1994
2037
|
}
|
data/ext/bossan/picoev_epoll.c
CHANGED
data/lib/bossan/version.rb
CHANGED
data/test/test_rack_spec.rb
CHANGED
@@ -29,7 +29,6 @@ class RackSpecTest < Test::Unit::TestCase
|
|
29
29
|
rescue
|
30
30
|
next
|
31
31
|
end
|
32
|
-
server_is_wake_up = true
|
33
32
|
$stderr.puts "*** running success ***"
|
34
33
|
return true
|
35
34
|
}
|
@@ -39,6 +38,8 @@ class RackSpecTest < Test::Unit::TestCase
|
|
39
38
|
private :server_is_wake_up?
|
40
39
|
|
41
40
|
def setup
|
41
|
+
$stderr.puts RUBY_DESCRIPTION
|
42
|
+
|
42
43
|
@pid = fork do
|
43
44
|
trap(:INT) { Bossan.stop }
|
44
45
|
Bossan.run(DEFAULT_HOST, DEFAULT_PORT, App.new)
|
metadata
CHANGED
@@ -1,20 +1,18 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bossan
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
5
|
-
prerelease:
|
4
|
+
version: 0.2.0
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Hiroki Noda
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date: 2013-
|
11
|
+
date: 2013-05-22 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
14
13
|
- !ruby/object:Gem::Dependency
|
15
14
|
name: rack
|
16
15
|
requirement: !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
16
|
requirements:
|
19
17
|
- - ~>
|
20
18
|
- !ruby/object:Gem::Version
|
@@ -22,7 +20,6 @@ dependencies:
|
|
22
20
|
type: :runtime
|
23
21
|
prerelease: false
|
24
22
|
version_requirements: !ruby/object:Gem::Requirement
|
25
|
-
none: false
|
26
23
|
requirements:
|
27
24
|
- - ~>
|
28
25
|
- !ruby/object:Gem::Version
|
@@ -37,10 +34,12 @@ extra_rdoc_files: []
|
|
37
34
|
files:
|
38
35
|
- .gitignore
|
39
36
|
- .travis.yml
|
37
|
+
- CHANGELOG.md
|
40
38
|
- Gemfile
|
41
39
|
- LICENSE.txt
|
42
40
|
- README.md
|
43
41
|
- Rakefile
|
42
|
+
- TODO
|
44
43
|
- bossan.gemspec
|
45
44
|
- examples/blog/README.md
|
46
45
|
- examples/blog/app.rb
|
@@ -53,6 +52,7 @@ files:
|
|
53
52
|
- examples/sinatra_app.rb
|
54
53
|
- examples/views/index.haml
|
55
54
|
- examples/views_sample.rb
|
55
|
+
- ext/bossan/bossan.h
|
56
56
|
- ext/bossan/bossan_ext.c
|
57
57
|
- ext/bossan/buffer.c
|
58
58
|
- ext/bossan/buffer.h
|
@@ -75,28 +75,27 @@ files:
|
|
75
75
|
- test/test_rack_spec.rb
|
76
76
|
homepage: https://github.com/kubo39/bossan
|
77
77
|
licenses: []
|
78
|
+
metadata: {}
|
78
79
|
post_install_message:
|
79
80
|
rdoc_options: []
|
80
81
|
require_paths:
|
81
82
|
- lib
|
82
83
|
- ext
|
83
84
|
required_ruby_version: !ruby/object:Gem::Requirement
|
84
|
-
none: false
|
85
85
|
requirements:
|
86
86
|
- - ! '>='
|
87
87
|
- !ruby/object:Gem::Version
|
88
88
|
version: 1.9.2
|
89
89
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
90
|
-
none: false
|
91
90
|
requirements:
|
92
91
|
- - ! '>='
|
93
92
|
- !ruby/object:Gem::Version
|
94
93
|
version: '0'
|
95
94
|
requirements: []
|
96
95
|
rubyforge_project:
|
97
|
-
rubygems_version:
|
96
|
+
rubygems_version: 2.0.3
|
98
97
|
signing_key:
|
99
|
-
specification_version:
|
98
|
+
specification_version: 4
|
100
99
|
summary: high performance asynchronous rack web server
|
101
100
|
test_files: []
|
102
101
|
has_rdoc:
|