michaelyta-thin 1.2.2

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 +257 -0
  2. data/COPYING +18 -0
  3. data/README +69 -0
  4. data/Rakefile +13 -0
  5. data/benchmark/abc +51 -0
  6. data/benchmark/benchmarker.rb +80 -0
  7. data/benchmark/runner +79 -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 +23 -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 +452 -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 +433 -0
  29. data/lib/rack/adapter/loader.rb +79 -0
  30. data/lib/rack/adapter/rails.rb +175 -0
  31. data/lib/thin.rb +49 -0
  32. data/lib/thin/backends/base.rb +141 -0
  33. data/lib/thin/backends/swiftiply_client.rb +56 -0
  34. data/lib/thin/backends/tcp_server.rb +68 -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 +214 -0
  38. data/lib/thin/controllers/cluster.rb +127 -0
  39. data/lib/thin/controllers/controller.rb +183 -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 +174 -0
  43. data/lib/thin/headers.rb +39 -0
  44. data/lib/thin/logging.rb +54 -0
  45. data/lib/thin/request.rb +158 -0
  46. data/lib/thin/response.rb +101 -0
  47. data/lib/thin/runner.rb +209 -0
  48. data/lib/thin/server.rb +247 -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/spec/backends/swiftiply_client_spec.rb +66 -0
  54. data/spec/backends/tcp_server_spec.rb +33 -0
  55. data/spec/backends/unix_server_spec.rb +37 -0
  56. data/spec/command_spec.rb +25 -0
  57. data/spec/configs/cluster.yml +9 -0
  58. data/spec/configs/single.yml +9 -0
  59. data/spec/connection_spec.rb +105 -0
  60. data/spec/controllers/cluster_spec.rb +235 -0
  61. data/spec/controllers/controller_spec.rb +129 -0
  62. data/spec/controllers/service_spec.rb +50 -0
  63. data/spec/daemonizing_spec.rb +192 -0
  64. data/spec/headers_spec.rb +40 -0
  65. data/spec/logging_spec.rb +46 -0
  66. data/spec/perf/request_perf_spec.rb +50 -0
  67. data/spec/perf/response_perf_spec.rb +19 -0
  68. data/spec/perf/server_perf_spec.rb +39 -0
  69. data/spec/rack/loader_spec.rb +29 -0
  70. data/spec/rack/rails_adapter_spec.rb +106 -0
  71. data/spec/rails_app/app/controllers/application.rb +10 -0
  72. data/spec/rails_app/app/controllers/simple_controller.rb +19 -0
  73. data/spec/rails_app/app/helpers/application_helper.rb +3 -0
  74. data/spec/rails_app/app/views/simple/index.html.erb +15 -0
  75. data/spec/rails_app/config/boot.rb +109 -0
  76. data/spec/rails_app/config/environment.rb +64 -0
  77. data/spec/rails_app/config/environments/development.rb +18 -0
  78. data/spec/rails_app/config/environments/production.rb +19 -0
  79. data/spec/rails_app/config/environments/test.rb +22 -0
  80. data/spec/rails_app/config/initializers/inflections.rb +10 -0
  81. data/spec/rails_app/config/initializers/mime_types.rb +5 -0
  82. data/spec/rails_app/config/routes.rb +35 -0
  83. data/spec/rails_app/public/404.html +30 -0
  84. data/spec/rails_app/public/422.html +30 -0
  85. data/spec/rails_app/public/500.html +30 -0
  86. data/spec/rails_app/public/dispatch.cgi +10 -0
  87. data/spec/rails_app/public/dispatch.fcgi +24 -0
  88. data/spec/rails_app/public/dispatch.rb +10 -0
  89. data/spec/rails_app/public/favicon.ico +0 -0
  90. data/spec/rails_app/public/images/rails.png +0 -0
  91. data/spec/rails_app/public/index.html +277 -0
  92. data/spec/rails_app/public/javascripts/application.js +2 -0
  93. data/spec/rails_app/public/javascripts/controls.js +963 -0
  94. data/spec/rails_app/public/javascripts/dragdrop.js +972 -0
  95. data/spec/rails_app/public/javascripts/effects.js +1120 -0
  96. data/spec/rails_app/public/javascripts/prototype.js +4225 -0
  97. data/spec/rails_app/public/robots.txt +5 -0
  98. data/spec/rails_app/script/about +3 -0
  99. data/spec/rails_app/script/console +3 -0
  100. data/spec/rails_app/script/destroy +3 -0
  101. data/spec/rails_app/script/generate +3 -0
  102. data/spec/rails_app/script/performance/benchmarker +3 -0
  103. data/spec/rails_app/script/performance/profiler +3 -0
  104. data/spec/rails_app/script/performance/request +3 -0
  105. data/spec/rails_app/script/plugin +3 -0
  106. data/spec/rails_app/script/process/inspector +3 -0
  107. data/spec/rails_app/script/process/reaper +3 -0
  108. data/spec/rails_app/script/process/spawner +3 -0
  109. data/spec/rails_app/script/runner +3 -0
  110. data/spec/rails_app/script/server +3 -0
  111. data/spec/request/mongrel_spec.rb +39 -0
  112. data/spec/request/parser_spec.rb +215 -0
  113. data/spec/request/persistent_spec.rb +35 -0
  114. data/spec/request/processing_spec.rb +45 -0
  115. data/spec/response_spec.rb +91 -0
  116. data/spec/runner_spec.rb +168 -0
  117. data/spec/server/builder_spec.rb +44 -0
  118. data/spec/server/pipelining_spec.rb +110 -0
  119. data/spec/server/robustness_spec.rb +34 -0
  120. data/spec/server/stopping_spec.rb +55 -0
  121. data/spec/server/swiftiply.yml +6 -0
  122. data/spec/server/swiftiply_spec.rb +32 -0
  123. data/spec/server/tcp_spec.rb +57 -0
  124. data/spec/server/threaded_spec.rb +27 -0
  125. data/spec/server/unix_socket_spec.rb +26 -0
  126. data/spec/server_spec.rb +96 -0
  127. data/spec/spec_helper.rb +219 -0
  128. data/tasks/announce.rake +22 -0
  129. data/tasks/deploy.rake +16 -0
  130. data/tasks/email.erb +30 -0
  131. data/tasks/ext.rake +42 -0
  132. data/tasks/gem.rake +108 -0
  133. data/tasks/rdoc.rake +25 -0
  134. data/tasks/site.rake +15 -0
  135. data/tasks/spec.rake +48 -0
  136. data/tasks/stats.rake +28 -0
  137. metadata +254 -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,433 @@
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
+
211
+ /* set some constants */
212
+ rb_hash_aset(req, global_server_protocol, global_server_protocol_value);
213
+ rb_hash_aset(req, global_url_scheme, global_url_scheme_value);
214
+ rb_hash_aset(req, global_script_name, global_empty);
215
+ }
216
+
217
+
218
+ void Thin_HttpParser_free(void *data) {
219
+ TRACE();
220
+
221
+ if(data) {
222
+ free(data);
223
+ }
224
+ }
225
+
226
+
227
+ VALUE Thin_HttpParser_alloc(VALUE klass)
228
+ {
229
+ VALUE obj;
230
+ http_parser *hp = ALLOC_N(http_parser, 1);
231
+ TRACE();
232
+ hp->http_field = http_field;
233
+ hp->request_method = request_method;
234
+ hp->request_uri = request_uri;
235
+ hp->fragment = fragment;
236
+ hp->request_path = request_path;
237
+ hp->query_string = query_string;
238
+ hp->http_version = http_version;
239
+ hp->header_done = header_done;
240
+ thin_http_parser_init(hp);
241
+
242
+ obj = Data_Wrap_Struct(klass, NULL, Thin_HttpParser_free, hp);
243
+
244
+ return obj;
245
+ }
246
+
247
+
248
+ /**
249
+ * call-seq:
250
+ * parser.new -> parser
251
+ *
252
+ * Creates a new parser.
253
+ */
254
+ VALUE Thin_HttpParser_init(VALUE self)
255
+ {
256
+ http_parser *http = NULL;
257
+ DATA_GET(self, http_parser, http);
258
+ thin_http_parser_init(http);
259
+
260
+ return self;
261
+ }
262
+
263
+
264
+ /**
265
+ * call-seq:
266
+ * parser.reset -> nil
267
+ *
268
+ * Resets the parser to it's initial state so that you can reuse it
269
+ * rather than making new ones.
270
+ */
271
+ VALUE Thin_HttpParser_reset(VALUE self)
272
+ {
273
+ http_parser *http = NULL;
274
+ DATA_GET(self, http_parser, http);
275
+ thin_http_parser_init(http);
276
+
277
+ return Qnil;
278
+ }
279
+
280
+
281
+ /**
282
+ * call-seq:
283
+ * parser.finish -> true/false
284
+ *
285
+ * Finishes a parser early which could put in a "good" or bad state.
286
+ * You should call reset after finish it or bad things will happen.
287
+ */
288
+ VALUE Thin_HttpParser_finish(VALUE self)
289
+ {
290
+ http_parser *http = NULL;
291
+ DATA_GET(self, http_parser, http);
292
+ thin_http_parser_finish(http);
293
+
294
+ return thin_http_parser_is_finished(http) ? Qtrue : Qfalse;
295
+ }
296
+
297
+
298
+ /**
299
+ * call-seq:
300
+ * parser.execute(req_hash, data, start) -> Integer
301
+ *
302
+ * Takes a Hash and a String of data, parses the String of data filling in the Hash
303
+ * returning an Integer to indicate how much of the data has been read. No matter
304
+ * what the return value, you should call HttpParser#finished? and HttpParser#error?
305
+ * to figure out if it's done parsing or there was an error.
306
+ *
307
+ * This function now throws an exception when there is a parsing error. This makes
308
+ * the logic for working with the parser much easier. You can still test for an
309
+ * error, but now you need to wrap the parser with an exception handling block.
310
+ *
311
+ * The third argument allows for parsing a partial request and then continuing
312
+ * the parsing from that position. It needs all of the original data as well
313
+ * so you have to append to the data buffer as you read.
314
+ */
315
+ VALUE Thin_HttpParser_execute(VALUE self, VALUE req_hash, VALUE data, VALUE start)
316
+ {
317
+ http_parser *http = NULL;
318
+ int from = 0;
319
+ char *dptr = NULL;
320
+ long dlen = 0;
321
+
322
+ DATA_GET(self, http_parser, http);
323
+
324
+ from = FIX2INT(start);
325
+ dptr = RSTRING_PTR(data);
326
+ dlen = RSTRING_LEN(data);
327
+
328
+ if(from >= dlen) {
329
+ rb_raise(eHttpParserError, "Requested start is after data buffer end.");
330
+ } else {
331
+ http->data = (void *)req_hash;
332
+ thin_http_parser_execute(http, dptr, dlen, from);
333
+
334
+ VALIDATE_MAX_LENGTH(http_parser_nread(http), HEADER);
335
+
336
+ if(thin_http_parser_has_error(http)) {
337
+ rb_raise(eHttpParserError, "Invalid HTTP format, parsing fails.");
338
+ } else {
339
+ return INT2FIX(http_parser_nread(http));
340
+ }
341
+ }
342
+ }
343
+
344
+
345
+
346
+ /**
347
+ * call-seq:
348
+ * parser.error? -> true/false
349
+ *
350
+ * Tells you whether the parser is in an error state.
351
+ */
352
+ VALUE Thin_HttpParser_has_error(VALUE self)
353
+ {
354
+ http_parser *http = NULL;
355
+ DATA_GET(self, http_parser, http);
356
+
357
+ return thin_http_parser_has_error(http) ? Qtrue : Qfalse;
358
+ }
359
+
360
+
361
+ /**
362
+ * call-seq:
363
+ * parser.finished? -> true/false
364
+ *
365
+ * Tells you whether the parser is finished or not and in a good state.
366
+ */
367
+ VALUE Thin_HttpParser_is_finished(VALUE self)
368
+ {
369
+ http_parser *http = NULL;
370
+ DATA_GET(self, http_parser, http);
371
+
372
+ return thin_http_parser_is_finished(http) ? Qtrue : Qfalse;
373
+ }
374
+
375
+
376
+ /**
377
+ * call-seq:
378
+ * parser.nread -> Integer
379
+ *
380
+ * Returns the amount of data processed so far during this processing cycle. It is
381
+ * set to 0 on initialize or reset calls and is incremented each time execute is called.
382
+ */
383
+ VALUE Thin_HttpParser_nread(VALUE self)
384
+ {
385
+ http_parser *http = NULL;
386
+ DATA_GET(self, http_parser, http);
387
+
388
+ return INT2FIX(http->nread);
389
+ }
390
+
391
+ void Init_thin_parser()
392
+ {
393
+
394
+ mThin = rb_define_module("Thin");
395
+
396
+ DEF_GLOBAL(empty, "");
397
+ DEF_GLOBAL(http_prefix, "HTTP_");
398
+ DEF_GLOBAL(request_method, "REQUEST_METHOD");
399
+ DEF_GLOBAL(request_uri, "REQUEST_URI");
400
+ DEF_GLOBAL(fragment, "FRAGMENT");
401
+ DEF_GLOBAL(query_string, "QUERY_STRING");
402
+ DEF_GLOBAL(http_version, "HTTP_VERSION");
403
+ DEF_GLOBAL(request_path, "REQUEST_PATH");
404
+ DEF_GLOBAL(content_length, "CONTENT_LENGTH");
405
+ DEF_GLOBAL(http_content_length, "HTTP_CONTENT_LENGTH");
406
+ DEF_GLOBAL(content_type, "CONTENT_TYPE");
407
+ DEF_GLOBAL(http_content_type, "HTTP_CONTENT_TYPE");
408
+ DEF_GLOBAL(gateway_interface, "GATEWAY_INTERFACE");
409
+ DEF_GLOBAL(gateway_interface_value, "CGI/1.2");
410
+ DEF_GLOBAL(server_name, "SERVER_NAME");
411
+ DEF_GLOBAL(server_port, "SERVER_PORT");
412
+ DEF_GLOBAL(server_protocol, "SERVER_PROTOCOL");
413
+ DEF_GLOBAL(server_protocol_value, "HTTP/1.1");
414
+ DEF_GLOBAL(http_host, "HTTP_HOST");
415
+ DEF_GLOBAL(port_80, "80");
416
+ DEF_GLOBAL(http_body, "rack.input");
417
+ DEF_GLOBAL(url_scheme, "rack.url_scheme");
418
+ DEF_GLOBAL(url_scheme_value, "http");
419
+ DEF_GLOBAL(script_name, "SCRIPT_NAME");
420
+ DEF_GLOBAL(path_info, "PATH_INFO");
421
+
422
+ eHttpParserError = rb_define_class_under(mThin, "InvalidRequest", rb_eIOError);
423
+
424
+ cHttpParser = rb_define_class_under(mThin, "HttpParser", rb_cObject);
425
+ rb_define_alloc_func(cHttpParser, Thin_HttpParser_alloc);
426
+ rb_define_method(cHttpParser, "initialize", Thin_HttpParser_init,0);
427
+ rb_define_method(cHttpParser, "reset", Thin_HttpParser_reset,0);
428
+ rb_define_method(cHttpParser, "finish", Thin_HttpParser_finish,0);
429
+ rb_define_method(cHttpParser, "execute", Thin_HttpParser_execute,3);
430
+ rb_define_method(cHttpParser, "error?", Thin_HttpParser_has_error,0);
431
+ rb_define_method(cHttpParser, "finished?", Thin_HttpParser_is_finished,0);
432
+ rb_define_method(cHttpParser, "nread", Thin_HttpParser_nread,0);
433
+ }