mongrel-maglev- 1.1.9.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. data/COPYING +55 -0
  2. data/History.txt +69 -0
  3. data/LICENSE +55 -0
  4. data/Manifest.txt +69 -0
  5. data/Rakefile +8 -0
  6. data/TODO +5 -0
  7. data/bin/mongrel_rails +284 -0
  8. data/examples/builder.rb +29 -0
  9. data/examples/camping/README +3 -0
  10. data/examples/camping/blog.rb +294 -0
  11. data/examples/camping/tepee.rb +149 -0
  12. data/examples/httpd.conf +474 -0
  13. data/examples/mime.yaml +3 -0
  14. data/examples/mongrel.conf +9 -0
  15. data/examples/monitrc +57 -0
  16. data/examples/random_thrash.rb +19 -0
  17. data/examples/simpletest.rb +52 -0
  18. data/examples/webrick_compare.rb +20 -0
  19. data/ext/http11/Http11Service.java +13 -0
  20. data/ext/http11/ext_help.h +15 -0
  21. data/ext/http11/extconf.rb +6 -0
  22. data/ext/http11/http11.c +534 -0
  23. data/ext/http11/http11_parser.c +1243 -0
  24. data/ext/http11/http11_parser.h +49 -0
  25. data/ext/http11/http11_parser.java.rl +159 -0
  26. data/ext/http11/http11_parser.rl +153 -0
  27. data/ext/http11/http11_parser_common.rl +54 -0
  28. data/ext/http11/org/jruby/mongrel/Http11.java +241 -0
  29. data/ext/http11/org/jruby/mongrel/Http11Parser.java +486 -0
  30. data/lib/mongrel.rb +366 -0
  31. data/lib/mongrel/camping.rb +107 -0
  32. data/lib/mongrel/cgi.rb +181 -0
  33. data/lib/mongrel/command.rb +220 -0
  34. data/lib/mongrel/configurator.rb +388 -0
  35. data/lib/mongrel/const.rb +110 -0
  36. data/lib/mongrel/debug.rb +203 -0
  37. data/lib/mongrel/gems.rb +22 -0
  38. data/lib/mongrel/handlers.rb +468 -0
  39. data/lib/mongrel/header_out.rb +28 -0
  40. data/lib/mongrel/http_request.rb +155 -0
  41. data/lib/mongrel/http_response.rb +166 -0
  42. data/lib/mongrel/init.rb +10 -0
  43. data/lib/mongrel/mime_types.yml +616 -0
  44. data/lib/mongrel/rails.rb +185 -0
  45. data/lib/mongrel/stats.rb +89 -0
  46. data/lib/mongrel/tcphack.rb +18 -0
  47. data/lib/mongrel/uri_classifier.rb +76 -0
  48. data/setup.rb +1585 -0
  49. data/tasks/gem.rake +28 -0
  50. data/tasks/native.rake +25 -0
  51. data/tasks/ragel.rake +20 -0
  52. data/test/mime.yaml +3 -0
  53. data/test/mongrel.conf +1 -0
  54. data/test/test_cgi_wrapper.rb +26 -0
  55. data/test/test_command.rb +86 -0
  56. data/test/test_conditional.rb +107 -0
  57. data/test/test_configurator.rb +87 -0
  58. data/test/test_debug.rb +25 -0
  59. data/test/test_handlers.rb +135 -0
  60. data/test/test_http11.rb +156 -0
  61. data/test/test_redirect_handler.rb +44 -0
  62. data/test/test_request_progress.rb +99 -0
  63. data/test/test_response.rb +127 -0
  64. data/test/test_stats.rb +35 -0
  65. data/test/test_uriclassifier.rb +261 -0
  66. data/test/test_ws.rb +115 -0
  67. data/test/testhelp.rb +71 -0
  68. data/tools/trickletest.rb +45 -0
  69. metadata +186 -0
