rhebok 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +15 -0
- data/.gitmodules +3 -0
- data/.travis.yml +8 -0
- data/Changes +4 -0
- data/Gemfile +5 -0
- data/LICENSE.txt +378 -0
- data/README.md +208 -0
- data/Rakefile +21 -0
- data/ext/rhebok/extconf.rb +2 -0
- data/ext/rhebok/picohttpparser/.gitattributes +1 -0
- data/ext/rhebok/picohttpparser/.gitmodules +3 -0
- data/ext/rhebok/picohttpparser/.travis.yml +6 -0
- data/ext/rhebok/picohttpparser/Makefile +40 -0
- data/ext/rhebok/picohttpparser/README.md +24 -0
- data/ext/rhebok/picohttpparser/bench.c +53 -0
- data/ext/rhebok/picohttpparser/picohttpparser.c +434 -0
- data/ext/rhebok/picohttpparser/picohttpparser.h +63 -0
- data/ext/rhebok/picohttpparser/test.c +253 -0
- data/ext/rhebok/rhebok.c +795 -0
- data/lib/rack/handler/rhebok.rb +210 -0
- data/lib/rhebok.rb +7 -0
- data/lib/rhebok/version.rb +3 -0
- data/rhebok.gemspec +51 -0
- data/test/spec_02_basic.rb +7 -0
- data/test/spec_02_server.rb +87 -0
- data/test/testrequest.rb +79 -0
- metadata +143 -0
@@ -0,0 +1,63 @@
|
|
1
|
+
/*
|
2
|
+
* Copyright (c) 2009-2014 Kazuho Oku, Tokuhiro Matsuno, Daisuke Murase,
|
3
|
+
* Shigeo Mitsunari
|
4
|
+
*
|
5
|
+
* The software is licensed under either the MIT License (below) or the Perl
|
6
|
+
* license.
|
7
|
+
*
|
8
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
9
|
+
* of this software and associated documentation files (the "Software"), to
|
10
|
+
* deal in the Software without restriction, including without limitation the
|
11
|
+
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
12
|
+
* sell copies of the Software, and to permit persons to whom the Software is
|
13
|
+
* furnished to do so, subject to the following conditions:
|
14
|
+
*
|
15
|
+
* The above copyright notice and this permission notice shall be included in
|
16
|
+
* all copies or substantial portions of the Software.
|
17
|
+
*
|
18
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
19
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
20
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
21
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
22
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
23
|
+
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
24
|
+
* IN THE SOFTWARE.
|
25
|
+
*/
|
26
|
+
|
27
|
+
#ifndef picohttpparser_h
|
28
|
+
#define picohttpparser_h
|
29
|
+
|
30
|
+
/* $Id$ */
|
31
|
+
|
32
|
+
#ifdef __cplusplus
|
33
|
+
extern "C" {
|
34
|
+
#endif
|
35
|
+
|
36
|
+
/* contains name and value of a header (name == NULL if is a continuing line
|
37
|
+
* of a multiline header */
|
38
|
+
struct phr_header {
|
39
|
+
const char* name;
|
40
|
+
size_t name_len;
|
41
|
+
const char* value;
|
42
|
+
size_t value_len;
|
43
|
+
};
|
44
|
+
|
45
|
+
/* returns number of bytes cosumed if successful, -2 if request is partial,
|
46
|
+
* -1 if failed */
|
47
|
+
int phr_parse_request(const char* buf, size_t len, const char** method,
|
48
|
+
size_t* method_len, const char** path,
|
49
|
+
size_t* path_len, int* minor_version,
|
50
|
+
struct phr_header* headers, size_t* num_headers,
|
51
|
+
size_t last_len);
|
52
|
+
|
53
|
+
/* ditto */
|
54
|
+
int phr_parse_response(const char* _buf, size_t len, int *minor_version,
|
55
|
+
int *status, const char **msg, size_t *msg_len,
|
56
|
+
struct phr_header* headers, size_t* num_headers,
|
57
|
+
size_t last_len);
|
58
|
+
|
59
|
+
#ifdef __cplusplus
|
60
|
+
}
|
61
|
+
#endif
|
62
|
+
|
63
|
+
#endif
|
@@ -0,0 +1,253 @@
|
|
1
|
+
/* use `make test` to run the test */
|
2
|
+
/*
|
3
|
+
* Copyright (c) 2009-2014 Kazuho Oku, Tokuhiro Matsuno, Daisuke Murase,
|
4
|
+
* Shigeo Mitsunari
|
5
|
+
*
|
6
|
+
* The software is licensed under either the MIT License (below) or the Perl
|
7
|
+
* license.
|
8
|
+
*
|
9
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
10
|
+
* of this software and associated documentation files (the "Software"), to
|
11
|
+
* deal in the Software without restriction, including without limitation the
|
12
|
+
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
13
|
+
* sell copies of the Software, and to permit persons to whom the Software is
|
14
|
+
* furnished to do so, subject to the following conditions:
|
15
|
+
*
|
16
|
+
* The above copyright notice and this permission notice shall be included in
|
17
|
+
* all copies or substantial portions of the Software.
|
18
|
+
*
|
19
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
20
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
21
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
22
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
23
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
24
|
+
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
25
|
+
* IN THE SOFTWARE.
|
26
|
+
*/
|
27
|
+
|
28
|
+
#include <stdio.h>
|
29
|
+
#include <string.h>
|
30
|
+
#include "picotest/picotest.h"
|
31
|
+
#include "picohttpparser.h"
|
32
|
+
|
33
|
+
static int bufis(const char* s, size_t l, const char* t)
|
34
|
+
{
|
35
|
+
return strlen(t) == l && memcmp(s, t, l) == 0;
|
36
|
+
}
|
37
|
+
|
38
|
+
static void test_request(void)
|
39
|
+
{
|
40
|
+
const char* method;
|
41
|
+
size_t method_len;
|
42
|
+
const char* path;
|
43
|
+
size_t path_len;
|
44
|
+
int minor_version;
|
45
|
+
struct phr_header headers[4];
|
46
|
+
size_t num_headers;
|
47
|
+
|
48
|
+
#define PARSE(s, last_len, exp, comment) \
|
49
|
+
do { \
|
50
|
+
note(comment); \
|
51
|
+
num_headers = sizeof(headers) / sizeof(headers[0]); \
|
52
|
+
ok(phr_parse_request(s, sizeof(s) - 1, &method, &method_len, &path, \
|
53
|
+
&path_len, &minor_version, headers, \
|
54
|
+
&num_headers, last_len) \
|
55
|
+
== (exp == 0 ? strlen(s) : exp)); \
|
56
|
+
} while (0)
|
57
|
+
|
58
|
+
PARSE("GET / HTTP/1.0\r\n\r\n", 0, 0, "simple");
|
59
|
+
ok(num_headers == 0);
|
60
|
+
ok(bufis(method, method_len, "GET"));
|
61
|
+
ok(bufis(path, path_len, "/"));
|
62
|
+
ok(minor_version == 0);
|
63
|
+
|
64
|
+
PARSE("GET / HTTP/1.0\r\n\r", 0, -2, "partial");
|
65
|
+
|
66
|
+
PARSE("GET /hoge HTTP/1.1\r\nHost: example.com\r\nCookie: \r\n\r\n", 0, 0,
|
67
|
+
"parse headers");
|
68
|
+
ok(num_headers == 2);
|
69
|
+
ok(bufis(method, method_len, "GET"));
|
70
|
+
ok(bufis(path, path_len, "/hoge"));
|
71
|
+
ok(minor_version == 1);
|
72
|
+
ok(bufis(headers[0].name, headers[0].name_len, "Host"));
|
73
|
+
ok(bufis(headers[0].value, headers[0].value_len, "example.com"));
|
74
|
+
ok(bufis(headers[1].name, headers[1].name_len, "Cookie"));
|
75
|
+
ok(bufis(headers[1].value, headers[1].value_len, ""));
|
76
|
+
|
77
|
+
PARSE("GET /hoge HTTP/1.1\r\nHost: example.com\r\nUser-Agent: \343\201\262\343/1.0\r\n\r\n", 0, 0,
|
78
|
+
"multibyte included");
|
79
|
+
ok(num_headers == 2);
|
80
|
+
ok(bufis(method, method_len, "GET"));
|
81
|
+
ok(bufis(path, path_len, "/hoge"));
|
82
|
+
ok(minor_version == 1);
|
83
|
+
ok(bufis(headers[0].name, headers[0].name_len, "Host"));
|
84
|
+
ok(bufis(headers[0].value, headers[0].value_len, "example.com"));
|
85
|
+
ok(bufis(headers[1].name, headers[1].name_len, "User-Agent"));
|
86
|
+
ok(bufis(headers[1].value, headers[1].value_len, "\343\201\262\343/1.0"));
|
87
|
+
|
88
|
+
PARSE("GET / HTTP/1.0\r\nfoo: \r\nfoo: b\r\n \tc\r\n\r\n", 0, 0,
|
89
|
+
"parse multiline");
|
90
|
+
ok(num_headers == 3);
|
91
|
+
ok(bufis(method, method_len, "GET"));
|
92
|
+
ok(bufis(path, path_len, "/"));
|
93
|
+
ok(minor_version == 0);
|
94
|
+
ok(bufis(headers[0].name, headers[0].name_len, "foo"));
|
95
|
+
ok(bufis(headers[0].value, headers[0].value_len, ""));
|
96
|
+
ok(bufis(headers[1].name, headers[1].name_len, "foo"));
|
97
|
+
ok(bufis(headers[1].value, headers[1].value_len, "b"));
|
98
|
+
ok(headers[2].name == NULL);
|
99
|
+
ok(bufis(headers[2].value, headers[2].value_len, " \tc"));
|
100
|
+
|
101
|
+
PARSE("GET / HTTP/1.0\r\nfoo : ab\r\n\r\n", 0, 0,
|
102
|
+
"parse header name with trailing space");
|
103
|
+
ok(num_headers == 1);
|
104
|
+
ok(bufis(method, method_len, "GET"));
|
105
|
+
ok(bufis(path, path_len, "/"));
|
106
|
+
ok(minor_version == 0);
|
107
|
+
ok(bufis(headers[0].name, headers[0].name_len, "foo "));
|
108
|
+
ok(bufis(headers[0].value, headers[0].value_len, "ab"));
|
109
|
+
|
110
|
+
PARSE("GET", 0, -2, "incomplete 1");
|
111
|
+
ok(method == NULL);
|
112
|
+
PARSE("GET ", 0, -2, "incomplete 2");
|
113
|
+
ok(bufis(method, method_len, "GET"));
|
114
|
+
PARSE("GET /", 0, -2, "incomplete 3");
|
115
|
+
ok(path == NULL);
|
116
|
+
PARSE("GET / ", 0, -2, "incomplete 4");
|
117
|
+
ok(bufis(path, path_len, "/"));
|
118
|
+
PARSE("GET / H", 0, -2, "incomplete 5");
|
119
|
+
PARSE("GET / HTTP/1.", 0, -2, "incomplete 6");
|
120
|
+
PARSE("GET / HTTP/1.0", 0, -2, "incomplete 7");
|
121
|
+
ok(minor_version == -1);
|
122
|
+
PARSE("GET / HTTP/1.0\r", 0, -2, "incomplete 8");
|
123
|
+
ok(minor_version == 0);
|
124
|
+
|
125
|
+
PARSE("GET /hoge HTTP/1.0\r\n\r", strlen("GET /hoge HTTP/1.0\r\n\r") - 1,
|
126
|
+
-2, "slowloris (incomplete)");
|
127
|
+
PARSE("GET /hoge HTTP/1.0\r\n\r\n", strlen("GET /hoge HTTP/1.0\r\n\r\n") - 1,
|
128
|
+
0, "slowloris (complete)");
|
129
|
+
|
130
|
+
PARSE("GET / HTTP/1.0\r\n:a\r\n\r\n", 0, -1, "empty header name");
|
131
|
+
PARSE("GET / HTTP/1.0\r\n :a\r\n\r\n", 0, -1, "header name (space only)");
|
132
|
+
|
133
|
+
PARSE("G\0T / HTTP/1.0\r\n\r\n", 0, -1, "NUL in method");
|
134
|
+
PARSE("G\tT / HTTP/1.0\r\n\r\n", 0, -1, "tab in method");
|
135
|
+
PARSE("GET /\x7fhello HTTP/1.0\r\n\r\n", 0, -1, "DEL in uri-path");
|
136
|
+
PARSE("GET / HTTP/1.0\r\na\0b: c\r\n\r\n", 0, -1, "NUL in header name");
|
137
|
+
PARSE("GET / HTTP/1.0\r\nab: c\0d\r\n\r\n", 0, -1, "NUL in header value");
|
138
|
+
PARSE("GET / HTTP/1.0\r\na\033b: c\r\n\r\n", 0, -1, "CTL in header name");
|
139
|
+
PARSE("GET / HTTP/1.0\r\nab: c\033\r\n\r\n", 0, -1, "CTL in header value");
|
140
|
+
PARSE("GET /\xa0 HTTP/1.0\r\nh: c\xa2y\r\n\r\n", 0, 0, "accept MSB chars");
|
141
|
+
ok(num_headers == 1);
|
142
|
+
ok(bufis(method, method_len, "GET"));
|
143
|
+
ok(bufis(path, path_len, "/\xa0"));
|
144
|
+
ok(minor_version == 0);
|
145
|
+
ok(bufis(headers[0].name, headers[0].name_len, "h"));
|
146
|
+
ok(bufis(headers[0].value, headers[0].value_len, "c\xa2y"));
|
147
|
+
|
148
|
+
#undef PARSE
|
149
|
+
}
|
150
|
+
|
151
|
+
static void test_response(void)
|
152
|
+
{
|
153
|
+
int minor_version;
|
154
|
+
int status;
|
155
|
+
const char *msg;
|
156
|
+
size_t msg_len;
|
157
|
+
struct phr_header headers[4];
|
158
|
+
size_t num_headers;
|
159
|
+
|
160
|
+
#define PARSE(s, last_len, exp, comment) \
|
161
|
+
do { \
|
162
|
+
note(comment); \
|
163
|
+
num_headers = sizeof(headers) / sizeof(headers[0]); \
|
164
|
+
ok(phr_parse_response(s, strlen(s), &minor_version, &status, \
|
165
|
+
&msg, &msg_len, headers, \
|
166
|
+
&num_headers, last_len) \
|
167
|
+
== (exp == 0 ? strlen(s) : exp)); \
|
168
|
+
} while (0)
|
169
|
+
|
170
|
+
PARSE("HTTP/1.0 200 OK\r\n\r\n", 0, 0, "simple");
|
171
|
+
ok(num_headers == 0);
|
172
|
+
ok(status == 200);
|
173
|
+
ok(minor_version = 1);
|
174
|
+
ok(bufis(msg, msg_len, "OK"));
|
175
|
+
|
176
|
+
PARSE("HTTP/1.0 200 OK\r\n\r", 0, -2, "partial");
|
177
|
+
|
178
|
+
PARSE("HTTP/1.1 200 OK\r\nHost: example.com\r\nCookie: \r\n\r\n", 0, 0,
|
179
|
+
"parse headers");
|
180
|
+
ok(num_headers == 2);
|
181
|
+
ok(minor_version == 1);
|
182
|
+
ok(status == 200);
|
183
|
+
ok(bufis(msg, msg_len, "OK"));
|
184
|
+
ok(bufis(headers[0].name, headers[0].name_len, "Host"));
|
185
|
+
ok(bufis(headers[0].value, headers[0].value_len, "example.com"));
|
186
|
+
ok(bufis(headers[1].name, headers[1].name_len, "Cookie"));
|
187
|
+
ok(bufis(headers[1].value, headers[1].value_len, ""));
|
188
|
+
|
189
|
+
PARSE("HTTP/1.0 200 OK\r\nfoo: \r\nfoo: b\r\n \tc\r\n\r\n", 0, 0,
|
190
|
+
"parse multiline");
|
191
|
+
ok(num_headers == 3);
|
192
|
+
ok(minor_version == 0);
|
193
|
+
ok(status == 200);
|
194
|
+
ok(bufis(msg, msg_len, "OK"));
|
195
|
+
ok(bufis(headers[0].name, headers[0].name_len, "foo"));
|
196
|
+
ok(bufis(headers[0].value, headers[0].value_len, ""));
|
197
|
+
ok(bufis(headers[1].name, headers[1].name_len, "foo"));
|
198
|
+
ok(bufis(headers[1].value, headers[1].value_len, "b"));
|
199
|
+
ok(headers[2].name == NULL);
|
200
|
+
ok(bufis(headers[2].value, headers[2].value_len, " \tc"));
|
201
|
+
|
202
|
+
PARSE("HTTP/1.0 500 Internal Server Error\r\n\r\n", 0, 0,
|
203
|
+
"internal server error");
|
204
|
+
ok(num_headers == 0);
|
205
|
+
ok(minor_version == 0);
|
206
|
+
ok(status == 500);
|
207
|
+
ok(bufis(msg, msg_len, "Internal Server Error"));
|
208
|
+
ok(msg_len == sizeof("Internal Server Error")-1);
|
209
|
+
|
210
|
+
PARSE("H", 0, -2, "incomplete 1");
|
211
|
+
PARSE("HTTP/1.", 0, -2, "incomplete 2");
|
212
|
+
PARSE("HTTP/1.1", 0, -2, "incomplete 3");
|
213
|
+
ok(minor_version == -1);
|
214
|
+
PARSE("HTTP/1.1 ", 0, -2, "incomplete 4");
|
215
|
+
ok(minor_version == 1);
|
216
|
+
PARSE("HTTP/1.1 2", 0, -2, "incomplete 5");
|
217
|
+
PARSE("HTTP/1.1 200", 0, -2, "incomplete 6");
|
218
|
+
ok(status == 0);
|
219
|
+
PARSE("HTTP/1.1 200 ", 0, -2, "incomplete 7");
|
220
|
+
ok(status == 200);
|
221
|
+
PARSE("HTTP/1.1 200 O", 0, -2, "incomplete 8");
|
222
|
+
PARSE("HTTP/1.1 200 OK\r", 0, -2, "incomplete 9");
|
223
|
+
ok(msg == NULL);
|
224
|
+
PARSE("HTTP/1.1 200 OK\r\n", 0, -2, "incomplete 10");
|
225
|
+
ok(bufis(msg, msg_len, "OK"));
|
226
|
+
PARSE("HTTP/1.1 200 OK\n", 0, -2, "incomplete 11");
|
227
|
+
ok(bufis(msg, msg_len, "OK"));
|
228
|
+
|
229
|
+
PARSE("HTTP/1.1 200 OK\r\nA: 1\r", 0, -2, "incomplete 11");
|
230
|
+
ok(num_headers == 0);
|
231
|
+
PARSE("HTTP/1.1 200 OK\r\nA: 1\r\n", 0, -2, "incomplete 12");
|
232
|
+
ok(num_headers == 1);
|
233
|
+
ok(bufis(headers[0].name, headers[0].name_len, "A"));
|
234
|
+
ok(bufis(headers[0].value, headers[0].value_len, "1"));
|
235
|
+
|
236
|
+
PARSE("HTTP/1.0 200 OK\r\n\r", strlen("GET /hoge HTTP/1.0\r\n\r") - 1,
|
237
|
+
-2, "slowloris (incomplete)");
|
238
|
+
PARSE("HTTP/1.0 200 OK\r\n\r\n", strlen("HTTP/1.0 200 OK\r\n\r\n") - 1,
|
239
|
+
0, "slowloris (complete)");
|
240
|
+
|
241
|
+
PARSE("HTTP/1. 200 OK\r\n\r\n", 0, -1, "invalid http version");
|
242
|
+
PARSE("HTTP/1.2z 200 OK\r\n\r\n", 0, -1, "invalid http version 2");
|
243
|
+
PARSE("HTTP/1.1 OK\r\n\r\n", 0, -1, "no status code");
|
244
|
+
|
245
|
+
#undef PARSE
|
246
|
+
}
|
247
|
+
|
248
|
+
int main(int argc, char **argv)
|
249
|
+
{
|
250
|
+
subtest("request", test_request);
|
251
|
+
subtest("response", test_response);
|
252
|
+
return done_testing();
|
253
|
+
}
|
data/ext/rhebok/rhebok.c
ADDED
@@ -0,0 +1,795 @@
|
|
1
|
+
#include <ruby.h>
|
2
|
+
#include <time.h>
|
3
|
+
#include <ctype.h>
|
4
|
+
#include <poll.h>
|
5
|
+
#include <sys/uio.h>
|
6
|
+
#include <errno.h>
|
7
|
+
#include <limits.h>
|
8
|
+
#include <unistd.h>
|
9
|
+
#include <fcntl.h>
|
10
|
+
#include <sys/types.h>
|
11
|
+
#include <sys/socket.h>
|
12
|
+
#include <netinet/in.h>
|
13
|
+
#include <netinet/tcp.h>
|
14
|
+
#include <arpa/inet.h>
|
15
|
+
#include "picohttpparser/picohttpparser.c"
|
16
|
+
|
17
|
+
#define MAX_HEADER_SIZE 16384
|
18
|
+
#define MAX_HEADER_NAME_LEN 1024
|
19
|
+
#define MAX_HEADERS 128
|
20
|
+
#define BAD_REQUEST "HTTP/1.0 400 Bad Request\r\nConnection: close\r\n\r\n400 Bad Request\r\n"
|
21
|
+
#define TOU(ch) (('a' <= ch && ch <= 'z') ? ch - ('a' - 'A') : ch)
|
22
|
+
|
23
|
+
static const char *DoW[] = {
|
24
|
+
"Sun","Mon","Tue","Wed","Thu","Fri","Sat"
|
25
|
+
};
|
26
|
+
static const char *MoY[] = {
|
27
|
+
"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"
|
28
|
+
};
|
29
|
+
|
30
|
+
/* stolen from HTTP::Status and Feersum */
|
31
|
+
/* Unmarked codes are from RFC 2616 */
|
32
|
+
/* See also: http://en.wikipedia.org/wiki/List_of_HTTP_status_codes */
|
33
|
+
static const char *
|
34
|
+
status_message (int code) {
|
35
|
+
switch (code) {
|
36
|
+
case 100: return "Continue";
|
37
|
+
case 101: return "Switching Protocols";
|
38
|
+
case 102: return "Processing"; /* RFC 2518 (WebDAV) */
|
39
|
+
case 200: return "OK";
|
40
|
+
case 201: return "Created";
|
41
|
+
case 202: return "Accepted";
|
42
|
+
case 203: return "Non-Authoritative Information";
|
43
|
+
case 204: return "No Content";
|
44
|
+
case 205: return "Reset Content";
|
45
|
+
case 206: return "Partial Content";
|
46
|
+
case 207: return "Multi-Status"; /* RFC 2518 (WebDAV) */
|
47
|
+
case 208: return "Already Reported"; /* RFC 5842 */
|
48
|
+
case 300: return "Multiple Choices";
|
49
|
+
case 301: return "Moved Permanently";
|
50
|
+
case 302: return "Found";
|
51
|
+
case 303: return "See Other";
|
52
|
+
case 304: return "Not Modified";
|
53
|
+
case 305: return "Use Proxy";
|
54
|
+
case 307: return "Temporary Redirect";
|
55
|
+
case 400: return "Bad Request";
|
56
|
+
case 401: return "Unauthorized";
|
57
|
+
case 402: return "Payment Required";
|
58
|
+
case 403: return "Forbidden";
|
59
|
+
case 404: return "Not Found";
|
60
|
+
case 405: return "Method Not Allowed";
|
61
|
+
case 406: return "Not Acceptable";
|
62
|
+
case 407: return "Proxy Authentication Required";
|
63
|
+
case 408: return "Request Timeout";
|
64
|
+
case 409: return "Conflict";
|
65
|
+
case 410: return "Gone";
|
66
|
+
case 411: return "Length Required";
|
67
|
+
case 412: return "Precondition Failed";
|
68
|
+
case 413: return "Request Entity Too Large";
|
69
|
+
case 414: return "Request-URI Too Large";
|
70
|
+
case 415: return "Unsupported Media Type";
|
71
|
+
case 416: return "Request Range Not Satisfiable";
|
72
|
+
case 417: return "Expectation Failed";
|
73
|
+
case 418: return "I'm a teapot"; /* RFC 2324 */
|
74
|
+
case 422: return "Unprocessable Entity"; /* RFC 2518 (WebDAV) */
|
75
|
+
case 423: return "Locked"; /* RFC 2518 (WebDAV) */
|
76
|
+
case 424: return "Failed Dependency"; /* RFC 2518 (WebDAV) */
|
77
|
+
case 425: return "No code"; /* WebDAV Advanced Collections */
|
78
|
+
case 426: return "Upgrade Required"; /* RFC 2817 */
|
79
|
+
case 428: return "Precondition Required";
|
80
|
+
case 429: return "Too Many Requests";
|
81
|
+
case 431: return "Request Header Fields Too Large";
|
82
|
+
case 449: return "Retry with"; /* unofficial Microsoft */
|
83
|
+
case 500: return "Internal Server Error";
|
84
|
+
case 501: return "Not Implemented";
|
85
|
+
case 502: return "Bad Gateway";
|
86
|
+
case 503: return "Service Unavailable";
|
87
|
+
case 504: return "Gateway Timeout";
|
88
|
+
case 505: return "HTTP Version Not Supported";
|
89
|
+
case 506: return "Variant Also Negotiates"; /* RFC 2295 */
|
90
|
+
case 507: return "Insufficient Storage"; /* RFC 2518 (WebDAV) */
|
91
|
+
case 509: return "Bandwidth Limit Exceeded"; /* unofficial */
|
92
|
+
case 510: return "Not Extended"; /* RFC 2774 */
|
93
|
+
case 511: return "Network Authentication Required";
|
94
|
+
default: break;
|
95
|
+
}
|
96
|
+
/* default to the Nxx group names in RFC 2616 */
|
97
|
+
if (100 <= code && code <= 199) {
|
98
|
+
return "Informational";
|
99
|
+
}
|
100
|
+
else if (200 <= code && code <= 299) {
|
101
|
+
return "Success";
|
102
|
+
}
|
103
|
+
else if (300 <= code && code <= 399) {
|
104
|
+
return "Redirection";
|
105
|
+
}
|
106
|
+
else if (400 <= code && code <= 499) {
|
107
|
+
return "Client Error";
|
108
|
+
}
|
109
|
+
else {
|
110
|
+
return "Error";
|
111
|
+
}
|
112
|
+
}
|
113
|
+
|
114
|
+
static VALUE cRhebok;
|
115
|
+
|
116
|
+
static VALUE request_method_key;
|
117
|
+
static VALUE request_uri_key;
|
118
|
+
static VALUE script_name_key;
|
119
|
+
static VALUE server_protocol_key;
|
120
|
+
static VALUE query_string_key;
|
121
|
+
static VALUE remote_addr_key;
|
122
|
+
static VALUE remote_port_key;
|
123
|
+
|
124
|
+
struct common_header {
|
125
|
+
const char * name;
|
126
|
+
size_t name_len;
|
127
|
+
VALUE key;
|
128
|
+
};
|
129
|
+
static int common_headers_num = 0;
|
130
|
+
static struct common_header common_headers[20];
|
131
|
+
|
132
|
+
static
|
133
|
+
void set_common_header(const char * key, int key_len, const int raw)
|
134
|
+
{
|
135
|
+
char tmp[MAX_HEADER_NAME_LEN + sizeof("HTTP_") - 1];
|
136
|
+
const char* name;
|
137
|
+
size_t name_len = 0;
|
138
|
+
const char * s;
|
139
|
+
char* d;
|
140
|
+
size_t n;
|
141
|
+
VALUE env_key;
|
142
|
+
|
143
|
+
if ( raw == 1) {
|
144
|
+
for (s = key, n = key_len, d = tmp;
|
145
|
+
n != 0;
|
146
|
+
s++, --n, d++) {
|
147
|
+
*d = *s == '-' ? '_' : TOU(*s);
|
148
|
+
name = tmp;
|
149
|
+
name_len = key_len;
|
150
|
+
}
|
151
|
+
} else {
|
152
|
+
strcpy(tmp, "HTTP_");
|
153
|
+
for (s = key, n = key_len, d = tmp + 5;
|
154
|
+
n != 0;
|
155
|
+
s++, --n, d++) {
|
156
|
+
*d = *s == '-' ? '_' : TOU(*s);
|
157
|
+
name = tmp;
|
158
|
+
name_len = key_len + 5;
|
159
|
+
}
|
160
|
+
}
|
161
|
+
env_key = rb_obj_freeze(rb_str_new(name,name_len));
|
162
|
+
common_headers[common_headers_num].name = key;
|
163
|
+
common_headers[common_headers_num].name_len = key_len;
|
164
|
+
common_headers[common_headers_num].key = env_key;
|
165
|
+
rb_gc_register_address(&common_headers[common_headers_num].key);
|
166
|
+
common_headers_num++;
|
167
|
+
}
|
168
|
+
|
169
|
+
|
170
|
+
static
|
171
|
+
size_t find_ch(const char* s, size_t len, char ch)
|
172
|
+
{
|
173
|
+
size_t i;
|
174
|
+
for (i = 0; i != len; ++i, ++s)
|
175
|
+
if (*s == ch)
|
176
|
+
break;
|
177
|
+
return i;
|
178
|
+
}
|
179
|
+
|
180
|
+
static
|
181
|
+
int header_is(const struct phr_header* header, const char* name,
|
182
|
+
size_t len)
|
183
|
+
{
|
184
|
+
const char* x, * y;
|
185
|
+
if (header->name_len != len)
|
186
|
+
return 0;
|
187
|
+
for (x = header->name, y = name; len != 0; --len, ++x, ++y)
|
188
|
+
if (TOU(*x) != *y)
|
189
|
+
return 0;
|
190
|
+
return 1;
|
191
|
+
}
|
192
|
+
|
193
|
+
static
|
194
|
+
VALUE find_common_header(const struct phr_header* header) {
|
195
|
+
int i;
|
196
|
+
for ( i = 0; i < common_headers_num; i++ ) {
|
197
|
+
if ( header_is(header, common_headers[i].name, common_headers[i].name_len) ) {
|
198
|
+
return common_headers[i].key;
|
199
|
+
}
|
200
|
+
}
|
201
|
+
return Qnil;
|
202
|
+
}
|
203
|
+
|
204
|
+
static
|
205
|
+
int store_path_info(VALUE env, const char* src, size_t src_len) {
|
206
|
+
size_t dlen = 0, i = 0;
|
207
|
+
char *d;
|
208
|
+
char s2, s3;
|
209
|
+
|
210
|
+
d = (char*)malloc(src_len * 3 + 1);
|
211
|
+
for (i = 0; i < src_len; i++ ) {
|
212
|
+
if ( src[i] == '%' ) {
|
213
|
+
if ( !isxdigit(src[i+1]) || !isxdigit(src[i+2]) ) {
|
214
|
+
free(d);
|
215
|
+
return -1;
|
216
|
+
}
|
217
|
+
s2 = src[i+1];
|
218
|
+
s3 = src[i+2];
|
219
|
+
s2 -= s2 <= '9' ? '0'
|
220
|
+
: s2 <= 'F' ? 'A' - 10
|
221
|
+
: 'a' - 10;
|
222
|
+
s3 -= s3 <= '9' ? '0'
|
223
|
+
: s3 <= 'F' ? 'A' - 10
|
224
|
+
: 'a' - 10;
|
225
|
+
d[dlen++] = s2 * 16 + s3;
|
226
|
+
i += 2;
|
227
|
+
}
|
228
|
+
else {
|
229
|
+
d[dlen++] = src[i];
|
230
|
+
}
|
231
|
+
}
|
232
|
+
d[dlen]='0';
|
233
|
+
rb_hash_aset(env, rb_str_new2("PATH_INFO"), rb_str_new(d, dlen));
|
234
|
+
free(d);
|
235
|
+
return dlen;
|
236
|
+
}
|
237
|
+
|
238
|
+
static
|
239
|
+
int _accept(int fileno, struct sockaddr *addr, unsigned int addrlen) {
|
240
|
+
int fd;
|
241
|
+
#ifdef SOCK_NONBLOCK
|
242
|
+
fd = accept4(fileno, addr, &addrlen, SOCK_CLOEXEC|SOCK_NONBLOCK);
|
243
|
+
#else
|
244
|
+
fd = accept(fileno, addr, &addrlen);
|
245
|
+
#endif
|
246
|
+
if (fd < 0) {
|
247
|
+
if ( errno == EINTR ) {
|
248
|
+
rb_thread_sleep(1);
|
249
|
+
}
|
250
|
+
return fd;
|
251
|
+
}
|
252
|
+
#ifndef SOCK_NONBLOCK
|
253
|
+
fcntl(fd, F_SETFD, FD_CLOEXEC);
|
254
|
+
fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
|
255
|
+
#endif
|
256
|
+
return fd;
|
257
|
+
}
|
258
|
+
|
259
|
+
static
|
260
|
+
ssize_t _writev_timeout(const int fileno, const double timeout, struct iovec *iovec, const int iovcnt, const int do_select ) {
|
261
|
+
int rv;
|
262
|
+
int nfound;
|
263
|
+
struct pollfd wfds[1];
|
264
|
+
if ( do_select == 1) goto WAIT_WRITE;
|
265
|
+
DO_WRITE:
|
266
|
+
rv = writev(fileno, iovec, iovcnt);
|
267
|
+
if ( rv >= 0 ) {
|
268
|
+
return rv;
|
269
|
+
}
|
270
|
+
if ( rv < 0 && errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK ) {
|
271
|
+
return rv;
|
272
|
+
}
|
273
|
+
WAIT_WRITE:
|
274
|
+
while (1) {
|
275
|
+
wfds[0].fd = fileno;
|
276
|
+
wfds[0].events = POLLOUT;
|
277
|
+
nfound = poll(wfds, 1, (int)timeout*1000);
|
278
|
+
if ( nfound == 1 ) {
|
279
|
+
break;
|
280
|
+
}
|
281
|
+
if ( nfound == 0 && errno != EINTR ) {
|
282
|
+
return -1;
|
283
|
+
}
|
284
|
+
}
|
285
|
+
goto DO_WRITE;
|
286
|
+
}
|
287
|
+
|
288
|
+
static
|
289
|
+
ssize_t _read_timeout(const int fileno, const double timeout, char * read_buf, const int read_len ) {
|
290
|
+
int rv;
|
291
|
+
int nfound;
|
292
|
+
struct pollfd rfds[1];
|
293
|
+
DO_READ:
|
294
|
+
rfds[0].fd = fileno;
|
295
|
+
rfds[0].events = POLLIN;
|
296
|
+
rv = read(fileno, read_buf, read_len);
|
297
|
+
if ( rv >= 0 ) {
|
298
|
+
return rv;
|
299
|
+
}
|
300
|
+
if ( rv < 0 && errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK ) {
|
301
|
+
return rv;
|
302
|
+
}
|
303
|
+
while (1) {
|
304
|
+
nfound = poll(rfds, 1, (int)timeout*1000);
|
305
|
+
if ( nfound == 1 ) {
|
306
|
+
break;
|
307
|
+
}
|
308
|
+
if ( nfound == 0 && errno != EINTR ) {
|
309
|
+
return -1;
|
310
|
+
}
|
311
|
+
}
|
312
|
+
goto DO_READ;
|
313
|
+
}
|
314
|
+
|
315
|
+
static
|
316
|
+
ssize_t _write_timeout(const int fileno, const double timeout, char * write_buf, const int write_len ) {
|
317
|
+
int rv;
|
318
|
+
int nfound;
|
319
|
+
struct pollfd wfds[1];
|
320
|
+
DO_WRITE:
|
321
|
+
rv = write(fileno, write_buf, write_len);
|
322
|
+
if ( rv >= 0 ) {
|
323
|
+
return rv;
|
324
|
+
}
|
325
|
+
if ( rv < 0 && errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK ) {
|
326
|
+
return rv;
|
327
|
+
}
|
328
|
+
while (1) {
|
329
|
+
wfds[0].fd = fileno;
|
330
|
+
wfds[0].events = POLLOUT;
|
331
|
+
nfound = poll(wfds, 1, (int)timeout*1000);
|
332
|
+
if ( nfound == 1 ) {
|
333
|
+
break;
|
334
|
+
}
|
335
|
+
if ( nfound == 0 && errno != EINTR ) {
|
336
|
+
return -1;
|
337
|
+
}
|
338
|
+
}
|
339
|
+
goto DO_WRITE;
|
340
|
+
}
|
341
|
+
|
342
|
+
static
|
343
|
+
void str_s(char * dst, int *dst_len, const char * src, int src_len) {
|
344
|
+
int i;
|
345
|
+
int dlen = *dst_len;
|
346
|
+
for ( i=0; i<src_len; i++) {
|
347
|
+
dst[dlen++] = src[i];
|
348
|
+
}
|
349
|
+
*dst_len = dlen;
|
350
|
+
}
|
351
|
+
|
352
|
+
static
|
353
|
+
void str_i(char * dst, int * dst_len, int src, int fig) {
|
354
|
+
int dlen = *dst_len + fig - 1;
|
355
|
+
do {
|
356
|
+
dst[dlen] = '0' + (src % 10);
|
357
|
+
dlen--;
|
358
|
+
src /= 10;
|
359
|
+
} while( dlen >= *dst_len );
|
360
|
+
*dst_len += fig;
|
361
|
+
}
|
362
|
+
|
363
|
+
static
|
364
|
+
int _date_line(char * date_line) {
|
365
|
+
struct tm gtm;
|
366
|
+
time_t lt;
|
367
|
+
int i = 0;
|
368
|
+
time(<);
|
369
|
+
gmtime_r(<, >m);
|
370
|
+
date_line[i++] = 'D';
|
371
|
+
date_line[i++] = 'a';
|
372
|
+
date_line[i++] = 't';
|
373
|
+
date_line[i++] = 'e';
|
374
|
+
date_line[i++] = ':';
|
375
|
+
date_line[i++] = ' ';
|
376
|
+
str_s(date_line, &i, DoW[gtm.tm_wday], 3);
|
377
|
+
date_line[i++] = ',';
|
378
|
+
date_line[i++] = ' ';
|
379
|
+
str_i(date_line, &i, gtm.tm_mday, 2);
|
380
|
+
date_line[i++] = ' ';
|
381
|
+
str_s(date_line, &i, MoY[gtm.tm_mon], 3);
|
382
|
+
date_line[i++] = ' ';
|
383
|
+
str_i(date_line, &i, gtm.tm_year + 1900, 4);
|
384
|
+
date_line[i++] = ' ';
|
385
|
+
str_i(date_line, &i, gtm.tm_hour,2);
|
386
|
+
date_line[i++] = ':';
|
387
|
+
str_i(date_line, &i, gtm.tm_min,2);
|
388
|
+
date_line[i++] = ':';
|
389
|
+
str_i(date_line, &i, gtm.tm_sec,2);
|
390
|
+
date_line[i++] = ' ';
|
391
|
+
date_line[i++] = 'G';
|
392
|
+
date_line[i++] = 'M';
|
393
|
+
date_line[i++] = 'T';
|
394
|
+
date_line[i++] = 13;
|
395
|
+
date_line[i++] = 10;
|
396
|
+
return i;
|
397
|
+
}
|
398
|
+
|
399
|
+
static
|
400
|
+
int _parse_http_request(char *buf, ssize_t buf_len, VALUE env) {
|
401
|
+
const char* method;
|
402
|
+
size_t method_len;
|
403
|
+
const char* path;
|
404
|
+
size_t path_len;
|
405
|
+
int minor_version;
|
406
|
+
struct phr_header headers[MAX_HEADERS];
|
407
|
+
size_t num_headers, question_at;
|
408
|
+
size_t i;
|
409
|
+
int ret;
|
410
|
+
char tmp[MAX_HEADER_NAME_LEN + sizeof("HTTP_") - 1];
|
411
|
+
VALUE last_value;
|
412
|
+
|
413
|
+
num_headers = MAX_HEADERS;
|
414
|
+
ret = phr_parse_request(buf, buf_len, &method, &method_len, &path,
|
415
|
+
&path_len, &minor_version, headers, &num_headers, 0);
|
416
|
+
if (ret < 0)
|
417
|
+
goto done;
|
418
|
+
|
419
|
+
rb_hash_aset(env, request_method_key, rb_str_new(method,method_len));
|
420
|
+
rb_hash_aset(env, request_uri_key, rb_str_new(path, path_len));
|
421
|
+
rb_hash_aset(env, script_name_key, rb_str_new2(""));
|
422
|
+
strcpy(tmp, "HTTP/1.");
|
423
|
+
tmp[7] = 48 + ((minor_version > 1 || minor_version < 0 ) ? 0 : minor_version);
|
424
|
+
rb_hash_aset(env, server_protocol_key, rb_str_new(tmp, sizeof("HTTP/1.0") - 1));
|
425
|
+
|
426
|
+
/* PATH_INFO QUERY_STRING */
|
427
|
+
path_len = find_ch(path, path_len, '#'); /* strip off all text after # after storing request_uri */
|
428
|
+
question_at = find_ch(path, path_len, '?');
|
429
|
+
if ( store_path_info(env, path, question_at) < 0 ) {
|
430
|
+
rb_hash_clear(env);
|
431
|
+
ret = -1;
|
432
|
+
goto done;
|
433
|
+
}
|
434
|
+
if (question_at != path_len) ++question_at;
|
435
|
+
rb_hash_aset(env, query_string_key, rb_str_new(path + question_at, path_len - question_at));
|
436
|
+
last_value = Qnil;
|
437
|
+
for (i = 0; i < num_headers; ++i) {
|
438
|
+
if (headers[i].name != NULL) {
|
439
|
+
const char* name;
|
440
|
+
size_t name_len;
|
441
|
+
VALUE slot;
|
442
|
+
VALUE env_key;
|
443
|
+
env_key = find_common_header(headers + i);
|
444
|
+
if ( env_key == Qnil ) {
|
445
|
+
const char* s;
|
446
|
+
char* d;
|
447
|
+
size_t n;
|
448
|
+
if (sizeof(tmp) - 5 < headers[i].name_len) {
|
449
|
+
rb_hash_clear(env);
|
450
|
+
ret = -1;
|
451
|
+
goto done;
|
452
|
+
}
|
453
|
+
strcpy(tmp, "HTTP_");
|
454
|
+
for (s = headers[i].name, n = headers[i].name_len, d = tmp + 5;
|
455
|
+
n != 0;
|
456
|
+
s++, --n, d++) {
|
457
|
+
*d = *s == '-' ? '_' : TOU(*s);
|
458
|
+
name = tmp;
|
459
|
+
name_len = headers[i].name_len + 5;
|
460
|
+
env_key = rb_str_new(name, name_len);
|
461
|
+
}
|
462
|
+
}
|
463
|
+
slot = rb_hash_aref(env, env_key);
|
464
|
+
if ( slot != Qnil ) {
|
465
|
+
rb_str_cat2(slot, ", ");
|
466
|
+
rb_str_cat(slot, headers[i].value, headers[i].value_len);
|
467
|
+
} else {
|
468
|
+
slot = rb_str_new(headers[i].value, headers[i].value_len);
|
469
|
+
rb_hash_aset(env, env_key, slot);
|
470
|
+
last_value = slot;
|
471
|
+
}
|
472
|
+
} else {
|
473
|
+
/* continuing lines of a mulitiline header */
|
474
|
+
if ( last_value != Qnil )
|
475
|
+
rb_str_cat(last_value, headers[i].value, headers[i].value_len);
|
476
|
+
}
|
477
|
+
}
|
478
|
+
|
479
|
+
done:
|
480
|
+
return ret;
|
481
|
+
}
|
482
|
+
|
483
|
+
|
484
|
+
|
485
|
+
static
|
486
|
+
VALUE rhe_accept(VALUE self, VALUE fileno, VALUE timeoutv, VALUE tcp, VALUE env) {
|
487
|
+
struct sockaddr_in cliaddr;
|
488
|
+
unsigned int len;
|
489
|
+
char read_buf[MAX_HEADER_SIZE];
|
490
|
+
VALUE req;
|
491
|
+
int flag = 1;
|
492
|
+
ssize_t rv = 0;
|
493
|
+
ssize_t buf_len;
|
494
|
+
ssize_t reqlen;
|
495
|
+
int fd;
|
496
|
+
double timeout = NUM2DBL(timeoutv);
|
497
|
+
|
498
|
+
len = sizeof(cliaddr);
|
499
|
+
fd = _accept(NUM2INT(fileno), (struct sockaddr *)&cliaddr, len);
|
500
|
+
|
501
|
+
/* endif */
|
502
|
+
if (fd < 0) {
|
503
|
+
goto badexit;
|
504
|
+
}
|
505
|
+
|
506
|
+
rv = _read_timeout(fd, timeout, &read_buf[0], MAX_HEADER_SIZE);
|
507
|
+
if ( rv <= 0 ) {
|
508
|
+
close(fd);
|
509
|
+
goto badexit;
|
510
|
+
}
|
511
|
+
|
512
|
+
if ( tcp == Qtrue ) {
|
513
|
+
setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char*)&flag, sizeof(int));
|
514
|
+
rb_hash_aset(env, remote_addr_key, rb_str_new2(inet_ntoa(cliaddr.sin_addr)));
|
515
|
+
rb_hash_aset(env, remote_port_key, rb_String(rb_int_new(ntohs(cliaddr.sin_port))));
|
516
|
+
}
|
517
|
+
else {
|
518
|
+
rb_hash_aset(env, remote_addr_key, rb_str_new("",0));
|
519
|
+
rb_hash_aset(env, remote_port_key, rb_String(rb_int_new(0)));
|
520
|
+
}
|
521
|
+
|
522
|
+
buf_len = rv;
|
523
|
+
while (1) {
|
524
|
+
reqlen = _parse_http_request(&read_buf[0],buf_len,env);
|
525
|
+
if ( reqlen >= 0 ) {
|
526
|
+
break;
|
527
|
+
}
|
528
|
+
else if ( reqlen == -1 ) {
|
529
|
+
/* error */
|
530
|
+
close(fd);
|
531
|
+
goto badexit;
|
532
|
+
}
|
533
|
+
if ( MAX_HEADER_SIZE - buf_len == 0 ) {
|
534
|
+
/* too large header */
|
535
|
+
char* badreq;
|
536
|
+
badreq = BAD_REQUEST;
|
537
|
+
rv = _write_timeout(fd, timeout, badreq, sizeof(BAD_REQUEST) - 1);
|
538
|
+
close(fd);
|
539
|
+
goto badexit;
|
540
|
+
}
|
541
|
+
/* request is incomplete */
|
542
|
+
rv = _read_timeout(fd, timeout, &read_buf[buf_len], MAX_HEADER_SIZE - buf_len);
|
543
|
+
if ( rv <= 0 ) {
|
544
|
+
close(fd);
|
545
|
+
goto badexit;
|
546
|
+
}
|
547
|
+
buf_len += rv;
|
548
|
+
}
|
549
|
+
|
550
|
+
req = rb_ary_new2(2);
|
551
|
+
rb_ary_push(req, rb_int_new(fd));
|
552
|
+
rb_ary_push(req, rb_str_new(&read_buf[reqlen],buf_len - reqlen));
|
553
|
+
return req;
|
554
|
+
badexit:
|
555
|
+
return Qnil;
|
556
|
+
}
|
557
|
+
|
558
|
+
static
|
559
|
+
VALUE rhe_read_timeout(VALUE self, VALUE fileno, VALUE rbuf, VALUE len, VALUE offset, VALUE timeout) {
|
560
|
+
char * d;
|
561
|
+
ssize_t rv;
|
562
|
+
d = malloc(len);
|
563
|
+
rv = _read_timeout(NUM2INT(fileno), NUM2DBL(timeout), &d[NUM2LONG(offset)], NUM2LONG(len));
|
564
|
+
if ( rv > 0 ) {
|
565
|
+
rb_str_cat(rbuf, d, rv);
|
566
|
+
}
|
567
|
+
free(d);
|
568
|
+
return rb_int_new(rv);
|
569
|
+
}
|
570
|
+
|
571
|
+
static
|
572
|
+
VALUE rhe_write_timeout(VALUE self, VALUE fileno, VALUE buf, VALUE len, VALUE offset, VALUE timeout) {
|
573
|
+
char* d;
|
574
|
+
ssize_t rv;
|
575
|
+
|
576
|
+
d = StringValuePtr(buf);
|
577
|
+
rv = _write_timeout(NUM2INT(fileno), NUM2DBL(timeout), &d[NUM2LONG(offset)], NUM2LONG(len));
|
578
|
+
if ( rv < 0 ) {
|
579
|
+
return Qnil;
|
580
|
+
}
|
581
|
+
return rb_int_new(rv);
|
582
|
+
}
|
583
|
+
|
584
|
+
|
585
|
+
static
|
586
|
+
VALUE rhe_write_all(VALUE self, VALUE fileno, VALUE buf, VALUE offsetv, VALUE timeout) {
|
587
|
+
char * d;
|
588
|
+
ssize_t buf_len;
|
589
|
+
ssize_t rv = 0;
|
590
|
+
ssize_t written = 0;
|
591
|
+
|
592
|
+
d = StringValuePtr(buf);
|
593
|
+
buf_len = RSTRING_LEN(buf);
|
594
|
+
|
595
|
+
written = 0;
|
596
|
+
while ( buf_len > written ) {
|
597
|
+
rv = _write_timeout(NUM2INT(fileno), NUM2DBL(timeout), &d[written], buf_len - written);
|
598
|
+
if ( rv <= 0 ) {
|
599
|
+
break;
|
600
|
+
}
|
601
|
+
written += rv;
|
602
|
+
}
|
603
|
+
if (rv < 0) {
|
604
|
+
return Qnil;
|
605
|
+
}
|
606
|
+
return rb_int_new(written);
|
607
|
+
}
|
608
|
+
|
609
|
+
static
|
610
|
+
int my_hash_keys(VALUE key, VALUE val, VALUE ary) {
|
611
|
+
rb_ary_push(ary, key);
|
612
|
+
return ST_CONTINUE;
|
613
|
+
}
|
614
|
+
|
615
|
+
static
|
616
|
+
VALUE rhe_close(VALUE self, VALUE fileno) {
|
617
|
+
close(NUM2INT(fileno));
|
618
|
+
return Qnil;
|
619
|
+
}
|
620
|
+
|
621
|
+
static
|
622
|
+
VALUE rhe_write_response(VALUE self, VALUE filenov, VALUE timeoutv, VALUE status_codev, VALUE headers, VALUE body) {
|
623
|
+
ssize_t hlen;
|
624
|
+
ssize_t blen;
|
625
|
+
|
626
|
+
ssize_t len;
|
627
|
+
ssize_t rv = 0;
|
628
|
+
ssize_t iovcnt;
|
629
|
+
ssize_t vec_offset;
|
630
|
+
ssize_t written;
|
631
|
+
int count;
|
632
|
+
int i;
|
633
|
+
char status_line[512];
|
634
|
+
char date_line[512];
|
635
|
+
int date_pushed = 0;
|
636
|
+
VALUE arr;
|
637
|
+
VALUE key_obj;
|
638
|
+
VALUE val_obj;
|
639
|
+
char * key;
|
640
|
+
const char * message;
|
641
|
+
|
642
|
+
int fileno = NUM2INT(filenov);
|
643
|
+
double timeout = NUM2DBL(timeoutv);
|
644
|
+
int status_code = NUM2INT(status_codev);
|
645
|
+
|
646
|
+
arr = rb_ary_new();
|
647
|
+
rb_hash_foreach(headers, my_hash_keys, arr);
|
648
|
+
hlen = RARRAY_LEN(arr);
|
649
|
+
blen = RARRAY_LEN(body);
|
650
|
+
iovcnt = 10 + (hlen * 2) + blen;
|
651
|
+
|
652
|
+
{
|
653
|
+
struct iovec v[iovcnt]; // Needs C99 compiler
|
654
|
+
/* status line */
|
655
|
+
iovcnt = 0;
|
656
|
+
i=0;
|
657
|
+
status_line[i++] = 'H';
|
658
|
+
status_line[i++] = 'T';
|
659
|
+
status_line[i++] = 'T';
|
660
|
+
status_line[i++] = 'P';
|
661
|
+
status_line[i++] = '/';
|
662
|
+
status_line[i++] = '1';
|
663
|
+
status_line[i++] = '.';
|
664
|
+
status_line[i++] = '0';
|
665
|
+
status_line[i++] = ' ';
|
666
|
+
str_i(status_line,&i,status_code,3);
|
667
|
+
status_line[i++] = ' ';
|
668
|
+
message = status_message(status_code);
|
669
|
+
str_s(status_line, &i, message, strlen(message));
|
670
|
+
status_line[i++] = 13;
|
671
|
+
status_line[i++] = 10;
|
672
|
+
v[iovcnt].iov_base = status_line;
|
673
|
+
v[iovcnt].iov_len = i;
|
674
|
+
iovcnt++;
|
675
|
+
|
676
|
+
v[iovcnt].iov_base = "Connection: close\r\nServer: Rhebok\r\n";
|
677
|
+
v[iovcnt].iov_len = sizeof("Connection: close\r\nServer: Rhebok\r\n")-1;
|
678
|
+
iovcnt++;
|
679
|
+
|
680
|
+
date_pushed = 0;
|
681
|
+
for ( i = 0; i < hlen; i++ ) {
|
682
|
+
key_obj = rb_ary_entry(arr, i);
|
683
|
+
key = StringValuePtr(key_obj);
|
684
|
+
len = RSTRING_LEN(key_obj);
|
685
|
+
if ( strncasecmp(key,"Connection",len) == 0 ) {
|
686
|
+
continue;
|
687
|
+
}
|
688
|
+
if ( strncasecmp(key,"Server",len) == 0 ) {
|
689
|
+
v[1].iov_len -= (sizeof("Server: Rhebok\r\n") - 1);
|
690
|
+
}
|
691
|
+
if ( strncasecmp(key,"Date",len) == 0 ) {
|
692
|
+
date_pushed = 1;
|
693
|
+
}
|
694
|
+
v[iovcnt].iov_base = key;
|
695
|
+
v[iovcnt].iov_len = len;
|
696
|
+
iovcnt++;
|
697
|
+
v[iovcnt].iov_base = ": ";
|
698
|
+
v[iovcnt].iov_len = sizeof(": ") - 1;
|
699
|
+
iovcnt++;
|
700
|
+
/* value */
|
701
|
+
val_obj = rb_hash_aref(headers, key_obj);
|
702
|
+
v[iovcnt].iov_base = StringValuePtr(val_obj);
|
703
|
+
v[iovcnt].iov_len = RSTRING_LEN(val_obj);
|
704
|
+
iovcnt++;
|
705
|
+
v[iovcnt].iov_base = "\r\n";
|
706
|
+
v[iovcnt].iov_len = sizeof("\r\n") - 1;
|
707
|
+
iovcnt++;
|
708
|
+
}
|
709
|
+
|
710
|
+
if ( date_pushed == 0 ) {
|
711
|
+
v[iovcnt].iov_len = _date_line(date_line);
|
712
|
+
v[iovcnt].iov_base = date_line;
|
713
|
+
iovcnt++;
|
714
|
+
}
|
715
|
+
|
716
|
+
v[iovcnt].iov_base = "\r\n";
|
717
|
+
v[iovcnt].iov_len = sizeof("\r\n") - 1;
|
718
|
+
iovcnt++;
|
719
|
+
|
720
|
+
for (i=0; i < blen; i++ ) {
|
721
|
+
val_obj = rb_ary_entry(body, i);
|
722
|
+
v[iovcnt].iov_base = StringValuePtr(val_obj);
|
723
|
+
v[iovcnt].iov_len = RSTRING_LEN(val_obj);
|
724
|
+
iovcnt++;
|
725
|
+
}
|
726
|
+
|
727
|
+
vec_offset = 0;
|
728
|
+
written = 0;
|
729
|
+
while ( iovcnt - vec_offset > 0 ) {
|
730
|
+
count = (iovcnt > IOV_MAX) ? IOV_MAX : iovcnt;
|
731
|
+
rv = _writev_timeout(fileno, timeout, &v[vec_offset], count - vec_offset, (vec_offset == 0) ? 0 : 1);
|
732
|
+
if ( rv <= 0 ) {
|
733
|
+
// error or disconnected
|
734
|
+
break;
|
735
|
+
}
|
736
|
+
written += rv;
|
737
|
+
while ( rv > 0 ) {
|
738
|
+
if ( (unsigned int)rv >= v[vec_offset].iov_len ) {
|
739
|
+
rv -= v[vec_offset].iov_len;
|
740
|
+
vec_offset++;
|
741
|
+
}
|
742
|
+
else {
|
743
|
+
v[vec_offset].iov_base = (char*)v[vec_offset].iov_base + rv;
|
744
|
+
v[vec_offset].iov_len -= rv;
|
745
|
+
rv = 0;
|
746
|
+
}
|
747
|
+
}
|
748
|
+
}
|
749
|
+
}
|
750
|
+
if ( rv < 0 ) {
|
751
|
+
return Qnil;
|
752
|
+
}
|
753
|
+
return rb_int_new(written);
|
754
|
+
}
|
755
|
+
|
756
|
+
void Init_rhebok()
|
757
|
+
{
|
758
|
+
request_method_key = rb_obj_freeze(rb_str_new2("REQUEST_METHOD"));
|
759
|
+
rb_gc_register_address(&request_method_key);
|
760
|
+
request_uri_key = rb_obj_freeze(rb_str_new2("REQUEST_URI"));
|
761
|
+
rb_gc_register_address(&request_uri_key);
|
762
|
+
script_name_key = rb_obj_freeze(rb_str_new2("SCRIPT_NAME"));
|
763
|
+
rb_gc_register_address(&script_name_key);
|
764
|
+
server_protocol_key = rb_obj_freeze(rb_str_new2("SERVER_PROTOCOL"));
|
765
|
+
rb_gc_register_address(&server_protocol_key);
|
766
|
+
query_string_key = rb_obj_freeze(rb_str_new2("QUERY_STRING"));
|
767
|
+
rb_gc_register_address(&query_string_key);
|
768
|
+
|
769
|
+
remote_addr_key = rb_obj_freeze(rb_str_new2("REMOTE_ADDR"));
|
770
|
+
rb_gc_register_address(&remote_addr_key);
|
771
|
+
remote_port_key = rb_obj_freeze(rb_str_new2("REMOTE_PORT"));
|
772
|
+
rb_gc_register_address(&remote_port_key);
|
773
|
+
|
774
|
+
set_common_header("ACCEPT",sizeof("ACCEPT") - 1, 0);
|
775
|
+
set_common_header("ACCEPT-ENCODING",sizeof("ACCEPT-ENCODING") - 1, 0);
|
776
|
+
set_common_header("ACCEPT-LANGUAGE",sizeof("ACCEPT-LANGUAGE") - 1, 0);
|
777
|
+
set_common_header("CACHE-CONTROL",sizeof("CACHE-CONTROL") - 1, 0);
|
778
|
+
set_common_header("CONNECTION",sizeof("CONNECTION") - 1, 0);
|
779
|
+
set_common_header("CONTENT-LENGTH",sizeof("CONTENT-LENGTH") - 1, 1);
|
780
|
+
set_common_header("CONTENT-TYPE",sizeof("CONTENT-TYPE") - 1, 1);
|
781
|
+
set_common_header("COOKIE",sizeof("COOKIE") - 1, 0);
|
782
|
+
set_common_header("HOST",sizeof("HOST") - 1, 0);
|
783
|
+
set_common_header("IF-MODIFIED-SINCE",sizeof("IF-MODIFIED-SINCE") - 1, 0);
|
784
|
+
set_common_header("REFERER",sizeof("REFERER") - 1, 0);
|
785
|
+
set_common_header("USER-AGENT",sizeof("USER-AGENT") - 1, 0);
|
786
|
+
set_common_header("X-FORWARDED-FOR",sizeof("X-FORWARDED-FOR") - 1, 0);
|
787
|
+
|
788
|
+
cRhebok = rb_const_get(rb_cObject, rb_intern("Rhebok"));
|
789
|
+
rb_define_module_function(cRhebok, "accept_rack", rhe_accept, 4);
|
790
|
+
rb_define_module_function(cRhebok, "read_timeout", rhe_read_timeout, 5);
|
791
|
+
rb_define_module_function(cRhebok, "write_timeout", rhe_write_timeout, 5);
|
792
|
+
rb_define_module_function(cRhebok, "write_all", rhe_write_all, 4);
|
793
|
+
rb_define_module_function(cRhebok, "close_rack", rhe_close, 1);
|
794
|
+
rb_define_module_function(cRhebok, "write_response", rhe_write_response, 5);
|
795
|
+
}
|