waitress-core 0.2.4 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +4 -4
  3. data/LICENSE +21 -21
  4. data/Rakefile +17 -17
  5. data/bin/waitress +22 -22
  6. data/ext/Thanks.md +1 -1
  7. data/ext/waitress_http11/ext_help.h +15 -15
  8. data/ext/waitress_http11/extconf.rb +6 -6
  9. data/ext/waitress_http11/http11.c +532 -532
  10. data/ext/waitress_http11/http11_parser.c +1216 -1216
  11. data/ext/waitress_http11/http11_parser.h +49 -49
  12. data/ext/waitress_http11/http11_parser.java.rl +171 -171
  13. data/ext/waitress_http11/http11_parser.rl +165 -165
  14. data/ext/waitress_http11/http11_parser_common.rl +55 -55
  15. data/ext/waitress_http11/http11_wrb_parser.h +91 -91
  16. data/lib/waitress.rb +100 -99
  17. data/lib/waitress/chef.rb +113 -113
  18. data/lib/waitress/configure.rb +127 -121
  19. data/lib/waitress/evalbind.rb +9 -9
  20. data/lib/waitress/handlers/dirhandler.rb +39 -39
  21. data/lib/waitress/handlers/handler.rb +57 -57
  22. data/lib/waitress/handlers/handler404.rb +25 -25
  23. data/lib/waitress/handlers/libhandler.rb +135 -58
  24. data/lib/waitress/kernel.rb +189 -189
  25. data/lib/waitress/parse/query.rb +60 -60
  26. data/lib/waitress/request.rb +45 -45
  27. data/lib/waitress/resources/default_config.rb +52 -52
  28. data/lib/waitress/resources/default_less.yml +7 -0
  29. data/lib/waitress/resources/http/404.html +18 -18
  30. data/lib/waitress/resources/http/css/hack.css +37 -37
  31. data/lib/waitress/resources/http/css/waitress.css +57 -57
  32. data/lib/waitress/resources/http/fonts/eot/latin/hack-bold-latin-webfont.eot +0 -0
  33. data/lib/waitress/resources/http/fonts/eot/latin/hack-bolditalic-latin-webfont.eot +0 -0
  34. data/lib/waitress/resources/http/fonts/eot/latin/hack-italic-latin-webfont.eot +0 -0
  35. data/lib/waitress/resources/http/fonts/eot/latin/hack-regular-latin-webfont.eot +0 -0
  36. data/lib/waitress/resources/http/fonts/svg/latin/hack-bold-latin-webfont.svg +240 -240
  37. data/lib/waitress/resources/http/fonts/svg/latin/hack-bolditalic-latin-webfont.svg +240 -240
  38. data/lib/waitress/resources/http/fonts/svg/latin/hack-italic-latin-webfont.svg +240 -240
  39. data/lib/waitress/resources/http/fonts/svg/latin/hack-regular-latin-webfont.svg +240 -240
  40. data/lib/waitress/resources/http/fonts/web-ttf/latin/hack-bold-latin-webfont.ttf +0 -0
  41. data/lib/waitress/resources/http/fonts/web-ttf/latin/hack-bolditalic-latin-webfont.ttf +0 -0
  42. data/lib/waitress/resources/http/fonts/web-ttf/latin/hack-italic-latin-webfont.ttf +0 -0
  43. data/lib/waitress/resources/http/fonts/web-ttf/latin/hack-regular-latin-webfont.ttf +0 -0
  44. data/lib/waitress/resources/http/fonts/woff/latin/hack-bold-latin-webfont.woff +0 -0
  45. data/lib/waitress/resources/http/fonts/woff/latin/hack-bolditalic-latin-webfont.woff +0 -0
  46. data/lib/waitress/resources/http/fonts/woff/latin/hack-italic-latin-webfont.woff +0 -0
  47. data/lib/waitress/resources/http/fonts/woff/latin/hack-regular-latin-webfont.woff +0 -0
  48. data/lib/waitress/resources/http/fonts/woff2/latin/hack-bold-latin-webfont.woff2 +0 -0
  49. data/lib/waitress/resources/http/fonts/woff2/latin/hack-bolditalic-latin-webfont.woff2 +0 -0
  50. data/lib/waitress/resources/http/fonts/woff2/latin/hack-italic-latin-webfont.woff2 +0 -0
  51. data/lib/waitress/resources/http/fonts/woff2/latin/hack-regular-latin-webfont.woff2 +0 -0
  52. data/lib/waitress/resources/http/img/404.png +0 -0
  53. data/lib/waitress/resources/http/index.html +15 -15
  54. data/lib/waitress/response.rb +105 -105
  55. data/lib/waitress/server.rb +160 -160
  56. data/lib/waitress/util/less_watcher.rb +56 -0
  57. data/lib/waitress/{util.rb → util/util.rb} +707 -707
  58. data/lib/waitress/version.rb +3 -3
  59. data/lib/waitress/vhost.rb +229 -227
  60. data/lib/waitress_http11.so +0 -0
  61. data/waitress-core.gemspec +32 -29
  62. metadata +48 -4
  63. data/lib/waitress_http11.bundle +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e8a87d8f5bf20f6b0452be008a74dbfa1328dac1
4
- data.tar.gz: 58f8146e2c4c63f68de3f5a41ef3cb5913bbf0fe
3
+ metadata.gz: d788390d6fd47ac3ea0120241146d3b940217b97
4
+ data.tar.gz: 162ee7fc128c28cf58dbaf959f8b2a7ea084b3c4
5
5
  SHA512:
