bossan 0.1.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.
- data/LICENSE.txt +22 -0
- data/ext/bossan/bossan_ext.c +2012 -0
- data/ext/bossan/extconf.rb +13 -0
- data/ext/bossan/http_parser.c +1578 -0
- data/ext/bossan/http_parser.h +169 -0
- data/ext/bossan/picoev.h +382 -0
- data/ext/bossan/picoev_epoll.c +166 -0
- data/ext/bossan/time_cache.c +124 -0
- data/ext/bossan/time_cache.h +20 -0
- data/lib/bossan.rb +5 -0
- data/lib/bossan/version.rb +3 -0
- metadata +58 -0
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
Copyright (c) 2012 Hiroki Noda
|
|
2
|
+
|
|
3
|
+
MIT License
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
6
|
+
a copy of this software and associated documentation files (the
|
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
11
|
+
the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be
|
|
14
|
+
included in all copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@@ -0,0 +1,2012 @@
|
|
|
1
|
+
#include "ruby.h"
|
|
2
|
+
#include <assert.h>
|
|
3
|
+
#include <fcntl.h>
|
|
4
|
+
#include <stddef.h>
|
|
5
|
+
#include <unistd.h>
|
|
6
|
+
#include <errno.h>
|
|
7
|
+
#include <stdio.h>
|
|
8
|
+
#include <stdlib.h>
|
|
9
|
+
#include <string.h>
|
|
10
|
+
#include <inttypes.h>
|
|
11
|
+
#include <arpa/inet.h>
|
|
12
|
+
#include <signal.h>
|
|
13
|
+
#include <sys/sendfile.h>
|
|
14
|
+
#include <sys/socket.h>
|
|
15
|
+
#include <sys/types.h>
|
|
16
|
+
#include <sys/prctl.h>
|
|
17
|
+
#include <sys/un.h>
|
|
18
|
+
#include <sys/stat.h>
|
|
19
|
+
#include <sys/file.h>
|
|
20
|
+
#include <sys/uio.h>
|
|
21
|
+
#include <netinet/in.h>
|
|
22
|
+
#include <netinet/tcp.h>
|
|
23
|
+
#include <netdb.h>
|
|
24
|
+
|
|
25
|
+
#include "time_cache.h"
|
|
26
|
+
#include "http_parser.h"
|
|
27
|
+
#include "picoev.h"
|
|
28
|
+
|
|
29
|
+
#define MAX_FDS 1024 * 8
|
|
30
|
+
#define ACCEPT_TIMEOUT_SECS 1
|
|
31
|
+
#define SHORT_TIMEOUT_SECS 2
|
|
32
|
+
#define WRITE_TIMEOUT_SECS 5
|
|
33
|
+
#define READ_LONG_TIMEOUT_SECS 5
|
|
34
|
+
|
|
35
|
+
#define BACKLOG 1024
|
|
36
|
+
#define MAX_BUFSIZE 1024 * 8
|
|
37
|
+
#define INPUT_BUF_SIZE 1024 * 8
|
|
38
|
+
|
|
39
|
+
#define LIMIT_MAX 1024 * 1024 * 1024
|
|
40
|
+
|
|
41
|
+
#define LIMIT_SIZE 1024 * 512
|
|
42
|
+
|
|
43
|
+
#define LIMIT_PATH 1024 * 4
|
|
44
|
+
#define LIMIT_FRAGMENT 1024
|
|
45
|
+
#define LIMIT_URI 1024 * 4
|
|
46
|
+
#define LIMIT_QUERY_STRING 1024 * 8
|
|
47
|
+
|
|
48
|
+
#define LIMIT_REQUEST_FIELDS 30
|
|
49
|
+
#define LIMIT_REQUEST_FIELD_SIZE 1024 * 8
|
|
50
|
+
|
|
51
|
+
#define CRLF "\r\n"
|
|
52
|
+
#define DELIM ": "
|
|
53
|
+
|
|
54
|
+
#define MSG_500 ("HTTP/1.0 500 Internal Server Error\r\nContent-Type: text/html\r\nServer: " SERVER "\r\n\r\n<html><head><title>500 Internal Server Error</head><h1>Internal Server Error</h1><p>The server encountered an internal error and was unable to complete your request. Either the server is overloaded or there is an error in the application.</p></html>\n")
|
|
55
|
+
|
|
56
|
+
#define MSG_400 ("HTTP/1.0 400 Bad Request\r\nContent-Type: text/html\r\nServer: " SERVER "\r\n\r\n<html><head><title>Bad Request</head><body><p>Bad Request.</p></body></html>")
|
|
57
|
+
|
|
58
|
+
#define MSG_411 ("HTTP/1.0 411 Length Required\r\nContent-Type: text/html\r\nServer: " SERVER "\r\n\r\n<html><head><title>Length Required</head><body><p>Length Required.</p></body></html>")
|
|
59
|
+
|
|
60
|
+
#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</head><body><p>Request Entity Too Large.</p></body></html>")
|
|
61
|
+
|
|
62
|
+
#define SERVER "bossan/0.1"
|
|
63
|
+
|
|
64
|
+
VALUE server; // Bossan::Server
|
|
65
|
+
|
|
66
|
+
static VALUE version_key;
|
|
67
|
+
static VALUE version_val;
|
|
68
|
+
static VALUE scheme_key;
|
|
69
|
+
static VALUE scheme_val;
|
|
70
|
+
static VALUE errors_key;
|
|
71
|
+
static VALUE errors_val;
|
|
72
|
+
static VALUE multithread_key;
|
|
73
|
+
static VALUE multithread_val;
|
|
74
|
+
static VALUE multiprocess_key;
|
|
75
|
+
static VALUE multiprocess_val;
|
|
76
|
+
static VALUE run_once_key;
|
|
77
|
+
static VALUE run_once_val;
|
|
78
|
+
|
|
79
|
+
static VALUE script_key;
|
|
80
|
+
static VALUE script_val;
|
|
81
|
+
static VALUE server_name_key;
|
|
82
|
+
static VALUE server_name_val;
|
|
83
|
+
static VALUE server_port_key;
|
|
84
|
+
static VALUE server_port_val;
|
|
85
|
+
|
|
86
|
+
static VALUE server_protocol;
|
|
87
|
+
static VALUE path_info;
|
|
88
|
+
static VALUE request_uri;
|
|
89
|
+
static VALUE query_string;
|
|
90
|
+
static VALUE http_fragment;
|
|
91
|
+
static VALUE request_method;
|
|
92
|
+
static VALUE rb_remote_addr;
|
|
93
|
+
static VALUE rb_remote_port;
|
|
94
|
+
static VALUE rack_input;
|
|
95
|
+
static VALUE http_connection;
|
|
96
|
+
|
|
97
|
+
static VALUE http_user_agent;
|
|
98
|
+
|
|
99
|
+
static VALUE i_keys;
|
|
100
|
+
static VALUE i_call;
|
|
101
|
+
static VALUE i_new;
|
|
102
|
+
|
|
103
|
+
static char *server_name = "127.0.0.1";
|
|
104
|
+
static short server_port = 8000;
|
|
105
|
+
|
|
106
|
+
static int listen_sock; // listen socket
|
|
107
|
+
|
|
108
|
+
static int loop_done; // main loop flag
|
|
109
|
+
static picoev_loop* main_loop; //main loop
|
|
110
|
+
static VALUE rack_app = NULL; //rack app
|
|
111
|
+
|
|
112
|
+
static char *log_path = NULL; //access log path
|
|
113
|
+
static int log_fd = -1; //access log
|
|
114
|
+
static char *error_log_path = NULL; //error log path
|
|
115
|
+
static int err_log_fd = -1; //error log
|
|
116
|
+
|
|
117
|
+
static int is_keep_alive = 0; //keep alive support
|
|
118
|
+
int max_content_length = 1024 * 1024 * 16; //max_content_length
|
|
119
|
+
|
|
120
|
+
static VALUE StringIO;
|
|
121
|
+
|
|
122
|
+
typedef enum {
|
|
123
|
+
WRITE_OK,
|
|
124
|
+
MEMORY_ERROR,
|
|
125
|
+
LIMIT_OVER,
|
|
126
|
+
} buffer_result;
|
|
127
|
+
|
|
128
|
+
typedef struct {
|
|
129
|
+
char *buf;
|
|
130
|
+
size_t buf_size;
|
|
131
|
+
size_t len;
|
|
132
|
+
size_t limit;
|
|
133
|
+
} buffer;
|
|
134
|
+
|
|
135
|
+
typedef struct {
|
|
136
|
+
VALUE filelike;
|
|
137
|
+
} FileWrapperObject;
|
|
138
|
+
|
|
139
|
+
typedef enum {
|
|
140
|
+
FIELD,
|
|
141
|
+
VAL,
|
|
142
|
+
} field_type;
|
|
143
|
+
|
|
144
|
+
typedef struct {
|
|
145
|
+
buffer *field;
|
|
146
|
+
buffer *value;
|
|
147
|
+
} header;
|
|
148
|
+
|
|
149
|
+
typedef struct {
|
|
150
|
+
buffer *path;
|
|
151
|
+
buffer *uri;
|
|
152
|
+
buffer *query_string;
|
|
153
|
+
buffer *fragment;
|
|
154
|
+
header *headers[LIMIT_REQUEST_FIELDS];
|
|
155
|
+
uint32_t num_headers;
|
|
156
|
+
field_type last_header_element;
|
|
157
|
+
} request;
|
|
158
|
+
|
|
159
|
+
typedef enum {
|
|
160
|
+
BODY_TYPE_NONE,
|
|
161
|
+
BODY_TYPE_TMPFILE,
|
|
162
|
+
BODY_TYPE_BUFFER
|
|
163
|
+
} request_body_type;
|
|
164
|
+
|
|
165
|
+
typedef struct _client {
|
|
166
|
+
int fd;
|
|
167
|
+
char *remote_addr;
|
|
168
|
+
uint32_t remote_port;
|
|
169
|
+
uint8_t keep_alive;
|
|
170
|
+
request *req;
|
|
171
|
+
uint32_t body_length;
|
|
172
|
+
int body_readed;
|
|
173
|
+
void *body;
|
|
174
|
+
int bad_request_code;
|
|
175
|
+
request_body_type body_type;
|
|
176
|
+
uint8_t complete;
|
|
177
|
+
|
|
178
|
+
http_parser *http; // http req parser
|
|
179
|
+
VALUE environ; // rack environ
|
|
180
|
+
int status_code; // response status code
|
|
181
|
+
|
|
182
|
+
VALUE http_status; // response status line
|
|
183
|
+
VALUE headers; // http response headers
|
|
184
|
+
uint8_t header_done; // header write status
|
|
185
|
+
VALUE response; // rack response object
|
|
186
|
+
VALUE response_iter; // rack response object
|
|
187
|
+
uint8_t content_length_set; // content_length_set flag
|
|
188
|
+
uint32_t content_length; // content_length
|
|
189
|
+
uint32_t write_bytes; // send body length
|
|
190
|
+
void *bucket; //write_data
|
|
191
|
+
uint8_t response_closed; //response closed flag
|
|
192
|
+
} client_t;
|
|
193
|
+
|
|
194
|
+
typedef struct iovec iovec_t;
|
|
195
|
+
|
|
196
|
+
typedef struct {
|
|
197
|
+
int fd;
|
|
198
|
+
iovec_t *iov;
|
|
199
|
+
uint32_t iov_cnt;
|
|
200
|
+
uint32_t iov_size;
|
|
201
|
+
uint32_t total;
|
|
202
|
+
uint32_t total_size;
|
|
203
|
+
} write_bucket;
|
|
204
|
+
|
|
205
|
+
inline buffer *
|
|
206
|
+
new_buffer(size_t buf_size, size_t limit)
|
|
207
|
+
{
|
|
208
|
+
buffer *buf;
|
|
209
|
+
buf = ruby_xmalloc(sizeof(buffer));
|
|
210
|
+
memset(buf, 0, sizeof(buffer));
|
|
211
|
+
buf->buf = ruby_xmalloc(sizeof(char) * buf_size);
|
|
212
|
+
buf->buf_size = buf_size;
|
|
213
|
+
if(limit){
|
|
214
|
+
buf->limit = limit;
|
|
215
|
+
}else{
|
|
216
|
+
buf->limit = LIMIT_MAX;
|
|
217
|
+
}
|
|
218
|
+
return buf;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
inline buffer_result
|
|
222
|
+
write2buf(buffer *buf, const char *c, size_t l) {
|
|
223
|
+
size_t newl;
|
|
224
|
+
char *newbuf;
|
|
225
|
+
buffer_result ret = WRITE_OK;
|
|
226
|
+
newl = buf->len + l;
|
|
227
|
+
|
|
228
|
+
if (newl >= buf->buf_size) {
|
|
229
|
+
buf->buf_size *= 2;
|
|
230
|
+
if(buf->buf_size <= newl) {
|
|
231
|
+
buf->buf_size = (int)(newl + 1);
|
|
232
|
+
}
|
|
233
|
+
if(buf->buf_size > buf->limit){
|
|
234
|
+
buf->buf_size = buf->limit + 1;
|
|
235
|
+
}
|
|
236
|
+
newbuf = (char*)ruby_xrealloc(buf->buf, buf->buf_size);
|
|
237
|
+
buf->buf = newbuf;
|
|
238
|
+
}
|
|
239
|
+
if(newl >= buf->buf_size){
|
|
240
|
+
l = buf->buf_size - buf->len -1;
|
|
241
|
+
ret = LIMIT_OVER;
|
|
242
|
+
}
|
|
243
|
+
memcpy(buf->buf + buf->len, c , l);
|
|
244
|
+
buf->len += (int)l;
|
|
245
|
+
return ret;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
inline void
|
|
249
|
+
free_buffer(buffer *buf)
|
|
250
|
+
{
|
|
251
|
+
ruby_xfree(buf->buf);
|
|
252
|
+
ruby_xfree(buf);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
inline VALUE
|
|
256
|
+
getRbString(buffer *buf)
|
|
257
|
+
{
|
|
258
|
+
VALUE o;
|
|
259
|
+
o = rb_str_new(buf->buf, buf->len);
|
|
260
|
+
free_buffer(buf);
|
|
261
|
+
return o;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
inline char *
|
|
265
|
+
getString(buffer *buf)
|
|
266
|
+
{
|
|
267
|
+
buf->buf[buf->len] = '\0';
|
|
268
|
+
return buf->buf;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
int
|
|
272
|
+
open_log_file(const char *path)
|
|
273
|
+
{
|
|
274
|
+
return open(path, O_CREAT|O_APPEND|O_WRONLY, 0744);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
static inline int
|
|
278
|
+
write_log(const char *new_path, int fd, const char *data, size_t len)
|
|
279
|
+
{
|
|
280
|
+
int openfd;
|
|
281
|
+
flock(fd, LOCK_EX);
|
|
282
|
+
|
|
283
|
+
if(write(fd, data, len) < 0){
|
|
284
|
+
flock(fd, LOCK_UN);
|
|
285
|
+
//reopen
|
|
286
|
+
openfd = open_log_file(new_path);
|
|
287
|
+
if(openfd < 0){
|
|
288
|
+
//fail
|
|
289
|
+
return -1;
|
|
290
|
+
}
|
|
291
|
+
flock(openfd, LOCK_EX);
|
|
292
|
+
if(write(openfd, data, len) < 0){
|
|
293
|
+
flock(openfd, LOCK_UN);
|
|
294
|
+
// write fail
|
|
295
|
+
return -1;
|
|
296
|
+
}
|
|
297
|
+
flock(openfd, LOCK_UN);
|
|
298
|
+
return openfd;
|
|
299
|
+
}
|
|
300
|
+
flock(fd, LOCK_UN);
|
|
301
|
+
return fd;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
int
|
|
305
|
+
write_access_log(client_t *cli, int log_fd, const char *log_path)
|
|
306
|
+
{
|
|
307
|
+
char buf[1024];
|
|
308
|
+
if(log_fd > 0){
|
|
309
|
+
VALUE obj;
|
|
310
|
+
char *method, *path, *version, *ua;
|
|
311
|
+
|
|
312
|
+
obj = rb_hash_aref(cli->environ, request_method);
|
|
313
|
+
if(obj){
|
|
314
|
+
method = StringValuePtr(obj);
|
|
315
|
+
}else{
|
|
316
|
+
method = "-";
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
obj = rb_hash_aref(cli->environ, path_info);
|
|
320
|
+
if(obj){
|
|
321
|
+
path = StringValuePtr(obj);
|
|
322
|
+
}else{
|
|
323
|
+
path = "-";
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
obj = rb_hash_aref(cli->environ, server_protocol);
|
|
327
|
+
if(obj){
|
|
328
|
+
version = StringValuePtr(obj);
|
|
329
|
+
}else{
|
|
330
|
+
version = "-";
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
obj = rb_hash_aref(cli->environ, http_user_agent);
|
|
334
|
+
if(obj){
|
|
335
|
+
ua = StringValuePtr(obj);
|
|
336
|
+
}else{
|
|
337
|
+
ua = "-";
|
|
338
|
+
}
|
|
339
|
+
//update
|
|
340
|
+
cache_time_update();
|
|
341
|
+
|
|
342
|
+
sprintf(buf, "%s - - [%s] \"%s %s %s\" %d %d \"-\" \"%s\"\n",
|
|
343
|
+
cli->remote_addr,
|
|
344
|
+
http_log_time,
|
|
345
|
+
method,
|
|
346
|
+
path,
|
|
347
|
+
version,
|
|
348
|
+
cli->status_code,
|
|
349
|
+
cli->write_bytes,
|
|
350
|
+
ua);
|
|
351
|
+
return write_log(log_path, log_fd, buf, strlen(buf));
|
|
352
|
+
}
|
|
353
|
+
return 0;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
static inline int
|
|
357
|
+
blocking_write(client_t *client, char *data, size_t len)
|
|
358
|
+
{
|
|
359
|
+
size_t r = 0, send_len = len;
|
|
360
|
+
while ( (int)len > 0 ){
|
|
361
|
+
if (len < send_len){
|
|
362
|
+
send_len = len;
|
|
363
|
+
}
|
|
364
|
+
r = write(client->fd, data, send_len);
|
|
365
|
+
switch(r){
|
|
366
|
+
case 0:
|
|
367
|
+
return 1;
|
|
368
|
+
break;
|
|
369
|
+
case -1:
|
|
370
|
+
if (errno == EAGAIN || errno == EWOULDBLOCK) { /* try again later */
|
|
371
|
+
//printf("EAGAIN \n");
|
|
372
|
+
usleep(500); //TODO try again later
|
|
373
|
+
break;
|
|
374
|
+
}else{
|
|
375
|
+
// fatal error
|
|
376
|
+
//close
|
|
377
|
+
rb_raise(rb_eException, "fatal error");
|
|
378
|
+
|
|
379
|
+
// TODO:
|
|
380
|
+
// raise exception from errno
|
|
381
|
+
/* rb_raise(rb_eIOError); */
|
|
382
|
+
/* write_error_log(__FILE__, __LINE__); */
|
|
383
|
+
client->keep_alive = 0;
|
|
384
|
+
return -1;
|
|
385
|
+
}
|
|
386
|
+
default:
|
|
387
|
+
data += (int)r;
|
|
388
|
+
len -= r;
|
|
389
|
+
client->content_length += r;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
return 1;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
void
|
|
396
|
+
send_error_page(client_t *client)
|
|
397
|
+
{
|
|
398
|
+
shutdown(client->fd, SHUT_RD);
|
|
399
|
+
if(client->header_done){
|
|
400
|
+
//already sended response data
|
|
401
|
+
//close connection
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
int status = client->bad_request_code;
|
|
405
|
+
int r = status < 0 ? status * -1 : status;
|
|
406
|
+
client->status_code = r;
|
|
407
|
+
switch(r){
|
|
408
|
+
case 400:
|
|
409
|
+
blocking_write(client, MSG_400, sizeof(MSG_400) -1);
|
|
410
|
+
break;
|
|
411
|
+
case 411:
|
|
412
|
+
blocking_write(client, MSG_411, sizeof(MSG_411) -1);
|
|
413
|
+
break;
|
|
414
|
+
case 413:
|
|
415
|
+
blocking_write(client, MSG_413, sizeof(MSG_413) -1);
|
|
416
|
+
break;
|
|
417
|
+
default:
|
|
418
|
+
//Internal Server Error
|
|
419
|
+
blocking_write(client, MSG_500, sizeof(MSG_500) -1);
|
|
420
|
+
break;
|
|
421
|
+
}
|
|
422
|
+
client->keep_alive = 0;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
static inline void
|
|
426
|
+
extent_sndbuf(client_t *client)
|
|
427
|
+
{
|
|
428
|
+
int bufsize = 1024 * 1024 * 2, r;
|
|
429
|
+
r = setsockopt(client->fd, SOL_SOCKET, SO_SNDBUF, &bufsize, sizeof(bufsize));
|
|
430
|
+
assert(r == 0);
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
static inline void
|
|
434
|
+
enable_cork(client_t *client)
|
|
435
|
+
{
|
|
436
|
+
int on = 1, r;
|
|
437
|
+
r = setsockopt(client->fd, IPPROTO_TCP, TCP_CORK, &on, sizeof(on));
|
|
438
|
+
assert(r == 0);
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
static inline write_bucket *
|
|
442
|
+
new_write_bucket(int fd, int cnt)
|
|
443
|
+
{
|
|
444
|
+
write_bucket *bucket;
|
|
445
|
+
bucket = ruby_xmalloc(sizeof(write_bucket));
|
|
446
|
+
memset(bucket, 0, sizeof(write_bucket));
|
|
447
|
+
|
|
448
|
+
bucket->fd = fd;
|
|
449
|
+
bucket->iov = (iovec_t *)ruby_xmalloc(sizeof(iovec_t) * cnt);
|
|
450
|
+
bucket->iov_size = cnt;
|
|
451
|
+
return bucket;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
static inline void
|
|
455
|
+
free_write_bucket(write_bucket *bucket)
|
|
456
|
+
{
|
|
457
|
+
ruby_xfree(bucket->iov);
|
|
458
|
+
ruby_xfree(bucket);
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
static inline void
|
|
462
|
+
set2bucket(write_bucket *bucket, char *buf, size_t len)
|
|
463
|
+
{
|
|
464
|
+
bucket->iov[bucket->iov_cnt].iov_base = buf;
|
|
465
|
+
bucket->iov[bucket->iov_cnt].iov_len = len;
|
|
466
|
+
bucket->iov_cnt++;
|
|
467
|
+
bucket->total += len;
|
|
468
|
+
bucket->total_size += len;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
static inline void
|
|
472
|
+
add_header(write_bucket *bucket, char *key, size_t keylen, char *val, size_t vallen)
|
|
473
|
+
{
|
|
474
|
+
set2bucket(bucket, key, keylen);
|
|
475
|
+
set2bucket(bucket, DELIM, 2);
|
|
476
|
+
set2bucket(bucket, val, vallen);
|
|
477
|
+
set2bucket(bucket, CRLF, 2);
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
static inline int
|
|
481
|
+
writev_bucket(write_bucket *data)
|
|
482
|
+
{
|
|
483
|
+
size_t w;
|
|
484
|
+
int i = 0;
|
|
485
|
+
w = writev(data->fd, data->iov, data->iov_cnt);
|
|
486
|
+
if(w == -1){
|
|
487
|
+
//error
|
|
488
|
+
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
|
489
|
+
// try again later
|
|
490
|
+
return 0;
|
|
491
|
+
}else{
|
|
492
|
+
//ERROR
|
|
493
|
+
rb_raise(rb_eException, "fatal error");
|
|
494
|
+
|
|
495
|
+
// TODO:
|
|
496
|
+
// raise exception from errno
|
|
497
|
+
/* rb_raise(rb_eIOError); */
|
|
498
|
+
/* write_error_log(__FILE__, __LINE__); */
|
|
499
|
+
return -1;
|
|
500
|
+
}
|
|
501
|
+
}if(w == 0){
|
|
502
|
+
return 1;
|
|
503
|
+
}else{
|
|
504
|
+
if(data->total > w){
|
|
505
|
+
for(; i < data->iov_cnt;i++){
|
|
506
|
+
if(w > data->iov[i].iov_len){
|
|
507
|
+
//already write
|
|
508
|
+
w -= data->iov[i].iov_len;
|
|
509
|
+
data->iov[i].iov_len = 0;
|
|
510
|
+
}else{
|
|
511
|
+
data->iov[i].iov_base += w;
|
|
512
|
+
data->iov[i].iov_len = data->iov[i].iov_len - w;
|
|
513
|
+
break;
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
data->total = data->total -w;
|
|
517
|
+
#ifdef DEBUG
|
|
518
|
+
printf("writev_bucket write %d progeress %d/%d \n", w, data->total, data->total_size);
|
|
519
|
+
#endif
|
|
520
|
+
//resume
|
|
521
|
+
// again later
|
|
522
|
+
return writev_bucket(data);
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
return 1;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
static inline int
|
|
529
|
+
write_headers(client_t *client, char *data, size_t datalen)
|
|
530
|
+
{
|
|
531
|
+
if(client->header_done){
|
|
532
|
+
return 1;
|
|
533
|
+
}
|
|
534
|
+
write_bucket *bucket;
|
|
535
|
+
uint32_t i = 0, hlen = 0;
|
|
536
|
+
|
|
537
|
+
VALUE arr = NULL;
|
|
538
|
+
VALUE object = NULL;
|
|
539
|
+
char *name = NULL;
|
|
540
|
+
ssize_t namelen;
|
|
541
|
+
char *value = NULL;
|
|
542
|
+
long valuelen;
|
|
543
|
+
|
|
544
|
+
if(client->headers){
|
|
545
|
+
if (TYPE(client->headers) != T_HASH) {
|
|
546
|
+
return -1;
|
|
547
|
+
}
|
|
548
|
+
arr = rb_funcall(client->headers, i_keys, 0);
|
|
549
|
+
hlen = RARRAY_LEN(arr);
|
|
550
|
+
}
|
|
551
|
+
bucket = new_write_bucket(client->fd, ( hlen * 4 * 2) + 32 );
|
|
552
|
+
|
|
553
|
+
object = client->http_status;
|
|
554
|
+
|
|
555
|
+
if(TYPE(object) != T_STRING){
|
|
556
|
+
return -1;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
if(object){
|
|
560
|
+
value = StringValuePtr(object);
|
|
561
|
+
valuelen = RSTRING_LEN(object);
|
|
562
|
+
//write status code
|
|
563
|
+
set2bucket(bucket, value, valuelen);
|
|
564
|
+
|
|
565
|
+
add_header(bucket, "Server", 6, SERVER, sizeof(SERVER) -1);
|
|
566
|
+
cache_time_update();
|
|
567
|
+
add_header(bucket, "Date", 4, (char *)http_time, 29);
|
|
568
|
+
if(client->keep_alive == 1){
|
|
569
|
+
add_header(bucket, "Connection", 10, "keep-alive", 10);
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
VALUE object1, object2;
|
|
574
|
+
|
|
575
|
+
//write header
|
|
576
|
+
if(client->headers){
|
|
577
|
+
for(i=0; i < hlen; i++){
|
|
578
|
+
object1 = rb_ary_entry(arr, i);
|
|
579
|
+
Check_Type(object1, T_STRING);
|
|
580
|
+
|
|
581
|
+
if (TYPE(client->headers)!=T_HASH){
|
|
582
|
+
goto error;
|
|
583
|
+
}
|
|
584
|
+
VALUE tmp = rb_funcall(client->headers, rb_intern("key?"), 1, object1);
|
|
585
|
+
if (tmp == Qfalse){
|
|
586
|
+
goto error;
|
|
587
|
+
}
|
|
588
|
+
object2 = rb_hash_aref(client->headers, object1);
|
|
589
|
+
|
|
590
|
+
Check_Type(object2, T_STRING);
|
|
591
|
+
|
|
592
|
+
name = StringValuePtr(object1);
|
|
593
|
+
namelen = RSTRING_LEN(object1);
|
|
594
|
+
|
|
595
|
+
value = StringValuePtr(object2);
|
|
596
|
+
valuelen = RSTRING_LEN(object2);
|
|
597
|
+
|
|
598
|
+
if (strchr(name, '\n') != 0 || strchr(value, '\n') != 0) {
|
|
599
|
+
rb_raise(rb_eArgError, "embedded newline in response header and value");
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
if (!strcasecmp(name, "Server") || !strcasecmp(name, "Date")) {
|
|
603
|
+
continue;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
if (!strcasecmp(name, "Content-Length")) {
|
|
607
|
+
char *v = value;
|
|
608
|
+
long l = 0;
|
|
609
|
+
|
|
610
|
+
errno = 0;
|
|
611
|
+
l = strtol(v, &v, 10);
|
|
612
|
+
if (*v || errno == ERANGE || l < 0) {
|
|
613
|
+
rb_raise(rb_eArgError, "invalid content length");
|
|
614
|
+
goto error;
|
|
615
|
+
}
|
|
616
|
+
client->content_length_set = 1;
|
|
617
|
+
client->content_length = l;
|
|
618
|
+
}
|
|
619
|
+
add_header(bucket, name, namelen, value, valuelen);
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
set2bucket(bucket, CRLF, 2);
|
|
623
|
+
|
|
624
|
+
if(data){
|
|
625
|
+
set2bucket(bucket, data, datalen);
|
|
626
|
+
}
|
|
627
|
+
client->bucket = bucket;
|
|
628
|
+
int ret = writev_bucket(bucket);
|
|
629
|
+
if(ret != 0){
|
|
630
|
+
client->header_done = 1;
|
|
631
|
+
if(ret > 0 && data){
|
|
632
|
+
client->write_bytes += datalen;
|
|
633
|
+
}
|
|
634
|
+
// clear
|
|
635
|
+
free_write_bucket(bucket);
|
|
636
|
+
client->bucket = NULL;
|
|
637
|
+
}
|
|
638
|
+
return ret;
|
|
639
|
+
error:
|
|
640
|
+
/* write_error_log(__FILE__, __LINE__); */
|
|
641
|
+
if(bucket){
|
|
642
|
+
free_write_bucket(bucket);
|
|
643
|
+
client->bucket = NULL;
|
|
644
|
+
}
|
|
645
|
+
return -1;
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
/* static inline int */
|
|
649
|
+
/* write_sendfile(int out_fd, int in_fd, size_t count) */
|
|
650
|
+
/* { */
|
|
651
|
+
/* int size = (int)count; */
|
|
652
|
+
|
|
653
|
+
/* if (size == 0) { */
|
|
654
|
+
/* struct stat info; */
|
|
655
|
+
/* if (fstat(in_fd, &info) == -1){ */
|
|
656
|
+
/* // TODO: */
|
|
657
|
+
/* // raise exception from errno */
|
|
658
|
+
/* /\* rb_raise(); *\/ */
|
|
659
|
+
/* /\* write_error_log(__FILE__, __LINE__); *\/ */
|
|
660
|
+
/* return -1; */
|
|
661
|
+
/* } */
|
|
662
|
+
/* size = info.st_size - lseek(in_fd, 0, SEEK_CUR); */
|
|
663
|
+
/* } */
|
|
664
|
+
/* return sendfile(out_fd, in_fd, NULL, count); */
|
|
665
|
+
/* } */
|
|
666
|
+
|
|
667
|
+
static inline void
|
|
668
|
+
close_response(client_t *client)
|
|
669
|
+
{
|
|
670
|
+
//send all response
|
|
671
|
+
//closing reponse object
|
|
672
|
+
client->response_closed = 1;
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
static inline int
|
|
676
|
+
processs_write(client_t *client)
|
|
677
|
+
{
|
|
678
|
+
VALUE iterator = NULL;
|
|
679
|
+
VALUE arr;
|
|
680
|
+
VALUE item;
|
|
681
|
+
char *buf;
|
|
682
|
+
ssize_t buflen;
|
|
683
|
+
write_bucket *bucket;
|
|
684
|
+
int ret;
|
|
685
|
+
|
|
686
|
+
// body
|
|
687
|
+
iterator = client->response_iter;
|
|
688
|
+
|
|
689
|
+
if(iterator != NULL){
|
|
690
|
+
/* Check_Type(iterator, T_ARRAY); */
|
|
691
|
+
/* assert(3 == RARRAY_LEN(iterator)); */
|
|
692
|
+
if (TYPE(iterator) != T_ARRAY || RARRAY_LEN(iterator) != 3){
|
|
693
|
+
return -1;
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
arr = rb_ary_entry(iterator, 2);
|
|
697
|
+
|
|
698
|
+
if (TYPE(arr) != T_ARRAY){
|
|
699
|
+
return -1;
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
item = rb_ary_entry(arr, 0);
|
|
703
|
+
|
|
704
|
+
if(TYPE(item) != T_STRING) {
|
|
705
|
+
return -1;
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
buf = StringValuePtr(item);
|
|
709
|
+
buflen = RSTRING_LEN(item);
|
|
710
|
+
//write
|
|
711
|
+
bucket = new_write_bucket(client->fd, 1);
|
|
712
|
+
set2bucket(bucket, buf, buflen);
|
|
713
|
+
ret = writev_bucket(bucket);
|
|
714
|
+
if(ret <= 0){
|
|
715
|
+
return ret;
|
|
716
|
+
}
|
|
717
|
+
//mark
|
|
718
|
+
client->write_bytes += buflen;
|
|
719
|
+
//check write_bytes/content_length
|
|
720
|
+
if(client->content_length_set){
|
|
721
|
+
if(client->content_length <= client->write_bytes){
|
|
722
|
+
// all done
|
|
723
|
+
//break;
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
close_response(client);
|
|
727
|
+
}
|
|
728
|
+
return 1;
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
inline int
|
|
732
|
+
process_body(client_t *client)
|
|
733
|
+
{
|
|
734
|
+
int ret;
|
|
735
|
+
write_bucket *bucket;
|
|
736
|
+
if(client->bucket){
|
|
737
|
+
bucket = (write_bucket *)client->bucket;
|
|
738
|
+
//retry send
|
|
739
|
+
ret = writev_bucket(bucket);
|
|
740
|
+
|
|
741
|
+
if(ret != 0){
|
|
742
|
+
client->write_bytes += bucket->total_size;
|
|
743
|
+
//free
|
|
744
|
+
free_write_bucket(bucket);
|
|
745
|
+
client->bucket = NULL;
|
|
746
|
+
}else{
|
|
747
|
+
return 0;
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
ret = processs_write(client);
|
|
751
|
+
return ret;
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
static inline int
|
|
755
|
+
start_response_write(client_t *client)
|
|
756
|
+
{
|
|
757
|
+
VALUE iterator;
|
|
758
|
+
VALUE arr;
|
|
759
|
+
VALUE item;
|
|
760
|
+
char *buf;
|
|
761
|
+
ssize_t buflen;
|
|
762
|
+
|
|
763
|
+
iterator = client->response;
|
|
764
|
+
client->response_iter = iterator;
|
|
765
|
+
|
|
766
|
+
if (TYPE(iterator) != T_ARRAY){
|
|
767
|
+
return -1;
|
|
768
|
+
}
|
|
769
|
+
assert(3 == RARRAY_LEN(iterator));
|
|
770
|
+
|
|
771
|
+
arr = rb_ary_entry(iterator, 2);
|
|
772
|
+
|
|
773
|
+
if (TYPE(arr) != T_ARRAY){
|
|
774
|
+
return -1;
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
VALUE v_body = rb_ary_entry(arr, 0);
|
|
778
|
+
Check_Type(v_body, T_STRING);
|
|
779
|
+
|
|
780
|
+
if (v_body) {
|
|
781
|
+
buf = StringValuePtr(v_body);
|
|
782
|
+
buflen = RSTRING_LEN(v_body);
|
|
783
|
+
#ifdef DEBUG
|
|
784
|
+
printf("start_response_write buflen %d \n", buflen);
|
|
785
|
+
#endif
|
|
786
|
+
return write_headers(client, buf, buflen);
|
|
787
|
+
}
|
|
788
|
+
return -1;
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
inline int
|
|
792
|
+
response_start(client_t *client)
|
|
793
|
+
{
|
|
794
|
+
int ret;
|
|
795
|
+
enable_cork(client);
|
|
796
|
+
if(client->status_code == 304){
|
|
797
|
+
return write_headers(client, NULL, 0);
|
|
798
|
+
}
|
|
799
|
+
ret = start_response_write(client);
|
|
800
|
+
#ifdef DEBUG
|
|
801
|
+
printf("start_response_write ret = %d \n", ret);
|
|
802
|
+
#endif
|
|
803
|
+
if(ret > 0){
|
|
804
|
+
// sended header
|
|
805
|
+
ret = processs_write(client);
|
|
806
|
+
}
|
|
807
|
+
return ret;
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
inline request *
|
|
811
|
+
new_request(void)
|
|
812
|
+
{
|
|
813
|
+
request *req = (request *)ruby_xmalloc(sizeof(request));
|
|
814
|
+
memset(req, 0, sizeof(request));
|
|
815
|
+
return req;
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
inline header *
|
|
819
|
+
new_header(size_t fsize, size_t flimit, size_t vsize, size_t vlimit)
|
|
820
|
+
{
|
|
821
|
+
header *h;
|
|
822
|
+
h = ruby_xmalloc(sizeof(header));
|
|
823
|
+
h->field = new_buffer(fsize, flimit);
|
|
824
|
+
h->value = new_buffer(vsize, vlimit);
|
|
825
|
+
return h;
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
inline void
|
|
829
|
+
free_header(header *h)
|
|
830
|
+
{
|
|
831
|
+
ruby_xfree(h);
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
inline void
|
|
835
|
+
free_request(request *req)
|
|
836
|
+
{
|
|
837
|
+
uint32_t i;
|
|
838
|
+
header *h;
|
|
839
|
+
if(req->path){
|
|
840
|
+
free_buffer(req->path);
|
|
841
|
+
req->path = NULL;
|
|
842
|
+
}
|
|
843
|
+
if(req->uri){
|
|
844
|
+
free_buffer(req->uri);
|
|
845
|
+
req->uri = NULL;
|
|
846
|
+
}
|
|
847
|
+
if(req->query_string){
|
|
848
|
+
free_buffer(req->query_string);
|
|
849
|
+
req->query_string = NULL;
|
|
850
|
+
}
|
|
851
|
+
if(req->fragment){
|
|
852
|
+
free_buffer(req->fragment);
|
|
853
|
+
req->fragment = NULL;
|
|
854
|
+
}
|
|
855
|
+
for(i = 0; i < req->num_headers+1; i++){
|
|
856
|
+
h = req->headers[i];
|
|
857
|
+
if(h){
|
|
858
|
+
free_buffer(h->field);
|
|
859
|
+
free_buffer(h->value);
|
|
860
|
+
free_header(h);
|
|
861
|
+
req->headers[i] = NULL;
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
ruby_xfree(req);
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
static inline void
|
|
868
|
+
key_upper(char *s, const char *key, size_t len)
|
|
869
|
+
{
|
|
870
|
+
int i = 0;
|
|
871
|
+
register int c;
|
|
872
|
+
for (i = 0; i < len; i++) {
|
|
873
|
+
c = key[i];
|
|
874
|
+
if(c == '-'){
|
|
875
|
+
s[i] = '_';
|
|
876
|
+
}else{
|
|
877
|
+
if(islower(c)){
|
|
878
|
+
s[i] = toupper(c);
|
|
879
|
+
}else{
|
|
880
|
+
s[i] = c;
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
static inline int
|
|
887
|
+
write_body2file(client_t *client, const char *buffer, size_t buffer_len)
|
|
888
|
+
{
|
|
889
|
+
FILE *tmp = (FILE *)client->body;
|
|
890
|
+
fwrite(buffer, 1, buffer_len, tmp);
|
|
891
|
+
client->body_readed += buffer_len;
|
|
892
|
+
#ifdef DEBUG
|
|
893
|
+
printf("write_body2file %d bytes \n", buffer_len);
|
|
894
|
+
#endif
|
|
895
|
+
return client->body_readed;
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
static inline int
|
|
899
|
+
write_body2mem(client_t *client, const char *buffer, size_t buffer_len)
|
|
900
|
+
{
|
|
901
|
+
printf("body2mem called\n");
|
|
902
|
+
VALUE obj = (VALUE)client->body;
|
|
903
|
+
rb_str_concat(obj, rb_str_new(buffer, buffer_len));
|
|
904
|
+
client->body_readed += buffer_len;
|
|
905
|
+
#ifdef DEBUG
|
|
906
|
+
printf("write_body2mem %d bytes \n", buffer_len);
|
|
907
|
+
#endif
|
|
908
|
+
return client->body_readed;
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
static inline int
|
|
912
|
+
write_body(client_t *cli, const char *buffer, size_t buffer_len)
|
|
913
|
+
{
|
|
914
|
+
return write_body2mem(cli, buffer, buffer_len);
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
typedef enum{
|
|
918
|
+
CONTENT_TYPE,
|
|
919
|
+
CONTENT_LENGTH,
|
|
920
|
+
OTHER
|
|
921
|
+
} rack_header_type;
|
|
922
|
+
|
|
923
|
+
static inline rack_header_type
|
|
924
|
+
check_header_type(const char *buf)
|
|
925
|
+
{
|
|
926
|
+
if(*buf++ != 'C'){
|
|
927
|
+
return OTHER;
|
|
928
|
+
}
|
|
929
|
+
if(*buf++ != 'O'){
|
|
930
|
+
return OTHER;
|
|
931
|
+
}
|
|
932
|
+
if(*buf++ != 'N'){
|
|
933
|
+
return OTHER;
|
|
934
|
+
}
|
|
935
|
+
if(*buf++ != 'T'){
|
|
936
|
+
return OTHER;
|
|
937
|
+
}
|
|
938
|
+
if(*buf++ != 'E'){
|
|
939
|
+
return OTHER;
|
|
940
|
+
}
|
|
941
|
+
if(*buf++ != 'N'){
|
|
942
|
+
return OTHER;
|
|
943
|
+
}
|
|
944
|
+
if(*buf++ != 'T'){
|
|
945
|
+
return OTHER;
|
|
946
|
+
}
|
|
947
|
+
if(*buf++ != '_'){
|
|
948
|
+
return OTHER;
|
|
949
|
+
}
|
|
950
|
+
char c = *buf++;
|
|
951
|
+
if(c == 'L'){
|
|
952
|
+
return CONTENT_LENGTH;
|
|
953
|
+
}else if(c == 'T'){
|
|
954
|
+
return CONTENT_TYPE;
|
|
955
|
+
}
|
|
956
|
+
return OTHER;
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
static inline client_t *
|
|
960
|
+
get_client(http_parser *p)
|
|
961
|
+
{
|
|
962
|
+
return (client_t *)p->data;
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
int
|
|
966
|
+
message_begin_cb(http_parser *p)
|
|
967
|
+
{
|
|
968
|
+
return 0;
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
int
|
|
972
|
+
header_field_cb (http_parser *p, const char *buf, size_t len, char partial)
|
|
973
|
+
{
|
|
974
|
+
uint32_t i;
|
|
975
|
+
register header *h;
|
|
976
|
+
client_t *client = get_client(p);
|
|
977
|
+
register request *req = client->req;
|
|
978
|
+
char temp[len];
|
|
979
|
+
|
|
980
|
+
buffer_result ret = MEMORY_ERROR;
|
|
981
|
+
if (req->last_header_element != FIELD){
|
|
982
|
+
if(LIMIT_REQUEST_FIELDS <= req->num_headers){
|
|
983
|
+
client->bad_request_code = 400;
|
|
984
|
+
return -1;
|
|
985
|
+
}
|
|
986
|
+
req->num_headers++;
|
|
987
|
+
}
|
|
988
|
+
i = req->num_headers;
|
|
989
|
+
h = req->headers[i];
|
|
990
|
+
|
|
991
|
+
key_upper(temp, buf, len);
|
|
992
|
+
if(h){
|
|
993
|
+
ret = write2buf(h->field, temp, len);
|
|
994
|
+
}else{
|
|
995
|
+
req->headers[i] = h = new_header(128, LIMIT_REQUEST_FIELD_SIZE, 1024, LIMIT_REQUEST_FIELD_SIZE);
|
|
996
|
+
rack_header_type type = check_header_type(temp);
|
|
997
|
+
if(type == OTHER){
|
|
998
|
+
ret = write2buf(h->field, "HTTP_", 5);
|
|
999
|
+
}
|
|
1000
|
+
ret = write2buf(h->field, temp, len);
|
|
1001
|
+
//printf("%s \n", getString(h->field));
|
|
1002
|
+
}
|
|
1003
|
+
switch(ret){
|
|
1004
|
+
case MEMORY_ERROR:
|
|
1005
|
+
client->bad_request_code = 500;
|
|
1006
|
+
return -1;
|
|
1007
|
+
case LIMIT_OVER:
|
|
1008
|
+
client->bad_request_code = 400;
|
|
1009
|
+
return -1;
|
|
1010
|
+
default:
|
|
1011
|
+
break;
|
|
1012
|
+
}
|
|
1013
|
+
req->last_header_element = FIELD;
|
|
1014
|
+
return 0;
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
int
|
|
1018
|
+
header_value_cb (http_parser *p, const char *buf, size_t len, char partial)
|
|
1019
|
+
{
|
|
1020
|
+
uint32_t i;
|
|
1021
|
+
register header *h;
|
|
1022
|
+
client_t *client = get_client(p);
|
|
1023
|
+
register request *req = client->req;
|
|
1024
|
+
|
|
1025
|
+
buffer_result ret = MEMORY_ERROR;
|
|
1026
|
+
i = req->num_headers;
|
|
1027
|
+
h = req->headers[i];
|
|
1028
|
+
|
|
1029
|
+
if(h){
|
|
1030
|
+
ret = write2buf(h->value, buf, len);
|
|
1031
|
+
}
|
|
1032
|
+
switch(ret){
|
|
1033
|
+
case MEMORY_ERROR:
|
|
1034
|
+
client->bad_request_code = 500;
|
|
1035
|
+
return -1;
|
|
1036
|
+
case LIMIT_OVER:
|
|
1037
|
+
client->bad_request_code = 400;
|
|
1038
|
+
return -1;
|
|
1039
|
+
default:
|
|
1040
|
+
break;
|
|
1041
|
+
}
|
|
1042
|
+
req->last_header_element = VAL;
|
|
1043
|
+
return 0;
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
int
|
|
1047
|
+
request_path_cb (http_parser *p, const char *buf, size_t len, char partial)
|
|
1048
|
+
{
|
|
1049
|
+
client_t *client = get_client(p);
|
|
1050
|
+
register request *req = client->req;
|
|
1051
|
+
buffer_result ret = MEMORY_ERROR;
|
|
1052
|
+
|
|
1053
|
+
if(req->path){
|
|
1054
|
+
ret = write2buf(req->path, buf, len);
|
|
1055
|
+
}else{
|
|
1056
|
+
req->path = new_buffer(1024, LIMIT_PATH);
|
|
1057
|
+
ret = write2buf(req->path, buf, len);
|
|
1058
|
+
}
|
|
1059
|
+
switch(ret){
|
|
1060
|
+
case MEMORY_ERROR:
|
|
1061
|
+
client->bad_request_code = 500;
|
|
1062
|
+
return -1;
|
|
1063
|
+
case LIMIT_OVER:
|
|
1064
|
+
client->bad_request_code = 400;
|
|
1065
|
+
return -1;
|
|
1066
|
+
default:
|
|
1067
|
+
break;
|
|
1068
|
+
}
|
|
1069
|
+
return 0;
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
int
|
|
1073
|
+
request_uri_cb (http_parser *p, const char *buf, size_t len, char partial)
|
|
1074
|
+
{
|
|
1075
|
+
client_t *client = get_client(p);
|
|
1076
|
+
register request *req = client->req;
|
|
1077
|
+
buffer_result ret = MEMORY_ERROR;
|
|
1078
|
+
|
|
1079
|
+
if(req->uri){
|
|
1080
|
+
ret = write2buf(req->uri, buf, len);
|
|
1081
|
+
}else{
|
|
1082
|
+
req->uri = new_buffer(1024, LIMIT_URI);
|
|
1083
|
+
ret = write2buf(req->uri, buf, len);
|
|
1084
|
+
}
|
|
1085
|
+
switch(ret){
|
|
1086
|
+
case MEMORY_ERROR:
|
|
1087
|
+
client->bad_request_code = 500;
|
|
1088
|
+
return -1;
|
|
1089
|
+
case LIMIT_OVER:
|
|
1090
|
+
client->bad_request_code = 400;
|
|
1091
|
+
return -1;
|
|
1092
|
+
default:
|
|
1093
|
+
break;
|
|
1094
|
+
}
|
|
1095
|
+
return 0;
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
int
|
|
1099
|
+
query_string_cb (http_parser *p, const char *buf, size_t len, char partial)
|
|
1100
|
+
{
|
|
1101
|
+
client_t *client = get_client(p);
|
|
1102
|
+
register request *req = client->req;
|
|
1103
|
+
buffer_result ret = MEMORY_ERROR;
|
|
1104
|
+
|
|
1105
|
+
if(req->query_string){
|
|
1106
|
+
ret = write2buf(req->query_string, buf, len);
|
|
1107
|
+
}else{
|
|
1108
|
+
req->query_string = new_buffer(1024*2, LIMIT_QUERY_STRING);
|
|
1109
|
+
ret = write2buf(req->query_string, buf, len);
|
|
1110
|
+
}
|
|
1111
|
+
switch(ret){
|
|
1112
|
+
case MEMORY_ERROR:
|
|
1113
|
+
client->bad_request_code = 500;
|
|
1114
|
+
return -1;
|
|
1115
|
+
case LIMIT_OVER:
|
|
1116
|
+
client->bad_request_code = 400;
|
|
1117
|
+
return -1;
|
|
1118
|
+
default:
|
|
1119
|
+
break;
|
|
1120
|
+
}
|
|
1121
|
+
return 0;
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
int
|
|
1125
|
+
fragment_cb (http_parser *p, const char *buf, size_t len, char partial)
|
|
1126
|
+
{
|
|
1127
|
+
client_t *client = get_client(p);
|
|
1128
|
+
register request *req = client->req;
|
|
1129
|
+
buffer_result ret = MEMORY_ERROR;
|
|
1130
|
+
|
|
1131
|
+
if(req->fragment){
|
|
1132
|
+
ret = write2buf(req->fragment, buf, len);
|
|
1133
|
+
}else{
|
|
1134
|
+
req->fragment = new_buffer(1024, LIMIT_FRAGMENT);
|
|
1135
|
+
ret = write2buf(req->fragment, buf, len);
|
|
1136
|
+
}
|
|
1137
|
+
switch(ret){
|
|
1138
|
+
case MEMORY_ERROR:
|
|
1139
|
+
client->bad_request_code = 500;
|
|
1140
|
+
return -1;
|
|
1141
|
+
case LIMIT_OVER:
|
|
1142
|
+
client->bad_request_code = 400;
|
|
1143
|
+
return -1;
|
|
1144
|
+
default:
|
|
1145
|
+
break;
|
|
1146
|
+
}
|
|
1147
|
+
return 0;
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
int
|
|
1151
|
+
body_cb (http_parser *p, const char *buf, size_t len, char partial)
|
|
1152
|
+
{
|
|
1153
|
+
client_t *client = get_client(p);
|
|
1154
|
+
if(max_content_length <= client->body_readed + len){
|
|
1155
|
+
client->bad_request_code = 413;
|
|
1156
|
+
return -1;
|
|
1157
|
+
}
|
|
1158
|
+
if(client->body_type == BODY_TYPE_NONE){
|
|
1159
|
+
if(client->body_length == 0){
|
|
1160
|
+
//Length Required
|
|
1161
|
+
client->bad_request_code = 411;
|
|
1162
|
+
return -1;
|
|
1163
|
+
}
|
|
1164
|
+
//default memory stream
|
|
1165
|
+
#ifdef DEBUG
|
|
1166
|
+
printf("client->body_length %d \n", client->body_length);
|
|
1167
|
+
#endif
|
|
1168
|
+
client->body_type = BODY_TYPE_BUFFER;
|
|
1169
|
+
#ifdef DEBUG
|
|
1170
|
+
printf("BODY_TYPE_BUFFER \n");
|
|
1171
|
+
#endif
|
|
1172
|
+
}
|
|
1173
|
+
write_body(client, buf, len);
|
|
1174
|
+
return 0;
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1177
|
+
int
|
|
1178
|
+
headers_complete_cb(http_parser *p)
|
|
1179
|
+
{
|
|
1180
|
+
register VALUE obj, key;
|
|
1181
|
+
client_t *client = get_client(p);
|
|
1182
|
+
request *req = client->req;
|
|
1183
|
+
register VALUE env = client->environ;
|
|
1184
|
+
register uint32_t i = 0;
|
|
1185
|
+
register header *h;
|
|
1186
|
+
|
|
1187
|
+
if(max_content_length < p->content_length){
|
|
1188
|
+
client->bad_request_code = 413;
|
|
1189
|
+
return -1;
|
|
1190
|
+
}
|
|
1191
|
+
|
|
1192
|
+
if (p->http_minor == 1) {
|
|
1193
|
+
obj = rb_str_new2("HTTP/1.1");
|
|
1194
|
+
} else {
|
|
1195
|
+
obj = rb_str_new2("HTTP/1.0");
|
|
1196
|
+
}
|
|
1197
|
+
rb_hash_aset(env, server_protocol, obj);
|
|
1198
|
+
|
|
1199
|
+
if(req->path){
|
|
1200
|
+
obj = getRbString(req->path);
|
|
1201
|
+
rb_hash_aset(env, path_info, obj);
|
|
1202
|
+
req->path = NULL;
|
|
1203
|
+
}
|
|
1204
|
+
if(req->uri){
|
|
1205
|
+
obj = getRbString(req->uri);
|
|
1206
|
+
rb_hash_aset(env, request_uri, obj);
|
|
1207
|
+
req->uri = NULL;
|
|
1208
|
+
}
|
|
1209
|
+
if(req->query_string){
|
|
1210
|
+
obj = getRbString(req->query_string);
|
|
1211
|
+
rb_hash_aset(env, query_string, obj);
|
|
1212
|
+
req->query_string = NULL;
|
|
1213
|
+
}
|
|
1214
|
+
if(req->fragment){
|
|
1215
|
+
obj = getRbString(req->fragment);
|
|
1216
|
+
rb_hash_aset(env, http_fragment, obj);
|
|
1217
|
+
req->fragment = NULL;
|
|
1218
|
+
}
|
|
1219
|
+
for(i = 0; i < req->num_headers+1; i++){
|
|
1220
|
+
h = req->headers[i];
|
|
1221
|
+
if(h){
|
|
1222
|
+
key = getRbString(h->field);
|
|
1223
|
+
obj = getRbString(h->value);
|
|
1224
|
+
rb_hash_aset(env, key, obj);
|
|
1225
|
+
free_header(h);
|
|
1226
|
+
req->headers[i] = NULL;
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
switch(p->method){
|
|
1231
|
+
case HTTP_DELETE:
|
|
1232
|
+
obj = rb_str_new("DELETE", 6);
|
|
1233
|
+
break;
|
|
1234
|
+
case HTTP_GET:
|
|
1235
|
+
obj = rb_str_new("GET", 3);
|
|
1236
|
+
break;
|
|
1237
|
+
case HTTP_HEAD:
|
|
1238
|
+
obj = rb_str_new("HEAD", 4);
|
|
1239
|
+
break;
|
|
1240
|
+
case HTTP_POST:
|
|
1241
|
+
obj = rb_str_new("POST", 4);
|
|
1242
|
+
break;
|
|
1243
|
+
case HTTP_PUT:
|
|
1244
|
+
obj = rb_str_new("PUT", 3);
|
|
1245
|
+
break;
|
|
1246
|
+
case HTTP_CONNECT:
|
|
1247
|
+
obj = rb_str_new("CONNECT", 7);
|
|
1248
|
+
break;
|
|
1249
|
+
case HTTP_OPTIONS:
|
|
1250
|
+
obj = rb_str_new("OPTIONS", 7);
|
|
1251
|
+
break;
|
|
1252
|
+
case HTTP_TRACE:
|
|
1253
|
+
obj = rb_str_new("TRACE", 5);
|
|
1254
|
+
break;
|
|
1255
|
+
case HTTP_COPY:
|
|
1256
|
+
obj = rb_str_new("COPY", 4);
|
|
1257
|
+
break;
|
|
1258
|
+
case HTTP_LOCK:
|
|
1259
|
+
obj = rb_str_new("LOCK", 4);
|
|
1260
|
+
break;
|
|
1261
|
+
case HTTP_MKCOL:
|
|
1262
|
+
obj = rb_str_new("MKCOL", 5);
|
|
1263
|
+
break;
|
|
1264
|
+
case HTTP_MOVE:
|
|
1265
|
+
obj = rb_str_new("MOVE", 4);
|
|
1266
|
+
break;
|
|
1267
|
+
case HTTP_PROPFIND:
|
|
1268
|
+
obj = rb_str_new("PROPFIND", 8);
|
|
1269
|
+
break;
|
|
1270
|
+
case HTTP_PROPPATCH:
|
|
1271
|
+
obj = rb_str_new("PROPPATCH", 9);
|
|
1272
|
+
break;
|
|
1273
|
+
case HTTP_UNLOCK:
|
|
1274
|
+
obj = rb_str_new("UNLOCK", 6);
|
|
1275
|
+
break;
|
|
1276
|
+
case HTTP_REPORT:
|
|
1277
|
+
obj = rb_str_new("REPORT", 6);
|
|
1278
|
+
break;
|
|
1279
|
+
case HTTP_MKACTIVITY:
|
|
1280
|
+
obj = rb_str_new("MKACTIVITY", 10);
|
|
1281
|
+
break;
|
|
1282
|
+
case HTTP_CHECKOUT:
|
|
1283
|
+
obj = rb_str_new("CHECKOUT", 8);
|
|
1284
|
+
break;
|
|
1285
|
+
case HTTP_MERGE:
|
|
1286
|
+
obj = rb_str_new("MERGE", 5);
|
|
1287
|
+
break;
|
|
1288
|
+
default:
|
|
1289
|
+
obj = rb_str_new("GET", 3);
|
|
1290
|
+
break;
|
|
1291
|
+
}
|
|
1292
|
+
|
|
1293
|
+
rb_hash_aset(env, request_method, obj);
|
|
1294
|
+
|
|
1295
|
+
ruby_xfree(req);
|
|
1296
|
+
client->req = NULL;
|
|
1297
|
+
client->body_length = p->content_length;
|
|
1298
|
+
return 0;
|
|
1299
|
+
}
|
|
1300
|
+
|
|
1301
|
+
int
|
|
1302
|
+
message_complete_cb (http_parser *p)
|
|
1303
|
+
{
|
|
1304
|
+
client_t *client = get_client(p);
|
|
1305
|
+
client->complete = 1;
|
|
1306
|
+
return 0;
|
|
1307
|
+
}
|
|
1308
|
+
|
|
1309
|
+
static http_parser_settings settings =
|
|
1310
|
+
{.on_message_begin = message_begin_cb
|
|
1311
|
+
,.on_header_field = header_field_cb
|
|
1312
|
+
,.on_header_value = header_value_cb
|
|
1313
|
+
,.on_path = request_path_cb
|
|
1314
|
+
,.on_url = request_uri_cb
|
|
1315
|
+
,.on_fragment = fragment_cb
|
|
1316
|
+
,.on_query_string = query_string_cb
|
|
1317
|
+
,.on_body = body_cb
|
|
1318
|
+
,.on_headers_complete = headers_complete_cb
|
|
1319
|
+
,.on_message_complete = message_complete_cb
|
|
1320
|
+
};
|
|
1321
|
+
|
|
1322
|
+
inline int
|
|
1323
|
+
init_parser(client_t *cli, const char *name, const short port)
|
|
1324
|
+
{
|
|
1325
|
+
register VALUE object;
|
|
1326
|
+
|
|
1327
|
+
cli->http = (http_parser*)ruby_xmalloc(sizeof(http_parser));
|
|
1328
|
+
memset(cli->http, 0, sizeof(http_parser));
|
|
1329
|
+
|
|
1330
|
+
cli->environ = rb_hash_new();
|
|
1331
|
+
|
|
1332
|
+
if (cli->environ == NULL) {
|
|
1333
|
+
return -1;
|
|
1334
|
+
}
|
|
1335
|
+
|
|
1336
|
+
rb_hash_aset(cli->environ, version_key, version_val);
|
|
1337
|
+
rb_hash_aset(cli->environ, scheme_key, scheme_val);
|
|
1338
|
+
rb_hash_aset(cli->environ, errors_key, errors_val);
|
|
1339
|
+
rb_hash_aset(cli->environ, multithread_key, multithread_val);
|
|
1340
|
+
rb_hash_aset(cli->environ, multiprocess_key, multiprocess_val);
|
|
1341
|
+
rb_hash_aset(cli->environ, run_once_key, run_once_val);
|
|
1342
|
+
rb_hash_aset(cli->environ, script_key, script_val);
|
|
1343
|
+
rb_hash_aset(cli->environ, server_name_key, server_name_val);
|
|
1344
|
+
rb_hash_aset(cli->environ, server_port_key, server_port_val);
|
|
1345
|
+
|
|
1346
|
+
object = rb_str_new2(cli->remote_addr);
|
|
1347
|
+
rb_hash_aset(cli->environ, rb_remote_addr, object);
|
|
1348
|
+
|
|
1349
|
+
object = INT2NUM(cli->remote_port);
|
|
1350
|
+
rb_hash_aset(cli->environ, rb_remote_port, object);
|
|
1351
|
+
|
|
1352
|
+
http_parser_init(cli->http, HTTP_REQUEST);
|
|
1353
|
+
cli->http->data = cli;
|
|
1354
|
+
|
|
1355
|
+
return 0;
|
|
1356
|
+
}
|
|
1357
|
+
|
|
1358
|
+
inline size_t
|
|
1359
|
+
execute_parse(client_t *cli, const char *data, size_t len)
|
|
1360
|
+
{
|
|
1361
|
+
return http_parser_execute(cli->http, &settings, data, len);
|
|
1362
|
+
}
|
|
1363
|
+
|
|
1364
|
+
inline int
|
|
1365
|
+
parser_finish(client_t *cli)
|
|
1366
|
+
{
|
|
1367
|
+
return cli->complete;
|
|
1368
|
+
}
|
|
1369
|
+
|
|
1370
|
+
inline void
|
|
1371
|
+
setup_static_env(char *name, int port)
|
|
1372
|
+
{
|
|
1373
|
+
version_val = rb_obj_freeze(rb_ary_new3(2, INT2FIX(1), INT2FIX(1)));
|
|
1374
|
+
version_key = rb_str_new2("rack.version");
|
|
1375
|
+
|
|
1376
|
+
scheme_val = rb_str_new2("http");
|
|
1377
|
+
scheme_key = rb_str_new2("rack.url_scheme");
|
|
1378
|
+
|
|
1379
|
+
errors_val = rb_stderr;
|
|
1380
|
+
errors_key = rb_str_new2("rack.errors");
|
|
1381
|
+
|
|
1382
|
+
multithread_val = Qfalse;
|
|
1383
|
+
multithread_key = rb_str_new2("rack.multithread");
|
|
1384
|
+
|
|
1385
|
+
multiprocess_val = Qfalse; /* or Qtrue? I have no clue.. */
|
|
1386
|
+
multiprocess_key = rb_str_new2("rack.multiprocess");
|
|
1387
|
+
|
|
1388
|
+
run_once_val = Qfalse;
|
|
1389
|
+
run_once_key = rb_str_new2("rack.run_once");
|
|
1390
|
+
|
|
1391
|
+
script_val = rb_str_new2("");
|
|
1392
|
+
script_key = rb_str_new2("SCRIPT_NAME");
|
|
1393
|
+
|
|
1394
|
+
server_name_val = rb_str_new2(name);
|
|
1395
|
+
server_name_key = rb_str_new2("SERVER_NAME");
|
|
1396
|
+
|
|
1397
|
+
server_port_val = INT2NUM(port);
|
|
1398
|
+
server_port_key = rb_str_new2("SERVER_PORT");
|
|
1399
|
+
|
|
1400
|
+
server_protocol = rb_str_new2("SERVER_PROTOCOL");
|
|
1401
|
+
path_info = rb_str_new2("PATH_INFO");
|
|
1402
|
+
request_uri = rb_str_new2("REQUEST_URI");
|
|
1403
|
+
query_string = rb_str_new2("QUERY_STRING");
|
|
1404
|
+
http_fragment = rb_str_new2("HTTP_FRAGMENT");
|
|
1405
|
+
request_method = rb_str_new2("REQUEST_METHOD");
|
|
1406
|
+
rb_remote_addr = rb_str_new2("REMOTE_ADDR");
|
|
1407
|
+
rb_remote_port = rb_str_new2("REMOTE_PORT");
|
|
1408
|
+
rack_input = rb_str_new2("rack.input");
|
|
1409
|
+
http_connection = rb_str_new2("HTTP_CONNECTION");
|
|
1410
|
+
|
|
1411
|
+
http_user_agent = rb_str_new2("HTTP_USER_AGENT");
|
|
1412
|
+
}
|
|
1413
|
+
|
|
1414
|
+
static inline int
|
|
1415
|
+
setsig(int sig, void* handler)
|
|
1416
|
+
{
|
|
1417
|
+
struct sigaction context, ocontext;
|
|
1418
|
+
context.sa_handler = handler;
|
|
1419
|
+
sigemptyset(&context.sa_mask);
|
|
1420
|
+
context.sa_flags = 0;
|
|
1421
|
+
return sigaction(sig, &context, &ocontext);
|
|
1422
|
+
}
|
|
1423
|
+
|
|
1424
|
+
static inline void
|
|
1425
|
+
setup_sock(int fd)
|
|
1426
|
+
{
|
|
1427
|
+
int on = 1, r;
|
|
1428
|
+
r = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on));
|
|
1429
|
+
assert(r == 0);
|
|
1430
|
+
r = fcntl(fd, F_SETFL, O_NONBLOCK);
|
|
1431
|
+
assert(r == 0);
|
|
1432
|
+
}
|
|
1433
|
+
|
|
1434
|
+
static inline void
|
|
1435
|
+
disable_cork(client_t *client)
|
|
1436
|
+
{
|
|
1437
|
+
int off = 0;
|
|
1438
|
+
int on = 1, r;
|
|
1439
|
+
r = setsockopt(client->fd, IPPROTO_TCP, TCP_CORK, &off, sizeof(off));
|
|
1440
|
+
assert(r == 0);
|
|
1441
|
+
|
|
1442
|
+
r = setsockopt(client->fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on));
|
|
1443
|
+
assert(r == 0);
|
|
1444
|
+
}
|
|
1445
|
+
|
|
1446
|
+
static inline client_t *
|
|
1447
|
+
new_client_t(int client_fd, struct sockaddr_in client_addr){
|
|
1448
|
+
client_t *client;
|
|
1449
|
+
|
|
1450
|
+
client = ruby_xmalloc(sizeof(client_t));
|
|
1451
|
+
memset(client, 0, sizeof(client_t));
|
|
1452
|
+
|
|
1453
|
+
/* printf("size %d\n", sizeof(client_t)); */
|
|
1454
|
+
|
|
1455
|
+
client->fd = client_fd;
|
|
1456
|
+
|
|
1457
|
+
client->remote_addr = inet_ntoa(client_addr.sin_addr);
|
|
1458
|
+
client->remote_port = ntohs(client_addr.sin_port);
|
|
1459
|
+
client->req = new_request();
|
|
1460
|
+
client->body_type = BODY_TYPE_NONE;
|
|
1461
|
+
//printf("input_buf_size %d\n", client->input_buf_size);
|
|
1462
|
+
return client;
|
|
1463
|
+
}
|
|
1464
|
+
|
|
1465
|
+
static inline void
|
|
1466
|
+
clean_cli(client_t *client)
|
|
1467
|
+
{
|
|
1468
|
+
write_access_log(client, log_fd, log_path);
|
|
1469
|
+
if(client->req){
|
|
1470
|
+
free_request(client->req);
|
|
1471
|
+
client->req = NULL;
|
|
1472
|
+
}
|
|
1473
|
+
|
|
1474
|
+
if(client->http != NULL){
|
|
1475
|
+
ruby_xfree(client->http);
|
|
1476
|
+
client->http = NULL;
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1479
|
+
|
|
1480
|
+
static inline void
|
|
1481
|
+
close_conn(client_t *cli, picoev_loop* loop)
|
|
1482
|
+
{
|
|
1483
|
+
if(!cli->keep_alive){
|
|
1484
|
+
picoev_del(loop, cli->fd);
|
|
1485
|
+
clean_cli(cli);
|
|
1486
|
+
close(cli->fd);
|
|
1487
|
+
#ifdef DEBUG
|
|
1488
|
+
printf("close fd %d \n", cli->fd);
|
|
1489
|
+
#endif
|
|
1490
|
+
ruby_xfree(cli);
|
|
1491
|
+
}else{
|
|
1492
|
+
clean_cli(cli);
|
|
1493
|
+
disable_cork(cli);
|
|
1494
|
+
cli->keep_alive = 1;
|
|
1495
|
+
cli->environ = NULL;
|
|
1496
|
+
cli->http_status = NULL;
|
|
1497
|
+
cli->headers = NULL;
|
|
1498
|
+
cli->header_done = 0;
|
|
1499
|
+
cli->body_type = BODY_TYPE_NONE;
|
|
1500
|
+
cli->status_code = 0;
|
|
1501
|
+
cli->response = NULL;
|
|
1502
|
+
cli->content_length_set = 0;
|
|
1503
|
+
cli->content_length = 0;
|
|
1504
|
+
cli->write_bytes = 0;
|
|
1505
|
+
cli->response_closed = 0;
|
|
1506
|
+
cli->bad_request_code = 0;
|
|
1507
|
+
init_parser(cli, server_name, server_port);
|
|
1508
|
+
}
|
|
1509
|
+
}
|
|
1510
|
+
|
|
1511
|
+
static inline int
|
|
1512
|
+
process_rack_app(client_t *cli)
|
|
1513
|
+
{
|
|
1514
|
+
VALUE args = NULL;
|
|
1515
|
+
char *status;
|
|
1516
|
+
|
|
1517
|
+
args = cli->environ;
|
|
1518
|
+
|
|
1519
|
+
// cli->response = [200, {}, []]
|
|
1520
|
+
cli->response = rb_funcall(rack_app, i_call, 1, args);
|
|
1521
|
+
|
|
1522
|
+
if(TYPE(cli->response) != T_ARRAY || RARRAY_LEN(cli->response) < 3) {
|
|
1523
|
+
return 0;
|
|
1524
|
+
}
|
|
1525
|
+
|
|
1526
|
+
VALUE* response_ary = RARRAY_PTR(cli->response);
|
|
1527
|
+
|
|
1528
|
+
if (TYPE(response_ary[0])!=T_FIXNUM ||
|
|
1529
|
+
TYPE(response_ary[1])!=T_HASH ||
|
|
1530
|
+
TYPE(response_ary[2])!=T_ARRAY){
|
|
1531
|
+
return 0;
|
|
1532
|
+
}
|
|
1533
|
+
|
|
1534
|
+
cli->status_code = NUM2INT(response_ary[0]);
|
|
1535
|
+
cli->headers = response_ary[1];
|
|
1536
|
+
|
|
1537
|
+
errno = 0;
|
|
1538
|
+
/* printf("status code: %d\n", cli->status_code); */
|
|
1539
|
+
|
|
1540
|
+
char buff[256];
|
|
1541
|
+
sprintf(buff, "HTTP/1.1 %d\r\n", cli->status_code);
|
|
1542
|
+
cli->http_status = rb_str_new(buff, strlen(buff));
|
|
1543
|
+
|
|
1544
|
+
//check response
|
|
1545
|
+
if(cli->response && cli->response == Qnil){
|
|
1546
|
+
/* write_error_log(__FILE__, __LINE__); */
|
|
1547
|
+
rb_raise(rb_eException, "response must be a iter or sequence object");
|
|
1548
|
+
}
|
|
1549
|
+
|
|
1550
|
+
return 1;
|
|
1551
|
+
}
|
|
1552
|
+
|
|
1553
|
+
static void
|
|
1554
|
+
w_callback(picoev_loop* loop, int fd, int events, void* cb_arg)
|
|
1555
|
+
{
|
|
1556
|
+
client_t *client = ( client_t *)(cb_arg);
|
|
1557
|
+
int ret;
|
|
1558
|
+
#ifdef DEBUG
|
|
1559
|
+
printf("call w_callback \n");
|
|
1560
|
+
#endif
|
|
1561
|
+
if ((events & PICOEV_TIMEOUT) != 0) {
|
|
1562
|
+
#ifdef DEBUG
|
|
1563
|
+
printf("** w_callback timeout ** \n");
|
|
1564
|
+
#endif
|
|
1565
|
+
//timeout
|
|
1566
|
+
client->keep_alive = 0;
|
|
1567
|
+
close_conn(client, loop);
|
|
1568
|
+
|
|
1569
|
+
} else if ((events & PICOEV_WRITE) != 0) {
|
|
1570
|
+
ret = process_body(client);
|
|
1571
|
+
picoev_set_timeout(loop, client->fd, WRITE_TIMEOUT_SECS);
|
|
1572
|
+
#ifdef DEBUG
|
|
1573
|
+
printf("process_body ret %d \n", ret);
|
|
1574
|
+
#endif
|
|
1575
|
+
if(ret != 0){
|
|
1576
|
+
//ok or die
|
|
1577
|
+
close_conn(client, loop);
|
|
1578
|
+
}
|
|
1579
|
+
}
|
|
1580
|
+
}
|
|
1581
|
+
|
|
1582
|
+
static inline void
|
|
1583
|
+
call_rack_app(client_t *client, picoev_loop* loop)
|
|
1584
|
+
{
|
|
1585
|
+
int ret;
|
|
1586
|
+
if(!process_rack_app(client)){
|
|
1587
|
+
//Internal Server Error
|
|
1588
|
+
client->bad_request_code = 500;
|
|
1589
|
+
send_error_page(client);
|
|
1590
|
+
close_conn(client, loop);
|
|
1591
|
+
return;
|
|
1592
|
+
}
|
|
1593
|
+
|
|
1594
|
+
ret = response_start(client);
|
|
1595
|
+
/* printf("response_start done: %d\n", ret); */
|
|
1596
|
+
switch(ret){
|
|
1597
|
+
case -1:
|
|
1598
|
+
// Internal Server Error
|
|
1599
|
+
client->bad_request_code = 500;
|
|
1600
|
+
send_error_page(client);
|
|
1601
|
+
close_conn(client, loop);
|
|
1602
|
+
return;
|
|
1603
|
+
case 0:
|
|
1604
|
+
// continue
|
|
1605
|
+
// set callback
|
|
1606
|
+
#ifdef DEBUG
|
|
1607
|
+
printf("set write callback %d \n", ret);
|
|
1608
|
+
#endif
|
|
1609
|
+
picoev_add(loop, client->fd, PICOEV_WRITE, WRITE_TIMEOUT_SECS, w_callback, (void *)client);
|
|
1610
|
+
return;
|
|
1611
|
+
default:
|
|
1612
|
+
// send OK
|
|
1613
|
+
close_conn(client, loop);
|
|
1614
|
+
}
|
|
1615
|
+
}
|
|
1616
|
+
|
|
1617
|
+
static inline void
|
|
1618
|
+
prepare_call_rack(client_t *client)
|
|
1619
|
+
{
|
|
1620
|
+
VALUE input = NULL, object = NULL, c = NULL;
|
|
1621
|
+
char *val;
|
|
1622
|
+
|
|
1623
|
+
object = rb_str_new2("");
|
|
1624
|
+
input = rb_funcall(StringIO, i_new, 1, object);
|
|
1625
|
+
rb_hash_aset((VALUE)client->environ, rack_input, input);
|
|
1626
|
+
client->body = object;
|
|
1627
|
+
|
|
1628
|
+
if(is_keep_alive){
|
|
1629
|
+
//support keep-alive
|
|
1630
|
+
c = rb_hash_aref(client->environ, http_connection);
|
|
1631
|
+
if(c){
|
|
1632
|
+
val = StringValuePtr(c);
|
|
1633
|
+
if(!strcasecmp(val, "keep-alive")){
|
|
1634
|
+
client->keep_alive = 1;
|
|
1635
|
+
}else{
|
|
1636
|
+
client->keep_alive = 0;
|
|
1637
|
+
}
|
|
1638
|
+
}else{
|
|
1639
|
+
client->keep_alive = 0;
|
|
1640
|
+
}
|
|
1641
|
+
}
|
|
1642
|
+
}
|
|
1643
|
+
|
|
1644
|
+
static void
|
|
1645
|
+
r_callback(picoev_loop* loop, int fd, int events, void* cb_arg)
|
|
1646
|
+
{
|
|
1647
|
+
client_t *cli = ( client_t *)(cb_arg);
|
|
1648
|
+
if ((events & PICOEV_TIMEOUT) != 0) {
|
|
1649
|
+
#ifdef DEBUG
|
|
1650
|
+
printf("** r_callback timeout ** \n");
|
|
1651
|
+
#endif
|
|
1652
|
+
//timeout
|
|
1653
|
+
cli->keep_alive = 0;
|
|
1654
|
+
close_conn(cli, loop);
|
|
1655
|
+
} else if ((events & PICOEV_READ) != 0) {
|
|
1656
|
+
#ifdef DEBUG
|
|
1657
|
+
printf("ready read \n");
|
|
1658
|
+
#endif
|
|
1659
|
+
/* update timeout, and read */
|
|
1660
|
+
int finish = 0, nread;
|
|
1661
|
+
char buf[INPUT_BUF_SIZE];
|
|
1662
|
+
ssize_t r;
|
|
1663
|
+
if(!cli->keep_alive){
|
|
1664
|
+
picoev_set_timeout(loop, cli->fd, SHORT_TIMEOUT_SECS);
|
|
1665
|
+
}
|
|
1666
|
+
r = read(cli->fd, buf, sizeof(buf));
|
|
1667
|
+
switch (r) {
|
|
1668
|
+
case 0:
|
|
1669
|
+
finish = 1;
|
|
1670
|
+
break;
|
|
1671
|
+
case -1: /* error */
|
|
1672
|
+
if (errno == EAGAIN || errno == EWOULDBLOCK) { /* try again later */
|
|
1673
|
+
break;
|
|
1674
|
+
} else { /* fatal error */
|
|
1675
|
+
rb_raise(rb_eException, "fatal error");
|
|
1676
|
+
// TODO:
|
|
1677
|
+
// raise exception from errno
|
|
1678
|
+
/* rb_raise(); */
|
|
1679
|
+
/* write_error_log(__FILE__, __LINE__); */
|
|
1680
|
+
cli->keep_alive = 0;
|
|
1681
|
+
cli->status_code = 500;
|
|
1682
|
+
close_conn(cli, loop);
|
|
1683
|
+
return;
|
|
1684
|
+
}
|
|
1685
|
+
break;
|
|
1686
|
+
default:
|
|
1687
|
+
#ifdef DEBUG
|
|
1688
|
+
printf("read request fd %d bufsize %d \n", cli->fd, r);
|
|
1689
|
+
#endif
|
|
1690
|
+
nread = execute_parse(cli, buf, r);
|
|
1691
|
+
|
|
1692
|
+
if(cli->bad_request_code > 0){
|
|
1693
|
+
#ifdef DEBUG
|
|
1694
|
+
printf("fd %d bad_request code %d \n", cli->fd, cli->bad_request_code);
|
|
1695
|
+
#endif
|
|
1696
|
+
send_error_page(cli);
|
|
1697
|
+
close_conn(cli, loop);
|
|
1698
|
+
return;
|
|
1699
|
+
}
|
|
1700
|
+
if( nread != r ){
|
|
1701
|
+
// parse error
|
|
1702
|
+
#ifdef DEBUG
|
|
1703
|
+
printf("fd %d parse error %d \n", cli->fd, cli->bad_request_code);
|
|
1704
|
+
#endif
|
|
1705
|
+
cli->bad_request_code = 400;
|
|
1706
|
+
send_error_page(cli);
|
|
1707
|
+
close_conn(cli, loop);
|
|
1708
|
+
return;
|
|
1709
|
+
}
|
|
1710
|
+
#ifdef DEBUG
|
|
1711
|
+
printf("parse ok, fd %d %d nread \n", cli->fd, nread);
|
|
1712
|
+
#endif
|
|
1713
|
+
if(parser_finish(cli) > 0){
|
|
1714
|
+
finish = 1;
|
|
1715
|
+
}
|
|
1716
|
+
break;
|
|
1717
|
+
}
|
|
1718
|
+
|
|
1719
|
+
if(finish == 1){
|
|
1720
|
+
prepare_call_rack(cli);
|
|
1721
|
+
call_rack_app(cli, loop);
|
|
1722
|
+
return;
|
|
1723
|
+
}
|
|
1724
|
+
}
|
|
1725
|
+
}
|
|
1726
|
+
|
|
1727
|
+
// TODO: use accept4 in linux
|
|
1728
|
+
static void
|
|
1729
|
+
accept_callback(picoev_loop* loop, int fd, int events, void* cb_arg)
|
|
1730
|
+
{
|
|
1731
|
+
int client_fd;
|
|
1732
|
+
client_t *client;
|
|
1733
|
+
struct sockaddr_in client_addr;
|
|
1734
|
+
if ((events & PICOEV_TIMEOUT) != 0) {
|
|
1735
|
+
// time out
|
|
1736
|
+
// next turn or other process
|
|
1737
|
+
return;
|
|
1738
|
+
}else if ((events & PICOEV_READ) != 0) {
|
|
1739
|
+
socklen_t client_len = sizeof(client_addr);
|
|
1740
|
+
client_fd = accept(fd, (struct sockaddr *)&client_addr, &client_len);
|
|
1741
|
+
|
|
1742
|
+
if (client_fd != -1) {
|
|
1743
|
+
#ifdef DEBUG
|
|
1744
|
+
printf("accept fd %d \n", client_fd);
|
|
1745
|
+
#endif
|
|
1746
|
+
setup_sock(client_fd);
|
|
1747
|
+
client = new_client_t(client_fd, client_addr);
|
|
1748
|
+
|
|
1749
|
+
client->environ = Qnil;
|
|
1750
|
+
rb_gc_register_address(&client->environ);
|
|
1751
|
+
|
|
1752
|
+
init_parser(client, server_name, server_port);
|
|
1753
|
+
|
|
1754
|
+
picoev_add(loop, client_fd, PICOEV_READ, READ_LONG_TIMEOUT_SECS, r_callback, (void *)client);
|
|
1755
|
+
}else{
|
|
1756
|
+
if (errno != EAGAIN && errno != EWOULDBLOCK) {
|
|
1757
|
+
// TODO:
|
|
1758
|
+
// raise exception from errno
|
|
1759
|
+
/* rb_raise(); */
|
|
1760
|
+
/* write_error_log(__FILE__, __LINE__); */
|
|
1761
|
+
// die
|
|
1762
|
+
loop_done = 0;
|
|
1763
|
+
}
|
|
1764
|
+
}
|
|
1765
|
+
}
|
|
1766
|
+
}
|
|
1767
|
+
|
|
1768
|
+
static inline void
|
|
1769
|
+
setup_server_env(void)
|
|
1770
|
+
{
|
|
1771
|
+
setup_sock(listen_sock);
|
|
1772
|
+
cache_time_init();
|
|
1773
|
+
|
|
1774
|
+
setup_static_env(server_name, server_port);
|
|
1775
|
+
}
|
|
1776
|
+
|
|
1777
|
+
static inline int
|
|
1778
|
+
inet_listen(void)
|
|
1779
|
+
{
|
|
1780
|
+
struct addrinfo hints, *servinfo, *p;
|
|
1781
|
+
int flag = 1;
|
|
1782
|
+
int rv;
|
|
1783
|
+
char strport[7];
|
|
1784
|
+
|
|
1785
|
+
memset(&hints, 0, sizeof hints);
|
|
1786
|
+
hints.ai_family = AF_UNSPEC;
|
|
1787
|
+
hints.ai_socktype = SOCK_STREAM;
|
|
1788
|
+
hints.ai_flags = AI_PASSIVE;
|
|
1789
|
+
|
|
1790
|
+
snprintf(strport, sizeof (strport), "%d", server_port);
|
|
1791
|
+
|
|
1792
|
+
if ((rv = getaddrinfo(server_name, strport, &hints, &servinfo)) == -1) {
|
|
1793
|
+
// TODO:
|
|
1794
|
+
// raise exception from errno
|
|
1795
|
+
/* rb_raise(); */
|
|
1796
|
+
return -1;
|
|
1797
|
+
}
|
|
1798
|
+
|
|
1799
|
+
// loop through all the results and bind to the first we can
|
|
1800
|
+
for(p = servinfo; p != NULL; p = p->ai_next) {
|
|
1801
|
+
if ((listen_sock = socket(p->ai_family, p->ai_socktype,
|
|
1802
|
+
p->ai_protocol)) == -1) {
|
|
1803
|
+
//perror("server: socket");
|
|
1804
|
+
continue;
|
|
1805
|
+
}
|
|
1806
|
+
|
|
1807
|
+
if (setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &flag,
|
|
1808
|
+
sizeof(int)) == -1) {
|
|
1809
|
+
close(listen_sock);
|
|
1810
|
+
// TODO:
|
|
1811
|
+
// raise exception from errno
|
|
1812
|
+
/* rb_raise(); */
|
|
1813
|
+
return -1;
|
|
1814
|
+
}
|
|
1815
|
+
|
|
1816
|
+
if (bind(listen_sock, p->ai_addr, p->ai_addrlen) == -1) {
|
|
1817
|
+
close(listen_sock);
|
|
1818
|
+
// TODO:
|
|
1819
|
+
// raise exception from errno
|
|
1820
|
+
/* rb_raise(); */
|
|
1821
|
+
return -1;
|
|
1822
|
+
}
|
|
1823
|
+
break;
|
|
1824
|
+
}
|
|
1825
|
+
|
|
1826
|
+
if (p == NULL) {
|
|
1827
|
+
close(listen_sock);
|
|
1828
|
+
rb_raise(rb_eIOError, "server: failed to bind\n");
|
|
1829
|
+
return -1;
|
|
1830
|
+
}
|
|
1831
|
+
|
|
1832
|
+
freeaddrinfo(servinfo); // all done with this structure
|
|
1833
|
+
|
|
1834
|
+
// BACKLOG 1024
|
|
1835
|
+
if (listen(listen_sock, BACKLOG) == -1) {
|
|
1836
|
+
close(listen_sock);
|
|
1837
|
+
// TODO:
|
|
1838
|
+
// raise exception from errno
|
|
1839
|
+
/* rb_raise(); */
|
|
1840
|
+
return -1;
|
|
1841
|
+
}
|
|
1842
|
+
setup_server_env();
|
|
1843
|
+
return 1;
|
|
1844
|
+
}
|
|
1845
|
+
|
|
1846
|
+
static void
|
|
1847
|
+
sigint_cb(int signum)
|
|
1848
|
+
{
|
|
1849
|
+
loop_done = 0;
|
|
1850
|
+
/* rb_interrupt(); */
|
|
1851
|
+
}
|
|
1852
|
+
|
|
1853
|
+
static void
|
|
1854
|
+
sigpipe_cb(int signum)
|
|
1855
|
+
{
|
|
1856
|
+
}
|
|
1857
|
+
|
|
1858
|
+
static VALUE
|
|
1859
|
+
bossan_stop(VALUE self)
|
|
1860
|
+
{
|
|
1861
|
+
loop_done = 0;
|
|
1862
|
+
return Qnil;
|
|
1863
|
+
}
|
|
1864
|
+
|
|
1865
|
+
static VALUE
|
|
1866
|
+
bossan_access_log(VALUE self, VALUE args)
|
|
1867
|
+
{
|
|
1868
|
+
log_path = StringValuePtr(args);
|
|
1869
|
+
|
|
1870
|
+
if(log_fd > 0){
|
|
1871
|
+
close(log_fd);
|
|
1872
|
+
}
|
|
1873
|
+
log_fd = open_log_file(log_path);
|
|
1874
|
+
|
|
1875
|
+
if(log_fd < 0){
|
|
1876
|
+
rb_raise(rb_eTypeError, "not open file. %s", log_path);
|
|
1877
|
+
}
|
|
1878
|
+
return Qnil;
|
|
1879
|
+
}
|
|
1880
|
+
|
|
1881
|
+
// Bossan.run('127.0.0.1', 8000) do |env|
|
|
1882
|
+
// ...
|
|
1883
|
+
// end
|
|
1884
|
+
static VALUE
|
|
1885
|
+
bossan_run_loop(VALUE self, VALUE args1, VALUE args2, VALUE args3)
|
|
1886
|
+
{
|
|
1887
|
+
int ret;
|
|
1888
|
+
|
|
1889
|
+
if(listen_sock > 0){
|
|
1890
|
+
rb_raise(rb_eException, "already set listen socket");
|
|
1891
|
+
}
|
|
1892
|
+
|
|
1893
|
+
server_name = StringValuePtr(args1);
|
|
1894
|
+
server_port = NUM2INT(args2);
|
|
1895
|
+
|
|
1896
|
+
long _port = NUM2INT(args2);
|
|
1897
|
+
|
|
1898
|
+
if (_port <= 0 || _port >= 65536) {
|
|
1899
|
+
// out of range
|
|
1900
|
+
rb_raise(rb_eArgError, "port number outside valid range");
|
|
1901
|
+
}
|
|
1902
|
+
|
|
1903
|
+
server_port = (short)_port;
|
|
1904
|
+
|
|
1905
|
+
ret = inet_listen();
|
|
1906
|
+
|
|
1907
|
+
if(ret < 0){
|
|
1908
|
+
//error
|
|
1909
|
+
listen_sock = -1;
|
|
1910
|
+
}
|
|
1911
|
+
|
|
1912
|
+
rack_app = args3;
|
|
1913
|
+
|
|
1914
|
+
if(listen_sock <= 0){
|
|
1915
|
+
rb_raise(rb_eTypeError, "not found listen socket");
|
|
1916
|
+
}
|
|
1917
|
+
|
|
1918
|
+
/* init picoev */
|
|
1919
|
+
picoev_init(MAX_FDS);
|
|
1920
|
+
/* create loop */
|
|
1921
|
+
main_loop = picoev_create_loop(60);
|
|
1922
|
+
loop_done = 1;
|
|
1923
|
+
|
|
1924
|
+
setsig(SIGPIPE, sigpipe_cb);
|
|
1925
|
+
setsig(SIGINT, sigint_cb);
|
|
1926
|
+
setsig(SIGTERM, sigint_cb);
|
|
1927
|
+
|
|
1928
|
+
picoev_add(main_loop, listen_sock, PICOEV_READ, ACCEPT_TIMEOUT_SECS, accept_callback, NULL);
|
|
1929
|
+
|
|
1930
|
+
/* loop */
|
|
1931
|
+
while (loop_done) {
|
|
1932
|
+
picoev_loop_once(main_loop, 10);
|
|
1933
|
+
}
|
|
1934
|
+
picoev_destroy_loop(main_loop);
|
|
1935
|
+
picoev_deinit();
|
|
1936
|
+
|
|
1937
|
+
printf("Bye.\n");
|
|
1938
|
+
return Qnil;
|
|
1939
|
+
}
|
|
1940
|
+
|
|
1941
|
+
VALUE
|
|
1942
|
+
bossan_set_max_content_length(VALUE self, VALUE args)
|
|
1943
|
+
{
|
|
1944
|
+
max_content_length = NUM2INT(args);
|
|
1945
|
+
return Qnil;
|
|
1946
|
+
}
|
|
1947
|
+
|
|
1948
|
+
VALUE
|
|
1949
|
+
bossan_get_max_content_length(VALUE self)
|
|
1950
|
+
{
|
|
1951
|
+
return INT2NUM(max_content_length);
|
|
1952
|
+
}
|
|
1953
|
+
|
|
1954
|
+
void
|
|
1955
|
+
Init_bossan_ext(void)
|
|
1956
|
+
{
|
|
1957
|
+
rb_gc_register_address(&version_key);
|
|
1958
|
+
rb_gc_register_address(&version_val);
|
|
1959
|
+
rb_gc_register_address(&scheme_key);
|
|
1960
|
+
rb_gc_register_address(&scheme_val);
|
|
1961
|
+
rb_gc_register_address(&errors_key);
|
|
1962
|
+
rb_gc_register_address(&errors_val);
|
|
1963
|
+
rb_gc_register_address(&multithread_key);
|
|
1964
|
+
rb_gc_register_address(&multithread_val);
|
|
1965
|
+
rb_gc_register_address(&multiprocess_key);
|
|
1966
|
+
rb_gc_register_address(&multiprocess_val);
|
|
1967
|
+
rb_gc_register_address(&run_once_key);
|
|
1968
|
+
rb_gc_register_address(&run_once_val);
|
|
1969
|
+
|
|
1970
|
+
rb_gc_register_address(&script_key);
|
|
1971
|
+
rb_gc_register_address(&script_val);
|
|
1972
|
+
rb_gc_register_address(&server_name_key);
|
|
1973
|
+
rb_gc_register_address(&server_name_val);
|
|
1974
|
+
rb_gc_register_address(&server_port_key);
|
|
1975
|
+
rb_gc_register_address(&server_port_val);
|
|
1976
|
+
|
|
1977
|
+
rb_gc_register_address(&server_protocol);
|
|
1978
|
+
rb_gc_register_address(&path_info);
|
|
1979
|
+
rb_gc_register_address(&request_uri);
|
|
1980
|
+
rb_gc_register_address(&query_string);
|
|
1981
|
+
rb_gc_register_address(&http_fragment);
|
|
1982
|
+
rb_gc_register_address(&request_method);
|
|
1983
|
+
rb_gc_register_address(&rb_remote_addr);
|
|
1984
|
+
rb_gc_register_address(&rb_remote_port);
|
|
1985
|
+
rb_gc_register_address(&rack_input);
|
|
1986
|
+
rb_gc_register_address(&http_connection);
|
|
1987
|
+
|
|
1988
|
+
rb_gc_register_address(&http_user_agent);
|
|
1989
|
+
|
|
1990
|
+
rb_gc_register_address(&i_keys);
|
|
1991
|
+
rb_gc_register_address(&i_call);
|
|
1992
|
+
rb_gc_register_address(&i_new);
|
|
1993
|
+
|
|
1994
|
+
rb_gc_register_address(&rack_app); //rack app
|
|
1995
|
+
|
|
1996
|
+
i_new = rb_intern("new");
|
|
1997
|
+
i_call = rb_intern("call");
|
|
1998
|
+
i_keys = rb_intern("keys");
|
|
1999
|
+
|
|
2000
|
+
server = rb_define_module("Bossan");
|
|
2001
|
+
rb_gc_register_address(&server);
|
|
2002
|
+
|
|
2003
|
+
rb_define_module_function(server, "run", bossan_run_loop, 3);
|
|
2004
|
+
rb_define_module_function(server, "stop", bossan_stop, 0);
|
|
2005
|
+
|
|
2006
|
+
rb_define_module_function(server, "access_log", bossan_access_log, 1);
|
|
2007
|
+
rb_define_module_function(server, "set_max_content_length", bossan_set_max_content_length, 1);
|
|
2008
|
+
rb_define_module_function(server, "get_max_content_length", bossan_get_max_content_length, 0);
|
|
2009
|
+
|
|
2010
|
+
rb_require("stringio");
|
|
2011
|
+
StringIO = rb_const_get(rb_cObject, rb_intern("StringIO"));
|
|
2012
|
+
}
|