waitress-core 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +4 -0
  3. data/LICENSE +21 -0
  4. data/Rakefile +4 -0
  5. data/bin/waitress +22 -0
  6. data/ext/Thanks.md +1 -0
  7. data/ext/waitress_http11/ext_help.h +15 -0
  8. data/ext/waitress_http11/extconf.rb +6 -0
  9. data/ext/waitress_http11/http11.c +532 -0
  10. data/ext/waitress_http11/http11_parser.c +1216 -0
  11. data/ext/waitress_http11/http11_parser.h +49 -0
  12. data/ext/waitress_http11/http11_parser.java.rl +171 -0
  13. data/ext/waitress_http11/http11_parser.rl +165 -0
  14. data/ext/waitress_http11/http11_parser_common.rl +55 -0
  15. data/ext/waitress_http11/http11_wrb_parser.h +83 -0
  16. data/lib/waitress.rb +98 -0
  17. data/lib/waitress/chef.rb +110 -0
  18. data/lib/waitress/configure.rb +116 -0
  19. data/lib/waitress/handlers/dirhandler.rb +39 -0
  20. data/lib/waitress/handlers/handler.rb +57 -0
  21. data/lib/waitress/handlers/handler404.rb +25 -0
  22. data/lib/waitress/handlers/libhandler.rb +58 -0
  23. data/lib/waitress/kernel.rb +182 -0
  24. data/lib/waitress/parse/query.rb +60 -0
  25. data/lib/waitress/request.rb +45 -0
  26. data/lib/waitress/resources/default_config.rb +52 -0
  27. data/lib/waitress/resources/http/404.html +18 -0
  28. data/lib/waitress/resources/http/css/hack.css +37 -0
  29. data/lib/waitress/resources/http/css/waitress.css +57 -0
  30. data/lib/waitress/resources/http/fonts/eot/latin/hack-bold-latin-webfont.eot +0 -0
  31. data/lib/waitress/resources/http/fonts/eot/latin/hack-bolditalic-latin-webfont.eot +0 -0
  32. data/lib/waitress/resources/http/fonts/eot/latin/hack-italic-latin-webfont.eot +0 -0
  33. data/lib/waitress/resources/http/fonts/eot/latin/hack-regular-latin-webfont.eot +0 -0
  34. data/lib/waitress/resources/http/fonts/svg/latin/hack-bold-latin-webfont.svg +241 -0
  35. data/lib/waitress/resources/http/fonts/svg/latin/hack-bolditalic-latin-webfont.svg +241 -0
  36. data/lib/waitress/resources/http/fonts/svg/latin/hack-italic-latin-webfont.svg +241 -0
  37. data/lib/waitress/resources/http/fonts/svg/latin/hack-regular-latin-webfont.svg +241 -0
  38. data/lib/waitress/resources/http/fonts/web-ttf/latin/hack-bold-latin-webfont.ttf +0 -0
  39. data/lib/waitress/resources/http/fonts/web-ttf/latin/hack-bolditalic-latin-webfont.ttf +0 -0
  40. data/lib/waitress/resources/http/fonts/web-ttf/latin/hack-italic-latin-webfont.ttf +0 -0
  41. data/lib/waitress/resources/http/fonts/web-ttf/latin/hack-regular-latin-webfont.ttf +0 -0
  42. data/lib/waitress/resources/http/fonts/woff/latin/hack-bold-latin-webfont.woff +0 -0
  43. data/lib/waitress/resources/http/fonts/woff/latin/hack-bolditalic-latin-webfont.woff +0 -0
  44. data/lib/waitress/resources/http/fonts/woff/latin/hack-italic-latin-webfont.woff +0 -0
  45. data/lib/waitress/resources/http/fonts/woff/latin/hack-regular-latin-webfont.woff +0 -0
  46. data/lib/waitress/resources/http/fonts/woff2/latin/hack-bold-latin-webfont.woff2 +0 -0
  47. data/lib/waitress/resources/http/fonts/woff2/latin/hack-bolditalic-latin-webfont.woff2 +0 -0
  48. data/lib/waitress/resources/http/fonts/woff2/latin/hack-italic-latin-webfont.woff2 +0 -0
  49. data/lib/waitress/resources/http/fonts/woff2/latin/hack-regular-latin-webfont.woff2 +0 -0
  50. data/lib/waitress/resources/http/img/404.png +0 -0
  51. data/lib/waitress/resources/http/index.html +15 -0
  52. data/lib/waitress/response.rb +104 -0
  53. data/lib/waitress/server.rb +115 -0
  54. data/lib/waitress/util.rb +713 -0
  55. data/lib/waitress/version.rb +3 -0
  56. data/lib/waitress/vhost.rb +217 -0
  57. data/lib/waitress_http11.so +0 -0
  58. data/waitress-core.gemspec +27 -0
  59. metadata +187 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 5b788b59420f9aa48b6ad259898e6190b9b697ee
4
+ data.tar.gz: 06786d47a77c194e29ff0c4c41b3b44744b06a8e
5
+ SHA512:
6
+ metadata.gz: de9344d02bb004d4bf8b1a1650a1780a7d9347f59d90aa2cd53cedf8319a538c3155779040c539b8e64389d51da58c7da0d45ac07adebff0f1c3e313c370b6b0
7
+ data.tar.gz: d8606b73b7f4a01e846dced6485bae88fed6fe89389e5e3d1ad35814acfd127da4fdc9e538e3d4df95a4a78ff132cec7a7ba2531384a1e925cb51af8f6cd16f2
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in waitress-core.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +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.
data/Rakefile ADDED
@@ -0,0 +1,4 @@
1
+ require 'rake/extensiontask'
2
+
3
+ spec = Gem::Specification.load('waitress-core.gemspec')
4
+ Rake::ExtensionTask.new('waitress_http11', spec)
data/bin/waitress ADDED
@@ -0,0 +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
data/ext/Thanks.md ADDED
@@ -0,0 +1 @@
1
+ The Native Extensions for the HTTP Parser are taken from the [mongrel project](https://github.com/mongrel/mongrel)
@@ -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("waitress_http11")
4
+ have_library("c", "main")
5
+
6
+ create_makefile("waitress_http11")
@@ -0,0 +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
+ }