gross 1.7.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG +410 -0
  3. data/README.md +102 -0
  4. data/Rakefile +25 -0
  5. data/bin/thin +6 -0
  6. data/example/adapter.rb +32 -0
  7. data/example/async_app.ru +126 -0
  8. data/example/async_chat.ru +247 -0
  9. data/example/async_tailer.ru +100 -0
  10. data/example/config.ru +22 -0
  11. data/example/monit_sockets +20 -0
  12. data/example/monit_unixsock +20 -0
  13. data/example/myapp.rb +1 -0
  14. data/example/ramaze.ru +12 -0
  15. data/example/thin.god +80 -0
  16. data/example/thin_solaris_smf.erb +36 -0
  17. data/example/thin_solaris_smf.readme.txt +150 -0
  18. data/example/vlad.rake +72 -0
  19. data/ext/thin_parser/common.rl +59 -0
  20. data/ext/thin_parser/ext_help.h +14 -0
  21. data/ext/thin_parser/extconf.rb +6 -0
  22. data/ext/thin_parser/parser.c +1447 -0
  23. data/ext/thin_parser/parser.h +49 -0
  24. data/ext/thin_parser/parser.rl +152 -0
  25. data/ext/thin_parser/thin.c +435 -0
  26. data/lib/rack/adapter/loader.rb +75 -0
  27. data/lib/rack/adapter/rails.rb +178 -0
  28. data/lib/thin.rb +45 -0
  29. data/lib/thin/backends/base.rb +167 -0
  30. data/lib/thin/backends/swiftiply_client.rb +56 -0
  31. data/lib/thin/backends/tcp_server.rb +34 -0
  32. data/lib/thin/backends/unix_server.rb +56 -0
  33. data/lib/thin/command.rb +53 -0
  34. data/lib/thin/connection.rb +215 -0
  35. data/lib/thin/controllers/cluster.rb +178 -0
  36. data/lib/thin/controllers/controller.rb +189 -0
  37. data/lib/thin/controllers/service.rb +76 -0
  38. data/lib/thin/controllers/service.sh.erb +39 -0
  39. data/lib/thin/daemonizing.rb +180 -0
  40. data/lib/thin/headers.rb +40 -0
  41. data/lib/thin/logging.rb +174 -0
  42. data/lib/thin/request.rb +162 -0
  43. data/lib/thin/response.rb +117 -0
  44. data/lib/thin/runner.rb +238 -0
  45. data/lib/thin/server.rb +290 -0
  46. data/lib/thin/stats.html.erb +216 -0
  47. data/lib/thin/stats.rb +52 -0
  48. data/lib/thin/statuses.rb +44 -0
  49. data/lib/thin/version.rb +32 -0
  50. metadata +156 -0
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Copyright (c) 2005 Zed A. Shaw
3
+ * You can redistribute it and/or modify it under the same terms as Ruby.
4
+ */
5
+
6
+ #ifndef http11_parser_h
7
+ #define http11_parser_h
8
+
9
+ #include <sys/types.h>
10
+
11
+ #if defined(_WIN32)
12
+ #include <stddef.h>
13
+ #endif
14
+
15
+ typedef void (*element_cb)(void *data, const char *at, size_t length);
16
+ typedef void (*field_cb)(void *data, const char *field, size_t flen, const char *value, size_t vlen);
17
+
18
+ typedef struct http_parser {
19
+ int cs;
20
+ size_t body_start;
21
+ int content_len;
22
+ size_t nread;
23
+ size_t mark;
24
+ size_t field_start;
25
+ size_t field_len;
26
+ size_t query_start;
27
+
28
+ void *data;
29
+
30
+ field_cb http_field;
31
+ element_cb request_method;
32
+ element_cb request_uri;
33
+ element_cb fragment;
34
+ element_cb request_path;
35
+ element_cb query_string;
36
+ element_cb http_version;
37
+ element_cb header_done;
38
+
39
+ } http_parser;
40
+
41
+ int http_parser_init(http_parser *parser);
42
+ int http_parser_finish(http_parser *parser);
43
+ size_t http_parser_execute(http_parser *parser, const char *data, size_t len, size_t off);
44
+ int http_parser_has_error(http_parser *parser);
45
+ int http_parser_is_finished(http_parser *parser);
46
+
47
+ #define http_parser_nread(parser) (parser)->nread
48
+
49
+ #endif
@@ -0,0 +1,152 @@
1
+ /**
2
+ * Copyright (c) 2005 Zed A. Shaw
3
+ * You can redistribute it and/or modify it under the same terms as Ruby.
4
+ */
5
+ #include "parser.h"
6
+ #include <stdio.h>
7
+ #include <assert.h>
8
+ #include <stdlib.h>
9
+ #include <ctype.h>
10
+ #include <string.h>
11
+
12
+ #define LEN(AT, FPC) (FPC - buffer - parser->AT)
13
+ #define MARK(M,FPC) (parser->M = (FPC) - buffer)
14
+ #define PTR_TO(F) (buffer + parser->F)
15
+
16
+ /** Machine **/
17
+
18
+ %%{
19
+
20
+ machine http_parser;
21
+
22
+ action mark {MARK(mark, fpc); }
23
+
24
+
25
+ action start_field { MARK(field_start, fpc); }
26
+ action write_field {
27
+ parser->field_len = LEN(field_start, fpc);
28
+ }
29
+
30
+ action start_value { MARK(mark, fpc); }
31
+ action write_value {
32
+ if (parser->http_field != NULL) {
33
+ parser->http_field(parser->data, PTR_TO(field_start), parser->field_len, PTR_TO(mark), LEN(mark, fpc));
34
+ }
35
+ }
36
+ action request_method {
37
+ if (parser->request_method != NULL) {
38
+ parser->request_method(parser->data, PTR_TO(mark), LEN(mark, fpc));
39
+ }
40
+ }
41
+ action request_uri {
42
+ if (parser->request_uri != NULL) {
43
+ parser->request_uri(parser->data, PTR_TO(mark), LEN(mark, fpc));
44
+ }
45
+ }
46
+ action fragment {
47
+ if (parser->fragment != NULL) {
48
+ parser->fragment(parser->data, PTR_TO(mark), LEN(mark, fpc));
49
+ }
50
+ }
51
+
52
+ action start_query {MARK(query_start, fpc); }
53
+ action query_string {
54
+ if (parser->query_string != NULL) {
55
+ parser->query_string(parser->data, PTR_TO(query_start), LEN(query_start, fpc));
56
+ }
57
+ }
58
+
59
+ action http_version {
60
+ if (parser->http_version != NULL) {
61
+ parser->http_version(parser->data, PTR_TO(mark), LEN(mark, fpc));
62
+ }
63
+ }
64
+
65
+ action request_path {
66
+ if (parser->request_path != NULL) {
67
+ parser->request_path(parser->data, PTR_TO(mark), LEN(mark,fpc));
68
+ }
69
+ }
70
+
71
+ action done {
72
+ parser->body_start = fpc - buffer + 1;
73
+ if (parser->header_done != NULL) {
74
+ parser->header_done(parser->data, fpc + 1, pe - fpc - 1);
75
+ }
76
+ fbreak;
77
+ }
78
+
79
+ include http_parser_common "common.rl";
80
+
81
+ }%%
82
+
83
+ /** Data **/
84
+ %% write data;
85
+
86
+ int thin_http_parser_init(http_parser *parser) {
87
+ int cs = 0;
88
+ %% write init;
89
+ parser->cs = cs;
90
+ parser->body_start = 0;
91
+ parser->content_len = 0;
92
+ parser->mark = 0;
93
+ parser->nread = 0;
94
+ parser->field_len = 0;
95
+ parser->field_start = 0;
96
+
97
+ return(1);
98
+ }
99
+
100
+
101
+ /** exec **/
102
+ size_t thin_http_parser_execute(http_parser *parser, const char *buffer, size_t len, size_t off) {
103
+ const char *p, *pe;
104
+ int cs = parser->cs;
105
+
106
+ assert(off <= len && "offset past end of buffer");
107
+
108
+ p = buffer+off;
109
+ pe = buffer+len;
110
+
111
+ assert(*pe == '\0' && "pointer does not end on NUL");
112
+ assert(pe - p == (long)(len - off) && "pointers aren't same distance");
113
+
114
+
115
+ %% write exec;
116
+
117
+ parser->cs = cs;
118
+ parser->nread += p - (buffer + off);
119
+
120
+ assert(p <= pe && "buffer overflow after parsing execute");
121
+ assert(parser->nread <= len && "nread longer than length");
122
+ assert(parser->body_start <= len && "body starts after buffer end");
123
+ assert(parser->mark < len && "mark is after buffer end");
124
+ assert(parser->field_len <= len && "field has length longer than whole buffer");
125
+ assert(parser->field_start < len && "field starts after buffer end");
126
+
127
+ if(parser->body_start) {
128
+ /* final \r\n combo encountered so stop right here */
129
+ parser->nread++;
130
+ }
131
+
132
+ return(parser->nread);
133
+ }
134
+
135
+ int thin_http_parser_has_error(http_parser *parser) {
136
+ return parser->cs == http_parser_error;
137
+ }
138
+
139
+ int thin_http_parser_is_finished(http_parser *parser) {
140
+ return parser->cs == http_parser_first_final;
141
+ }
142
+
143
+ int thin_http_parser_finish(http_parser *parser)
144
+ {
145
+ if (thin_http_parser_has_error(parser) ) {
146
+ return -1;
147
+ } else if (thin_http_parser_is_finished(parser) ) {
148
+ return 1;
149
+ } else {
150
+ return 0;
151
+ }
152
+ }
@@ -0,0 +1,435 @@
1
+ /**
2
+ * Mongrel Parser adpated to Thin and to play more nicely with Rack specs.
3
+ *
4
+ * Orignal version Copyright (c) 2005 Zed A. Shaw
5
+ * You can redistribute it and/or modify it under the same terms as Ruby.
6
+ */
7
+ #include "ruby.h"
8
+ #include "ext_help.h"
9
+ #include <assert.h>
10
+ #include <string.h>
11
+ #include "parser.h"
12
+ #include <ctype.h>
13
+
14
+ static VALUE mThin;
15
+ static VALUE cHttpParser;
16
+ static VALUE eHttpParserError;
17
+
18
+ static VALUE global_empty;
19
+ static VALUE global_http_prefix;
20
+ static VALUE global_request_method;
21
+ static VALUE global_request_uri;
22
+ static VALUE global_fragment;
23
+ static VALUE global_query_string;
24
+ static VALUE global_http_version;
25
+ static VALUE global_content_length;
26
+ static VALUE global_http_content_length;
27
+ static VALUE global_request_path;
28
+ static VALUE global_content_type;
29
+ static VALUE global_http_content_type;
30
+ static VALUE global_gateway_interface;
31
+ static VALUE global_gateway_interface_value;
32
+ static VALUE global_server_name;
33
+ static VALUE global_server_port;
34
+ static VALUE global_server_protocol;
35
+ static VALUE global_server_protocol_value;
36
+ static VALUE global_http_host;
37
+ static VALUE global_port_80;
38
+ static VALUE global_http_body;
39
+ static VALUE global_url_scheme;
40
+ static VALUE global_url_scheme_value;
41
+ static VALUE global_script_name;
42
+ static VALUE global_path_info;
43
+
44
+ #define TRIE_INCREASE 30
45
+
46
+ /** Defines common length and error messages for input length validation. */
47
+ #define DEF_MAX_LENGTH(N,length) const size_t MAX_##N##_LENGTH = length; const char *MAX_##N##_LENGTH_ERR = "HTTP element " # N " is longer than the " # length " allowed length."
48
+
49
+ /** Validates the max length of given input and throws an HttpParserError exception if over. */
50
+ #define VALIDATE_MAX_LENGTH(len, N) if(len > MAX_##N##_LENGTH) { rb_raise(eHttpParserError, "%s", MAX_##N##_LENGTH_ERR); }
51
+
52
+ /** Defines global strings in the init method. */
53
+ #define DEF_GLOBAL(N, val) global_##N = rb_obj_freeze(rb_str_new2(val)); rb_global_variable(&global_##N)
54
+
55
+ /* for compatibility with Ruby 1.8.5, which doesn't declare RSTRING_PTR */
56
+ #ifndef RSTRING_PTR
57
+ #define RSTRING_PTR(s) (RSTRING(s)->ptr)
58
+ #endif
59
+
60
+ /* for compatibility with Ruby 1.8.5, which doesn't declare RSTRING_LEN */
61
+ #ifndef RSTRING_LEN
62
+ #define RSTRING_LEN(s) (RSTRING(s)->len)
63
+ #endif
64
+
65
+ /* Defines the maximum allowed lengths for various input elements.*/
66
+ DEF_MAX_LENGTH(FIELD_NAME, 256);
67
+ DEF_MAX_LENGTH(FIELD_VALUE, 80 * 1024);
68
+ DEF_MAX_LENGTH(REQUEST_URI, 1024 * 32);
69
+ DEF_MAX_LENGTH(FRAGMENT, 1024); /* Don't know if this length is specified somewhere or not */
70
+ DEF_MAX_LENGTH(REQUEST_PATH, 2048);
71
+ DEF_MAX_LENGTH(QUERY_STRING, (1024 * 32));
72
+ DEF_MAX_LENGTH(HEADER, (1024 * (80 + 32)));
73
+
74
+ static void http_field(void *data, const char *field, size_t flen, const char *value, size_t vlen)
75
+ {
76
+ char *ch, *end;
77
+ VALUE req = (VALUE)data;
78
+ VALUE v = Qnil;
79
+ VALUE f = Qnil;
80
+
81
+ VALIDATE_MAX_LENGTH(flen, FIELD_NAME);
82
+ VALIDATE_MAX_LENGTH(vlen, FIELD_VALUE);
83
+
84
+ v = rb_str_new(value, vlen);
85
+ f = rb_str_dup(global_http_prefix);
86
+ f = rb_str_buf_cat(f, field, flen);
87
+
88
+ for(ch = RSTRING_PTR(f) + RSTRING_LEN(global_http_prefix), end = RSTRING_PTR(f) + RSTRING_LEN(f); ch < end; ch++) {
89
+ if (*ch >= 'a' && *ch <= 'z') {
90
+ *ch &= ~0x20; // upcase
91
+ } else if (*ch == '-') {
92
+ *ch = '_';
93
+ }
94
+ }
95
+
96
+ rb_hash_aset(req, f, v);
97
+ }
98
+
99
+ static void request_method(void *data, const char *at, size_t length)
100
+ {
101
+ VALUE req = (VALUE)data;
102
+ VALUE val = Qnil;
103
+
104
+ val = rb_str_new(at, length);
105
+ rb_hash_aset(req, global_request_method, val);
106
+ }
107
+
108
+ static void request_uri(void *data, const char *at, size_t length)
109
+ {
110
+ VALUE req = (VALUE)data;
111
+ VALUE val = Qnil;
112
+
113
+ VALIDATE_MAX_LENGTH(length, REQUEST_URI);
114
+
115
+ val = rb_str_new(at, length);
116
+ rb_hash_aset(req, global_request_uri, val);
117
+ }
118
+
119
+ static void fragment(void *data, const char *at, size_t length)
120
+ {
121
+ VALUE req = (VALUE)data;
122
+ VALUE val = Qnil;
123
+
124
+ VALIDATE_MAX_LENGTH(length, FRAGMENT);
125
+
126
+ val = rb_str_new(at, length);
127
+ rb_hash_aset(req, global_fragment, val);
128
+ }
129
+
130
+ static void request_path(void *data, const char *at, size_t length)
131
+ {
132
+ VALUE req = (VALUE)data;
133
+ VALUE val = Qnil;
134
+
135
+ VALIDATE_MAX_LENGTH(length, REQUEST_PATH);
136
+
137
+ val = rb_str_new(at, length);
138
+ rb_hash_aset(req, global_request_path, val);
139
+ rb_hash_aset(req, global_path_info, val);
140
+ }
141
+
142
+ static void query_string(void *data, const char *at, size_t length)
143
+ {
144
+ VALUE req = (VALUE)data;
145
+ VALUE val = Qnil;
146
+
147
+ VALIDATE_MAX_LENGTH(length, QUERY_STRING);
148
+
149
+ val = rb_str_new(at, length);
150
+ rb_hash_aset(req, global_query_string, val);
151
+ }
152
+
153
+ static void http_version(void *data, const char *at, size_t length)
154
+ {
155
+ VALUE req = (VALUE)data;
156
+ VALUE val = rb_str_new(at, length);
157
+ rb_hash_aset(req, global_http_version, val);
158
+ }
159
+
160
+ /** Finalizes the request header to have a bunch of stuff that's
161
+ needed. */
162
+
163
+ static void header_done(void *data, const char *at, size_t length)
164
+ {
165
+ VALUE req = (VALUE)data;
166
+ VALUE temp = Qnil;
167
+ VALUE ctype = Qnil;
168
+ VALUE clen = Qnil;
169
+ VALUE body = Qnil;
170
+ char *colon = NULL;
171
+
172
+ clen = rb_hash_aref(req, global_http_content_length);
173
+ if(clen != Qnil) {
174
+ rb_hash_aset(req, global_content_length, clen);
175
+ rb_hash_delete(req, global_http_content_length);
176
+ }
177
+
178
+ ctype = rb_hash_aref(req, global_http_content_type);
179
+ if(ctype != Qnil) {
180
+ rb_hash_aset(req, global_content_type, ctype);
181
+ rb_hash_delete(req, global_http_content_type);
182
+ }
183
+
184
+ rb_hash_aset(req, global_gateway_interface, global_gateway_interface_value);
185
+ if((temp = rb_hash_aref(req, global_http_host)) != Qnil) {
186
+ /* ruby better close strings off with a '\0' dammit */
187
+ colon = strchr(RSTRING_PTR(temp), ':');
188
+ if(colon != NULL) {
189
+ rb_hash_aset(req, global_server_name, rb_str_substr(temp, 0, colon - RSTRING_PTR(temp)));
190
+ rb_hash_aset(req, global_server_port,
191
+ rb_str_substr(temp, colon - RSTRING_PTR(temp)+1,
192
+ RSTRING_LEN(temp)));
193
+ } else {
194
+ rb_hash_aset(req, global_server_name, temp);
195
+ rb_hash_aset(req, global_server_port, global_port_80);
196
+ }
197
+ }
198
+
199
+ /* grab the initial body and stuff it into the hash */
200
+ if(length > 0) {
201
+ body = rb_hash_aref(req, global_http_body);
202
+ rb_io_write(body, rb_str_new(at, length));
203
+ }
204
+
205
+ /* according to Rack specs, query string must be empty string if none */
206
+ if (rb_hash_aref(req, global_query_string) == Qnil) {
207
+ rb_hash_aset(req, global_query_string, global_empty);
208
+ }
209
+ if (rb_hash_aref(req, global_path_info) == Qnil) {
210
+ rb_hash_aset(req, global_path_info, global_empty);
211
+ }
212
+
213
+ /* set some constants */
214
+ rb_hash_aset(req, global_server_protocol, global_server_protocol_value);
215
+ rb_hash_aset(req, global_url_scheme, global_url_scheme_value);
216
+ rb_hash_aset(req, global_script_name, global_empty);
217
+ }
218
+
219
+
220
+ void Thin_HttpParser_free(void *data) {
221
+ TRACE();
222
+
223
+ if(data) {
224
+ free(data);
225
+ }
226
+ }
227
+
228
+
229
+ VALUE Thin_HttpParser_alloc(VALUE klass)
230
+ {
231
+ VALUE obj;
232
+ http_parser *hp = ALLOC_N(http_parser, 1);
233
+ TRACE();
234
+ hp->http_field = http_field;
235
+ hp->request_method = request_method;
236
+ hp->request_uri = request_uri;
237
+ hp->fragment = fragment;
238
+ hp->request_path = request_path;
239
+ hp->query_string = query_string;
240
+ hp->http_version = http_version;
241
+ hp->header_done = header_done;
242
+ thin_http_parser_init(hp);
243
+
244
+ obj = Data_Wrap_Struct(klass, NULL, Thin_HttpParser_free, hp);
245
+
246
+ return obj;
247
+ }
248
+
249
+
250
+ /**
251
+ * call-seq:
252
+ * parser.new -> parser
253
+ *
254
+ * Creates a new parser.
255
+ */
256
+ VALUE Thin_HttpParser_init(VALUE self)
257
+ {
258
+ http_parser *http = NULL;
259
+ DATA_GET(self, http_parser, http);
260
+ thin_http_parser_init(http);
261
+
262
+ return self;
263
+ }
264
+
265
+
266
+ /**
267
+ * call-seq:
268
+ * parser.reset -> nil
269
+ *
270
+ * Resets the parser to it's initial state so that you can reuse it
271
+ * rather than making new ones.
272
+ */
273
+ VALUE Thin_HttpParser_reset(VALUE self)
274
+ {
275
+ http_parser *http = NULL;
276
+ DATA_GET(self, http_parser, http);
277
+ thin_http_parser_init(http);
278
+
279
+ return Qnil;
280
+ }
281
+
282
+
283
+ /**
284
+ * call-seq:
285
+ * parser.finish -> true/false
286
+ *
287
+ * Finishes a parser early which could put in a "good" or bad state.
288
+ * You should call reset after finish it or bad things will happen.
289
+ */
290
+ VALUE Thin_HttpParser_finish(VALUE self)
291
+ {
292
+ http_parser *http = NULL;
293
+ DATA_GET(self, http_parser, http);
294
+ thin_http_parser_finish(http);
295
+
296
+ return thin_http_parser_is_finished(http) ? Qtrue : Qfalse;
297
+ }
298
+
299
+
300
+ /**
301
+ * call-seq:
302
+ * parser.execute(req_hash, data, start) -> Integer
303
+ *
304
+ * Takes a Hash and a String of data, parses the String of data filling in the Hash
305
+ * returning an Integer to indicate how much of the data has been read. No matter
306
+ * what the return value, you should call HttpParser#finished? and HttpParser#error?
307
+ * to figure out if it's done parsing or there was an error.
308
+ *
309
+ * This function now throws an exception when there is a parsing error. This makes
310
+ * the logic for working with the parser much easier. You can still test for an
311
+ * error, but now you need to wrap the parser with an exception handling block.
312
+ *
313
+ * The third argument allows for parsing a partial request and then continuing
314
+ * the parsing from that position. It needs all of the original data as well
315
+ * so you have to append to the data buffer as you read.
316
+ */
317
+ VALUE Thin_HttpParser_execute(VALUE self, VALUE req_hash, VALUE data, VALUE start)
318
+ {
319
+ http_parser *http = NULL;
320
+ int from = 0;
321
+ char *dptr = NULL;
322
+ long dlen = 0;
323
+
324
+ DATA_GET(self, http_parser, http);
325
+
326
+ from = FIX2INT(start);
327
+ dptr = RSTRING_PTR(data);
328
+ dlen = RSTRING_LEN(data);
329
+
330
+ if(from >= dlen) {
331
+ rb_raise(eHttpParserError, "%s", "Requested start is after data buffer end.");
332
+ } else {
333
+ http->data = (void *)req_hash;
334
+ thin_http_parser_execute(http, dptr, dlen, from);
335
+
336
+ VALIDATE_MAX_LENGTH(http_parser_nread(http), HEADER);
337
+
338
+ if(thin_http_parser_has_error(http)) {
339
+ rb_raise(eHttpParserError, "%s", "Invalid HTTP format, parsing fails.");
340
+ } else {
341
+ return INT2FIX(http_parser_nread(http));
342
+ }
343
+ }
344
+ }
345
+
346
+
347
+
348
+ /**
349
+ * call-seq:
350
+ * parser.error? -> true/false
351
+ *
352
+ * Tells you whether the parser is in an error state.
353
+ */
354
+ VALUE Thin_HttpParser_has_error(VALUE self)
355
+ {
356
+ http_parser *http = NULL;
357
+ DATA_GET(self, http_parser, http);
358
+
359
+ return thin_http_parser_has_error(http) ? Qtrue : Qfalse;
360
+ }
361
+
362
+
363
+ /**
364
+ * call-seq:
365
+ * parser.finished? -> true/false
366
+ *
367
+ * Tells you whether the parser is finished or not and in a good state.
368
+ */
369
+ VALUE Thin_HttpParser_is_finished(VALUE self)
370
+ {
371
+ http_parser *http = NULL;
372
+ DATA_GET(self, http_parser, http);
373
+
374
+ return thin_http_parser_is_finished(http) ? Qtrue : Qfalse;
375
+ }
376
+
377
+
378
+ /**
379
+ * call-seq:
380
+ * parser.nread -> Integer
381
+ *
382
+ * Returns the amount of data processed so far during this processing cycle. It is
383
+ * set to 0 on initialize or reset calls and is incremented each time execute is called.
384
+ */
385
+ VALUE Thin_HttpParser_nread(VALUE self)
386
+ {
387
+ http_parser *http = NULL;
388
+ DATA_GET(self, http_parser, http);
389
+
390
+ return INT2FIX(http->nread);
391
+ }
392
+
393
+ void Init_thin_parser()
394
+ {
395
+
396
+ mThin = rb_define_module("Thin");
397
+
398
+ DEF_GLOBAL(empty, "");
399
+ DEF_GLOBAL(http_prefix, "HTTP_");
400
+ DEF_GLOBAL(request_method, "REQUEST_METHOD");
401
+ DEF_GLOBAL(request_uri, "REQUEST_URI");
402
+ DEF_GLOBAL(fragment, "FRAGMENT");
403
+ DEF_GLOBAL(query_string, "QUERY_STRING");
404
+ DEF_GLOBAL(http_version, "HTTP_VERSION");
405
+ DEF_GLOBAL(request_path, "REQUEST_PATH");
406
+ DEF_GLOBAL(content_length, "CONTENT_LENGTH");
407
+ DEF_GLOBAL(http_content_length, "HTTP_CONTENT_LENGTH");
408
+ DEF_GLOBAL(content_type, "CONTENT_TYPE");
409
+ DEF_GLOBAL(http_content_type, "HTTP_CONTENT_TYPE");
410
+ DEF_GLOBAL(gateway_interface, "GATEWAY_INTERFACE");
411
+ DEF_GLOBAL(gateway_interface_value, "CGI/1.2");
412
+ DEF_GLOBAL(server_name, "SERVER_NAME");
413
+ DEF_GLOBAL(server_port, "SERVER_PORT");
414
+ DEF_GLOBAL(server_protocol, "SERVER_PROTOCOL");
415
+ DEF_GLOBAL(server_protocol_value, "HTTP/1.1");
416
+ DEF_GLOBAL(http_host, "HTTP_HOST");
417
+ DEF_GLOBAL(port_80, "80");
418
+ DEF_GLOBAL(http_body, "rack.input");
419
+ DEF_GLOBAL(url_scheme, "rack.url_scheme");
420
+ DEF_GLOBAL(url_scheme_value, "http");
421
+ DEF_GLOBAL(script_name, "SCRIPT_NAME");
422
+ DEF_GLOBAL(path_info, "PATH_INFO");
423
+
424
+ eHttpParserError = rb_define_class_under(mThin, "InvalidRequest", rb_eIOError);
425
+
426
+ cHttpParser = rb_define_class_under(mThin, "HttpParser", rb_cObject);
427
+ rb_define_alloc_func(cHttpParser, Thin_HttpParser_alloc);
428
+ rb_define_method(cHttpParser, "initialize", Thin_HttpParser_init,0);
429
+ rb_define_method(cHttpParser, "reset", Thin_HttpParser_reset,0);
430
+ rb_define_method(cHttpParser, "finish", Thin_HttpParser_finish,0);
431
+ rb_define_method(cHttpParser, "execute", Thin_HttpParser_execute,3);
432
+ rb_define_method(cHttpParser, "error?", Thin_HttpParser_has_error,0);
433
+ rb_define_method(cHttpParser, "finished?", Thin_HttpParser_is_finished,0);
434
+ rb_define_method(cHttpParser, "nread", Thin_HttpParser_nread,0);
435
+ }