@@ -0,0 +1,3 @@
1
+ ---
2
+ .jpeg: image/jpeg
3
+ .png: image/test
@@ -0,0 +1,9 @@
1
+ ---
2
+ :environment: production
3
+ :daemon: "true"
4
+ :host: 0.0.0.0
5
+ :log_file: log/mongrel.log
6
+ :docroot: public
7
+ :debug: "false"
8
+ :port: 3000
9
+ :pid_file: log/mongrel.pid
data/examples/monitrc ADDED
@@ -0,0 +1,57 @@
1
+ set daemon 60
2
+ set logfile syslog facility log_daemon
3
+ set mailserver localhost
4
+ set mail-format { from: monit@localhost }
5
+ set alert root@localhost
6
+
7
+ check process sshd with pidfile /var/run/sshd.pid
8
+ start program "/etc/init.d/ssh start"
9
+ stop program "/etc/init.d/ssh stop"
10
+ if failed port 22 protocol ssh then restart
11
+ if 5 restarts within 5 cycles then timeout
12
+
13
+ check process mysql with pidfile /var/run/mysqld/mysqld.pid
14
+ group database
15
+ start program = "/etc/init.d/mysql start"
16
+ stop program = "/etc/init.d/mysql stop"
17
+ if failed host 127.0.0.1 port 3306 then restart
18
+ if 5 restarts within 5 cycles then timeout
19
+
20
+ check process httpd with pidfile /usr/local/apache2/logs/httpd.pid
21
+ group www-data
22
+ start program "/usr/local/apache2/bin/apachectl start"
23
+ stop program "/usr/local/apache2/bin/apachectl stop"
24
+ if failed host localhost port 80 protocol http
25
+ and request "/" then alert
26
+ if cpu is greater than 60% for 2 cycles then alert
27
+ if cpu > 80% for 5 cycles then restart
28
+ if children > 250 then restart
29
+ if loadavg(5min) greater than 10 for 8 cycles then alert
30
+ if 3 restarts within 5 cycles then timeout
31
+
32
+ check process mongrel_8000 with pidfile /var/rails/MYAPP/log/mongrel.8000.pid
33
+ group root
34
+ if failed host 127.0.0.1 port 8000 protocol http
35
+ and request "/" then alert
36
+ if cpu is greater than 60% for 2 cycles then alert
37
+ if cpu > 80% for 5 cycles then restart
38
+ if loadavg(5min) greater than 10 for 8 cycles then restart
39
+ if 3 restarts within 5 cycles then timeout
40
+
41
+ check process mongrel_8001 with pidfile /var/rails/MYAPP/log/mongrel.8001.pid
42
+ group root
43
+ if failed host 127.0.0.1 port 8001 protocol http
44
+ and request "/" then alert
45
+ if cpu is greater than 60% for 2 cycles then alert
46
+ if cpu > 80% for 5 cycles then alert
47
+ if loadavg(5min) greater than 10 for 8 cycles then alert
48
+ if 3 restarts within 5 cycles then timeout
49
+
50
+ check process postfix with pidfile /var/spool/postfix/pid/master.pid
51
+ group mail
52
+ start program = "/etc/init.d/postfix start"
53
+ stop program = "/etc/init.d/postfix stop"
54
+ if failed port 25 protocol smtp then restart
55
+ if 5 restarts within 5 cycles then timeout
56
+
57
+
@@ -0,0 +1,19 @@
1
+ require 'socket'
2
+ devrand = open("/dev/random","r")
3
+
4
+ loop do
5
+ s = TCPSocket.new(ARGV[0],ARGV[1])
6
+ s.write("GET / HTTP/1.1\r\n")
7
+ total = 0
8
+ begin
9
+ loop do
10
+ r = devrand.read(10)
11
+ n = s.write(r)
12
+ total += n
13
+ end
14
+ rescue Object
15
+ STDERR.puts "#$!: #{total}"
16
+ end
17
+ s.close
18
+ sleep 1
19
+ end
@@ -0,0 +1,52 @@
1
+ $LOAD_PATH << File.join(File.dirname(__FILE__), "..", "lib")
2
+ require 'mongrel'
3
+ require 'yaml'
4
+
5
+ class SimpleHandler < Mongrel::HttpHandler
6
+ def process(request, response)
7
+ response.start do |head,out|
8
+ head["Content-Type"] = "text/html"
9
+ results = "<html><body>Your request:<br /><pre>#{request.params.to_yaml}</pre><a href=\"/files\">View the files.</a></body></html>"
10
+ out << results
11
+ end
12
+ end
13
+ end
14
+
15
+ class DumbHandler < Mongrel::HttpHandler
16
+ def process(request, response)
17
+ response.start do |head,out|
18
+ head["Content-Type"] = "text/html"
19
+ out.write("test")
20
+ end
21
+ end
22
+ end
23
+
24
+
25
+ if ARGV.length != 3
26
+ STDERR.puts "usage: simpletest.rb <host> <port> <docroot>"
27
+ exit(1)
28
+ end
29
+
30
+ stats = Mongrel::StatisticsFilter.new(:sample_rate => 1)
31
+
32
+ config = Mongrel::Configurator.new :host => ARGV[0], :port => ARGV[1] do
33
+ listener do
34
+ uri "/", :handler => SimpleHandler.new
35
+ uri "/", :handler => Mongrel::DeflateFilter.new
36
+ uri "/", :handler => stats
37
+ uri "/dumb", :handler => DumbHandler.new
38
+ uri "/dumb", :handler => Mongrel::DeflateFilter.new
39
+ uri "/dumb", :handler => stats
40
+ uri "/files", :handler => Mongrel::DirHandler.new(ARGV[2])
41
+ uri "/files", :handler => stats
42
+ uri "/status", :handler => Mongrel::StatusHandler.new(:stats_filter => stats)
43
+ redirect "/redir1", "/"
44
+ redirect "/to", /to/, 'w'
45
+ end
46
+
47
+ trap("INT") { stop }
48
+ run
49
+ end
50
+
51
+ puts "Mongrel running on #{ARGV[0]}:#{ARGV[1]} with docroot #{ARGV[2]}"
52
+ config.join
@@ -0,0 +1,20 @@
1
+ #!/usr/local/bin/ruby
2
+ require 'webrick'
3
+ include WEBrick
4
+
5
+ s = HTTPServer.new( :Port => 4000 )
6
+
7
+ # HTTPServer#mount(path, servletclass)
8
+ # When a request referring "/hello" is received,
9
+ # the HTTPServer get an instance of servletclass
10
+ # and then call a method named do_"a HTTP method".
11
+
12
+ class HelloServlet < HTTPServlet::AbstractServlet
13
+ def do_GET(req, res)
14
+ res.body = "hello!"
15
+ res['Content-Type'] = "text/html"
16
+ end
17
+ end
18
+ s.mount("/test", HelloServlet)
19
+
20
+ s.start
@@ -0,0 +1,13 @@
1
+ import java.io.IOException;
2
+
3
+ import org.jruby.Ruby;
4
+ import org.jruby.runtime.load.BasicLibraryService;
5
+
6
+ import org.jruby.mongrel.Http11;
7
+
8
+ public class Http11Service implements BasicLibraryService {
9
+ public boolean basicLoad(final Ruby runtime) throws IOException {
10
+ Http11.createHttp11(runtime);
11
+ return true;
12
+ }
13
+ }
@@ -0,0 +1,15 @@
1
+ #ifndef ext_help_h
2
+ #define ext_help_h
3
+
4
+ #define RAISE_NOT_NULL(T) if(T == NULL) rb_raise(rb_eArgError, "NULL found for " # T " when shouldn't be.");
5
+ #define DATA_GET(from,type,name) Data_Get_Struct(from,type,name); RAISE_NOT_NULL(name);
6
+ #define REQUIRE_TYPE(V, T) if(TYPE(V) != T) rb_raise(rb_eTypeError, "Wrong argument type for " # V " required " # T);
7
+ #define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
8
+
9
+ #ifdef DEBUG
10
+ #define TRACE() fprintf(stderr, "> %s:%d:%s\n", __FILE__, __LINE__, __FUNCTION__)
11
+ #else
12
+ #define TRACE()
13
+ #endif
14
+
15
+ #endif
@@ -0,0 +1,6 @@
1
+ require 'mkmf'
2
+
3
+ dir_config("http11")
4
+ have_library("c", "main")
5
+
6
+ create_makefile("http11")
@@ -0,0 +1,534 @@
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 "ruby.h"
6
+ #include "ext_help.h"
7
+ #include <assert.h>
8
+ #include <string.h>
9
+ #include "http11_parser.h"
10
+
11
+ #ifndef RSTRING_PTR
12
+ #define RSTRING_PTR(s) (RSTRING(s)->ptr)
13
+ #endif
14
+ #ifndef RSTRING_LEN
15
+ #define RSTRING_LEN(s) (RSTRING(s)->len)
16
+ #endif
17
+
18
+ #ifndef RSTRING_PTR
19
+ #define RSTRING_PTR(s) (RSTRING(s)->ptr)
20
+ #endif
21
+ #ifndef RSTRING_LEN
22
+ #define RSTRING_LEN(s) (RSTRING(s)->len)
23
+ #endif
24
+
25
+ static VALUE mMongrel;
26
+ static VALUE cHttpParser;
27
+ static VALUE eHttpParserError;
28
+
29
+ #define id_handler_map rb_intern("@handler_map")
30
+ #define id_http_body rb_intern("@http_body")
31
+ #define HTTP_PREFIX "HTTP_"
32
+ #define HTTP_PREFIX_LEN (sizeof(HTTP_PREFIX) - 1)
33
+
34
+ static VALUE global_request_method;
35
+ static VALUE global_request_uri;
36
+ static VALUE global_fragment;
37
+ static VALUE global_query_string;
38
+ static VALUE global_http_version;
39
+ static VALUE global_content_length;
40
+ static VALUE global_http_content_length;
41
+ static VALUE global_request_path;
42
+ static VALUE global_content_type;
43
+ static VALUE global_http_content_type;
44
+ static VALUE global_gateway_interface;
45
+ static VALUE global_gateway_interface_value;
46
+ static VALUE global_server_name;
47
+ static VALUE global_server_port;
48
+ static VALUE global_server_protocol;
49
+ static VALUE global_server_protocol_value;
50
+ static VALUE global_http_host;
51
+ static VALUE global_mongrel_version;
52
+ static VALUE global_server_software;
53
+ static VALUE global_port_80;
54
+
55
+ #define TRIE_INCREASE 30
56
+
57
+ /** Defines common length and error messages for input length validation. */
58
+ #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."
59
+
60
+ /** Validates the max length of given input and throws an HttpParserError exception if over. */
61
+ #define VALIDATE_MAX_LENGTH(len, N) if(len > MAX_##N##_LENGTH) { rb_raise(eHttpParserError, MAX_##N##_LENGTH_ERR); }
62
+
63
+ /** Defines global strings in the init method. */
64
+ #define DEF_GLOBAL(N, val) global_##N = rb_obj_freeze(rb_str_new2(val)); rb_global_variable(&global_##N)
65
+
66
+
67
+ /* Defines the maximum allowed lengths for various input elements.*/
68
+ DEF_MAX_LENGTH(FIELD_NAME, 256);
69
+ DEF_MAX_LENGTH(FIELD_VALUE, 80 * 1024);
70
+ DEF_MAX_LENGTH(REQUEST_URI, 1024 * 12);
71
+ DEF_MAX_LENGTH(FRAGMENT, 1024); /* Don't know if this length is specified somewhere or not */
72
+ DEF_MAX_LENGTH(REQUEST_PATH, 1024);
73
+ DEF_MAX_LENGTH(QUERY_STRING, (1024 * 10));
74
+ DEF_MAX_LENGTH(HEADER, (1024 * (80 + 32)));
75
+
76
+ struct common_field {
77
+ const signed long len;
78
+ const char *name;
79
+ VALUE value;
80
+ };
81
+
82
+ /*
83
+ * A list of common HTTP headers we expect to receive.
84
+ * This allows us to avoid repeatedly creating identical string
85
+ * objects to be used with rb_hash_aset().
86
+ */
87
+ static struct common_field common_http_fields[] = {
88
+ # define f(N) { (sizeof(N) - 1), N, Qnil }
89
+ f("ACCEPT"),
90
+ f("ACCEPT_CHARSET"),
91
+ f("ACCEPT_ENCODING"),
92
+ f("ACCEPT_LANGUAGE"),
93
+ f("ALLOW"),
94
+ f("AUTHORIZATION"),
95
+ f("CACHE_CONTROL"),
96
+ f("CONNECTION"),
97
+ f("CONTENT_ENCODING"),
98
+ f("CONTENT_LENGTH"),
99
+ f("CONTENT_TYPE"),
100
+ f("COOKIE"),
101
+ f("DATE"),
102
+ f("EXPECT"),
103
+ f("FROM"),
104
+ f("HOST"),
105
+ f("IF_MATCH"),
106
+ f("IF_MODIFIED_SINCE"),
107
+ f("IF_NONE_MATCH"),
108
+ f("IF_RANGE"),
109
+ f("IF_UNMODIFIED_SINCE"),
110
+ f("KEEP_ALIVE"), /* Firefox sends this */
111
+ f("MAX_FORWARDS"),
112
+ f("PRAGMA"),
113
+ f("PROXY_AUTHORIZATION"),
114
+ f("RANGE"),
115
+ f("REFERER"),
116
+ f("TE"),
117
+ f("TRAILER"),
118
+ f("TRANSFER_ENCODING"),
119
+ f("UPGRADE"),
120
+ f("USER_AGENT"),
121
+ f("VIA"),
122
+ f("X_FORWARDED_FOR"), /* common for proxies */
123
+ f("X_REAL_IP"), /* common for proxies */
124
+ f("WARNING")
125
+ # undef f
126
+ };
127
+
128
+ /*
129
+ * qsort(3) and bsearch(3) improve average performance slightly, but may
130
+ * not be worth it for lack of portability to certain platforms...
131
+ */
132
+ #if defined(HAVE_QSORT_BSEARCH)
133
+ /* sort by length, then by name if there's a tie */
134
+ static int common_field_cmp(const void *a, const void *b)
135
+ {
136
+ struct common_field *cfa = (struct common_field *)a;
137
+ struct common_field *cfb = (struct common_field *)b;
138
+ signed long diff = cfa->len - cfb->len;
139
+ return diff ? diff : memcmp(cfa->name, cfb->name, cfa->len);
140
+ }
141
+ #endif /* HAVE_QSORT_BSEARCH */
142
+
143
+ static void init_common_fields(void)
144
+ {
145
+ int i;
146
+ struct common_field *cf = common_http_fields;
147
+ char tmp[256]; /* MAX_FIELD_NAME_LENGTH */
148
+ memcpy(tmp, HTTP_PREFIX, HTTP_PREFIX_LEN);
149
+
150
+ for(i = 0; i < ARRAY_SIZE(common_http_fields); cf++, i++) {
151
+ memcpy(tmp + HTTP_PREFIX_LEN, cf->name, cf->len + 1);
152
+ cf->value = rb_obj_freeze(rb_str_new(tmp, HTTP_PREFIX_LEN + cf->len));
153
+ rb_global_variable(&cf->value);
154
+ }
155
+
156
+ #if defined(HAVE_QSORT_BSEARCH)
157
+ qsort(common_http_fields,
158
+ ARRAY_SIZE(common_http_fields),
159
+ sizeof(struct common_field),
160
+ common_field_cmp);
161
+ #endif /* HAVE_QSORT_BSEARCH */
162
+ }
163
+
164
+ static VALUE find_common_field_value(const char *field, size_t flen)
165
+ {
166
+ #if defined(HAVE_QSORT_BSEARCH)
167
+ struct common_field key;
168
+ struct common_field *found;
169
+ key.name = field;
170
+ key.len = (signed long)flen;
171
+ found = (struct common_field *)bsearch(&key, common_http_fields,
172
+ ARRAY_SIZE(common_http_fields),
173
+ sizeof(struct common_field),
174
+ common_field_cmp);
175
+ return found ? found->value : Qnil;
176
+ #else /* !HAVE_QSORT_BSEARCH */
177
+ int i;
178
+ struct common_field *cf = common_http_fields;
179
+ for(i = 0; i < ARRAY_SIZE(common_http_fields); i++, cf++) {
180
+ if (cf->len == flen && !memcmp(cf->name, field, flen))
181
+ return cf->value;
182
+ }
183
+ return Qnil;
184
+ #endif /* !HAVE_QSORT_BSEARCH */
185
+ }
186
+
187
+ void http_field(void *data, const char *field, size_t flen, const char *value, size_t vlen)
188
+ {
189
+ VALUE req = (VALUE)data;
190
+ VALUE v = Qnil;
191
+ VALUE f = Qnil;
192
+
193
+ VALIDATE_MAX_LENGTH(flen, FIELD_NAME);
194
+ VALIDATE_MAX_LENGTH(vlen, FIELD_VALUE);
195
+
196
+ v = rb_str_new(value, vlen);
197
+
198
+ f = find_common_field_value(field, flen);
199
+
200
+ if (f == Qnil) {
201
+ /*
202
+ * We got a strange header that we don't have a memoized value for.
203
+ * Fallback to creating a new string to use as a hash key.
204
+ *
205
+ * using rb_str_new(NULL, len) here is faster than rb_str_buf_new(len)
206
+ * in my testing, because: there's no minimum allocation length (and
207
+ * no check for it, either), RSTRING_LEN(f) does not need to be
208
+ * written twice, and and RSTRING_PTR(f) will already be
209
+ * null-terminated for us.
210
+ */
211
+ f = rb_str_new(NULL, HTTP_PREFIX_LEN + flen);
212
+ memcpy(RSTRING_PTR(f), HTTP_PREFIX, HTTP_PREFIX_LEN);
213
+ memcpy(RSTRING_PTR(f) + HTTP_PREFIX_LEN, field, flen);
214
+ assert(*(RSTRING_PTR(f) + RSTRING_LEN(f)) == '\0'); /* paranoia */
215
+ /* fprintf(stderr, "UNKNOWN HEADER <%s>\n", RSTRING_PTR(f)); */
216
+ }
217
+
218
+ rb_hash_aset(req, f, v);
219
+ }
220
+
221
+ void request_method(void *data, const char *at, size_t length)
222
+ {
223
+ VALUE req = (VALUE)data;
224
+ VALUE val = Qnil;
225
+
226
+ val = rb_str_new(at, length);
227
+ rb_hash_aset(req, global_request_method, val);
228
+ }
229
+
230
+ void request_uri(void *data, const char *at, size_t length)
231
+ {
232
+ VALUE req = (VALUE)data;
233
+ VALUE val = Qnil;
234
+
235
+ VALIDATE_MAX_LENGTH(length, REQUEST_URI);
236
+
237
+ val = rb_str_new(at, length);
238
+ rb_hash_aset(req, global_request_uri, val);
239
+ }
240
+
241
+ void fragment(void *data, const char *at, size_t length)
242
+ {
243
+ VALUE req = (VALUE)data;
244
+ VALUE val = Qnil;
245
+
246
+ VALIDATE_MAX_LENGTH(length, FRAGMENT);
247
+
248
+ val = rb_str_new(at, length);
249
+ rb_hash_aset(req, global_fragment, val);
250
+ }
251
+
252
+ void request_path(void *data, const char *at, size_t length)
253
+ {
254
+ VALUE req = (VALUE)data;
255
+ VALUE val = Qnil;
256
+
257
+ VALIDATE_MAX_LENGTH(length, REQUEST_PATH);
258
+
259
+ val = rb_str_new(at, length);
260
+ rb_hash_aset(req, global_request_path, val);
261
+ }
262
+
263
+ void query_string(void *data, const char *at, size_t length)
264
+ {
265
+ VALUE req = (VALUE)data;
266
+ VALUE val = Qnil;
267
+
268
+ VALIDATE_MAX_LENGTH(length, QUERY_STRING);
269
+
270
+ val = rb_str_new(at, length);
271
+ rb_hash_aset(req, global_query_string, val);
272
+ }
273
+
274
+ void http_version(void *data, const char *at, size_t length)
275
+ {
276
+ VALUE req = (VALUE)data;
277
+ VALUE val = rb_str_new(at, length);
278
+ rb_hash_aset(req, global_http_version, val);
279
+ }
280
+
281
+ /** Finalizes the request header to have a bunch of stuff that's
282
+ needed. */
283
+
284
+ void header_done(void *data, const char *at, size_t length)
285
+ {
286
+ VALUE req = (VALUE)data;
287
+ VALUE temp = Qnil;
288
+ VALUE ctype = Qnil;
289
+ VALUE clen = Qnil;
290
+ char *colon = NULL;
291
+
292
+ clen = rb_hash_aref(req, global_http_content_length);
293
+ if(clen != Qnil) {
294
+ rb_hash_aset(req, global_content_length, clen);
295
+ }
296
+
297
+ ctype = rb_hash_aref(req, global_http_content_type);
298
+ if(ctype != Qnil) {
299
+ rb_hash_aset(req, global_content_type, ctype);
300
+ }
301
+
302
+ rb_hash_aset(req, global_gateway_interface, global_gateway_interface_value);
303
+ if((temp = rb_hash_aref(req, global_http_host)) != Qnil) {
304
+ colon = memchr(RSTRING_PTR(temp), ':', RSTRING_LEN(temp));
305
+ if(colon != NULL) {
306
+ rb_hash_aset(req, global_server_name, rb_str_substr(temp, 0, colon - RSTRING_PTR(temp)));
307
+ rb_hash_aset(req, global_server_port,
308
+ rb_str_substr(temp, colon - RSTRING_PTR(temp)+1,
309
+ RSTRING_LEN(temp)));
310
+ } else {
311
+ rb_hash_aset(req, global_server_name, temp);
312
+ rb_hash_aset(req, global_server_port, global_port_80);
313
+ }
314
+ }
315
+
316
+ /* grab the initial body and stuff it into an ivar */
317
+ rb_ivar_set(req, id_http_body, rb_str_new(at, length));
318
+ rb_hash_aset(req, global_server_protocol, global_server_protocol_value);
319
+ rb_hash_aset(req, global_server_software, global_mongrel_version);
320
+ }
321
+
322
+
323
+ void HttpParser_free(void *data) {
324
+ TRACE();
325
+
326
+ if(data) {
327
+ free(data);
328
+ }
329
+ }
330
+
331
+
332
+ VALUE HttpParser_alloc(VALUE klass)
333
+ {
334
+ VALUE obj;
335
+ http_parser *hp = ALLOC_N(http_parser, 1);
336
+ TRACE();
337
+ hp->http_field = http_field;
338
+ hp->request_method = request_method;
339
+ hp->request_uri = request_uri;
340
+ hp->fragment = fragment;
341
+ hp->request_path = request_path;
342
+ hp->query_string = query_string;
343
+ hp->http_version = http_version;
344
+ hp->header_done = header_done;
345
+ http_parser_init(hp);
346
+
347
+ obj = Data_Wrap_Struct(klass, NULL, HttpParser_free, hp);
348
+
349
+ return obj;
350
+ }
351
+
352
+
353
+ /**
354
+ * call-seq:
355
+ * parser.new -> parser
356
+ *
357
+ * Creates a new parser.
358
+ */
359
+ VALUE HttpParser_init(VALUE self)
360
+ {
361
+ http_parser *http = NULL;
362
+ DATA_GET(self, http_parser, http);
363
+ http_parser_init(http);
364
+
365
+ return self;
366
+ }
367
+
368
+
369
+ /**
370
+ * call-seq:
371
+ * parser.reset -> nil
372
+ *
373
+ * Resets the parser to it's initial state so that you can reuse it
374
+ * rather than making new ones.
375
+ */
376
+ VALUE HttpParser_reset(VALUE self)
377
+ {
378
+ http_parser *http = NULL;
379
+ DATA_GET(self, http_parser, http);
380
+ http_parser_init(http);
381
+
382
+ return Qnil;
383
+ }
384
+
385
+
386
+ /**
387
+ * call-seq:
388
+ * parser.finish -> true/false
389
+ *
390
+ * Finishes a parser early which could put in a "good" or bad state.
391
+ * You should call reset after finish it or bad things will happen.
392
+ */
393
+ VALUE HttpParser_finish(VALUE self)
394
+ {
395
+ http_parser *http = NULL;
396
+ DATA_GET(self, http_parser, http);
397
+ http_parser_finish(http);
398
+
399
+ return http_parser_is_finished(http) ? Qtrue : Qfalse;
400
+ }
401
+
402
+
403
+ /**
404
+ * call-seq:
405
+ * parser.execute(req_hash, data, start) -> Integer
406
+ *
407
+ * Takes a Hash and a String of data, parses the String of data filling in the Hash
408
+ * returning an Integer to indicate how much of the data has been read. No matter
409
+ * what the return value, you should call HttpParser#finished? and HttpParser#error?
410
+ * to figure out if it's done parsing or there was an error.
411
+ *
412
+ * This function now throws an exception when there is a parsing error. This makes
413
+ * the logic for working with the parser much easier. You can still test for an
414
+ * error, but now you need to wrap the parser with an exception handling block.
415
+ *
416
+ * The third argument allows for parsing a partial request and then continuing
417
+ * the parsing from that position. It needs all of the original data as well
418
+ * so you have to append to the data buffer as you read.
419
+ */
420
+ VALUE HttpParser_execute(VALUE self, VALUE req_hash, VALUE data, VALUE start)
421
+ {
422
+ http_parser *http = NULL;
423
+ int from = 0;
424
+ char *dptr = NULL;
425
+ long dlen = 0;
426
+
427
+ DATA_GET(self, http_parser, http);
428
+
429
+ from = FIX2INT(start);
430
+ dptr = RSTRING_PTR(data);
431
+ dlen = RSTRING_LEN(data);
432
+
433
+ if(from >= dlen) {
434
+ rb_raise(eHttpParserError, "Requested start is after data buffer end.");
435
+ } else {
436
+ http->data = (void *)req_hash;
437
+ http_parser_execute(http, dptr, dlen, from);
438
+
439
+ VALIDATE_MAX_LENGTH(http_parser_nread(http), HEADER);
440
+
441
+ if(http_parser_has_error(http)) {
442
+ rb_raise(eHttpParserError, "Invalid HTTP format, parsing fails.");
443
+ } else {
444
+ return INT2FIX(http_parser_nread(http));
445
+ }
446
+ }
447
+ }
448
+
449
+
450
+
451
+ /**
452
+ * call-seq:
453
+ * parser.error? -> true/false
454
+ *
455
+ * Tells you whether the parser is in an error state.
456
+ */
457
+ VALUE HttpParser_has_error(VALUE self)
458
+ {
459
+ http_parser *http = NULL;
460
+ DATA_GET(self, http_parser, http);
461
+
462
+ return http_parser_has_error(http) ? Qtrue : Qfalse;
463
+ }
464
+
465
+
466
+ /**
467
+ * call-seq:
468
+ * parser.finished? -> true/false
469
+ *
470
+ * Tells you whether the parser is finished or not and in a good state.
471
+ */
472
+ VALUE HttpParser_is_finished(VALUE self)
473
+ {
474
+ http_parser *http = NULL;
475
+ DATA_GET(self, http_parser, http);
476
+
477
+ return http_parser_is_finished(http) ? Qtrue : Qfalse;
478
+ }
479
+
480
+
481
+ /**
482
+ * call-seq:
483
+ * parser.nread -> Integer
484
+ *
485
+ * Returns the amount of data processed so far during this processing cycle. It is
486
+ * set to 0 on initialize or reset calls and is incremented each time execute is called.
487
+ */
488
+ VALUE HttpParser_nread(VALUE self)
489
+ {
490
+ http_parser *http = NULL;
491
+ DATA_GET(self, http_parser, http);
492
+
493
+ return INT2FIX(http->nread);
494
+ }
495
+
496
+ void Init_http11()
497
+ {
498
+
499
+ mMongrel = rb_define_module("Mongrel");
500
+
501
+ DEF_GLOBAL(request_method, "REQUEST_METHOD");
502
+ DEF_GLOBAL(request_uri, "REQUEST_URI");
503
+ DEF_GLOBAL(fragment, "FRAGMENT");
504
+ DEF_GLOBAL(query_string, "QUERY_STRING");
505
+ DEF_GLOBAL(http_version, "HTTP_VERSION");
506
+ DEF_GLOBAL(request_path, "REQUEST_PATH");
507
+ DEF_GLOBAL(content_length, "CONTENT_LENGTH");
508
+ DEF_GLOBAL(http_content_length, "HTTP_CONTENT_LENGTH");
509
+ DEF_GLOBAL(content_type, "CONTENT_TYPE");
510
+ DEF_GLOBAL(http_content_type, "HTTP_CONTENT_TYPE");
511
+ DEF_GLOBAL(gateway_interface, "GATEWAY_INTERFACE");
512
+ DEF_GLOBAL(gateway_interface_value, "CGI/1.2");
513
+ DEF_GLOBAL(server_name, "SERVER_NAME");
514
+ DEF_GLOBAL(server_port, "SERVER_PORT");
515
+ DEF_GLOBAL(server_protocol, "SERVER_PROTOCOL");
516
+ DEF_GLOBAL(server_protocol_value, "HTTP/1.1");
517
+ DEF_GLOBAL(http_host, "HTTP_HOST");
518
+ DEF_GLOBAL(mongrel_version, "Mongrel 1.2.0.beta.1"); /* XXX Why is this defined here? */
519
+ DEF_GLOBAL(server_software, "SERVER_SOFTWARE");
520
+ DEF_GLOBAL(port_80, "80");
521
+
522
+ eHttpParserError = rb_define_class_under(mMongrel, "HttpParserError", rb_eIOError);
523
+
524
+ cHttpParser = rb_define_class_under(mMongrel, "HttpParser", rb_cObject);
525
+ rb_define_alloc_func(cHttpParser, HttpParser_alloc);
526
+ rb_define_method(cHttpParser, "initialize", HttpParser_init,0);
527
+ rb_define_method(cHttpParser, "reset", HttpParser_reset,0);
528
+ rb_define_method(cHttpParser, "finish", HttpParser_finish,0);
529
+ rb_define_method(cHttpParser, "execute", HttpParser_execute,3);
530
+ rb_define_method(cHttpParser, "error?", HttpParser_has_error,0);
531
+ rb_define_method(cHttpParser, "finished?", HttpParser_is_finished,0);
532
+ rb_define_method(cHttpParser, "nread", HttpParser_nread,0);
533
+ init_common_fields();
534
+ }