steamcannon-thin 1.2.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (137) hide show
  1. data/CHANGELOG +288 -0
  2. data/COPYING +18 -0
  3. data/README +69 -0
  4. data/Rakefile +44 -0
  5. data/benchmark/abc +51 -0
  6. data/benchmark/benchmarker.rb +80 -0
  7. data/benchmark/runner +82 -0
  8. data/bin/thin +6 -0
  9. data/example/adapter.rb +32 -0
  10. data/example/async_app.ru +126 -0
  11. data/example/async_chat.ru +247 -0
  12. data/example/async_tailer.ru +100 -0
  13. data/example/config.ru +22 -0
  14. data/example/monit_sockets +20 -0
  15. data/example/monit_unixsock +20 -0
  16. data/example/myapp.rb +1 -0
  17. data/example/ramaze.ru +12 -0
  18. data/example/thin.god +80 -0
  19. data/example/thin_solaris_smf.erb +36 -0
  20. data/example/thin_solaris_smf.readme.txt +150 -0
  21. data/example/vlad.rake +64 -0
  22. data/ext/thin_parser/common.rl +55 -0
  23. data/ext/thin_parser/ext_help.h +14 -0
  24. data/ext/thin_parser/extconf.rb +6 -0
  25. data/ext/thin_parser/parser.c +1249 -0
  26. data/ext/thin_parser/parser.h +49 -0
  27. data/ext/thin_parser/parser.rl +157 -0
  28. data/ext/thin_parser/thin.c +436 -0
  29. data/lib/rack/adapter/loader.rb +91 -0
  30. data/lib/rack/adapter/rails.rb +183 -0
  31. data/lib/thin.rb +56 -0
  32. data/lib/thin/backends/base.rb +149 -0
  33. data/lib/thin/backends/swiftiply_client.rb +56 -0
  34. data/lib/thin/backends/tcp_server.rb +29 -0
  35. data/lib/thin/backends/unix_server.rb +51 -0
  36. data/lib/thin/command.rb +53 -0
  37. data/lib/thin/connection.rb +224 -0
  38. data/lib/thin/controllers/cluster.rb +178 -0
  39. data/lib/thin/controllers/controller.rb +188 -0
  40. data/lib/thin/controllers/service.rb +75 -0
  41. data/lib/thin/controllers/service.sh.erb +39 -0
  42. data/lib/thin/daemonizing.rb +180 -0
  43. data/lib/thin/headers.rb +39 -0
  44. data/lib/thin/logging.rb +54 -0
  45. data/lib/thin/request.rb +156 -0
  46. data/lib/thin/response.rb +101 -0
  47. data/lib/thin/runner.rb +220 -0
  48. data/lib/thin/server.rb +253 -0
  49. data/lib/thin/stats.html.erb +216 -0
  50. data/lib/thin/stats.rb +52 -0
  51. data/lib/thin/statuses.rb +43 -0
  52. data/lib/thin/version.rb +32 -0
  53. data/lib/thin_parser.so +0 -0
  54. data/spec/backends/swiftiply_client_spec.rb +66 -0
  55. data/spec/backends/tcp_server_spec.rb +33 -0
  56. data/spec/backends/unix_server_spec.rb +37 -0
  57. data/spec/command_spec.rb +25 -0
  58. data/spec/configs/cluster.yml +9 -0
  59. data/spec/configs/single.yml +9 -0
  60. data/spec/connection_spec.rb +106 -0
  61. data/spec/controllers/cluster_spec.rb +267 -0
  62. data/spec/controllers/controller_spec.rb +129 -0
  63. data/spec/controllers/service_spec.rb +50 -0
  64. data/spec/daemonizing_spec.rb +196 -0
  65. data/spec/headers_spec.rb +40 -0
  66. data/spec/logging_spec.rb +46 -0
  67. data/spec/perf/request_perf_spec.rb +50 -0
  68. data/spec/perf/response_perf_spec.rb +19 -0
  69. data/spec/perf/server_perf_spec.rb +39 -0
  70. data/spec/rack/loader_spec.rb +42 -0
  71. data/spec/rack/rails_adapter_spec.rb +173 -0
  72. data/spec/rails_app/app/controllers/application.rb +10 -0
  73. data/spec/rails_app/app/controllers/simple_controller.rb +19 -0
  74. data/spec/rails_app/app/helpers/application_helper.rb +3 -0
  75. data/spec/rails_app/app/views/simple/index.html.erb +15 -0
  76. data/spec/rails_app/config/boot.rb +109 -0
  77. data/spec/rails_app/config/environment.rb +64 -0
  78. data/spec/rails_app/config/environments/development.rb +18 -0
  79. data/spec/rails_app/config/environments/production.rb +19 -0
  80. data/spec/rails_app/config/environments/test.rb +22 -0
  81. data/spec/rails_app/config/initializers/inflections.rb +10 -0
  82. data/spec/rails_app/config/initializers/mime_types.rb +5 -0
  83. data/spec/rails_app/config/routes.rb +35 -0
  84. data/spec/rails_app/public/404.html +30 -0
  85. data/spec/rails_app/public/422.html +30 -0
  86. data/spec/rails_app/public/500.html +30 -0
  87. data/spec/rails_app/public/dispatch.cgi +10 -0
  88. data/spec/rails_app/public/dispatch.fcgi +24 -0
  89. data/spec/rails_app/public/dispatch.rb +10 -0
  90. data/spec/rails_app/public/favicon.ico +0 -0
  91. data/spec/rails_app/public/images/rails.png +0 -0
  92. data/spec/rails_app/public/index.html +277 -0
  93. data/spec/rails_app/public/javascripts/application.js +2 -0
  94. data/spec/rails_app/public/javascripts/controls.js +963 -0
  95. data/spec/rails_app/public/javascripts/dragdrop.js +972 -0
  96. data/spec/rails_app/public/javascripts/effects.js +1120 -0
  97. data/spec/rails_app/public/javascripts/prototype.js +4225 -0
  98. data/spec/rails_app/public/robots.txt +5 -0
  99. data/spec/rails_app/script/about +3 -0
  100. data/spec/rails_app/script/console +3 -0
  101. data/spec/rails_app/script/destroy +3 -0
  102. data/spec/rails_app/script/generate +3 -0
  103. data/spec/rails_app/script/performance/benchmarker +3 -0
  104. data/spec/rails_app/script/performance/profiler +3 -0
  105. data/spec/rails_app/script/performance/request +3 -0
  106. data/spec/rails_app/script/plugin +3 -0
  107. data/spec/rails_app/script/process/inspector +3 -0
  108. data/spec/rails_app/script/process/reaper +3 -0
  109. data/spec/rails_app/script/process/spawner +3 -0
  110. data/spec/rails_app/script/runner +3 -0
  111. data/spec/rails_app/script/server +3 -0
  112. data/spec/request/mongrel_spec.rb +39 -0
  113. data/spec/request/parser_spec.rb +254 -0
  114. data/spec/request/persistent_spec.rb +35 -0
  115. data/spec/request/processing_spec.rb +50 -0
  116. data/spec/response_spec.rb +91 -0
  117. data/spec/runner_spec.rb +168 -0
  118. data/spec/server/builder_spec.rb +44 -0
  119. data/spec/server/pipelining_spec.rb +110 -0
  120. data/spec/server/robustness_spec.rb +34 -0
  121. data/spec/server/stopping_spec.rb +55 -0
  122. data/spec/server/swiftiply.yml +6 -0
  123. data/spec/server/swiftiply_spec.rb +32 -0
  124. data/spec/server/tcp_spec.rb +57 -0
  125. data/spec/server/threaded_spec.rb +27 -0
  126. data/spec/server/unix_socket_spec.rb +26 -0
  127. data/spec/server_spec.rb +100 -0
  128. data/spec/spec_helper.rb +220 -0
  129. data/tasks/announce.rake +22 -0
  130. data/tasks/deploy.rake +13 -0
  131. data/tasks/email.erb +30 -0
  132. data/tasks/gem.rake +66 -0
  133. data/tasks/rdoc.rake +25 -0
  134. data/tasks/site.rake +15 -0
  135. data/tasks/spec.rake +43 -0
  136. data/tasks/stats.rake +28 -0
  137. 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
+ }