steamcannon-thin 1.2.8
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +288 -0
- data/COPYING +18 -0
- data/README +69 -0
- data/Rakefile +44 -0
- data/benchmark/abc +51 -0
- data/benchmark/benchmarker.rb +80 -0
- data/benchmark/runner +82 -0
- data/bin/thin +6 -0
- data/example/adapter.rb +32 -0
- data/example/async_app.ru +126 -0
- data/example/async_chat.ru +247 -0
- data/example/async_tailer.ru +100 -0
- data/example/config.ru +22 -0
- data/example/monit_sockets +20 -0
- data/example/monit_unixsock +20 -0
- data/example/myapp.rb +1 -0
- data/example/ramaze.ru +12 -0
- data/example/thin.god +80 -0
- data/example/thin_solaris_smf.erb +36 -0
- data/example/thin_solaris_smf.readme.txt +150 -0
- data/example/vlad.rake +64 -0
- data/ext/thin_parser/common.rl +55 -0
- data/ext/thin_parser/ext_help.h +14 -0
- data/ext/thin_parser/extconf.rb +6 -0
- data/ext/thin_parser/parser.c +1249 -0
- data/ext/thin_parser/parser.h +49 -0
- data/ext/thin_parser/parser.rl +157 -0
- data/ext/thin_parser/thin.c +436 -0
- data/lib/rack/adapter/loader.rb +91 -0
- data/lib/rack/adapter/rails.rb +183 -0
- data/lib/thin.rb +56 -0
- data/lib/thin/backends/base.rb +149 -0
- data/lib/thin/backends/swiftiply_client.rb +56 -0
- data/lib/thin/backends/tcp_server.rb +29 -0
- data/lib/thin/backends/unix_server.rb +51 -0
- data/lib/thin/command.rb +53 -0
- data/lib/thin/connection.rb +224 -0
- data/lib/thin/controllers/cluster.rb +178 -0
- data/lib/thin/controllers/controller.rb +188 -0
- data/lib/thin/controllers/service.rb +75 -0
- data/lib/thin/controllers/service.sh.erb +39 -0
- data/lib/thin/daemonizing.rb +180 -0
- data/lib/thin/headers.rb +39 -0
- data/lib/thin/logging.rb +54 -0
- data/lib/thin/request.rb +156 -0
- data/lib/thin/response.rb +101 -0
- data/lib/thin/runner.rb +220 -0
- data/lib/thin/server.rb +253 -0
- data/lib/thin/stats.html.erb +216 -0
- data/lib/thin/stats.rb +52 -0
- data/lib/thin/statuses.rb +43 -0
- data/lib/thin/version.rb +32 -0
- data/lib/thin_parser.so +0 -0
- data/spec/backends/swiftiply_client_spec.rb +66 -0
- data/spec/backends/tcp_server_spec.rb +33 -0
- data/spec/backends/unix_server_spec.rb +37 -0
- data/spec/command_spec.rb +25 -0
- data/spec/configs/cluster.yml +9 -0
- data/spec/configs/single.yml +9 -0
- data/spec/connection_spec.rb +106 -0
- data/spec/controllers/cluster_spec.rb +267 -0
- data/spec/controllers/controller_spec.rb +129 -0
- data/spec/controllers/service_spec.rb +50 -0
- data/spec/daemonizing_spec.rb +196 -0
- data/spec/headers_spec.rb +40 -0
- data/spec/logging_spec.rb +46 -0
- data/spec/perf/request_perf_spec.rb +50 -0
- data/spec/perf/response_perf_spec.rb +19 -0
- data/spec/perf/server_perf_spec.rb +39 -0
- data/spec/rack/loader_spec.rb +42 -0
- data/spec/rack/rails_adapter_spec.rb +173 -0
- data/spec/rails_app/app/controllers/application.rb +10 -0
- data/spec/rails_app/app/controllers/simple_controller.rb +19 -0
- data/spec/rails_app/app/helpers/application_helper.rb +3 -0
- data/spec/rails_app/app/views/simple/index.html.erb +15 -0
- data/spec/rails_app/config/boot.rb +109 -0
- data/spec/rails_app/config/environment.rb +64 -0
- data/spec/rails_app/config/environments/development.rb +18 -0
- data/spec/rails_app/config/environments/production.rb +19 -0
- data/spec/rails_app/config/environments/test.rb +22 -0
- data/spec/rails_app/config/initializers/inflections.rb +10 -0
- data/spec/rails_app/config/initializers/mime_types.rb +5 -0
- data/spec/rails_app/config/routes.rb +35 -0
- data/spec/rails_app/public/404.html +30 -0
- data/spec/rails_app/public/422.html +30 -0
- data/spec/rails_app/public/500.html +30 -0
- data/spec/rails_app/public/dispatch.cgi +10 -0
- data/spec/rails_app/public/dispatch.fcgi +24 -0
- data/spec/rails_app/public/dispatch.rb +10 -0
- data/spec/rails_app/public/favicon.ico +0 -0
- data/spec/rails_app/public/images/rails.png +0 -0
- data/spec/rails_app/public/index.html +277 -0
- data/spec/rails_app/public/javascripts/application.js +2 -0
- data/spec/rails_app/public/javascripts/controls.js +963 -0
- data/spec/rails_app/public/javascripts/dragdrop.js +972 -0
- data/spec/rails_app/public/javascripts/effects.js +1120 -0
- data/spec/rails_app/public/javascripts/prototype.js +4225 -0
- data/spec/rails_app/public/robots.txt +5 -0
- data/spec/rails_app/script/about +3 -0
- data/spec/rails_app/script/console +3 -0
- data/spec/rails_app/script/destroy +3 -0
- data/spec/rails_app/script/generate +3 -0
- data/spec/rails_app/script/performance/benchmarker +3 -0
- data/spec/rails_app/script/performance/profiler +3 -0
- data/spec/rails_app/script/performance/request +3 -0
- data/spec/rails_app/script/plugin +3 -0
- data/spec/rails_app/script/process/inspector +3 -0
- data/spec/rails_app/script/process/reaper +3 -0
- data/spec/rails_app/script/process/spawner +3 -0
- data/spec/rails_app/script/runner +3 -0
- data/spec/rails_app/script/server +3 -0
- data/spec/request/mongrel_spec.rb +39 -0
- data/spec/request/parser_spec.rb +254 -0
- data/spec/request/persistent_spec.rb +35 -0
- data/spec/request/processing_spec.rb +50 -0
- data/spec/response_spec.rb +91 -0
- data/spec/runner_spec.rb +168 -0
- data/spec/server/builder_spec.rb +44 -0
- data/spec/server/pipelining_spec.rb +110 -0
- data/spec/server/robustness_spec.rb +34 -0
- data/spec/server/stopping_spec.rb +55 -0
- data/spec/server/swiftiply.yml +6 -0
- data/spec/server/swiftiply_spec.rb +32 -0
- data/spec/server/tcp_spec.rb +57 -0
- data/spec/server/threaded_spec.rb +27 -0
- data/spec/server/unix_socket_spec.rb +26 -0
- data/spec/server_spec.rb +100 -0
- data/spec/spec_helper.rb +220 -0
- data/tasks/announce.rake +22 -0
- data/tasks/deploy.rake +13 -0
- data/tasks/email.erb +30 -0
- data/tasks/gem.rake +66 -0
- data/tasks/rdoc.rake +25 -0
- data/tasks/site.rake +15 -0
- data/tasks/spec.rake +43 -0
- data/tasks/stats.rake +28 -0
- metadata +251 -0
@@ -0,0 +1,49 @@
|
|
1
|
+
/**
|
2
|
+
* Copyright (c) 2005 Zed A. Shaw
|
3
|
+
* You can redistribute it and/or modify it under the same terms as Ruby.
|
4
|
+
*/
|
5
|
+
|
6
|
+
#ifndef http11_parser_h
|
7
|
+
#define http11_parser_h
|
8
|
+
|
9
|
+
#include <sys/types.h>
|
10
|
+
|
11
|
+
#if defined(_WIN32)
|
12
|
+
#include <stddef.h>
|
13
|
+
#endif
|
14
|
+
|
15
|
+
typedef void (*element_cb)(void *data, const char *at, size_t length);
|
16
|
+
typedef void (*field_cb)(void *data, const char *field, size_t flen, const char *value, size_t vlen);
|
17
|
+
|
18
|
+
typedef struct http_parser {
|
19
|
+
int cs;
|
20
|
+
size_t body_start;
|
21
|
+
int content_len;
|
22
|
+
size_t nread;
|
23
|
+
size_t mark;
|
24
|
+
size_t field_start;
|
25
|
+
size_t field_len;
|
26
|
+
size_t query_start;
|
27
|
+
|
28
|
+
void *data;
|
29
|
+
|
30
|
+
field_cb http_field;
|
31
|
+
element_cb request_method;
|
32
|
+
element_cb request_uri;
|
33
|
+
element_cb fragment;
|
34
|
+
element_cb request_path;
|
35
|
+
element_cb query_string;
|
36
|
+
element_cb http_version;
|
37
|
+
element_cb header_done;
|
38
|
+
|
39
|
+
} http_parser;
|
40
|
+
|
41
|
+
int http_parser_init(http_parser *parser);
|
42
|
+
int http_parser_finish(http_parser *parser);
|
43
|
+
size_t http_parser_execute(http_parser *parser, const char *data, size_t len, size_t off);
|
44
|
+
int http_parser_has_error(http_parser *parser);
|
45
|
+
int http_parser_is_finished(http_parser *parser);
|
46
|
+
|
47
|
+
#define http_parser_nread(parser) (parser)->nread
|
48
|
+
|
49
|
+
#endif
|
@@ -0,0 +1,157 @@
|
|
1
|
+
/**
|
2
|
+
* Copyright (c) 2005 Zed A. Shaw
|
3
|
+
* You can redistribute it and/or modify it under the same terms as Ruby.
|
4
|
+
*/
|
5
|
+
#include "parser.h"
|
6
|
+
#include <stdio.h>
|
7
|
+
#include <assert.h>
|
8
|
+
#include <stdlib.h>
|
9
|
+
#include <ctype.h>
|
10
|
+
#include <string.h>
|
11
|
+
|
12
|
+
#define LEN(AT, FPC) (FPC - buffer - parser->AT)
|
13
|
+
#define MARK(M,FPC) (parser->M = (FPC) - buffer)
|
14
|
+
#define PTR_TO(F) (buffer + parser->F)
|
15
|
+
|
16
|
+
/** Machine **/
|
17
|
+
|
18
|
+
%%{
|
19
|
+
|
20
|
+
machine http_parser;
|
21
|
+
|
22
|
+
action mark {MARK(mark, fpc); }
|
23
|
+
|
24
|
+
|
25
|
+
action start_field { MARK(field_start, fpc); }
|
26
|
+
action write_field {
|
27
|
+
parser->field_len = LEN(field_start, fpc);
|
28
|
+
}
|
29
|
+
|
30
|
+
action start_value { MARK(mark, fpc); }
|
31
|
+
action write_value {
|
32
|
+
if (parser->http_field != NULL) {
|
33
|
+
parser->http_field(parser->data, PTR_TO(field_start), parser->field_len, PTR_TO(mark), LEN(mark, fpc));
|
34
|
+
}
|
35
|
+
}
|
36
|
+
action request_method {
|
37
|
+
if (parser->request_method != NULL) {
|
38
|
+
parser->request_method(parser->data, PTR_TO(mark), LEN(mark, fpc));
|
39
|
+
}
|
40
|
+
}
|
41
|
+
action request_uri {
|
42
|
+
if (parser->request_uri != NULL) {
|
43
|
+
parser->request_uri(parser->data, PTR_TO(mark), LEN(mark, fpc));
|
44
|
+
}
|
45
|
+
}
|
46
|
+
action fragment {
|
47
|
+
if (parser->fragment != NULL) {
|
48
|
+
parser->fragment(parser->data, PTR_TO(mark), LEN(mark, fpc));
|
49
|
+
}
|
50
|
+
}
|
51
|
+
|
52
|
+
action start_query {MARK(query_start, fpc); }
|
53
|
+
action query_string {
|
54
|
+
if (parser->query_string != NULL) {
|
55
|
+
parser->query_string(parser->data, PTR_TO(query_start), LEN(query_start, fpc));
|
56
|
+
}
|
57
|
+
}
|
58
|
+
|
59
|
+
action http_version {
|
60
|
+
if (parser->http_version != NULL) {
|
61
|
+
parser->http_version(parser->data, PTR_TO(mark), LEN(mark, fpc));
|
62
|
+
}
|
63
|
+
}
|
64
|
+
|
65
|
+
action request_path {
|
66
|
+
if (parser->request_path != NULL) {
|
67
|
+
parser->request_path(parser->data, PTR_TO(mark), LEN(mark,fpc));
|
68
|
+
}
|
69
|
+
}
|
70
|
+
|
71
|
+
action done {
|
72
|
+
parser->body_start = fpc - buffer + 1;
|
73
|
+
if (parser->header_done != NULL) {
|
74
|
+
parser->header_done(parser->data, fpc + 1, pe - fpc - 1);
|
75
|
+
}
|
76
|
+
fbreak;
|
77
|
+
}
|
78
|
+
|
79
|
+
include http_parser_common "common.rl";
|
80
|
+
|
81
|
+
}%%
|
82
|
+
|
83
|
+
/** Data **/
|
84
|
+
%% write data;
|
85
|
+
|
86
|
+
int thin_http_parser_init(http_parser *parser) {
|
87
|
+
int cs = 0;
|
88
|
+
%% write init;
|
89
|
+
parser->cs = cs;
|
90
|
+
parser->body_start = 0;
|
91
|
+
parser->content_len = 0;
|
92
|
+
parser->mark = 0;
|
93
|
+
parser->nread = 0;
|
94
|
+
parser->field_len = 0;
|
95
|
+
parser->field_start = 0;
|
96
|
+
|
97
|
+
return(1);
|
98
|
+
}
|
99
|
+
|
100
|
+
|
101
|
+
/** exec **/
|
102
|
+
size_t thin_http_parser_execute(http_parser *parser, const char *buffer, size_t len, size_t off) {
|
103
|
+
const char *p, *pe;
|
104
|
+
int cs = parser->cs;
|
105
|
+
|
106
|
+
assert(off <= len && "offset past end of buffer");
|
107
|
+
|
108
|
+
p = buffer+off;
|
109
|
+
pe = buffer+len;
|
110
|
+
|
111
|
+
assert(*pe == '\0' && "pointer does not end on NUL");
|
112
|
+
assert(pe - p == len - off && "pointers aren't same distance");
|
113
|
+
|
114
|
+
|
115
|
+
%% write exec;
|
116
|
+
|
117
|
+
parser->cs = cs;
|
118
|
+
parser->nread += p - (buffer + off);
|
119
|
+
|
120
|
+
assert(p <= pe && "buffer overflow after parsing execute");
|
121
|
+
assert(parser->nread <= len && "nread longer than length");
|
122
|
+
assert(parser->body_start <= len && "body starts after buffer end");
|
123
|
+
assert(parser->mark < len && "mark is after buffer end");
|
124
|
+
assert(parser->field_len <= len && "field has length longer than whole buffer");
|
125
|
+
assert(parser->field_start < len && "field starts after buffer end");
|
126
|
+
|
127
|
+
if(parser->body_start) {
|
128
|
+
/* final \r\n combo encountered so stop right here */
|
129
|
+
parser->nread++;
|
130
|
+
}
|
131
|
+
|
132
|
+
return(parser->nread);
|
133
|
+
}
|
134
|
+
|
135
|
+
int thin_http_parser_finish(http_parser *parser)
|
136
|
+
{
|
137
|
+
int cs = parser->cs;
|
138
|
+
|
139
|
+
|
140
|
+
parser->cs = cs;
|
141
|
+
|
142
|
+
if (thin_http_parser_has_error(parser) ) {
|
143
|
+
return -1;
|
144
|
+
} else if (thin_http_parser_is_finished(parser) ) {
|
145
|
+
return 1;
|
146
|
+
} else {
|
147
|
+
return 0;
|
148
|
+
}
|
149
|
+
}
|
150
|
+
|
151
|
+
int thin_http_parser_has_error(http_parser *parser) {
|
152
|
+
return parser->cs == http_parser_error;
|
153
|
+
}
|
154
|
+
|
155
|
+
int thin_http_parser_is_finished(http_parser *parser) {
|
156
|
+
return parser->cs == http_parser_first_final;
|
157
|
+
}
|
@@ -0,0 +1,436 @@
|
|
1
|
+
/**
|
2
|
+
* Mongrel Parser adpated to Thin and to play more nicely with Rack specs.
|
3
|
+
*
|
4
|
+
* Orignal version Copyright (c) 2005 Zed A. Shaw
|
5
|
+
* You can redistribute it and/or modify it under the same terms as Ruby.
|
6
|
+
*/
|
7
|
+
#include "ruby.h"
|
8
|
+
#include "ext_help.h"
|
9
|
+
#include <assert.h>
|
10
|
+
#include <string.h>
|
11
|
+
#include "parser.h"
|
12
|
+
#include <ctype.h>
|
13
|
+
|
14
|
+
static VALUE mThin;
|
15
|
+
static VALUE cHttpParser;
|
16
|
+
static VALUE eHttpParserError;
|
17
|
+
|
18
|
+
static VALUE global_empty;
|
19
|
+
static VALUE global_http_prefix;
|
20
|
+
static VALUE global_request_method;
|
21
|
+
static VALUE global_request_uri;
|
22
|
+
static VALUE global_fragment;
|
23
|
+
static VALUE global_query_string;
|
24
|
+
static VALUE global_http_version;
|
25
|
+
static VALUE global_content_length;
|
26
|
+
static VALUE global_http_content_length;
|
27
|
+
static VALUE global_request_path;
|
28
|
+
static VALUE global_content_type;
|
29
|
+
static VALUE global_http_content_type;
|
30
|
+
static VALUE global_gateway_interface;
|
31
|
+
static VALUE global_gateway_interface_value;
|
32
|
+
static VALUE global_server_name;
|
33
|
+
static VALUE global_server_port;
|
34
|
+
static VALUE global_server_protocol;
|
35
|
+
static VALUE global_server_protocol_value;
|
36
|
+
static VALUE global_http_host;
|
37
|
+
static VALUE global_port_80;
|
38
|
+
static VALUE global_http_body;
|
39
|
+
static VALUE global_url_scheme;
|
40
|
+
static VALUE global_url_scheme_value;
|
41
|
+
static VALUE global_script_name;
|
42
|
+
static VALUE global_path_info;
|
43
|
+
|
44
|
+
#define TRIE_INCREASE 30
|
45
|
+
|
46
|
+
/** Defines common length and error messages for input length validation. */
|
47
|
+
#define DEF_MAX_LENGTH(N,length) const size_t MAX_##N##_LENGTH = length; const char *MAX_##N##_LENGTH_ERR = "HTTP element " # N " is longer than the " # length " allowed length."
|
48
|
+
|
49
|
+
/** Validates the max length of given input and throws an HttpParserError exception if over. */
|
50
|
+
#define VALIDATE_MAX_LENGTH(len, N) if(len > MAX_##N##_LENGTH) { rb_raise(eHttpParserError, MAX_##N##_LENGTH_ERR); }
|
51
|
+
|
52
|
+
/** Defines global strings in the init method. */
|
53
|
+
#define DEF_GLOBAL(N, val) global_##N = rb_obj_freeze(rb_str_new2(val)); rb_global_variable(&global_##N)
|
54
|
+
|
55
|
+
/* for compatibility with Ruby 1.8.5, which doesn't declare RSTRING_PTR */
|
56
|
+
#ifndef RSTRING_PTR
|
57
|
+
#define RSTRING_PTR(s) (RSTRING(s)->ptr)
|
58
|
+
#endif
|
59
|
+
|
60
|
+
/* for compatibility with Ruby 1.8.5, which doesn't declare RSTRING_LEN */
|
61
|
+
#ifndef RSTRING_LEN
|
62
|
+
#define RSTRING_LEN(s) (RSTRING(s)->len)
|
63
|
+
#endif
|
64
|
+
|
65
|
+
/* Defines the maximum allowed lengths for various input elements.*/
|
66
|
+
DEF_MAX_LENGTH(FIELD_NAME, 256);
|
67
|
+
DEF_MAX_LENGTH(FIELD_VALUE, 80 * 1024);
|
68
|
+
DEF_MAX_LENGTH(REQUEST_URI, 1024 * 12);
|
69
|
+
DEF_MAX_LENGTH(FRAGMENT, 1024); /* Don't know if this length is specified somewhere or not */
|
70
|
+
DEF_MAX_LENGTH(REQUEST_PATH, 1024);
|
71
|
+
DEF_MAX_LENGTH(QUERY_STRING, (1024 * 10));
|
72
|
+
DEF_MAX_LENGTH(HEADER, (1024 * (80 + 32)));
|
73
|
+
|
74
|
+
|
75
|
+
static void http_field(void *data, const char *field, size_t flen, const char *value, size_t vlen)
|
76
|
+
{
|
77
|
+
char *ch, *end;
|
78
|
+
VALUE req = (VALUE)data;
|
79
|
+
VALUE v = Qnil;
|
80
|
+
VALUE f = Qnil;
|
81
|
+
|
82
|
+
VALIDATE_MAX_LENGTH(flen, FIELD_NAME);
|
83
|
+
VALIDATE_MAX_LENGTH(vlen, FIELD_VALUE);
|
84
|
+
|
85
|
+
v = rb_str_new(value, vlen);
|
86
|
+
f = rb_str_dup(global_http_prefix);
|
87
|
+
f = rb_str_buf_cat(f, field, flen);
|
88
|
+
|
89
|
+
for(ch = RSTRING_PTR(f) + RSTRING_LEN(global_http_prefix), end = RSTRING_PTR(f) + RSTRING_LEN(f); ch < end; ch++) {
|
90
|
+
if(*ch == '-') {
|
91
|
+
*ch = '_';
|
92
|
+
} else {
|
93
|
+
*ch = toupper(*ch);
|
94
|
+
}
|
95
|
+
}
|
96
|
+
|
97
|
+
rb_hash_aset(req, f, v);
|
98
|
+
}
|
99
|
+
|
100
|
+
static void request_method(void *data, const char *at, size_t length)
|
101
|
+
{
|
102
|
+
VALUE req = (VALUE)data;
|
103
|
+
VALUE val = Qnil;
|
104
|
+
|
105
|
+
val = rb_str_new(at, length);
|
106
|
+
rb_hash_aset(req, global_request_method, val);
|
107
|
+
}
|
108
|
+
|
109
|
+
static void request_uri(void *data, const char *at, size_t length)
|
110
|
+
{
|
111
|
+
VALUE req = (VALUE)data;
|
112
|
+
VALUE val = Qnil;
|
113
|
+
|
114
|
+
VALIDATE_MAX_LENGTH(length, REQUEST_URI);
|
115
|
+
|
116
|
+
val = rb_str_new(at, length);
|
117
|
+
rb_hash_aset(req, global_request_uri, val);
|
118
|
+
}
|
119
|
+
|
120
|
+
static void fragment(void *data, const char *at, size_t length)
|
121
|
+
{
|
122
|
+
VALUE req = (VALUE)data;
|
123
|
+
VALUE val = Qnil;
|
124
|
+
|
125
|
+
VALIDATE_MAX_LENGTH(length, FRAGMENT);
|
126
|
+
|
127
|
+
val = rb_str_new(at, length);
|
128
|
+
rb_hash_aset(req, global_fragment, val);
|
129
|
+
}
|
130
|
+
|
131
|
+
static void request_path(void *data, const char *at, size_t length)
|
132
|
+
{
|
133
|
+
VALUE req = (VALUE)data;
|
134
|
+
VALUE val = Qnil;
|
135
|
+
|
136
|
+
VALIDATE_MAX_LENGTH(length, REQUEST_PATH);
|
137
|
+
|
138
|
+
val = rb_str_new(at, length);
|
139
|
+
rb_hash_aset(req, global_request_path, val);
|
140
|
+
rb_hash_aset(req, global_path_info, val);
|
141
|
+
}
|
142
|
+
|
143
|
+
static void query_string(void *data, const char *at, size_t length)
|
144
|
+
{
|
145
|
+
VALUE req = (VALUE)data;
|
146
|
+
VALUE val = Qnil;
|
147
|
+
|
148
|
+
VALIDATE_MAX_LENGTH(length, QUERY_STRING);
|
149
|
+
|
150
|
+
val = rb_str_new(at, length);
|
151
|
+
rb_hash_aset(req, global_query_string, val);
|
152
|
+
}
|
153
|
+
|
154
|
+
static void http_version(void *data, const char *at, size_t length)
|
155
|
+
{
|
156
|
+
VALUE req = (VALUE)data;
|
157
|
+
VALUE val = rb_str_new(at, length);
|
158
|
+
rb_hash_aset(req, global_http_version, val);
|
159
|
+
}
|
160
|
+
|
161
|
+
/** Finalizes the request header to have a bunch of stuff that's
|
162
|
+
needed. */
|
163
|
+
|
164
|
+
static void header_done(void *data, const char *at, size_t length)
|
165
|
+
{
|
166
|
+
VALUE req = (VALUE)data;
|
167
|
+
VALUE temp = Qnil;
|
168
|
+
VALUE ctype = Qnil;
|
169
|
+
VALUE clen = Qnil;
|
170
|
+
VALUE body = Qnil;
|
171
|
+
char *colon = NULL;
|
172
|
+
|
173
|
+
clen = rb_hash_aref(req, global_http_content_length);
|
174
|
+
if(clen != Qnil) {
|
175
|
+
rb_hash_aset(req, global_content_length, clen);
|
176
|
+
rb_hash_delete(req, global_http_content_length);
|
177
|
+
}
|
178
|
+
|
179
|
+
ctype = rb_hash_aref(req, global_http_content_type);
|
180
|
+
if(ctype != Qnil) {
|
181
|
+
rb_hash_aset(req, global_content_type, ctype);
|
182
|
+
rb_hash_delete(req, global_http_content_type);
|
183
|
+
}
|
184
|
+
|
185
|
+
rb_hash_aset(req, global_gateway_interface, global_gateway_interface_value);
|
186
|
+
if((temp = rb_hash_aref(req, global_http_host)) != Qnil) {
|
187
|
+
/* ruby better close strings off with a '\0' dammit */
|
188
|
+
colon = strchr(RSTRING_PTR(temp), ':');
|
189
|
+
if(colon != NULL) {
|
190
|
+
rb_hash_aset(req, global_server_name, rb_str_substr(temp, 0, colon - RSTRING_PTR(temp)));
|
191
|
+
rb_hash_aset(req, global_server_port,
|
192
|
+
rb_str_substr(temp, colon - RSTRING_PTR(temp)+1,
|
193
|
+
RSTRING_LEN(temp)));
|
194
|
+
} else {
|
195
|
+
rb_hash_aset(req, global_server_name, temp);
|
196
|
+
rb_hash_aset(req, global_server_port, global_port_80);
|
197
|
+
}
|
198
|
+
}
|
199
|
+
|
200
|
+
/* grab the initial body and stuff it into the hash */
|
201
|
+
if(length > 0) {
|
202
|
+
body = rb_hash_aref(req, global_http_body);
|
203
|
+
rb_io_write(body, rb_str_new(at, length));
|
204
|
+
}
|
205
|
+
|
206
|
+
/* according to Rack specs, query string must be empty string if none */
|
207
|
+
if (rb_hash_aref(req, global_query_string) == Qnil) {
|
208
|
+
rb_hash_aset(req, global_query_string, global_empty);
|
209
|
+
}
|
210
|
+
if (rb_hash_aref(req, global_path_info) == Qnil) {
|
211
|
+
rb_hash_aset(req, global_path_info, global_empty);
|
212
|
+
}
|
213
|
+
|
214
|
+
/* set some constants */
|
215
|
+
rb_hash_aset(req, global_server_protocol, global_server_protocol_value);
|
216
|
+
rb_hash_aset(req, global_url_scheme, global_url_scheme_value);
|
217
|
+
rb_hash_aset(req, global_script_name, global_empty);
|
218
|
+
}
|
219
|
+
|
220
|
+
|
221
|
+
void Thin_HttpParser_free(void *data) {
|
222
|
+
TRACE();
|
223
|
+
|
224
|
+
if(data) {
|
225
|
+
free(data);
|
226
|
+
}
|
227
|
+
}
|
228
|
+
|
229
|
+
|
230
|
+
VALUE Thin_HttpParser_alloc(VALUE klass)
|
231
|
+
{
|
232
|
+
VALUE obj;
|
233
|
+
http_parser *hp = ALLOC_N(http_parser, 1);
|
234
|
+
TRACE();
|
235
|
+
hp->http_field = http_field;
|
236
|
+
hp->request_method = request_method;
|
237
|
+
hp->request_uri = request_uri;
|
238
|
+
hp->fragment = fragment;
|
239
|
+
hp->request_path = request_path;
|
240
|
+
hp->query_string = query_string;
|
241
|
+
hp->http_version = http_version;
|
242
|
+
hp->header_done = header_done;
|
243
|
+
thin_http_parser_init(hp);
|
244
|
+
|
245
|
+
obj = Data_Wrap_Struct(klass, NULL, Thin_HttpParser_free, hp);
|
246
|
+
|
247
|
+
return obj;
|
248
|
+
}
|
249
|
+
|
250
|
+
|
251
|
+
/**
|
252
|
+
* call-seq:
|
253
|
+
* parser.new -> parser
|
254
|
+
*
|
255
|
+
* Creates a new parser.
|
256
|
+
*/
|
257
|
+
VALUE Thin_HttpParser_init(VALUE self)
|
258
|
+
{
|
259
|
+
http_parser *http = NULL;
|
260
|
+
DATA_GET(self, http_parser, http);
|
261
|
+
thin_http_parser_init(http);
|
262
|
+
|
263
|
+
return self;
|
264
|
+
}
|
265
|
+
|
266
|
+
|
267
|
+
/**
|
268
|
+
* call-seq:
|
269
|
+
* parser.reset -> nil
|
270
|
+
*
|
271
|
+
* Resets the parser to it's initial state so that you can reuse it
|
272
|
+
* rather than making new ones.
|
273
|
+
*/
|
274
|
+
VALUE Thin_HttpParser_reset(VALUE self)
|
275
|
+
{
|
276
|
+
http_parser *http = NULL;
|
277
|
+
DATA_GET(self, http_parser, http);
|
278
|
+
thin_http_parser_init(http);
|
279
|
+
|
280
|
+
return Qnil;
|
281
|
+
}
|
282
|
+
|
283
|
+
|
284
|
+
/**
|
285
|
+
* call-seq:
|
286
|
+
* parser.finish -> true/false
|
287
|
+
*
|
288
|
+
* Finishes a parser early which could put in a "good" or bad state.
|
289
|
+
* You should call reset after finish it or bad things will happen.
|
290
|
+
*/
|
291
|
+
VALUE Thin_HttpParser_finish(VALUE self)
|
292
|
+
{
|
293
|
+
http_parser *http = NULL;
|
294
|
+
DATA_GET(self, http_parser, http);
|
295
|
+
thin_http_parser_finish(http);
|
296
|
+
|
297
|
+
return thin_http_parser_is_finished(http) ? Qtrue : Qfalse;
|
298
|
+
}
|
299
|
+
|
300
|
+
|
301
|
+
/**
|
302
|
+
* call-seq:
|
303
|
+
* parser.execute(req_hash, data, start) -> Integer
|
304
|
+
*
|
305
|
+
* Takes a Hash and a String of data, parses the String of data filling in the Hash
|
306
|
+
* returning an Integer to indicate how much of the data has been read. No matter
|
307
|
+
* what the return value, you should call HttpParser#finished? and HttpParser#error?
|
308
|
+
* to figure out if it's done parsing or there was an error.
|
309
|
+
*
|
310
|
+
* This function now throws an exception when there is a parsing error. This makes
|
311
|
+
* the logic for working with the parser much easier. You can still test for an
|
312
|
+
* error, but now you need to wrap the parser with an exception handling block.
|
313
|
+
*
|
314
|
+
* The third argument allows for parsing a partial request and then continuing
|
315
|
+
* the parsing from that position. It needs all of the original data as well
|
316
|
+
* so you have to append to the data buffer as you read.
|
317
|
+
*/
|
318
|
+
VALUE Thin_HttpParser_execute(VALUE self, VALUE req_hash, VALUE data, VALUE start)
|
319
|
+
{
|
320
|
+
http_parser *http = NULL;
|
321
|
+
int from = 0;
|
322
|
+
char *dptr = NULL;
|
323
|
+
long dlen = 0;
|
324
|
+
|
325
|
+
DATA_GET(self, http_parser, http);
|
326
|
+
|
327
|
+
from = FIX2INT(start);
|
328
|
+
dptr = RSTRING_PTR(data);
|
329
|
+
dlen = RSTRING_LEN(data);
|
330
|
+
|
331
|
+
if(from >= dlen) {
|
332
|
+
rb_raise(eHttpParserError, "Requested start is after data buffer end.");
|
333
|
+
} else {
|
334
|
+
http->data = (void *)req_hash;
|
335
|
+
thin_http_parser_execute(http, dptr, dlen, from);
|
336
|
+
|
337
|
+
VALIDATE_MAX_LENGTH(http_parser_nread(http), HEADER);
|
338
|
+
|
339
|
+
if(thin_http_parser_has_error(http)) {
|
340
|
+
rb_raise(eHttpParserError, "Invalid HTTP format, parsing fails.");
|
341
|
+
} else {
|
342
|
+
return INT2FIX(http_parser_nread(http));
|
343
|
+
}
|
344
|
+
}
|
345
|
+
}
|
346
|
+
|
347
|
+
|
348
|
+
|
349
|
+
/**
|
350
|
+
* call-seq:
|
351
|
+
* parser.error? -> true/false
|
352
|
+
*
|
353
|
+
* Tells you whether the parser is in an error state.
|
354
|
+
*/
|
355
|
+
VALUE Thin_HttpParser_has_error(VALUE self)
|
356
|
+
{
|
357
|
+
http_parser *http = NULL;
|
358
|
+
DATA_GET(self, http_parser, http);
|
359
|
+
|
360
|
+
return thin_http_parser_has_error(http) ? Qtrue : Qfalse;
|
361
|
+
}
|
362
|
+
|
363
|
+
|
364
|
+
/**
|
365
|
+
* call-seq:
|
366
|
+
* parser.finished? -> true/false
|
367
|
+
*
|
368
|
+
* Tells you whether the parser is finished or not and in a good state.
|
369
|
+
*/
|
370
|
+
VALUE Thin_HttpParser_is_finished(VALUE self)
|
371
|
+
{
|
372
|
+
http_parser *http = NULL;
|
373
|
+
DATA_GET(self, http_parser, http);
|
374
|
+
|
375
|
+
return thin_http_parser_is_finished(http) ? Qtrue : Qfalse;
|
376
|
+
}
|
377
|
+
|
378
|
+
|
379
|
+
/**
|
380
|
+
* call-seq:
|
381
|
+
* parser.nread -> Integer
|
382
|
+
*
|
383
|
+
* Returns the amount of data processed so far during this processing cycle. It is
|
384
|
+
* set to 0 on initialize or reset calls and is incremented each time execute is called.
|
385
|
+
*/
|
386
|
+
VALUE Thin_HttpParser_nread(VALUE self)
|
387
|
+
{
|
388
|
+
http_parser *http = NULL;
|
389
|
+
DATA_GET(self, http_parser, http);
|
390
|
+
|
391
|
+
return INT2FIX(http->nread);
|
392
|
+
}
|
393
|
+
|
394
|
+
void Init_thin_parser()
|
395
|
+
{
|
396
|
+
|
397
|
+
mThin = rb_define_module("Thin");
|
398
|
+
|
399
|
+
DEF_GLOBAL(empty, "");
|
400
|
+
DEF_GLOBAL(http_prefix, "HTTP_");
|
401
|
+
DEF_GLOBAL(request_method, "REQUEST_METHOD");
|
402
|
+
DEF_GLOBAL(request_uri, "REQUEST_URI");
|
403
|
+
DEF_GLOBAL(fragment, "FRAGMENT");
|
404
|
+
DEF_GLOBAL(query_string, "QUERY_STRING");
|
405
|
+
DEF_GLOBAL(http_version, "HTTP_VERSION");
|
406
|
+
DEF_GLOBAL(request_path, "REQUEST_PATH");
|
407
|
+
DEF_GLOBAL(content_length, "CONTENT_LENGTH");
|
408
|
+
DEF_GLOBAL(http_content_length, "HTTP_CONTENT_LENGTH");
|
409
|
+
DEF_GLOBAL(content_type, "CONTENT_TYPE");
|
410
|
+
DEF_GLOBAL(http_content_type, "HTTP_CONTENT_TYPE");
|
411
|
+
DEF_GLOBAL(gateway_interface, "GATEWAY_INTERFACE");
|
412
|
+
DEF_GLOBAL(gateway_interface_value, "CGI/1.2");
|
413
|
+
DEF_GLOBAL(server_name, "SERVER_NAME");
|
414
|
+
DEF_GLOBAL(server_port, "SERVER_PORT");
|
415
|
+
DEF_GLOBAL(server_protocol, "SERVER_PROTOCOL");
|
416
|
+
DEF_GLOBAL(server_protocol_value, "HTTP/1.1");
|
417
|
+
DEF_GLOBAL(http_host, "HTTP_HOST");
|
418
|
+
DEF_GLOBAL(port_80, "80");
|
419
|
+
DEF_GLOBAL(http_body, "rack.input");
|
420
|
+
DEF_GLOBAL(url_scheme, "rack.url_scheme");
|
421
|
+
DEF_GLOBAL(url_scheme_value, "http");
|
422
|
+
DEF_GLOBAL(script_name, "SCRIPT_NAME");
|
423
|
+
DEF_GLOBAL(path_info, "PATH_INFO");
|
424
|
+
|
425
|
+
eHttpParserError = rb_define_class_under(mThin, "InvalidRequest", rb_eIOError);
|
426
|
+
|
427
|
+
cHttpParser = rb_define_class_under(mThin, "HttpParser", rb_cObject);
|
428
|
+
rb_define_alloc_func(cHttpParser, Thin_HttpParser_alloc);
|
429
|
+
rb_define_method(cHttpParser, "initialize", Thin_HttpParser_init,0);
|
430
|
+
rb_define_method(cHttpParser, "reset", Thin_HttpParser_reset,0);
|
431
|
+
rb_define_method(cHttpParser, "finish", Thin_HttpParser_finish,0);
|
432
|
+
rb_define_method(cHttpParser, "execute", Thin_HttpParser_execute,3);
|
433
|
+
rb_define_method(cHttpParser, "error?", Thin_HttpParser_has_error,0);
|
434
|
+
rb_define_method(cHttpParser, "finished?", Thin_HttpParser_is_finished,0);
|
435
|
+
rb_define_method(cHttpParser, "nread", Thin_HttpParser_nread,0);
|
436
|
+
}
|