bossan 0.1.8 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|