6
- metadata.gz: c3630c12f3e0f0a4fb866619bdd0c354b63a1ca0b0a1b7008d791c20bcba4269489f17aae2feaecd309eb691a90b19acae41188b6e874d57c11bb62339341764
7
- data.tar.gz: b2540cb3f16aa45004b8b95ad73d368d71effacf62a387857ca8b999c529d97ce3b62af0c9e27ea80b3ce47550bc69c2a70538b8fb37f4ed2b10e0bb3df1e011
6
+ metadata.gz: 2af638e5e7084801949f532ea4702c1b58a94660c6ca39fe1030dd400a5430c056ddeb67d43cb190e5c7d9121cc42a722890a304b910e39f3717d2d206f830c3
7
+ data.tar.gz: 236b011d0e408bc37eef94173fc921b0d687ec053c02b9d835145aedecab5628c28b112dad7888e8318d63a6ae94e41c0432dc5eea8795e1493055a9f71afc40
data/Gemfile CHANGED
@@ -1,4 +1,4 @@
1
- source 'https://rubygems.org'
2
-
3
- # Specify your gem's dependencies in waitress-core.gemspec
4
- gemspec
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in waitress-core.gemspec
4
+ gemspec
data/LICENSE CHANGED
@@ -1,21 +1,21 @@
1
- The MIT License (MIT)
2
-
3
- Copyright (c) 2015 Jaci R Brunning
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Jaci R Brunning
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/Rakefile CHANGED
@@ -1,17 +1,17 @@
1
- require 'rake/extensiontask'
2
- require 'rake/testtask'
3
-
4
- spec = Gem::Specification.load('waitress-core.gemspec')
5
- Rake::ExtensionTask.new('waitress_http11', spec)
6
-
7
- task default: %w[test]
8
-
9
- Rake::TestTask.new do |t|
10
- t.libs << "test"
11
- t.test_files = FileList['test/unit.rb']
12
- t.verbose = true
13
- end
14
-
15
- task :bench do
16
- load "./bench/benchmark.rb"
17
- end
1
+ require 'rake/extensiontask'
2
+ require 'rake/testtask'
3
+
4
+ spec = Gem::Specification.load('waitress-core.gemspec')
5
+ Rake::ExtensionTask.new('waitress_http11', spec)
6
+
7
+ task default: %w[test]
8
+
9
+ Rake::TestTask.new do |t|
10
+ t.libs << "test"
11
+ t.test_files = FileList['test/unit.rb']
12
+ t.verbose = true
13
+ end
14
+
15
+ task :bench do
16
+ load "./bench/benchmark.rb"
17
+ end
data/bin/waitress CHANGED
@@ -1,22 +1,22 @@
1
- #!/usr/bin/env ruby
2
-
3
- require "waitress"
4
- require "optparse"
5
-
6
- trap("INT") { exit }
7
-
8
- conf = nil
9
-
10
- @options = {}
11
-
12
- OptionParser.new do |o|
13
- o.on("-h", "--home HOME_DIR", "Change the home directory for Waitress (default: ~/.waitress)") { |x| @options[:home] = x }
14
- end.parse!
15
-
16
- if @options.include? :home
17
- @conf = Waitress.serve! true, @options[:home]
18
- else
19
- @conf = Waitress.serve! true
20
- end
21
- @conf.run
22
- @conf.join
1
+ #!/usr/bin/env ruby
2
+
3
+ require "waitress"
4
+ require "optparse"
5
+
6
+ trap("INT") { exit }
7
+
8
+ conf = nil
9
+
10
+ @options = {}
11
+
12
+ OptionParser.new do |o|
13
+ o.on("-h", "--home HOME_DIR", "Change the home directory for Waitress (default: ~/.waitress)") { |x| @options[:home] = x }
14
+ end.parse!
15
+
16
+ if @options.include? :home
17
+ @conf = Waitress.serve! true, @options[:home]
18
+ else
19
+ @conf = Waitress.serve! true
20
+ end
21
+ @conf.run
22
+ @conf.join
data/ext/Thanks.md CHANGED
@@ -1 +1 @@
1
- The Native Extensions for the HTTP Parser are taken from the [mongrel project](https://github.com/mongrel/mongrel)
1
+ The Native Extensions for the HTTP Parser are taken from the [mongrel project](https://github.com/mongrel/mongrel)
@@ -1,15 +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
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
@@ -1,6 +1,6 @@
1
- require 'mkmf'
2
-
3
- dir_config("waitress_http11")
4
- have_library("c", "main")
5
-
6
- create_makefile("waitress_http11")
1
+ require 'mkmf'
2
+
3
+ dir_config("waitress_http11")
4
+ have_library("c", "main")
5
+
6
+ create_makefile("waitress_http11")
@@ -1,532 +1,532 @@
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
- #include "http11_wrb_parser.h"
11
-
12
- #ifndef RSTRING_PTR
13
- #define RSTRING_PTR(s) (RSTRING(s)->ptr)
14
- #endif
15
- #ifndef RSTRING_LEN
16
- #define RSTRING_LEN(s) (RSTRING(s)->len)
17
- #endif
18
-
19
- static VALUE rubyModule;
20
- static VALUE cHttpParser;
21
- static VALUE eHttpParserError;
22
-
23
- #define id_handler_map rb_intern("@handler_map")
24
- #define id_http_body rb_intern("@http_body")
25
- #define HTTP_PREFIX "HTTP_HEAD_"
26
- #define HTTP_PREFIX_LEN (sizeof(HTTP_PREFIX) - 1)
27
-
28
- static VALUE global_request_method;
29
- static VALUE global_request_uri;
30
- static VALUE global_fragment;
31
- static VALUE global_query_string;
32
- static VALUE global_http_version;
33
- static VALUE global_content_length;
34
- static VALUE global_http_content_length;
35
- static VALUE global_request_path;
36
- static VALUE global_content_type;
37
- static VALUE global_http_content_type;
38
- static VALUE global_gateway_interface;
39
- static VALUE global_gateway_interface_value;
40
- static VALUE global_server_name;
41
- static VALUE global_server_port;
42
- static VALUE global_server_protocol;
43
- static VALUE global_server_protocol_value;
44
- static VALUE global_http_host;
45
- static VALUE global_server_version;
46
- static VALUE global_server_software;
47
- static VALUE global_port_80;
48
-
49
- #define TRIE_INCREASE 30
50
-
51
- /** Defines common length and error messages for input length validation. */
52
- #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."
53
-
54
- /** Validates the max length of given input and throws an HttpParserError exception if over. */
55
- #define VALIDATE_MAX_LENGTH(len, N) if(len > MAX_##N##_LENGTH) { rb_raise(eHttpParserError, MAX_##N##_LENGTH_ERR); }
56
-
57
- /** Defines global strings in the init method. */
58
- #define DEF_GLOBAL(N, val) global_##N = rb_obj_freeze(rb_str_new2(val)); rb_global_variable(&global_##N)
59
-
60
-
61
- /* Defines the maximum allowed lengths for various input elements.*/
62
- DEF_MAX_LENGTH(FIELD_NAME, 256);
63
- DEF_MAX_LENGTH(FIELD_VALUE, 80 * 1024);
64
- DEF_MAX_LENGTH(REQUEST_URI, 1024 * 12);
65
- DEF_MAX_LENGTH(FRAGMENT, 1024); /* Don't know if this length is specified somewhere or not */
66
- DEF_MAX_LENGTH(REQUEST_PATH, 1024);
67
- DEF_MAX_LENGTH(QUERY_STRING, (1024 * 10));
68
- DEF_MAX_LENGTH(HEADER, (1024 * (80 + 32)));
69
-
70
- struct common_field {
71
- const signed long len;
72
- const char *name;
73
- VALUE value;
74
- };
75
-
76
- /*
77
- * A list of common HTTP headers we expect to receive.
78
- * This allows us to avoid repeatedly creating identical string
79
- * objects to be used with rb_hash_aset().
80
- */
81
- static struct common_field common_http_fields[] = {
82
- # define f(N) { (sizeof(N) - 1), N, Qnil }
83
- f("ACCEPT"),
84
- f("ACCEPT_CHARSET"),
85
- f("ACCEPT_ENCODING"),
86
- f("ACCEPT_LANGUAGE"),
87
- f("ALLOW"),
88
- f("AUTHORIZATION"),
89
- f("CACHE_CONTROL"),
90
- f("CONNECTION"),
91
- f("CONTENT_ENCODING"),
92
- f("CONTENT_LENGTH"),
93
- f("CONTENT_TYPE"),
94
- f("COOKIE"),
95
- f("DATE"),
96
- f("EXPECT"),
97
- f("FROM"),
98
- f("HOST"),
99
- f("IF_MATCH"),
100
- f("IF_MODIFIED_SINCE"),
101
- f("IF_NONE_MATCH"),
102
- f("IF_RANGE"),
103
- f("IF_UNMODIFIED_SINCE"),
104
- f("KEEP_ALIVE"), /* Firefox sends this */
105
- f("MAX_FORWARDS"),
106
- f("PRAGMA"),
107
- f("PROXY_AUTHORIZATION"),
108
- f("RANGE"),
109
- f("REFERER"),
110
- f("TE"),
111
- f("TRAILER"),
112
- f("TRANSFER_ENCODING"),
113
- f("UPGRADE"),
114
- f("USER_AGENT"),
115
- f("VIA"),
116
- f("X_FORWARDED_FOR"), /* common for proxies */
117
- f("X_REAL_IP"), /* common for proxies */
118
- f("WARNING")
119
- # undef f
120
- };
121
-
122
- /*
123
- * qsort(3) and bsearch(3) improve average performance slightly, but may
124
- * not be worth it for lack of portability to certain platforms...
125
- */
126
- #if defined(HAVE_QSORT_BSEARCH)
127
- /* sort by length, then by name if there's a tie */
128
- static int common_field_cmp(const void *a, const void *b)
129
- {
130
- struct common_field *cfa = (struct common_field *)a;
131
- struct common_field *cfb = (struct common_field *)b;
132
- signed long diff = cfa->len - cfb->len;
133
- return diff ? diff : memcmp(cfa->name, cfb->name, cfa->len);
134
- }
135
- #endif /* HAVE_QSORT_BSEARCH */
136
-
137
- static void init_common_fields(void)
138
- {
139
- int i;
140
- struct common_field *cf = common_http_fields;
141
- char tmp[256]; /* MAX_FIELD_NAME_LENGTH */
142
- memcpy(tmp, HTTP_PREFIX, HTTP_PREFIX_LEN);
143
-
144
- for(i = 0; i < ARRAY_SIZE(common_http_fields); cf++, i++) {
145
- memcpy(tmp + HTTP_PREFIX_LEN, cf->name, cf->len + 1);
146
- cf->value = rb_obj_freeze(rb_str_new(tmp, HTTP_PREFIX_LEN + cf->len));
147
- rb_global_variable(&cf->value);
148
- }
149
-
150
- #if defined(HAVE_QSORT_BSEARCH)
151
- qsort(common_http_fields,
152
- ARRAY_SIZE(common_http_fields),
153
- sizeof(struct common_field),
154
- common_field_cmp);
155
- #endif /* HAVE_QSORT_BSEARCH */
156
- }
157
-
158
- static VALUE find_common_field_value(const char *field, size_t flen)
159
- {
160
- #if defined(HAVE_QSORT_BSEARCH)
161
- struct common_field key;
162
- struct common_field *found;
163
- key.name = field;
164
- key.len = (signed long)flen;
165
- found = (struct common_field *)bsearch(&key, common_http_fields,
166
- ARRAY_SIZE(common_http_fields),
167
- sizeof(struct common_field),
168
- common_field_cmp);
169
- return found ? found->value : Qnil;
170
- #else /* !HAVE_QSORT_BSEARCH */
171
- int i;
172
- struct common_field *cf = common_http_fields;
173
- for(i = 0; i < ARRAY_SIZE(common_http_fields); i++, cf++) {
174
- if (cf->len == flen && !memcmp(cf->name, field, flen))
175
- return cf->value;
176
- }
177
- return Qnil;
178
- #endif /* !HAVE_QSORT_BSEARCH */
179
- }
180
-
181
- void http_field(void *data, const char *field, size_t flen, const char *value, size_t vlen)
182
- {
183
- VALUE req = (VALUE)data;
184
- VALUE v = Qnil;
185
- VALUE f = Qnil;
186
-
187
- VALIDATE_MAX_LENGTH(flen, FIELD_NAME);
188
- VALIDATE_MAX_LENGTH(vlen, FIELD_VALUE);
189
-
190
- v = rb_str_new(value, vlen);
191
-
192
- f = find_common_field_value(field, flen);
193
-
194
- if (f == Qnil) {
195
- /*
196
- * We got a strange header that we don't have a memoized value for.
197
- * Fallback to creating a new string to use as a hash key.
198
- *
199
- * using rb_str_new(NULL, len) here is faster than rb_str_buf_new(len)
200
- * in my testing, because: there's no minimum allocation length (and
201
- * no check for it, either), RSTRING_LEN(f) does not need to be
202
- * written twice, and and RSTRING_PTR(f) will already be
203
- * null-terminated for us.
204
- */
205
- f = rb_str_new(NULL, HTTP_PREFIX_LEN + flen);
206
- memcpy(RSTRING_PTR(f), HTTP_PREFIX, HTTP_PREFIX_LEN);
207
- memcpy(RSTRING_PTR(f) + HTTP_PREFIX_LEN, field, flen);
208
- assert(*(RSTRING_PTR(f) + RSTRING_LEN(f)) == '\0'); /* paranoia */
209
- /* fprintf(stderr, "UNKNOWN HEADER <%s>\n", RSTRING_PTR(f)); */
210
- }
211
-
212
- rb_hash_aset(req, f, v);
213
- }
214
-
215
- void request_method(void *data, const char *at, size_t length)
216
- {
217
- VALUE req = (VALUE)data;
218
- VALUE val = Qnil;
219
-
220
- val = rb_str_new(at, length);
221
- rb_hash_aset(req, global_request_method, val);
222
- }
223
-
224
- void request_uri(void *data, const char *at, size_t length)
225
- {
226
- VALUE req = (VALUE)data;
227
- VALUE val = Qnil;
228
-
229
- VALIDATE_MAX_LENGTH(length, REQUEST_URI);
230
-
231
- val = rb_str_new(at, length);
232
- rb_hash_aset(req, global_request_uri, val);
233
- }
234
-
235
- void fragment(void *data, const char *at, size_t length)
236
- {
237
- VALUE req = (VALUE)data;
238
- VALUE val = Qnil;
239
-
240
- VALIDATE_MAX_LENGTH(length, FRAGMENT);
241
-
242
- val = rb_str_new(at, length);
243
- rb_hash_aset(req, global_fragment, val);
244
- }
245
-
246
- void request_path(void *data, const char *at, size_t length)
247
- {
248
- VALUE req = (VALUE)data;
249
- VALUE val = Qnil;
250
-
251
- VALIDATE_MAX_LENGTH(length, REQUEST_PATH);
252
-
253
- val = rb_str_new(at, length);
254
- rb_hash_aset(req, global_request_path, val);
255
- }
256
-
257
- void query_string(void *data, const char *at, size_t length)
258
- {
259
- VALUE req = (VALUE)data;
260
- VALUE val = Qnil;
261
-
262
- VALIDATE_MAX_LENGTH(length, QUERY_STRING);
263
-
264
- val = rb_str_new(at, length);
265
- rb_hash_aset(req, global_query_string, val);
266
- }
267
-
268
- void http_version(void *data, const char *at, size_t length)
269
- {
270
- VALUE req = (VALUE)data;
271
- VALUE val = rb_str_new(at, length);
272
- rb_hash_aset(req, global_http_version, val);
273
- }
274
-
275
- /** Finalizes the request header to have a bunch of stuff that's
276
- needed. */
277
-
278
- void header_done(void *data, const char *at, size_t length)
279
- {
280
- VALUE req = (VALUE)data;
281
- VALUE temp = Qnil;
282
- VALUE ctype = Qnil;
283
- VALUE clen = Qnil;
284
- char *colon = NULL;
285
-
286
- clen = rb_hash_aref(req, global_http_content_length);
287
- if(clen != Qnil) {
288
- rb_hash_aset(req, global_content_length, clen);
289
- }
290
-
291
- ctype = rb_hash_aref(req, global_http_content_type);
292
- if(ctype != Qnil) {
293
- rb_hash_aset(req, global_content_type, ctype);
294
- }
295
-
296
- rb_hash_aset(req, global_gateway_interface, global_gateway_interface_value);
297
- if((temp = rb_hash_aref(req, global_http_host)) != Qnil) {
298
- colon = memchr(RSTRING_PTR(temp), ':', RSTRING_LEN(temp));
299
- if(colon != NULL) {
300
- rb_hash_aset(req, global_server_name, rb_str_substr(temp, 0, colon - RSTRING_PTR(temp)));
301
- rb_hash_aset(req, global_server_port,
302
- rb_str_substr(temp, colon - RSTRING_PTR(temp)+1,
303
- RSTRING_LEN(temp)));
304
- } else {
305
- rb_hash_aset(req, global_server_name, temp);
306
- rb_hash_aset(req, global_server_port, global_port_80);
307
- }
308
- }
309
-
310
- /* grab the initial body and stuff it into an ivar */
311
- rb_ivar_set(req, id_http_body, rb_str_new(at, length));
312
- rb_hash_aset(req, global_server_protocol, global_server_protocol_value);
313
- rb_hash_aset(req, global_server_software, global_server_version);
314
- }
315
-
316
-
317
- void HttpParser_free(void *data) {
318
- TRACE();
319
-
320
- if(data) {
321
- free(data);
322
- }
323
- }
324
-
325
-
326
- VALUE HttpParser_alloc(VALUE klass)
327
- {
328
- VALUE obj;
329
- http_parser *hp = ALLOC_N(http_parser, 1);
330
- TRACE();
331
- hp->http_field = http_field;
332
- hp->request_method = request_method;
333
- hp->request_uri = request_uri;
334
- hp->fragment = fragment;
335
- hp->request_path = request_path;
336
- hp->query_string = query_string;
337
- hp->http_version = http_version;
338
- hp->header_done = header_done;
339
- http_parser_init(hp);
340
-
341
- obj = Data_Wrap_Struct(klass, NULL, HttpParser_free, hp);
342
-
343
- return obj;
344
- }
345
-
346
-
347
- /**
348
- * call-seq:
349
- * parser.new -> parser
350
- *
351
- * Creates a new parser.
352
- */
353
- VALUE HttpParser_init(VALUE self)
354
- {
355
- http_parser *http = NULL;
356
- DATA_GET(self, http_parser, http);
357
- http_parser_init(http);
358
-
359
- return self;
360
- }
361
-
362
-
363
- /**
364
- * call-seq:
365
- * parser.reset -> nil
366
- *
367
- * Resets the parser to it's initial state so that you can reuse it
368
- * rather than making new ones.
369
- */
370
- VALUE HttpParser_reset(VALUE self)
371
- {
372
- http_parser *http = NULL;
373
- DATA_GET(self, http_parser, http);
374
- http_parser_init(http);
375
-
376
- return Qnil;
377
- }
378
-
379
-
380
- /**
381
- * call-seq:
382
- * parser.finish -> true/false
383
- *
384
- * Finishes a parser early which could put in a "good" or bad state.
385
- * You should call reset after finish it or bad things will happen.
386
- */
387
- VALUE HttpParser_finish(VALUE self)
388
- {
389
- http_parser *http = NULL;
390
- DATA_GET(self, http_parser, http);
391
- http_parser_finish(http);
392
-
393
- return http_parser_is_finished(http) ? Qtrue : Qfalse;
394
- }
395
-
396
-
397
- /**
398
- * call-seq:
399
- * parser.execute(req_hash, data, start) -> Integer
400
- *
401
- * Takes a Hash and a String of data, parses the String of data filling in the Hash
402
- * returning an Integer to indicate how much of the data has been read. No matter
403
- * what the return value, you should call HttpParser#finished? and HttpParser#error?
404
- * to figure out if it's done parsing or there was an error.
405
- *
406
- * This function now throws an exception when there is a parsing error. This makes
407
- * the logic for working with the parser much easier. You can still test for an
408
- * error, but now you need to wrap the parser with an exception handling block.
409
- *
410
- * The third argument allows for parsing a partial request and then continuing
411
- * the parsing from that position. It needs all of the original data as well
412
- * so you have to append to the data buffer as you read.
413
- */
414
- VALUE HttpParser_execute(VALUE self, VALUE req_hash, VALUE data, VALUE start)
415
- {
416
- http_parser *http = NULL;
417
- int from = 0;
418
- char *dptr = NULL;
419
- long dlen = 0;
420
-
421
- DATA_GET(self, http_parser, http);
422
-
423
- from = FIX2INT(start);
424
- dptr = RSTRING_PTR(data);
425
- dlen = RSTRING_LEN(data);
426
-
427
- if(from >= dlen) {
428
- rb_raise(eHttpParserError, "Requested start is after data buffer end.");
429
- } else {
430
- http->data = (void *)req_hash;
431
- http_parser_execute(http, dptr, dlen, from);
432
-
433
- VALIDATE_MAX_LENGTH(http_parser_nread(http), HEADER);
434
-
435
- if(http_parser_has_error(http)) {
436
- rb_raise(eHttpParserError, "Invalid HTTP format, parsing fails.");
437
- } else {
438
- return INT2FIX(http_parser_nread(http));
439
- }
440
- }
441
- }
442
-
443
-
444
-
445
- /**
446
- * call-seq:
447
- * parser.error? -> true/false
448
- *
449
- * Tells you whether the parser is in an error state.
450
- */
451
- VALUE HttpParser_has_error(VALUE self)
452
- {
453
- http_parser *http = NULL;
454
- DATA_GET(self, http_parser, http);
455
-
456
- return http_parser_has_error(http) ? Qtrue : Qfalse;
457
- }
458
-
459
-
460
- /**
461
- * call-seq:
462
- * parser.finished? -> true/false
463
- *
464
- * Tells you whether the parser is finished or not and in a good state.
465
- */
466
- VALUE HttpParser_is_finished(VALUE self)
467
- {
468
- http_parser *http = NULL;
469
- DATA_GET(self, http_parser, http);
470
-
471
- return http_parser_is_finished(http) ? Qtrue : Qfalse;
472
- }
473
-
474
-
475
- /**
476
- * call-seq:
477
- * parser.nread -> Integer
478
- *
479
- * Returns the amount of data processed so far during this processing cycle. It is
480
- * set to 0 on initialize or reset calls and is incremented each time execute is called.
481
- */
482
- VALUE HttpParser_nread(VALUE self)
483
- {
484
- http_parser *http = NULL;
485
- DATA_GET(self, http_parser, http);
486
-
487
- return INT2FIX(http->nread);
488
- }
489
-
490
- VALUE waitress_version() {
491
- return RSTRING_PTR(rb_str_plus(rb_str_new2("Waitress "), rb_const_get(rb_define_module("Waitress"), rb_intern("VERSION"))));
492
- }
493
-
494
- void Init_waitress_http11()
495
- {
496
- rubyModule = rb_define_module("Waitress");
497
-
498
- DEF_GLOBAL(request_method, "REQUEST_METHOD");
499
- DEF_GLOBAL(request_uri, "REQUEST_URI");
500
- DEF_GLOBAL(fragment, "FRAGMENT");
501
- DEF_GLOBAL(query_string, "QUERY_STRING");
502
- DEF_GLOBAL(http_version, "HTTP_VERSION");
503
- DEF_GLOBAL(request_path, "REQUEST_PATH");
504
- DEF_GLOBAL(content_length, "CONTENT_LENGTH");
505
- DEF_GLOBAL(http_content_length, "HTTP_CONTENT_LENGTH");
506
- DEF_GLOBAL(content_type, "CONTENT_TYPE");
507
- DEF_GLOBAL(http_content_type, "HTTP_CONTENT_TYPE");
508
- DEF_GLOBAL(gateway_interface, "GATEWAY_INTERFACE");
509
- DEF_GLOBAL(gateway_interface_value, "CGI/1.2");
510
- DEF_GLOBAL(server_name, "SERVER_NAME");
511
- DEF_GLOBAL(server_port, "SERVER_PORT");
512
- DEF_GLOBAL(server_protocol, "SERVER_PROTOCOL");
513
- DEF_GLOBAL(server_protocol_value, "HTTP/1.1");
514
- DEF_GLOBAL(http_host, "HTTP_HOST");
515
- DEF_GLOBAL(server_version, waitress_version());
516
- DEF_GLOBAL(server_software, "SERVER_SOFTWARE");
517
- DEF_GLOBAL(port_80, "80");
518
-
519
- eHttpParserError = rb_define_class_under(rubyModule, "HttpParserError", rb_eIOError);
520
-
521
- cHttpParser = rb_define_class_under(rubyModule, "HttpParser", rb_cObject);
522
- rb_define_alloc_func(cHttpParser, HttpParser_alloc);
523
- rb_define_method(cHttpParser, "initialize", HttpParser_init,0);
524
- rb_define_method(cHttpParser, "reset", HttpParser_reset,0);
525
- rb_define_method(cHttpParser, "finish", HttpParser_finish,0);
526
- rb_define_method(cHttpParser, "execute", HttpParser_execute,3);
527
- rb_define_method(cHttpParser, "error?", HttpParser_has_error,0);
528
- rb_define_method(cHttpParser, "finished?", HttpParser_is_finished,0);
529
- rb_define_method(cHttpParser, "nread", HttpParser_nread,0);
530
- init_common_fields();
531
- init_parse(rubyModule);
532
- }
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
+ #include "http11_wrb_parser.h"
11
+
12
+ #ifndef RSTRING_PTR
13
+ #define RSTRING_PTR(s) (RSTRING(s)->ptr)
14
+ #endif
15
+ #ifndef RSTRING_LEN
16
+ #define RSTRING_LEN(s) (RSTRING(s)->len)
17
+ #endif
18
+
19
+ static VALUE rubyModule;
20
+ static VALUE cHttpParser;
21
+ static VALUE eHttpParserError;
22
+
23
+ #define id_handler_map rb_intern("@handler_map")
24
+ #define id_http_body rb_intern("@http_body")
25
+ #define HTTP_PREFIX "HTTP_HEAD_"
26
+ #define HTTP_PREFIX_LEN (sizeof(HTTP_PREFIX) - 1)
27
+
28
+ static VALUE global_request_method;
29
+ static VALUE global_request_uri;
30
+ static VALUE global_fragment;
31
+ static VALUE global_query_string;
32
+ static VALUE global_http_version;
33
+ static VALUE global_content_length;
34
+ static VALUE global_http_content_length;
35
+ static VALUE global_request_path;
36
+ static VALUE global_content_type;
37
+ static VALUE global_http_content_type;
38
+ static VALUE global_gateway_interface;
39
+ static VALUE global_gateway_interface_value;
40
+ static VALUE global_server_name;
41
+ static VALUE global_server_port;
42
+ static VALUE global_server_protocol;
43
+ static VALUE global_server_protocol_value;
44
+ static VALUE global_http_host;
45
+ static VALUE global_server_version;
46
+ static VALUE global_server_software;
47
+ static VALUE global_port_80;
48
+
49
+ #define TRIE_INCREASE 30
50
+
51
+ /** Defines common length and error messages for input length validation. */
52
+ #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."
53
+
54
+ /** Validates the max length of given input and throws an HttpParserError exception if over. */
55
+ #define VALIDATE_MAX_LENGTH(len, N) if(len > MAX_##N##_LENGTH) { rb_raise(eHttpParserError, MAX_##N##_LENGTH_ERR); }
56
+
57
+ /** Defines global strings in the init method. */
58
+ #define DEF_GLOBAL(N, val) global_##N = rb_obj_freeze(rb_str_new2(val)); rb_global_variable(&global_##N)
59
+
60
+
61
+ /* Defines the maximum allowed lengths for various input elements.*/
62
+ DEF_MAX_LENGTH(FIELD_NAME, 256);
63
+ DEF_MAX_LENGTH(FIELD_VALUE, 80 * 1024);
64
+ DEF_MAX_LENGTH(REQUEST_URI, 1024 * 12);
65
+ DEF_MAX_LENGTH(FRAGMENT, 1024); /* Don't know if this length is specified somewhere or not */
66
+ DEF_MAX_LENGTH(REQUEST_PATH, 1024);
67
+ DEF_MAX_LENGTH(QUERY_STRING, (1024 * 10));
68
+ DEF_MAX_LENGTH(HEADER, (1024 * (80 + 32)));
69
+
70
+ struct common_field {
71
+ const signed long len;
72
+ const char *name;
73
+ VALUE value;
74
+ };
75
+
76
+ /*
77
+ * A list of common HTTP headers we expect to receive.
78
+ * This allows us to avoid repeatedly creating identical string
79
+ * objects to be used with rb_hash_aset().
80
+ */
81
+ static struct common_field common_http_fields[] = {
82
+ # define f(N) { (sizeof(N) - 1), N, Qnil }
83
+ f("ACCEPT"),
84
+ f("ACCEPT_CHARSET"),
85
+ f("ACCEPT_ENCODING"),
86
+ f("ACCEPT_LANGUAGE"),
87
+ f("ALLOW"),
88
+ f("AUTHORIZATION"),
89
+ f("CACHE_CONTROL"),
90
+ f("CONNECTION"),
91
+ f("CONTENT_ENCODING"),
92
+ f("CONTENT_LENGTH"),
93
+ f("CONTENT_TYPE"),
94
+ f("COOKIE"),
95
+ f("DATE"),
96
+ f("EXPECT"),
97
+ f("FROM"),
98
+ f("HOST"),
99
+ f("IF_MATCH"),
100
+ f("IF_MODIFIED_SINCE"),
101
+ f("IF_NONE_MATCH"),
102
+ f("IF_RANGE"),
103
+ f("IF_UNMODIFIED_SINCE"),
104
+ f("KEEP_ALIVE"), /* Firefox sends this */
105
+ f("MAX_FORWARDS"),
106
+ f("PRAGMA"),
107
+ f("PROXY_AUTHORIZATION"),
108
+ f("RANGE"),
109
+ f("REFERER"),
110
+ f("TE"),
111
+ f("TRAILER"),
112
+ f("TRANSFER_ENCODING"),
113
+ f("UPGRADE"),
114
+ f("USER_AGENT"),
115
+ f("VIA"),
116
+ f("X_FORWARDED_FOR"), /* common for proxies */
117
+ f("X_REAL_IP"), /* common for proxies */
118
+ f("WARNING")
119
+ # undef f
120
+ };
121
+
122
+ /*
123
+ * qsort(3) and bsearch(3) improve average performance slightly, but may
124
+ * not be worth it for lack of portability to certain platforms...
125
+ */
126
+ #if defined(HAVE_QSORT_BSEARCH)
127
+ /* sort by length, then by name if there's a tie */
128
+ static int common_field_cmp(const void *a, const void *b)
129
+ {
130
+ struct common_field *cfa = (struct common_field *)a;
131
+ struct common_field *cfb = (struct common_field *)b;
132
+ signed long diff = cfa->len - cfb->len;
133
+ return diff ? diff : memcmp(cfa->name, cfb->name, cfa->len);
134
+ }
135
+ #endif /* HAVE_QSORT_BSEARCH */
136
+
137
+ static void init_common_fields(void)
138
+ {
139
+ int i;
140
+ struct common_field *cf = common_http_fields;
141
+ char tmp[256]; /* MAX_FIELD_NAME_LENGTH */
142
+ memcpy(tmp, HTTP_PREFIX, HTTP_PREFIX_LEN);
143
+
144
+ for(i = 0; i < ARRAY_SIZE(common_http_fields); cf++, i++) {
145
+ memcpy(tmp + HTTP_PREFIX_LEN, cf->name, cf->len + 1);
146
+ cf->value = rb_obj_freeze(rb_str_new(tmp, HTTP_PREFIX_LEN + cf->len));
147
+ rb_global_variable(&cf->value);
148
+ }
149
+
150
+ #if defined(HAVE_QSORT_BSEARCH)
151
+ qsort(common_http_fields,
152
+ ARRAY_SIZE(common_http_fields),
153
+ sizeof(struct common_field),
154
+ common_field_cmp);
155
+ #endif /* HAVE_QSORT_BSEARCH */
156
+ }
157
+
158
+ static VALUE find_common_field_value(const char *field, size_t flen)
159
+ {
160
+ #if defined(HAVE_QSORT_BSEARCH)
161
+ struct common_field key;
162
+ struct common_field *found;
163
+ key.name = field;
164
+ key.len = (signed long)flen;
165
+ found = (struct common_field *)bsearch(&key, common_http_fields,
166
+ ARRAY_SIZE(common_http_fields),
167
+ sizeof(struct common_field),
168
+ common_field_cmp);
169
+ return found ? found->value : Qnil;
170
+ #else /* !HAVE_QSORT_BSEARCH */
171
+ int i;
172
+ struct common_field *cf = common_http_fields;
173
+ for(i = 0; i < ARRAY_SIZE(common_http_fields); i++, cf++) {
174
+ if (cf->len == flen && !memcmp(cf->name, field, flen))
175
+ return cf->value;
176
+ }
177
+ return Qnil;
178
+ #endif /* !HAVE_QSORT_BSEARCH */
179
+ }
180
+
181
+ void http_field(void *data, const char *field, size_t flen, const char *value, size_t vlen)
182
+ {
183
+ VALUE req = (VALUE)data;
184
+ VALUE v = Qnil;
185
+ VALUE f = Qnil;
186
+
187
+ VALIDATE_MAX_LENGTH(flen, FIELD_NAME);
188
+ VALIDATE_MAX_LENGTH(vlen, FIELD_VALUE);
189
+
190
+ v = rb_str_new(value, vlen);
191
+
192
+ f = find_common_field_value(field, flen);
193
+
194
+ if (f == Qnil) {
195
+ /*
196
+ * We got a strange header that we don't have a memoized value for.
197
+ * Fallback to creating a new string to use as a hash key.
198
+ *
199
+ * using rb_str_new(NULL, len) here is faster than rb_str_buf_new(len)
200
+ * in my testing, because: there's no minimum allocation length (and
201
+ * no check for it, either), RSTRING_LEN(f) does not need to be
202
+ * written twice, and and RSTRING_PTR(f) will already be
203
+ * null-terminated for us.
204
+ */
205
+ f = rb_str_new(NULL, HTTP_PREFIX_LEN + flen);
206
+ memcpy(RSTRING_PTR(f), HTTP_PREFIX, HTTP_PREFIX_LEN);
207
+ memcpy(RSTRING_PTR(f) + HTTP_PREFIX_LEN, field, flen);
208
+ assert(*(RSTRING_PTR(f) + RSTRING_LEN(f)) == '\0'); /* paranoia */
209
+ /* fprintf(stderr, "UNKNOWN HEADER <%s>\n", RSTRING_PTR(f)); */
210
+ }
211
+
212
+ rb_hash_aset(req, f, v);
213
+ }
214
+
215
+ void request_method(void *data, const char *at, size_t length)
216
+ {
217
+ VALUE req = (VALUE)data;
218
+ VALUE val = Qnil;
219
+
220
+ val = rb_str_new(at, length);
221
+ rb_hash_aset(req, global_request_method, val);
222
+ }
223
+
224
+ void request_uri(void *data, const char *at, size_t length)
225
+ {
226
+ VALUE req = (VALUE)data;
227
+ VALUE val = Qnil;
228
+
229
+ VALIDATE_MAX_LENGTH(length, REQUEST_URI);
230
+
231
+ val = rb_str_new(at, length);
232
+ rb_hash_aset(req, global_request_uri, val);
233
+ }
234
+
235
+ void fragment(void *data, const char *at, size_t length)
236
+ {
237
+ VALUE req = (VALUE)data;
238
+ VALUE val = Qnil;
239
+
240
+ VALIDATE_MAX_LENGTH(length, FRAGMENT);
241
+
242
+ val = rb_str_new(at, length);
243
+ rb_hash_aset(req, global_fragment, val);
244
+ }
245
+
246
+ void request_path(void *data, const char *at, size_t length)
247
+ {
248
+ VALUE req = (VALUE)data;
249
+ VALUE val = Qnil;
250
+
251
+ VALIDATE_MAX_LENGTH(length, REQUEST_PATH);
252
+
253
+ val = rb_str_new(at, length);
254
+ rb_hash_aset(req, global_request_path, val);
255
+ }
256
+
257
+ void query_string(void *data, const char *at, size_t length)
258
+ {
259
+ VALUE req = (VALUE)data;
260
+ VALUE val = Qnil;
261
+
262
+ VALIDATE_MAX_LENGTH(length, QUERY_STRING);
263
+
264
+ val = rb_str_new(at, length);
265
+ rb_hash_aset(req, global_query_string, val);
266
+ }
267
+
268
+ void http_version(void *data, const char *at, size_t length)
269
+ {
270
+ VALUE req = (VALUE)data;
271
+ VALUE val = rb_str_new(at, length);
272
+ rb_hash_aset(req, global_http_version, val);
273
+ }
274
+
275
+ /** Finalizes the request header to have a bunch of stuff that's
276
+ needed. */
277
+
278
+ void header_done(void *data, const char *at, size_t length)
279
+ {
280
+ VALUE req = (VALUE)data;
281
+ VALUE temp = Qnil;
282
+ VALUE ctype = Qnil;
283
+ VALUE clen = Qnil;
284
+ char *colon = NULL;
285
+
286
+ clen = rb_hash_aref(req, global_http_content_length);
287
+ if(clen != Qnil) {
288
+ rb_hash_aset(req, global_content_length, clen);
289
+ }
290
+
291
+ ctype = rb_hash_aref(req, global_http_content_type);
292
+ if(ctype != Qnil) {
293
+ rb_hash_aset(req, global_content_type, ctype);
294
+ }
295
+
296
+ rb_hash_aset(req, global_gateway_interface, global_gateway_interface_value);
297
+ if((temp = rb_hash_aref(req, global_http_host)) != Qnil) {
298
+ colon = memchr(RSTRING_PTR(temp), ':', RSTRING_LEN(temp));
299
+ if(colon != NULL) {
300
+ rb_hash_aset(req, global_server_name, rb_str_substr(temp, 0, colon - RSTRING_PTR(temp)));
301
+ rb_hash_aset(req, global_server_port,
302
+ rb_str_substr(temp, colon - RSTRING_PTR(temp)+1,
303
+ RSTRING_LEN(temp)));
304
+ } else {
305
+ rb_hash_aset(req, global_server_name, temp);
306
+ rb_hash_aset(req, global_server_port, global_port_80);
307
+ }
308
+ }
309
+
310
+ /* grab the initial body and stuff it into an ivar */
311
+ rb_ivar_set(req, id_http_body, rb_str_new(at, length));
312
+ rb_hash_aset(req, global_server_protocol, global_server_protocol_value);
313
+ rb_hash_aset(req, global_server_software, global_server_version);
314
+ }
315
+
316
+
317
+ void HttpParser_free(void *data) {
318
+ TRACE();
319
+
320
+ if(data) {
321
+ free(data);
322
+ }
323
+ }
324
+
325
+
326
+ VALUE HttpParser_alloc(VALUE klass)
327
+ {
328
+ VALUE obj;
329
+ http_parser *hp = ALLOC_N(http_parser, 1);
330
+ TRACE();
331
+ hp->http_field = http_field;
332
+ hp->request_method = request_method;
333
+ hp->request_uri = request_uri;
334
+ hp->fragment = fragment;
335
+ hp->request_path = request_path;
336
+ hp->query_string = query_string;
337
+ hp->http_version = http_version;
338
+ hp->header_done = header_done;
339
+ http_parser_init(hp);
340
+
341
+ obj = Data_Wrap_Struct(klass, NULL, HttpParser_free, hp);
342
+
343
+ return obj;
344
+ }
345
+
346
+
347
+ /**
348
+ * call-seq:
349
+ * parser.new -> parser
350
+ *
351
+ * Creates a new parser.
352
+ */
353
+ VALUE HttpParser_init(VALUE self)
354
+ {
355
+ http_parser *http = NULL;
356
+ DATA_GET(self, http_parser, http);
357
+ http_parser_init(http);
358
+
359
+ return self;
360
+ }
361
+
362
+
363
+ /**
364
+ * call-seq:
365
+ * parser.reset -> nil
366
+ *
367
+ * Resets the parser to it's initial state so that you can reuse it
368
+ * rather than making new ones.
369
+ */
370
+ VALUE HttpParser_reset(VALUE self)
371
+ {
372
+ http_parser *http = NULL;
373
+ DATA_GET(self, http_parser, http);
374
+ http_parser_init(http);
375
+
376
+ return Qnil;
377
+ }
378
+
379
+
380
+ /**
381
+ * call-seq:
382
+ * parser.finish -> true/false
383
+ *
384
+ * Finishes a parser early which could put in a "good" or bad state.
385
+ * You should call reset after finish it or bad things will happen.
386
+ */
387
+ VALUE HttpParser_finish(VALUE self)
388
+ {
389
+ http_parser *http = NULL;
390
+ DATA_GET(self, http_parser, http);
391
+ http_parser_finish(http);
392
+
393
+ return http_parser_is_finished(http) ? Qtrue : Qfalse;
394
+ }
395
+
396
+
397
+ /**
398
+ * call-seq:
399
+ * parser.execute(req_hash, data, start) -> Integer
400
+ *
401
+ * Takes a Hash and a String of data, parses the String of data filling in the Hash
402
+ * returning an Integer to indicate how much of the data has been read. No matter
403
+ * what the return value, you should call HttpParser#finished? and HttpParser#error?
404
+ * to figure out if it's done parsing or there was an error.
405
+ *
406
+ * This function now throws an exception when there is a parsing error. This makes
407
+ * the logic for working with the parser much easier. You can still test for an
408
+ * error, but now you need to wrap the parser with an exception handling block.
409
+ *
410
+ * The third argument allows for parsing a partial request and then continuing
411
+ * the parsing from that position. It needs all of the original data as well
412
+ * so you have to append to the data buffer as you read.
413
+ */
414
+ VALUE HttpParser_execute(VALUE self, VALUE req_hash, VALUE data, VALUE start)
415
+ {
416
+ http_parser *http = NULL;
417
+ int from = 0;
418
+ char *dptr = NULL;
419
+ long dlen = 0;
420
+
421
+ DATA_GET(self, http_parser, http);
422
+
423
+ from = FIX2INT(start);
424
+ dptr = RSTRING_PTR(data);
425
+ dlen = RSTRING_LEN(data);
426
+
427
+ if(from >= dlen) {
428
+ rb_raise(eHttpParserError, "Requested start is after data buffer end.");
429
+ } else {
430
+ http->data = (void *)req_hash;
431
+ http_parser_execute(http, dptr, dlen, from);
432
+
433
+ VALIDATE_MAX_LENGTH(http_parser_nread(http), HEADER);
434
+
435
+ if(http_parser_has_error(http)) {
436
+ rb_raise(eHttpParserError, "Invalid HTTP format, parsing fails.");
437
+ } else {
438
+ return INT2FIX(http_parser_nread(http));
439
+ }
440
+ }
441
+ }
442
+
443
+
444
+
445
+ /**
446
+ * call-seq:
447
+ * parser.error? -> true/false
448
+ *
449
+ * Tells you whether the parser is in an error state.
450
+ */
451
+ VALUE HttpParser_has_error(VALUE self)
452
+ {
453
+ http_parser *http = NULL;
454
+ DATA_GET(self, http_parser, http);
455
+
456
+ return http_parser_has_error(http) ? Qtrue : Qfalse;
457
+ }
458
+
459
+
460
+ /**
461
+ * call-seq:
462
+ * parser.finished? -> true/false
463
+ *
464
+ * Tells you whether the parser is finished or not and in a good state.
465
+ */
466
+ VALUE HttpParser_is_finished(VALUE self)
467
+ {
468
+ http_parser *http = NULL;
469
+ DATA_GET(self, http_parser, http);
470
+
471
+ return http_parser_is_finished(http) ? Qtrue : Qfalse;
472
+ }
473
+
474
+
475
+ /**
476
+ * call-seq:
477
+ * parser.nread -> Integer
478
+ *
479
+ * Returns the amount of data processed so far during this processing cycle. It is
480
+ * set to 0 on initialize or reset calls and is incremented each time execute is called.
481
+ */
482
+ VALUE HttpParser_nread(VALUE self)
483
+ {
484
+ http_parser *http = NULL;
485
+ DATA_GET(self, http_parser, http);
486
+
487
+ return INT2FIX(http->nread);
488
+ }
489
+
490
+ VALUE waitress_version() {
491
+ return RSTRING_PTR(rb_str_plus(rb_str_new2("Waitress "), rb_const_get(rb_define_module("Waitress"), rb_intern("VERSION"))));
492
+ }
493
+
494
+ void Init_waitress_http11()
495
+ {
496
+ rubyModule = rb_define_module("Waitress");
497
+
498
+ DEF_GLOBAL(request_method, "REQUEST_METHOD");
499
+ DEF_GLOBAL(request_uri, "REQUEST_URI");
500
+ DEF_GLOBAL(fragment, "FRAGMENT");
501
+ DEF_GLOBAL(query_string, "QUERY_STRING");
502
+ DEF_GLOBAL(http_version, "HTTP_VERSION");
503
+ DEF_GLOBAL(request_path, "REQUEST_PATH");
504
+ DEF_GLOBAL(content_length, "CONTENT_LENGTH");
505
+ DEF_GLOBAL(http_content_length, "HTTP_CONTENT_LENGTH");
506
+ DEF_GLOBAL(content_type, "CONTENT_TYPE");
507
+ DEF_GLOBAL(http_content_type, "HTTP_CONTENT_TYPE");
508
+ DEF_GLOBAL(gateway_interface, "GATEWAY_INTERFACE");
509
+ DEF_GLOBAL(gateway_interface_value, "CGI/1.2");
510
+ DEF_GLOBAL(server_name, "SERVER_NAME");
511
+ DEF_GLOBAL(server_port, "SERVER_PORT");
512
+ DEF_GLOBAL(server_protocol, "SERVER_PROTOCOL");
513
+ DEF_GLOBAL(server_protocol_value, "HTTP/1.1");
514
+ DEF_GLOBAL(http_host, "HTTP_HOST");
515
+ DEF_GLOBAL(server_version, waitress_version());
516
+ DEF_GLOBAL(server_software, "SERVER_SOFTWARE");
517
+ DEF_GLOBAL(port_80, "80");
518
+
519
+ eHttpParserError = rb_define_class_under(rubyModule, "HttpParserError", rb_eIOError);
520
+
521
+ cHttpParser = rb_define_class_under(rubyModule, "HttpParser", rb_cObject);
522
+ rb_define_alloc_func(cHttpParser, HttpParser_alloc);
523
+ rb_define_method(cHttpParser, "initialize", HttpParser_init,0);
524
+ rb_define_method(cHttpParser, "reset", HttpParser_reset,0);
525
+ rb_define_method(cHttpParser, "finish", HttpParser_finish,0);
526
+ rb_define_method(cHttpParser, "execute", HttpParser_execute,3);
527
+ rb_define_method(cHttpParser, "error?", HttpParser_has_error,0);
528
+ rb_define_method(cHttpParser, "finished?", HttpParser_is_finished,0);
529
+ rb_define_method(cHttpParser, "nread", HttpParser_nread,0);
530
+ init_common_fields();
531
+ init_parse(rubyModule);
532
+ }