kcar 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,14 @@
1
+ # -*- encoding: binary -*-
2
+ require 'mkmf'
3
+
4
+ dir_config("kcar")
5
+
6
+ have_macro("SIZEOF_OFF_T", "ruby.h") or check_sizeof("off_t", "sys/types.h")
7
+ have_macro("SIZEOF_LONG", "ruby.h") or check_sizeof("long", "sys/types.h")
8
+ have_func("rb_str_set_len", "ruby.h")
9
+ have_func("rb_str_modify", "ruby.h")
10
+
11
+ # -fPIC is needed for Rubinius, MRI already uses it regardless
12
+ with_cflags($CFLAGS + " -fPIC ") do
13
+ create_makefile("kcar_ext")
14
+ end
data/ext/kcar/kcar.rl ADDED
@@ -0,0 +1,656 @@
1
+ /**
2
+ * Copyright (c) 2009, 2010 Eric Wong (all bugs are Eric's fault)
3
+ * Copyright (c) 2005 Zed A. Shaw
4
+ * You can redistribute it and/or modify it under the same terms as Ruby.
5
+ */
6
+ #include "ruby.h"
7
+ #include "ext_help.h"
8
+ #include <assert.h>
9
+ #include <stdlib.h>
10
+ #include <string.h>
11
+ #include <sys/types.h>
12
+ #include "c_util.h"
13
+
14
+ static VALUE eParserError;
15
+ static ID id_sq, id_sq_set;
16
+
17
+ /** Defines common length and error messages for input length validation. */
18
+ #define DEF_MAX_LENGTH(N, length) \
19
+ static const size_t MAX_##N##_LENGTH = length; \
20
+ static const char MAX_##N##_LENGTH_ERR[] = \
21
+ "HTTP element " # N " is longer than the " # length " allowed length."
22
+
23
+ /**
24
+ * Validates the max length of given input and throws an ParserError
25
+ * exception if over.
26
+ */
27
+ #define VALIDATE_MAX_LENGTH(len, N) do { \
28
+ if (len > MAX_##N##_LENGTH) \
29
+ rb_raise(eParserError, MAX_##N##_LENGTH_ERR); \
30
+ } while (0)
31
+
32
+ /* Defines the maximum allowed lengths for various input elements.*/
33
+ DEF_MAX_LENGTH(FIELD_NAME, 256);
34
+ DEF_MAX_LENGTH(FIELD_VALUE, 80 * 1024);
35
+ DEF_MAX_LENGTH(HEADER, (1024 * (80 + 32)));
36
+ DEF_MAX_LENGTH(REASON, 256);
37
+
38
+ #define UH_FL_CHUNKED 0x1
39
+ #define UH_FL_HASBODY 0x2
40
+ #define UH_FL_INBODY 0x4
41
+ #define UH_FL_HASTRAILER 0x8
42
+ #define UH_FL_INTRAILER 0x10
43
+ #define UH_FL_INCHUNK 0x20
44
+ #define UH_FL_KEEPALIVE 0x40
45
+
46
+ struct http_parser {
47
+ int cs; /* Ragel internal state */
48
+ unsigned int flags;
49
+ size_t mark;
50
+ size_t offset;
51
+ union { /* these 2 fields don't nest */
52
+ size_t field;
53
+ size_t query;
54
+ } start;
55
+ union {
56
+ size_t field_len; /* only used during header processing */
57
+ size_t dest_offset; /* only used during body processing */
58
+ } s;
59
+ VALUE cont; /* Qfalse: unset, Qnil: ignored header, T_STRING: append */
60
+ VALUE status; /* String or Qnil */
61
+ union {
62
+ off_t content;
63
+ off_t chunk;
64
+ } len;
65
+ };
66
+
67
+ #define REMAINING (unsigned long)(pe - p)
68
+ #define LEN(AT, FPC) (FPC - buffer - hp->AT)
69
+ #define MARK(M,FPC) (hp->M = (FPC) - buffer)
70
+ #define PTR_TO(F) (buffer + hp->F)
71
+ #define STR_NEW(M,FPC) rb_str_new(PTR_TO(M), LEN(M, FPC))
72
+
73
+ #define HP_FL_TEST(hp,fl) ((hp)->flags & (UH_FL_##fl))
74
+ #define HP_FL_SET(hp,fl) ((hp)->flags |= (UH_FL_##fl))
75
+ #define HP_FL_UNSET(hp,fl) ((hp)->flags &= ~(UH_FL_##fl))
76
+ #define HP_FL_ALL(hp,fl) (HP_FL_TEST(hp, fl) == (UH_FL_##fl))
77
+
78
+ static void finalize_header(struct http_parser *hp)
79
+ {
80
+ if ((HP_FL_TEST(hp, HASTRAILER) && ! HP_FL_TEST(hp, CHUNKED)))
81
+ rb_raise(eParserError, "trailer but not chunked");
82
+ }
83
+
84
+ /*
85
+ * handles values of the "Connection:" header, keepalive is implied
86
+ * for HTTP/1.1 but needs to be explicitly enabled with HTTP/1.0
87
+ * Additionally, we require GET/HEAD requests to support keepalive.
88
+ */
89
+ static void hp_keepalive_connection(struct http_parser *hp, VALUE val)
90
+ {
91
+ /* REQUEST_METHOD is always set before any headers */
92
+ if (STR_CSTR_CASE_EQ(val, "keep-alive")) {
93
+ /* basically have HTTP/1.0 masquerade as HTTP/1.1+ */
94
+ HP_FL_SET(hp, KEEPALIVE);
95
+ } else if (STR_CSTR_CASE_EQ(val, "close")) {
96
+ /*
97
+ * it doesn't matter what HTTP version or request method we have,
98
+ * if a server says "Connection: close", we disable keepalive
99
+ */
100
+ HP_FL_UNSET(hp, KEEPALIVE);
101
+ } else {
102
+ /*
103
+ * server could've sent anything, ignore it for now. Maybe
104
+ * "HP_FL_UNSET(hp, KEEPALIVE);" just in case?
105
+ * Raising an exception might be too mean...
106
+ */
107
+ }
108
+ }
109
+
110
+ static void
111
+ http_version(struct http_parser *hp, VALUE hdr, const char *ptr, size_t len)
112
+ {
113
+ if (CONST_MEM_EQ("HTTP/1.1", ptr, len)) {
114
+ /* HTTP/1.1 implies keepalive unless "Connection: close" is set */
115
+ HP_FL_SET(hp, KEEPALIVE);
116
+ }
117
+ }
118
+
119
+ static void
120
+ status_phrase(struct http_parser *hp, VALUE hdr, const char *ptr, size_t len)
121
+ {
122
+ long nr;
123
+
124
+ hp->status = rb_str_new(ptr, len);
125
+
126
+ /* RSTRING_PTR is null terminated, ptr is not */
127
+ nr = strtol(RSTRING_PTR(hp->status), NULL, 10);
128
+
129
+ if (nr < 100 || nr > 999)
130
+ rb_raise(eParserError, "invalid status: %s", RSTRING_PTR(hp->status));
131
+
132
+ if ( !((nr >= 100 && nr <= 199) || nr == 204 || nr == 304) )
133
+ HP_FL_SET(hp, HASBODY);
134
+ }
135
+
136
+ static inline void invalid_if_trailer(struct http_parser *hp)
137
+ {
138
+ if (HP_FL_TEST(hp, INTRAILER))
139
+ rb_raise(eParserError, "invalid Trailer");
140
+ }
141
+
142
+ static void write_cont_value(struct http_parser *hp,
143
+ char *buffer, const char *p)
144
+ {
145
+ char *vptr;
146
+
147
+ if (hp->cont == Qfalse)
148
+ rb_raise(eParserError, "invalid continuation line");
149
+
150
+ if (NIL_P(hp->cont))
151
+ return; /* we're ignoring this header (probably Status:) */
152
+
153
+ assert(TYPE(hp->cont) == T_STRING && "continuation line is not a string");
154
+ assert(hp->mark > 0 && "impossible continuation line offset");
155
+
156
+ if (LEN(mark, p) == 0)
157
+ return;
158
+
159
+ if (RSTRING_LEN(hp->cont) > 0)
160
+ --hp->mark;
161
+
162
+ vptr = PTR_TO(mark);
163
+
164
+ if (RSTRING_LEN(hp->cont) > 0) {
165
+ assert((' ' == *vptr || '\t' == *vptr) && "invalid leading white space");
166
+ *vptr = ' ';
167
+ }
168
+ rb_str_buf_cat(hp->cont, vptr, LEN(mark, p));
169
+ }
170
+
171
+ static void write_value(VALUE hdr, struct http_parser *hp,
172
+ const char *buffer, const char *p)
173
+ {
174
+ VALUE f, v;
175
+ VALUE hclass;
176
+ const char *fptr = PTR_TO(start.field);
177
+ long flen = hp->s.field_len;
178
+ const char *vptr;
179
+ long vlen;
180
+
181
+ /* Rack does not like Status headers, so we never send them */
182
+ if (CSTR_CASE_EQ(fptr, flen, "status")) {
183
+ hp->cont = Qnil;
184
+ return;
185
+ }
186
+
187
+ vptr = PTR_TO(mark);
188
+ vlen = LEN(mark, p);
189
+ VALIDATE_MAX_LENGTH(vlen, FIELD_VALUE);
190
+ VALIDATE_MAX_LENGTH(flen, FIELD_NAME);
191
+ f = rb_str_new(fptr, flen);
192
+ v = rb_str_new(vptr, vlen);
193
+
194
+ /* needs more tests for error-checking here */
195
+ /*
196
+ * TODO:
197
+ * some of these tests might be too strict for real-world HTTP servers,
198
+ * report real-world examples as we find them:
199
+ */
200
+ if (STR_CSTR_CASE_EQ(f, "connection")) {
201
+ hp_keepalive_connection(hp, v);
202
+ } else if (STR_CSTR_CASE_EQ(f, "content-length")) {
203
+ if (! HP_FL_TEST(hp, HASBODY))
204
+ rb_raise(eParserError, "Content-Length with no body expected");
205
+ if (HP_FL_TEST(hp, CHUNKED))
206
+ rb_raise(eParserError,
207
+ "Content-Length when chunked Transfer-Encoding is set");
208
+ hp->len.content = parse_length(vptr, vlen);
209
+
210
+ if (hp->len.content < 0)
211
+ rb_raise(eParserError, "invalid Content-Length");
212
+
213
+ invalid_if_trailer(hp);
214
+ } else if (STR_CSTR_CASE_EQ(f, "transfer-encoding")) {
215
+ if (STR_CSTR_CASE_EQ(v, "chunked")) {
216
+ if (! HP_FL_TEST(hp, HASBODY))
217
+ rb_raise(eParserError,
218
+ "chunked Transfer-Encoding with no body expected");
219
+ if (hp->len.content >= 0)
220
+ rb_raise(eParserError,
221
+ "chunked Transfer-Encoding when Content-Length is set");
222
+
223
+ hp->len.chunk = 0;
224
+ HP_FL_SET(hp, CHUNKED);
225
+ }
226
+ invalid_if_trailer(hp);
227
+ } else if (STR_CSTR_CASE_EQ(f, "trailer")) {
228
+ if (! HP_FL_TEST(hp, HASBODY))
229
+ rb_raise(eParserError, "trailer with no body");
230
+ HP_FL_SET(hp, HASTRAILER);
231
+ invalid_if_trailer(hp);
232
+ }
233
+
234
+ hclass = CLASS_OF(hdr);
235
+ if (hclass == rb_cArray) {
236
+ rb_ary_push(hdr, rb_ary_new3(2, f, v));
237
+ hp->cont = v;
238
+ } else {
239
+ /* hash-ish, try rb_hash_* first and fall back to slow rb_funcall */
240
+ VALUE e;
241
+
242
+ /* try to read the existing value */
243
+ if (hclass == rb_cHash)
244
+ e = rb_hash_aref(hdr, f);
245
+ else
246
+ e = rb_funcall(hdr, id_sq, 1, f);
247
+
248
+ if (NIL_P(e)) {
249
+ /* new value, freeze it since it speeds up MRI slightly */
250
+ OBJ_FREEZE(f);
251
+
252
+ if (hclass == rb_cHash)
253
+ rb_hash_aset(hdr, f, v);
254
+ else
255
+ rb_funcall(hdr, id_sq_set, 2, f, v);
256
+
257
+ hp->cont = v;
258
+ } else {
259
+ /*
260
+ * existing value, append to it, Rack 1.x uses newlines to represent
261
+ * repeated cookies:
262
+ * { 'Set-Cookie' => "a=b\nc=d" }
263
+ * becomes:
264
+ * "Set-Cookie: a=b\r\nSet-Cookie: c=d\r\n"
265
+ */
266
+ rb_str_buf_cat(e, "\n", 1);
267
+ hp->cont = rb_str_buf_append(e, v);
268
+ }
269
+ }
270
+ }
271
+
272
+ /** Machine **/
273
+
274
+ %%{
275
+ machine http_parser;
276
+
277
+ action mark {MARK(mark, fpc); }
278
+
279
+ action start_field { MARK(start.field, fpc); }
280
+ action write_field { hp->s.field_len = LEN(start.field, fpc); }
281
+ action start_value { MARK(mark, fpc); }
282
+ action write_value { write_value(hdr, hp, buffer, fpc); }
283
+ action write_cont_value { write_cont_value(hp, buffer, fpc); }
284
+ action http_version { http_version(hp, hdr, PTR_TO(mark), LEN(mark, fpc)); }
285
+ action status_phrase { status_phrase(hp, hdr, PTR_TO(mark), LEN(mark, fpc)); }
286
+
287
+ action add_to_chunk_size {
288
+ hp->len.chunk = step_incr(hp->len.chunk, fc, 16);
289
+ if (hp->len.chunk < 0)
290
+ rb_raise(eParserError, "invalid chunk size");
291
+ }
292
+ action header_done {
293
+ finalize_header(hp);
294
+ cs = http_parser_first_final;
295
+
296
+ if (HP_FL_TEST(hp, CHUNKED))
297
+ cs = http_parser_en_ChunkedBody;
298
+
299
+ /*
300
+ * go back to Ruby so we can call the Rack application, we'll reenter
301
+ * the parser iff the body needs to be processed.
302
+ */
303
+ goto post_exec;
304
+ }
305
+
306
+ action end_trailers {
307
+ cs = http_parser_first_final;
308
+ goto post_exec;
309
+ }
310
+
311
+ action end_chunked_body {
312
+ HP_FL_SET(hp, INTRAILER);
313
+ cs = http_parser_en_Trailers;
314
+ ++p;
315
+ assert(p <= pe && "buffer overflow after chunked body");
316
+ goto post_exec;
317
+ }
318
+
319
+ action skip_chunk_data {
320
+ skip_chunk_data_hack: {
321
+ size_t nr = MIN((size_t)hp->len.chunk, REMAINING);
322
+ memcpy(RSTRING_PTR(hdr) + hp->s.dest_offset, fpc, nr);
323
+ hp->s.dest_offset += nr;
324
+ hp->len.chunk -= nr;
325
+ p += nr;
326
+ assert(hp->len.chunk >= 0 && "negative chunk length");
327
+ if ((size_t)hp->len.chunk > REMAINING) {
328
+ HP_FL_SET(hp, INCHUNK);
329
+ goto post_exec;
330
+ } else {
331
+ fhold;
332
+ fgoto chunk_end;
333
+ }
334
+ }}
335
+
336
+ include kcar_http_common "kcar_http_common.rl";
337
+ }%%
338
+
339
+ /** Data **/
340
+ %% write data;
341
+
342
+ static void http_parser_init(struct http_parser *hp)
343
+ {
344
+ int cs = 0;
345
+ memset(hp, 0, sizeof(struct http_parser));
346
+ hp->cont = Qfalse; /* zero on MRI, should be optimized away by above */
347
+ hp->status = Qnil;
348
+ hp->len.content = -1;
349
+ %% write init;
350
+ hp->cs = cs;
351
+ }
352
+
353
+ /** exec **/
354
+ static void http_parser_execute(struct http_parser *hp,
355
+ VALUE hdr, char *buffer, size_t len)
356
+ {
357
+ const char *p, *pe;
358
+ int cs = hp->cs;
359
+ size_t off = hp->offset;
360
+
361
+ if (cs == http_parser_first_final)
362
+ return;
363
+
364
+ assert(off <= len && "offset past end of buffer");
365
+
366
+ p = buffer+off;
367
+ pe = buffer+len;
368
+
369
+ assert((void *)(pe - p) == (void *)(len - off) &&
370
+ "pointers aren't same distance");
371
+
372
+ if (HP_FL_TEST(hp, INCHUNK)) {
373
+ HP_FL_UNSET(hp, INCHUNK);
374
+ goto skip_chunk_data_hack;
375
+ }
376
+ %% write exec;
377
+ post_exec: /* "_out:" also goes here */
378
+ if (hp->cs != http_parser_error)
379
+ hp->cs = cs;
380
+ hp->offset = p - buffer;
381
+
382
+ assert(p <= pe && "buffer overflow after parsing execute");
383
+ assert(hp->offset <= len && "offset longer than length");
384
+ }
385
+
386
+ static struct http_parser *data_get(VALUE self)
387
+ {
388
+ struct http_parser *hp;
389
+
390
+ Data_Get_Struct(self, struct http_parser, hp);
391
+ assert(hp && "failed to extract http_parser struct");
392
+ return hp;
393
+ }
394
+
395
+ static void mark(void *ptr)
396
+ {
397
+ struct http_parser *hp = ptr;
398
+
399
+ rb_gc_mark(hp->cont);
400
+ rb_gc_mark(hp->status);
401
+ }
402
+
403
+ static VALUE alloc(VALUE klass)
404
+ {
405
+ struct http_parser *hp;
406
+ return Data_Make_Struct(klass, struct http_parser, mark, -1, hp);
407
+ }
408
+
409
+ /**
410
+ * call-seq:
411
+ * Kcar::Parser.new => parser
412
+ *
413
+ * Creates a new parser.
414
+ *
415
+ * Document-method: reset
416
+ *
417
+ * call-seq:
418
+ * parser.reset => parser
419
+ *
420
+ * Resets the parser so it can be reused by another client
421
+ */
422
+ static VALUE initialize(VALUE self)
423
+ {
424
+ http_parser_init(data_get(self));
425
+
426
+ return self;
427
+ }
428
+
429
+ static void advance_str(VALUE str, off_t nr)
430
+ {
431
+ long len = RSTRING_LEN(str);
432
+
433
+ if (len == 0)
434
+ return;
435
+
436
+ rb_str_modify(str);
437
+
438
+ assert(nr <= len && "trying to advance past end of buffer");
439
+ len -= nr;
440
+ if (len > 0) /* unlikely, len is usually 0 */
441
+ memmove(RSTRING_PTR(str), RSTRING_PTR(str) + nr, len);
442
+ rb_str_set_len(str, len);
443
+ }
444
+
445
+ /**
446
+ * call-seq:
447
+ * parser.body_bytes_left => nil or Integer
448
+ *
449
+ * Returns the number of bytes left to run through Parser#filter_body.
450
+ * This will initially be the value of the "Content-Length" HTTP header
451
+ * after header parsing is complete and will decrease in value as
452
+ * Parser#filter_body is called for each chunk. This should return
453
+ * zero for responses with no body.
454
+ *
455
+ * This will return nil on "Transfer-Encoding: chunked" responses as
456
+ * well as HTTP/1.0 responses where Content-Length is not set
457
+ */
458
+ static VALUE body_bytes_left(VALUE self)
459
+ {
460
+ struct http_parser *hp = data_get(self);
461
+
462
+ if (HP_FL_TEST(hp, CHUNKED))
463
+ return Qnil;
464
+ if (hp->len.content >= 0)
465
+ return OFFT2NUM(hp->len.content);
466
+
467
+ return Qnil;
468
+ }
469
+
470
+ static VALUE chunked(VALUE self)
471
+ {
472
+ struct http_parser *hp = data_get(self);
473
+
474
+ return HP_FL_TEST(hp, CHUNKED) ? Qtrue : Qfalse;
475
+ }
476
+
477
+ /**
478
+ * Document-method: headers
479
+ * call-seq:
480
+ * parser.headers(hdr, data) => hdr or nil
481
+ *
482
+ * Takes a Hash and a String of data, parses the String of data filling
483
+ * in the Hash returning the Hash if parsing is finished, nil otherwise
484
+ * When returning the hdr Hash, it may modify data to point to where
485
+ * body processing should begin.
486
+ *
487
+ * Raises ParserError if there are parsing errors.
488
+ */
489
+ static VALUE headers(VALUE self, VALUE hdr, VALUE data)
490
+ {
491
+ struct http_parser *hp = data_get(self);
492
+
493
+ rb_str_update(data);
494
+
495
+ http_parser_execute(hp, hdr, RSTRING_PTR(data), RSTRING_LEN(data));
496
+ VALIDATE_MAX_LENGTH(hp->offset, HEADER);
497
+
498
+ if (hp->cs == http_parser_first_final ||
499
+ hp->cs == http_parser_en_ChunkedBody) {
500
+ advance_str(data, hp->offset + 1);
501
+ hp->offset = 0;
502
+ if (HP_FL_TEST(hp, INTRAILER))
503
+ return hdr;
504
+ else
505
+ return rb_ary_new3(2, hp->status, hdr);
506
+ }
507
+
508
+ if (hp->cs == http_parser_error)
509
+ rb_raise(eParserError, "Invalid HTTP format, parsing fails.");
510
+
511
+ return Qnil;
512
+ }
513
+
514
+ static int chunked_eof(struct http_parser *hp)
515
+ {
516
+ return ((hp->cs == http_parser_first_final) || HP_FL_TEST(hp, INTRAILER));
517
+ }
518
+
519
+ /**
520
+ * call-seq:
521
+ * parser.body_eof? => true or false
522
+ *
523
+ * Detects if we're done filtering the body or not. This can be used
524
+ * to detect when to stop calling Parser#filter_body.
525
+ */
526
+ static VALUE body_eof(VALUE self)
527
+ {
528
+ struct http_parser *hp = data_get(self);
529
+
530
+ if (HP_FL_TEST(hp, CHUNKED))
531
+ return chunked_eof(hp) ? Qtrue : Qfalse;
532
+
533
+ if (! HP_FL_TEST(hp, HASBODY))
534
+ return Qtrue;
535
+
536
+ return hp->len.content == 0 ? Qtrue : Qfalse;
537
+ }
538
+
539
+ /**
540
+ * call-seq:
541
+ * parser.keepalive? => true or false
542
+ *
543
+ * This should be used to detect if a request can really handle
544
+ * keepalives and pipelining. Currently, the rules are:
545
+ *
546
+ * 1. MUST be HTTP/1.1 +or+ HTTP/1.0 with "Connection: keep-alive"
547
+ * 2. MUST NOT have "Connection: close" set
548
+ * 3. If there is a response body, either a) Content-Length is set
549
+ * or b) chunked encoding is used
550
+ */
551
+ static VALUE keepalive(VALUE self)
552
+ {
553
+ struct http_parser *hp = data_get(self);
554
+
555
+ if (HP_FL_ALL(hp, KEEPALIVE)) {
556
+ if ( HP_FL_TEST(hp, HASBODY) ) {
557
+ if (HP_FL_TEST(hp, CHUNKED) || (hp->len.content >= 0))
558
+ return Qtrue;
559
+
560
+ /* unknown Content-Length and not chunked, we must assume close */
561
+ return Qfalse;
562
+ } else {
563
+ /* 100 Continue, 304 Not Modified, etc... */
564
+ return Qtrue;
565
+ }
566
+ }
567
+ return Qfalse;
568
+ }
569
+
570
+ /**
571
+ * call-seq:
572
+ * parser.filter_body(buf, data) => nil/data
573
+ *
574
+ * Takes a String of +data+, will modify data if dechunking is done.
575
+ * Returns +nil+ if there is more data left to process. Returns
576
+ * +data+ if body processing is complete. When returning +data+,
577
+ * it may modify +data+ so the start of the string points to where
578
+ * the body ended so that trailer processing can begin.
579
+ *
580
+ * Raises ParserError if there are dechunking errors.
581
+ * Basically this is a glorified memcpy(3) that copies +data+
582
+ * into +buf+ while filtering it through the dechunker.
583
+ */
584
+ static VALUE filter_body(VALUE self, VALUE buf, VALUE data)
585
+ {
586
+ struct http_parser *hp = data_get(self);
587
+ char *dptr;
588
+ long dlen;
589
+
590
+ rb_str_update(data);
591
+ dptr = RSTRING_PTR(data);
592
+ dlen = RSTRING_LEN(data);
593
+
594
+ StringValue(buf);
595
+ rb_str_resize(buf, dlen); /* we can never copy more than dlen bytes */
596
+ OBJ_TAINT(buf); /* keep weirdo $SAFE users happy */
597
+
598
+ if (!HP_FL_TEST(hp, CHUNKED))
599
+ rb_raise(rb_eRuntimeError, "filter_body is only for chunked bodies");
600
+
601
+ if (!chunked_eof(hp)) {
602
+ hp->s.dest_offset = 0;
603
+ http_parser_execute(hp, buf, dptr, dlen);
604
+ if (hp->cs == http_parser_error)
605
+ rb_raise(eParserError, "Invalid HTTP format, parsing fails.");
606
+
607
+ assert(hp->s.dest_offset <= hp->offset &&
608
+ "destination buffer overflow");
609
+ advance_str(data, hp->offset);
610
+ rb_str_set_len(buf, hp->s.dest_offset);
611
+
612
+ if (RSTRING_LEN(buf) == 0 && chunked_eof(hp)) {
613
+ assert(hp->len.chunk == 0 && "chunk at EOF but more to parse");
614
+ } else {
615
+ data = Qnil;
616
+ }
617
+ }
618
+ hp->offset = 0; /* for trailer parsing */
619
+ return data;
620
+ }
621
+
622
+ void Init_kcar_ext(void)
623
+ {
624
+ VALUE mKcar = rb_define_module("Kcar");
625
+ VALUE cParser = rb_define_class_under(mKcar, "Parser", rb_cObject);
626
+
627
+ eParserError = rb_define_class_under(mKcar, "ParserError", rb_eIOError);
628
+
629
+ rb_define_alloc_func(cParser, alloc);
630
+ rb_define_method(cParser, "initialize", initialize, 0);
631
+ rb_define_method(cParser, "reset", initialize, 0);
632
+ rb_define_method(cParser, "headers", headers, 2);
633
+ rb_define_method(cParser, "trailers", headers, 2);
634
+ rb_define_method(cParser, "filter_body", filter_body, 2);
635
+ rb_define_method(cParser, "body_bytes_left", body_bytes_left, 0);
636
+ rb_define_method(cParser, "body_eof?", body_eof, 0);
637
+ rb_define_method(cParser, "keepalive?", keepalive, 0);
638
+ rb_define_method(cParser, "chunked?", chunked, 0);
639
+
640
+ /*
641
+ * The maximum size a single chunk when using chunked transfer encoding.
642
+ * This is only a theoretical maximum used to detect errors in clients,
643
+ * it is highly unlikely to encounter clients that send more than
644
+ * several kilobytes at once.
645
+ */
646
+ rb_define_const(cParser, "CHUNK_MAX", OFFT2NUM(UH_OFF_T_MAX));
647
+
648
+ /*
649
+ * The maximum size of the body as specified by Content-Length.
650
+ * This is only a theoretical maximum, the actual limit is subject
651
+ * to the limits of the file system used for +Dir.tmpdir+.
652
+ */
653
+ rb_define_const(cParser, "LENGTH_MAX", OFFT2NUM(UH_OFF_T_MAX));
654
+ id_sq = rb_intern("[]");
655
+ id_sq_set = rb_intern("[]=");
656
+ }