http_parser.rb 0.5.0-x86-mswin32-60 → 0.5.1-x86-mswin32-60
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Gemfile +2 -0
- data/Gemfile.lock +32 -0
- data/README.md +44 -4
- data/ext/ruby_http_parser/extconf.rb +8 -0
- data/ext/ruby_http_parser/org/ruby_http_parser/RubyHttpParser.java +1 -1
- data/ext/ruby_http_parser/ruby_http_parser.c +15 -15
- data/ext/ruby_http_parser/vendor/http-parser/http_parser.c +63 -51
- data/ext/ruby_http_parser/vendor/http-parser/http_parser.h +16 -2
- data/ext/ruby_http_parser/vendor/http-parser/test.c +117 -6
- data/http_parser.rb.gemspec +5 -1
- data/spec/parser_spec.rb +29 -0
- data/spec/spec_helper.rb +0 -1
- data/spec/support/requests.json +21 -2
- data/spec/support/responses.json +46 -3
- data/tasks/compile.rake +1 -0
- data/tasks/fixtures.rake +65 -0
- metadata +39 -14
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
http_parser.rb (0.5.1)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: http://rubygems.org/
|
8
|
+
specs:
|
9
|
+
diff-lcs (1.1.2)
|
10
|
+
json (1.4.6)
|
11
|
+
rake (0.8.7)
|
12
|
+
rake-compiler (0.7.0)
|
13
|
+
rake (>= 0.8.3, < 0.9)
|
14
|
+
rspec (2.0.1)
|
15
|
+
rspec-core (~> 2.0.1)
|
16
|
+
rspec-expectations (~> 2.0.1)
|
17
|
+
rspec-mocks (~> 2.0.1)
|
18
|
+
rspec-core (2.0.1)
|
19
|
+
rspec-expectations (2.0.1)
|
20
|
+
diff-lcs (>= 1.1.2)
|
21
|
+
rspec-mocks (2.0.1)
|
22
|
+
rspec-core (~> 2.0.1)
|
23
|
+
rspec-expectations (~> 2.0.1)
|
24
|
+
|
25
|
+
PLATFORMS
|
26
|
+
ruby
|
27
|
+
|
28
|
+
DEPENDENCIES
|
29
|
+
http_parser.rb!
|
30
|
+
json (>= 1.4.6)
|
31
|
+
rake-compiler (>= 0.7.0)
|
32
|
+
rspec (>= 2.0.1)
|
data/README.md
CHANGED
@@ -20,14 +20,15 @@ This gem aims to work on all major Ruby platforms, including:
|
|
20
20
|
|
21
21
|
parser = Http::Parser.new
|
22
22
|
|
23
|
-
parser.on_headers_complete = proc do
|
24
|
-
p parser.http_method
|
23
|
+
parser.on_headers_complete = proc do
|
25
24
|
p parser.http_version
|
26
25
|
|
27
|
-
p parser.
|
26
|
+
p parser.http_method # for requests
|
27
|
+
p parser.request_url
|
28
|
+
|
28
29
|
p parser.status_code # for responses
|
29
30
|
|
30
|
-
p headers
|
31
|
+
p parser.headers
|
31
32
|
end
|
32
33
|
|
33
34
|
parser.on_body = proc do |chunk|
|
@@ -43,3 +44,42 @@ This gem aims to work on all major Ruby platforms, including:
|
|
43
44
|
# Feed raw data from the socket to the parser
|
44
45
|
parser << raw_data
|
45
46
|
|
47
|
+
## Advanced Usage
|
48
|
+
|
49
|
+
### Accept callbacks on an object
|
50
|
+
|
51
|
+
module MyHttpConnection
|
52
|
+
def connection_completed
|
53
|
+
@parser = Http::Parser.new(self)
|
54
|
+
end
|
55
|
+
|
56
|
+
def receive_data(data)
|
57
|
+
@parser << data
|
58
|
+
end
|
59
|
+
|
60
|
+
def on_message_begin
|
61
|
+
@headers = nil
|
62
|
+
@body = ''
|
63
|
+
end
|
64
|
+
|
65
|
+
def on_headers_complete
|
66
|
+
@headers = @parser.headers
|
67
|
+
end
|
68
|
+
|
69
|
+
def on_body(chunk)
|
70
|
+
@body << chunk
|
71
|
+
end
|
72
|
+
|
73
|
+
def on_message_complete
|
74
|
+
p [@headers, @body]
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
### Stop parsing after headers
|
79
|
+
|
80
|
+
parser = Http::Parser.new
|
81
|
+
parser.on_headers_complete = proc{ :stop }
|
82
|
+
|
83
|
+
offset = parser << request_data
|
84
|
+
body = request_data[offset..-1]
|
85
|
+
|
@@ -1,5 +1,13 @@
|
|
1
1
|
require 'mkmf'
|
2
2
|
|
3
|
+
# check out code if it hasn't been already
|
4
|
+
if Dir[File.expand_path('../vendor/http-parser/*', __FILE__)].empty?
|
5
|
+
Dir.chdir(File.expand_path('../../../', __FILE__)) do
|
6
|
+
xsystem 'git submodule init'
|
7
|
+
xsystem 'git submodule update'
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
3
11
|
# mongrel and http-parser both define http_parser_(init|execute), so we
|
4
12
|
# rename functions in http-parser before using them.
|
5
13
|
vendor_dir = File.expand_path('../vendor/http-parser/', __FILE__)
|
@@ -120,7 +120,7 @@ public class RubyHttpParser extends RubyObject {
|
|
120
120
|
if (_current_header == null)
|
121
121
|
_current_header = new String(data);
|
122
122
|
else
|
123
|
-
_current_header.concat(new String(data));
|
123
|
+
_current_header = _current_header.concat(new String(data));
|
124
124
|
|
125
125
|
return 0;
|
126
126
|
}
|
@@ -30,10 +30,10 @@ typedef struct ParserWrapper {
|
|
30
30
|
|
31
31
|
VALUE callback_object;
|
32
32
|
VALUE stopped;
|
33
|
+
VALUE completed;
|
33
34
|
|
34
35
|
VALUE last_field_name;
|
35
|
-
|
36
|
-
size_t last_field_name_length;
|
36
|
+
VALUE curr_field_name;
|
37
37
|
|
38
38
|
enum ryah_http_parser_type type;
|
39
39
|
} ParserWrapper;
|
@@ -50,10 +50,10 @@ void ParserWrapper_init(ParserWrapper *wrapper) {
|
|
50
50
|
wrapper->fragment = Qnil;
|
51
51
|
|
52
52
|
wrapper->headers = Qnil;
|
53
|
+
wrapper->completed = Qfalse;
|
53
54
|
|
54
55
|
wrapper->last_field_name = Qnil;
|
55
|
-
wrapper->
|
56
|
-
wrapper->last_field_name_length = 0;
|
56
|
+
wrapper->curr_field_name = Qnil;
|
57
57
|
}
|
58
58
|
|
59
59
|
void ParserWrapper_mark(void *data) {
|
@@ -70,6 +70,7 @@ void ParserWrapper_mark(void *data) {
|
|
70
70
|
rb_gc_mark_maybe(wrapper->on_message_complete);
|
71
71
|
rb_gc_mark_maybe(wrapper->callback_object);
|
72
72
|
rb_gc_mark_maybe(wrapper->last_field_name);
|
73
|
+
rb_gc_mark_maybe(wrapper->curr_field_name);
|
73
74
|
}
|
74
75
|
}
|
75
76
|
|
@@ -147,13 +148,11 @@ int on_fragment(ryah_http_parser *parser, const char *at, size_t length) {
|
|
147
148
|
int on_header_field(ryah_http_parser *parser, const char *at, size_t length) {
|
148
149
|
GET_WRAPPER(wrapper, parser);
|
149
150
|
|
150
|
-
wrapper->
|
151
|
-
|
152
|
-
|
153
|
-
wrapper->last_field_name_at = at;
|
154
|
-
wrapper->last_field_name_length = length;
|
151
|
+
if (wrapper->curr_field_name == Qnil) {
|
152
|
+
wrapper->last_field_name = Qnil;
|
153
|
+
wrapper->curr_field_name = rb_str_new(at, length);
|
155
154
|
} else {
|
156
|
-
wrapper->
|
155
|
+
rb_str_cat(wrapper->curr_field_name, at, length);
|
157
156
|
}
|
158
157
|
|
159
158
|
return 0;
|
@@ -163,15 +162,13 @@ int on_header_value(ryah_http_parser *parser, const char *at, size_t length) {
|
|
163
162
|
GET_WRAPPER(wrapper, parser);
|
164
163
|
|
165
164
|
if (wrapper->last_field_name == Qnil) {
|
166
|
-
wrapper->last_field_name =
|
165
|
+
wrapper->last_field_name = wrapper->curr_field_name;
|
166
|
+
wrapper->curr_field_name = Qnil;
|
167
167
|
|
168
168
|
VALUE val = rb_hash_aref(wrapper->headers, wrapper->last_field_name);
|
169
169
|
if (val != Qnil) {
|
170
170
|
rb_str_cat(val, ", ", 2);
|
171
171
|
}
|
172
|
-
|
173
|
-
wrapper->last_field_name_at = NULL;
|
174
|
-
wrapper->last_field_name_length = 0;
|
175
172
|
}
|
176
173
|
|
177
174
|
HASH_CAT(wrapper->headers, wrapper->last_field_name, at, length);
|
@@ -221,6 +218,7 @@ int on_message_complete(ryah_http_parser *parser) {
|
|
221
218
|
GET_WRAPPER(wrapper, parser);
|
222
219
|
|
223
220
|
VALUE ret = Qnil;
|
221
|
+
wrapper->completed = Qtrue;
|
224
222
|
|
225
223
|
if (wrapper->callback_object != Qnil && rb_respond_to(wrapper->callback_object, Ion_message_complete)) {
|
226
224
|
ret = rb_funcall(wrapper->callback_object, Ion_message_complete, 0);
|
@@ -290,6 +288,8 @@ VALUE Parser_initialize(int argc, VALUE *argv, VALUE self) {
|
|
290
288
|
|
291
289
|
VALUE Parser_execute(VALUE self, VALUE data) {
|
292
290
|
ParserWrapper *wrapper = NULL;
|
291
|
+
|
292
|
+
Check_Type(data, T_STRING);
|
293
293
|
char *ptr = RSTRING_PTR(data);
|
294
294
|
long len = RSTRING_LEN(data);
|
295
295
|
|
@@ -301,7 +301,7 @@ VALUE Parser_execute(VALUE self, VALUE data) {
|
|
301
301
|
if (wrapper->parser.upgrade) {
|
302
302
|
// upgrade request
|
303
303
|
} else if (nparsed != len) {
|
304
|
-
if (!RTEST(wrapper->stopped))
|
304
|
+
if (!RTEST(wrapper->stopped) && !RTEST(wrapper->completed))
|
305
305
|
rb_raise(eParserError, "Could not parse data entirely");
|
306
306
|
else
|
307
307
|
nparsed += 1; // error states fail on the current character
|
@@ -19,16 +19,6 @@
|
|
19
19
|
* IN THE SOFTWARE.
|
20
20
|
*/
|
21
21
|
#include <http_parser.h>
|
22
|
-
#if defined(_WIN32) && !defined(__MINGW32__)
|
23
|
-
typedef __int8 int8_t;
|
24
|
-
typedef unsigned __int8 uint8_t;
|
25
|
-
typedef __int16 int16_t;
|
26
|
-
typedef unsigned __int16 uint16_t;
|
27
|
-
typedef __int16 int32_t;
|
28
|
-
typedef unsigned __int32 uint32_t;
|
29
|
-
#else
|
30
|
-
#include <stdint.h>
|
31
|
-
#endif
|
32
22
|
#include <assert.h>
|
33
23
|
#include <stddef.h>
|
34
24
|
|
@@ -103,12 +93,21 @@ static const char *method_strings[] =
|
|
103
93
|
, "MKACTIVITY"
|
104
94
|
, "CHECKOUT"
|
105
95
|
, "MERGE"
|
96
|
+
, "M-SEARCH"
|
97
|
+
, "NOTIFY"
|
98
|
+
, "SUBSCRIBE"
|
99
|
+
, "UNSUBSCRIBE"
|
106
100
|
};
|
107
101
|
|
108
102
|
|
109
|
-
/*
|
110
|
-
|
111
|
-
|
103
|
+
/* Tokens as defined by rfc 2616. Also lowercases them.
|
104
|
+
* token = 1*<any CHAR except CTLs or separators>
|
105
|
+
* separators = "(" | ")" | "<" | ">" | "@"
|
106
|
+
* | "," | ";" | ":" | "\" | <">
|
107
|
+
* | "/" | "[" | "]" | "?" | "="
|
108
|
+
* | "{" | "}" | SP | HT
|
109
|
+
*/
|
110
|
+
static const char tokens[256] = {
|
112
111
|
/* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */
|
113
112
|
0, 0, 0, 0, 0, 0, 0, 0,
|
114
113
|
/* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */
|
@@ -118,9 +117,9 @@ static const char acceptable_header[256] = {
|
|
118
117
|
/* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */
|
119
118
|
0, 0, 0, 0, 0, 0, 0, 0,
|
120
119
|
/* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */
|
121
|
-
' ',
|
120
|
+
' ', '!', '"', '#', '$', '%', '&', '\'',
|
122
121
|
/* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */
|
123
|
-
0, 0,
|
122
|
+
0, 0, '*', '+', 0, '-', '.', '/',
|
124
123
|
/* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */
|
125
124
|
'0', '1', '2', '3', '4', '5', '6', '7',
|
126
125
|
/* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */
|
@@ -132,15 +131,15 @@ static const char acceptable_header[256] = {
|
|
132
131
|
/* 80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W */
|
133
132
|
'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
|
134
133
|
/* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */
|
135
|
-
'x', 'y', 'z', 0, 0, 0,
|
134
|
+
'x', 'y', 'z', 0, 0, 0, '^', '_',
|
136
135
|
/* 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g */
|
137
|
-
|
136
|
+
'`', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
|
138
137
|
/* 104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o */
|
139
138
|
'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
|
140
139
|
/* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */
|
141
140
|
'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
|
142
141
|
/* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */
|
143
|
-
'x', 'y', 'z', 0,
|
142
|
+
'x', 'y', 'z', 0, '|', '}', '~', 0 };
|
144
143
|
|
145
144
|
|
146
145
|
static const int8_t unhex[256] =
|
@@ -302,6 +301,7 @@ enum flags
|
|
302
301
|
#define CR '\r'
|
303
302
|
#define LF '\n'
|
304
303
|
#define LOWER(c) (unsigned char)(c | 0x20)
|
304
|
+
#define TOKEN(c) tokens[(unsigned char)c]
|
305
305
|
|
306
306
|
|
307
307
|
#define start_state (parser->type == HTTP_REQUEST ? s_start_req : s_start_res)
|
@@ -579,12 +579,14 @@ size_t http_parser_execute (http_parser *parser,
|
|
579
579
|
case 'G': parser->method = HTTP_GET; break;
|
580
580
|
case 'H': parser->method = HTTP_HEAD; break;
|
581
581
|
case 'L': parser->method = HTTP_LOCK; break;
|
582
|
-
case 'M': parser->method = HTTP_MKCOL; /* or MOVE, MKACTIVITY, MERGE */ break;
|
582
|
+
case 'M': parser->method = HTTP_MKCOL; /* or MOVE, MKACTIVITY, MERGE, M-SEARCH */ break;
|
583
|
+
case 'N': parser->method = HTTP_NOTIFY; break;
|
583
584
|
case 'O': parser->method = HTTP_OPTIONS; break;
|
584
585
|
case 'P': parser->method = HTTP_POST; /* or PROPFIND or PROPPATCH or PUT */ break;
|
585
586
|
case 'R': parser->method = HTTP_REPORT; break;
|
587
|
+
case 'S': parser->method = HTTP_SUBSCRIBE; break;
|
586
588
|
case 'T': parser->method = HTTP_TRACE; break;
|
587
|
-
case 'U': parser->method = HTTP_UNLOCK; break;
|
589
|
+
case 'U': parser->method = HTTP_UNLOCK; /* or UNSUBSCRIBE */ break;
|
588
590
|
default: goto error;
|
589
591
|
}
|
590
592
|
state = s_req_method;
|
@@ -612,6 +614,8 @@ size_t http_parser_execute (http_parser *parser,
|
|
612
614
|
parser->method = HTTP_MOVE;
|
613
615
|
} else if (index == 1 && ch == 'E') {
|
614
616
|
parser->method = HTTP_MERGE;
|
617
|
+
} else if (index == 1 && ch == '-') {
|
618
|
+
parser->method = HTTP_MSEARCH;
|
615
619
|
} else if (index == 2 && ch == 'A') {
|
616
620
|
parser->method = HTTP_MKACTIVITY;
|
617
621
|
}
|
@@ -619,6 +623,8 @@ size_t http_parser_execute (http_parser *parser,
|
|
619
623
|
parser->method = HTTP_PROPFIND; /* or HTTP_PROPPATCH */
|
620
624
|
} else if (index == 1 && parser->method == HTTP_POST && ch == 'U') {
|
621
625
|
parser->method = HTTP_PUT;
|
626
|
+
} else if (index == 2 && parser->method == HTTP_UNLOCK && ch == 'S') {
|
627
|
+
parser->method = HTTP_UNSUBSCRIBE;
|
622
628
|
} else if (index == 4 && parser->method == HTTP_PROPFIND && ch == 'P') {
|
623
629
|
parser->method = HTTP_PROPPATCH;
|
624
630
|
} else {
|
@@ -632,7 +638,7 @@ size_t http_parser_execute (http_parser *parser,
|
|
632
638
|
{
|
633
639
|
if (ch == ' ') break;
|
634
640
|
|
635
|
-
if (ch == '/') {
|
641
|
+
if (ch == '/' || ch == '*') {
|
636
642
|
MARK(url);
|
637
643
|
MARK(path);
|
638
644
|
state = s_req_path;
|
@@ -662,6 +668,9 @@ size_t http_parser_execute (http_parser *parser,
|
|
662
668
|
} else if (ch == '.') {
|
663
669
|
state = s_req_host;
|
664
670
|
break;
|
671
|
+
} else if ('0' <= ch && ch <= '9') {
|
672
|
+
state = s_req_host;
|
673
|
+
break;
|
665
674
|
}
|
666
675
|
|
667
676
|
goto error;
|
@@ -739,12 +748,14 @@ size_t http_parser_execute (http_parser *parser,
|
|
739
748
|
case CR:
|
740
749
|
CALLBACK(url);
|
741
750
|
CALLBACK(path);
|
751
|
+
parser->http_major = 0;
|
742
752
|
parser->http_minor = 9;
|
743
753
|
state = s_req_line_almost_done;
|
744
754
|
break;
|
745
755
|
case LF:
|
746
756
|
CALLBACK(url);
|
747
757
|
CALLBACK(path);
|
758
|
+
parser->http_major = 0;
|
748
759
|
parser->http_minor = 9;
|
749
760
|
state = s_header_field_start;
|
750
761
|
break;
|
@@ -779,11 +790,13 @@ size_t http_parser_execute (http_parser *parser,
|
|
779
790
|
break;
|
780
791
|
case CR:
|
781
792
|
CALLBACK(url);
|
793
|
+
parser->http_major = 0;
|
782
794
|
parser->http_minor = 9;
|
783
795
|
state = s_req_line_almost_done;
|
784
796
|
break;
|
785
797
|
case LF:
|
786
798
|
CALLBACK(url);
|
799
|
+
parser->http_major = 0;
|
787
800
|
parser->http_minor = 9;
|
788
801
|
state = s_header_field_start;
|
789
802
|
break;
|
@@ -812,12 +825,14 @@ size_t http_parser_execute (http_parser *parser,
|
|
812
825
|
case CR:
|
813
826
|
CALLBACK(url);
|
814
827
|
CALLBACK(query_string);
|
828
|
+
parser->http_major = 0;
|
815
829
|
parser->http_minor = 9;
|
816
830
|
state = s_req_line_almost_done;
|
817
831
|
break;
|
818
832
|
case LF:
|
819
833
|
CALLBACK(url);
|
820
834
|
CALLBACK(query_string);
|
835
|
+
parser->http_major = 0;
|
821
836
|
parser->http_minor = 9;
|
822
837
|
state = s_header_field_start;
|
823
838
|
break;
|
@@ -846,11 +861,13 @@ size_t http_parser_execute (http_parser *parser,
|
|
846
861
|
break;
|
847
862
|
case CR:
|
848
863
|
CALLBACK(url);
|
864
|
+
parser->http_major = 0;
|
849
865
|
parser->http_minor = 9;
|
850
866
|
state = s_req_line_almost_done;
|
851
867
|
break;
|
852
868
|
case LF:
|
853
869
|
CALLBACK(url);
|
870
|
+
parser->http_major = 0;
|
854
871
|
parser->http_minor = 9;
|
855
872
|
state = s_header_field_start;
|
856
873
|
break;
|
@@ -879,12 +896,14 @@ size_t http_parser_execute (http_parser *parser,
|
|
879
896
|
case CR:
|
880
897
|
CALLBACK(url);
|
881
898
|
CALLBACK(fragment);
|
899
|
+
parser->http_major = 0;
|
882
900
|
parser->http_minor = 9;
|
883
901
|
state = s_req_line_almost_done;
|
884
902
|
break;
|
885
903
|
case LF:
|
886
904
|
CALLBACK(url);
|
887
905
|
CALLBACK(fragment);
|
906
|
+
parser->http_major = 0;
|
888
907
|
parser->http_minor = 9;
|
889
908
|
state = s_header_field_start;
|
890
909
|
break;
|
@@ -1006,9 +1025,9 @@ size_t http_parser_execute (http_parser *parser,
|
|
1006
1025
|
goto headers_almost_done;
|
1007
1026
|
}
|
1008
1027
|
|
1009
|
-
c =
|
1028
|
+
c = TOKEN(ch);
|
1010
1029
|
|
1011
|
-
if (c
|
1030
|
+
if (!c) goto error;
|
1012
1031
|
|
1013
1032
|
MARK(header_field);
|
1014
1033
|
|
@@ -1041,7 +1060,7 @@ size_t http_parser_execute (http_parser *parser,
|
|
1041
1060
|
|
1042
1061
|
case s_header_field:
|
1043
1062
|
{
|
1044
|
-
c =
|
1063
|
+
c = TOKEN(ch);
|
1045
1064
|
|
1046
1065
|
if (c) {
|
1047
1066
|
switch (header_state) {
|
@@ -1177,23 +1196,18 @@ size_t http_parser_execute (http_parser *parser,
|
|
1177
1196
|
state = s_header_value;
|
1178
1197
|
index = 0;
|
1179
1198
|
|
1180
|
-
c =
|
1181
|
-
|
1182
|
-
if (!c) {
|
1183
|
-
if (ch == CR) {
|
1184
|
-
CALLBACK(header_value);
|
1185
|
-
header_state = h_general;
|
1186
|
-
state = s_header_almost_done;
|
1187
|
-
break;
|
1188
|
-
}
|
1189
|
-
|
1190
|
-
if (ch == LF) {
|
1191
|
-
CALLBACK(header_value);
|
1192
|
-
state = s_header_field_start;
|
1193
|
-
break;
|
1194
|
-
}
|
1199
|
+
c = LOWER(ch);
|
1195
1200
|
|
1201
|
+
if (ch == CR) {
|
1202
|
+
CALLBACK(header_value);
|
1196
1203
|
header_state = h_general;
|
1204
|
+
state = s_header_almost_done;
|
1205
|
+
break;
|
1206
|
+
}
|
1207
|
+
|
1208
|
+
if (ch == LF) {
|
1209
|
+
CALLBACK(header_value);
|
1210
|
+
state = s_header_field_start;
|
1197
1211
|
break;
|
1198
1212
|
}
|
1199
1213
|
|
@@ -1238,22 +1252,19 @@ size_t http_parser_execute (http_parser *parser,
|
|
1238
1252
|
|
1239
1253
|
case s_header_value:
|
1240
1254
|
{
|
1241
|
-
c =
|
1242
|
-
|
1243
|
-
if (!c) {
|
1244
|
-
if (ch == CR) {
|
1245
|
-
CALLBACK(header_value);
|
1246
|
-
state = s_header_almost_done;
|
1247
|
-
break;
|
1248
|
-
}
|
1255
|
+
c = LOWER(ch);
|
1249
1256
|
|
1250
|
-
|
1251
|
-
|
1252
|
-
|
1253
|
-
}
|
1257
|
+
if (ch == CR) {
|
1258
|
+
CALLBACK(header_value);
|
1259
|
+
state = s_header_almost_done;
|
1254
1260
|
break;
|
1255
1261
|
}
|
1256
1262
|
|
1263
|
+
if (ch == LF) {
|
1264
|
+
CALLBACK(header_value);
|
1265
|
+
goto header_almost_done;
|
1266
|
+
}
|
1267
|
+
|
1257
1268
|
switch (header_state) {
|
1258
1269
|
case h_general:
|
1259
1270
|
break;
|
@@ -1373,6 +1384,7 @@ size_t http_parser_execute (http_parser *parser,
|
|
1373
1384
|
break;
|
1374
1385
|
|
1375
1386
|
default:
|
1387
|
+
parser->state = state;
|
1376
1388
|
return p - data; /* Error */
|
1377
1389
|
}
|
1378
1390
|
}
|
@@ -26,11 +26,20 @@ extern "C" {
|
|
26
26
|
|
27
27
|
|
28
28
|
#include <sys/types.h>
|
29
|
-
#include <stdint.h>
|
30
|
-
|
31
29
|
#if defined(_WIN32) && !defined(__MINGW32__)
|
30
|
+
typedef __int8 int8_t;
|
31
|
+
typedef unsigned __int8 uint8_t;
|
32
|
+
typedef __int16 int16_t;
|
33
|
+
typedef unsigned __int16 uint16_t;
|
34
|
+
typedef __int32 int32_t;
|
35
|
+
typedef unsigned __int32 uint32_t;
|
36
|
+
typedef __int64 int64_t;
|
37
|
+
typedef unsigned __int64 uint64_t;
|
38
|
+
|
32
39
|
typedef unsigned int size_t;
|
33
40
|
typedef int ssize_t;
|
41
|
+
#else
|
42
|
+
#include <stdint.h>
|
34
43
|
#endif
|
35
44
|
|
36
45
|
/* Compile with -DHTTP_PARSER_STRICT=0 to make less checks, but run
|
@@ -92,6 +101,11 @@ enum http_method
|
|
92
101
|
, HTTP_MKACTIVITY
|
93
102
|
, HTTP_CHECKOUT
|
94
103
|
, HTTP_MERGE
|
104
|
+
/* upnp */
|
105
|
+
, HTTP_MSEARCH
|
106
|
+
, HTTP_NOTIFY
|
107
|
+
, HTTP_SUBSCRIBE
|
108
|
+
, HTTP_UNSUBSCRIBE
|
95
109
|
};
|
96
110
|
|
97
111
|
|
@@ -31,7 +31,7 @@
|
|
31
31
|
#undef FALSE
|
32
32
|
#define FALSE 0
|
33
33
|
|
34
|
-
#define MAX_HEADERS
|
34
|
+
#define MAX_HEADERS 13
|
35
35
|
#define MAX_ELEMENT_SIZE 500
|
36
36
|
|
37
37
|
#define MIN(a,b) ((a) < (b) ? (a) : (b))
|
@@ -498,7 +498,7 @@ const struct message requests[] =
|
|
498
498
|
#define CONNECT_REQUEST 17
|
499
499
|
, {.name = "connect request"
|
500
500
|
,.type= HTTP_REQUEST
|
501
|
-
,.raw= "CONNECT
|
501
|
+
,.raw= "CONNECT home0.netscape.com:443 HTTP/1.0\r\n"
|
502
502
|
"User-agent: Mozilla/1.1N\r\n"
|
503
503
|
"Proxy-authorization: basic aGVsbG86d29ybGQ=\r\n"
|
504
504
|
"\r\n"
|
@@ -510,7 +510,7 @@ const struct message requests[] =
|
|
510
510
|
,.query_string= ""
|
511
511
|
,.fragment= ""
|
512
512
|
,.request_path= ""
|
513
|
-
,.request_url= "
|
513
|
+
,.request_url= "home0.netscape.com:443"
|
514
514
|
,.num_headers= 2
|
515
515
|
,.upgrade=1
|
516
516
|
,.headers= { { "User-agent", "Mozilla/1.1N" }
|
@@ -538,6 +538,50 @@ const struct message requests[] =
|
|
538
538
|
,.body= ""
|
539
539
|
}
|
540
540
|
|
541
|
+
#define NO_HTTP_VERSION 19
|
542
|
+
, {.name= "request with no http version"
|
543
|
+
,.type= HTTP_REQUEST
|
544
|
+
,.raw= "GET /\r\n"
|
545
|
+
"\r\n"
|
546
|
+
,.should_keep_alive= FALSE
|
547
|
+
,.message_complete_on_eof= FALSE
|
548
|
+
,.http_major= 0
|
549
|
+
,.http_minor= 9
|
550
|
+
,.method= HTTP_GET
|
551
|
+
,.query_string= ""
|
552
|
+
,.fragment= ""
|
553
|
+
,.request_path= "/"
|
554
|
+
,.request_url= "/"
|
555
|
+
,.num_headers= 0
|
556
|
+
,.headers= {}
|
557
|
+
,.body= ""
|
558
|
+
}
|
559
|
+
|
560
|
+
#define MSEARCH_REQ 19
|
561
|
+
, {.name= "m-search request"
|
562
|
+
,.type= HTTP_REQUEST
|
563
|
+
,.raw= "M-SEARCH * HTTP/1.1\r\n"
|
564
|
+
"HOST: 239.255.255.250:1900\r\n"
|
565
|
+
"MAN: \"ssdp:discover\"\r\n"
|
566
|
+
"ST: \"ssdp:all\"\r\n"
|
567
|
+
"\r\n"
|
568
|
+
,.should_keep_alive= TRUE
|
569
|
+
,.message_complete_on_eof= FALSE
|
570
|
+
,.http_major= 1
|
571
|
+
,.http_minor= 1
|
572
|
+
,.method= HTTP_MSEARCH
|
573
|
+
,.query_string= ""
|
574
|
+
,.fragment= ""
|
575
|
+
,.request_path= "*"
|
576
|
+
,.request_url= "*"
|
577
|
+
,.num_headers= 3
|
578
|
+
,.headers= { { "HOST", "239.255.255.250:1900" }
|
579
|
+
, { "MAN", "\"ssdp:discover\"" }
|
580
|
+
, { "ST", "\"ssdp:all\"" }
|
581
|
+
}
|
582
|
+
,.body= ""
|
583
|
+
}
|
584
|
+
|
541
585
|
, {.name= NULL } /* sentinel */
|
542
586
|
};
|
543
587
|
|
@@ -551,9 +595,10 @@ const struct message responses[] =
|
|
551
595
|
"Content-Type: text/html; charset=UTF-8\r\n"
|
552
596
|
"Date: Sun, 26 Apr 2009 11:11:49 GMT\r\n"
|
553
597
|
"Expires: Tue, 26 May 2009 11:11:49 GMT\r\n"
|
598
|
+
"X-$PrototypeBI-Version: 1.6.0.3\r\n" /* $ char in header field */
|
554
599
|
"Cache-Control: public, max-age=2592000\r\n"
|
555
600
|
"Server: gws\r\n"
|
556
|
-
"Content-Length:
|
601
|
+
"Content-Length: 219 \r\n"
|
557
602
|
"\r\n"
|
558
603
|
"<HTML><HEAD><meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\">\n"
|
559
604
|
"<TITLE>301 Moved</TITLE></HEAD><BODY>\n"
|
@@ -566,15 +611,16 @@ const struct message responses[] =
|
|
566
611
|
,.http_major= 1
|
567
612
|
,.http_minor= 1
|
568
613
|
,.status_code= 301
|
569
|
-
,.num_headers=
|
614
|
+
,.num_headers= 8
|
570
615
|
,.headers=
|
571
616
|
{ { "Location", "http://www.google.com/" }
|
572
617
|
, { "Content-Type", "text/html; charset=UTF-8" }
|
573
618
|
, { "Date", "Sun, 26 Apr 2009 11:11:49 GMT" }
|
574
619
|
, { "Expires", "Tue, 26 May 2009 11:11:49 GMT" }
|
620
|
+
, { "X-$PrototypeBI-Version", "1.6.0.3" }
|
575
621
|
, { "Cache-Control", "public, max-age=2592000" }
|
576
622
|
, { "Server", "gws" }
|
577
|
-
, { "Content-Length", "219" }
|
623
|
+
, { "Content-Length", "219 " }
|
578
624
|
}
|
579
625
|
,.body= "<HTML><HEAD><meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\">\n"
|
580
626
|
"<TITLE>301 Moved</TITLE></HEAD><BODY>\n"
|
@@ -833,6 +879,71 @@ const struct message responses[] =
|
|
833
879
|
,.body= "<xml>hello</xml>"
|
834
880
|
}
|
835
881
|
|
882
|
+
|
883
|
+
#define RES_FIELD_UNDERSCORE 10
|
884
|
+
/* Should handle spaces in header fields */
|
885
|
+
, {.name= "field underscore"
|
886
|
+
,.type= HTTP_RESPONSE
|
887
|
+
,.raw= "HTTP/1.1 200 OK\r\n"
|
888
|
+
"Date: Tue, 28 Sep 2010 01:14:13 GMT\r\n"
|
889
|
+
"Server: Apache\r\n"
|
890
|
+
"Cache-Control: no-cache, must-revalidate\r\n"
|
891
|
+
"Expires: Mon, 26 Jul 1997 05:00:00 GMT\r\n"
|
892
|
+
".et-Cookie: PlaxoCS=1274804622353690521; path=/; domain=.plaxo.com\r\n"
|
893
|
+
"Vary: Accept-Encoding\r\n"
|
894
|
+
"_eep-Alive: timeout=45\r\n" /* semantic value ignored */
|
895
|
+
"_onnection: Keep-Alive\r\n" /* semantic value ignored */
|
896
|
+
"Transfer-Encoding: chunked\r\n"
|
897
|
+
"Content-Type: text/html\r\n"
|
898
|
+
"Connection: close\r\n"
|
899
|
+
"\r\n"
|
900
|
+
"0\r\n\r\n"
|
901
|
+
,.should_keep_alive= FALSE
|
902
|
+
,.message_complete_on_eof= FALSE
|
903
|
+
,.http_major= 1
|
904
|
+
,.http_minor= 1
|
905
|
+
,.status_code= 200
|
906
|
+
,.num_headers= 11
|
907
|
+
,.headers=
|
908
|
+
{ { "Date", "Tue, 28 Sep 2010 01:14:13 GMT" }
|
909
|
+
, { "Server", "Apache" }
|
910
|
+
, { "Cache-Control", "no-cache, must-revalidate" }
|
911
|
+
, { "Expires", "Mon, 26 Jul 1997 05:00:00 GMT" }
|
912
|
+
, { ".et-Cookie", "PlaxoCS=1274804622353690521; path=/; domain=.plaxo.com" }
|
913
|
+
, { "Vary", "Accept-Encoding" }
|
914
|
+
, { "_eep-Alive", "timeout=45" }
|
915
|
+
, { "_onnection", "Keep-Alive" }
|
916
|
+
, { "Transfer-Encoding", "chunked" }
|
917
|
+
, { "Content-Type", "text/html" }
|
918
|
+
, { "Connection", "close" }
|
919
|
+
}
|
920
|
+
,.body= ""
|
921
|
+
}
|
922
|
+
|
923
|
+
#define NON_ASCII_IN_STATUS_LINE 11
|
924
|
+
/* Should handle non-ASCII in status line */
|
925
|
+
, {.name= "non-ASCII in status line"
|
926
|
+
,.type= HTTP_RESPONSE
|
927
|
+
,.raw= "HTTP/1.1 500 Oriëntatieprobleem\r\n"
|
928
|
+
"Date: Fri, 5 Nov 2010 23:07:12 GMT+2\r\n"
|
929
|
+
"Content-Length: 0\r\n"
|
930
|
+
"Connection: close\r\n"
|
931
|
+
"\r\n"
|
932
|
+
,.should_keep_alive= FALSE
|
933
|
+
,.message_complete_on_eof= FALSE
|
934
|
+
,.http_major= 1
|
935
|
+
,.http_minor= 1
|
936
|
+
,.status_code= 500
|
937
|
+
,.num_headers= 3
|
938
|
+
,.headers=
|
939
|
+
{ { "Date", "Fri, 5 Nov 2010 23:07:12 GMT+2" }
|
940
|
+
, { "Content-Length", "0" }
|
941
|
+
, { "Connection", "close" }
|
942
|
+
}
|
943
|
+
,.body= ""
|
944
|
+
}
|
945
|
+
|
946
|
+
|
836
947
|
, {.name= NULL } /* sentinel */
|
837
948
|
};
|
838
949
|
|
data/http_parser.rb.gemspec
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = "http_parser.rb"
|
3
|
-
s.version = "0.5.
|
3
|
+
s.version = "0.5.1"
|
4
4
|
s.summary = "Simple callback-based HTTP request/response parser"
|
5
5
|
s.description = "Ruby bindings to http://github.com/ry/http-parser and http://github.com/a2800276/http-parser.java"
|
6
6
|
|
@@ -12,4 +12,8 @@ Gem::Specification.new do |s|
|
|
12
12
|
|
13
13
|
s.require_paths = ["lib"]
|
14
14
|
s.extensions = ["ext/ruby_http_parser/extconf.rb"]
|
15
|
+
|
16
|
+
s.add_development_dependency 'rake-compiler', '>= 0.7.5'
|
17
|
+
s.add_development_dependency 'rspec', '>= 2.0.1'
|
18
|
+
s.add_development_dependency 'json', '>= 1.4.6'
|
15
19
|
end
|
data/spec/parser_spec.rb
CHANGED
@@ -115,6 +115,23 @@ describe HTTP::Parser do
|
|
115
115
|
@done.should be_true
|
116
116
|
end
|
117
117
|
|
118
|
+
it "should parse headers incrementally" do
|
119
|
+
request =
|
120
|
+
"GET / HTTP/1.0\r\n" +
|
121
|
+
"Header1: value 1\r\n" +
|
122
|
+
"Header2: value 2\r\n" +
|
123
|
+
"\r\n"
|
124
|
+
|
125
|
+
while chunk = request.slice!(0,2) and !chunk.empty?
|
126
|
+
@parser << chunk
|
127
|
+
end
|
128
|
+
|
129
|
+
@parser.headers.should == {
|
130
|
+
'Header1' => 'value 1',
|
131
|
+
'Header2' => 'value 2'
|
132
|
+
}
|
133
|
+
end
|
134
|
+
|
118
135
|
it "should handle multiple headers" do
|
119
136
|
@parser <<
|
120
137
|
"GET / HTTP/1.0\r\n" +
|
@@ -143,6 +160,18 @@ describe HTTP::Parser do
|
|
143
160
|
@done.should be_true
|
144
161
|
end
|
145
162
|
|
163
|
+
it "should ignore extra content beyond specified length" do
|
164
|
+
@parser <<
|
165
|
+
"GET / HTTP/1.0\r\n" +
|
166
|
+
"Content-Length: 5\r\n" +
|
167
|
+
"\r\n" +
|
168
|
+
"hello" +
|
169
|
+
" \n"
|
170
|
+
|
171
|
+
@body.should == 'hello'
|
172
|
+
@done.should be_true
|
173
|
+
end
|
174
|
+
|
146
175
|
%w[ request response ].each do |type|
|
147
176
|
JSON.parse(File.read(File.expand_path("../support/#{type}s.json", __FILE__))).each do |test|
|
148
177
|
test['headers'] ||= {}
|
data/spec/spec_helper.rb
CHANGED
data/spec/support/requests.json
CHANGED
@@ -341,7 +341,7 @@
|
|
341
341
|
{
|
342
342
|
"name": "connect request",
|
343
343
|
"type": "HTTP_REQUEST",
|
344
|
-
"raw": "CONNECT
|
344
|
+
"raw": "CONNECT home0.netscape.com:443 HTTP/1.0\r\nUser-agent: Mozilla/1.1N\r\nProxy-authorization: basic aGVsbG86d29ybGQ=\r\n\r\n",
|
345
345
|
"should_keep_alive": false,
|
346
346
|
"message_complete_on_eof": false,
|
347
347
|
"http_major": 1,
|
@@ -350,7 +350,7 @@
|
|
350
350
|
"query_string": "",
|
351
351
|
"fragment": "",
|
352
352
|
"request_path": "",
|
353
|
-
"request_url": "
|
353
|
+
"request_url": "home0.netscape.com:443",
|
354
354
|
"num_headers": 2,
|
355
355
|
"upgrade": 1,
|
356
356
|
"headers": {
|
@@ -375,6 +375,25 @@
|
|
375
375
|
"num_headers": 0,
|
376
376
|
"headers": {
|
377
377
|
|
378
|
+
},
|
379
|
+
"body": ""
|
380
|
+
},
|
381
|
+
{
|
382
|
+
"name": "request with no http version",
|
383
|
+
"type": "HTTP_REQUEST",
|
384
|
+
"raw": "GET /\r\n\r\n",
|
385
|
+
"should_keep_alive": false,
|
386
|
+
"message_complete_on_eof": false,
|
387
|
+
"http_major": 0,
|
388
|
+
"http_minor": 9,
|
389
|
+
"method": "GET",
|
390
|
+
"query_string": "",
|
391
|
+
"fragment": "",
|
392
|
+
"request_path": "/",
|
393
|
+
"request_url": "/",
|
394
|
+
"num_headers": 0,
|
395
|
+
"headers": {
|
396
|
+
|
378
397
|
},
|
379
398
|
"body": ""
|
380
399
|
}
|
data/spec/support/responses.json
CHANGED
@@ -2,21 +2,22 @@
|
|
2
2
|
{
|
3
3
|
"name": "google 301",
|
4
4
|
"type": "HTTP_RESPONSE",
|
5
|
-
"raw": "HTTP/1.1 301 Moved Permanently\r\nLocation: http://www.google.com/\r\nContent-Type: text/html; charset=UTF-8\r\nDate: Sun, 26 Apr 2009 11:11:49 GMT\r\nExpires: Tue, 26 May 2009 11:11:49 GMT\r\nCache-Control: public, max-age=2592000\r\nServer: gws\r\nContent-Length:
|
5
|
+
"raw": "HTTP/1.1 301 Moved Permanently\r\nLocation: http://www.google.com/\r\nContent-Type: text/html; charset=UTF-8\r\nDate: Sun, 26 Apr 2009 11:11:49 GMT\r\nExpires: Tue, 26 May 2009 11:11:49 GMT\r\nX-$PrototypeBI-Version: 1.6.0.3\r\nCache-Control: public, max-age=2592000\r\nServer: gws\r\nContent-Length: 219 \r\n\r\n<HTML><HEAD><meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\">\n<TITLE>301 Moved</TITLE></HEAD><BODY>\n<H1>301 Moved</H1>\nThe document has moved\n<A HREF=\"http://www.google.com/\">here</A>.\r\n</BODY></HTML>\r\n",
|
6
6
|
"should_keep_alive": true,
|
7
7
|
"message_complete_on_eof": false,
|
8
8
|
"http_major": 1,
|
9
9
|
"http_minor": 1,
|
10
10
|
"status_code": 301,
|
11
|
-
"num_headers":
|
11
|
+
"num_headers": 8,
|
12
12
|
"headers": {
|
13
13
|
"Location": "http://www.google.com/",
|
14
14
|
"Content-Type": "text/html; charset=UTF-8",
|
15
15
|
"Date": "Sun, 26 Apr 2009 11:11:49 GMT",
|
16
16
|
"Expires": "Tue, 26 May 2009 11:11:49 GMT",
|
17
|
+
"X-$PrototypeBI-Version": "1.6.0.3",
|
17
18
|
"Cache-Control": "public, max-age=2592000",
|
18
19
|
"Server": "gws",
|
19
|
-
"Content-Length": "219"
|
20
|
+
"Content-Length": "219 "
|
20
21
|
},
|
21
22
|
"body": "<HTML><HEAD><meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\">\n<TITLE>301 Moved</TITLE></HEAD><BODY>\n<H1>301 Moved</H1>\nThe document has moved\n<A HREF=\"http://www.google.com/\">here</A>.\r\n</BODY></HTML>\r\n"
|
22
23
|
},
|
@@ -182,5 +183,47 @@
|
|
182
183
|
"Connection": "keep-alive"
|
183
184
|
},
|
184
185
|
"body": "<xml>hello</xml>"
|
186
|
+
},
|
187
|
+
{
|
188
|
+
"name": "field underscore",
|
189
|
+
"type": "HTTP_RESPONSE",
|
190
|
+
"raw": "HTTP/1.1 200 OK\r\nDate: Tue, 28 Sep 2010 01:14:13 GMT\r\nServer: Apache\r\nCache-Control: no-cache, must-revalidate\r\nExpires: Mon, 26 Jul 1997 05:00:00 GMT\r\n.et-Cookie: PlaxoCS=1274804622353690521; path=/; domain=.plaxo.com\r\nVary: Accept-Encoding\r\n_eep-Alive: timeout=45\r\n_onnection: Keep-Alive\r\nTransfer-Encoding: chunked\r\nContent-Type: text/html\r\nConnection: close\r\n\r\n0\r\n\r\n",
|
191
|
+
"should_keep_alive": false,
|
192
|
+
"message_complete_on_eof": false,
|
193
|
+
"http_major": 1,
|
194
|
+
"http_minor": 1,
|
195
|
+
"status_code": 200,
|
196
|
+
"num_headers": 11,
|
197
|
+
"headers": {
|
198
|
+
"Date": "Tue, 28 Sep 2010 01:14:13 GMT",
|
199
|
+
"Server": "Apache",
|
200
|
+
"Cache-Control": "no-cache, must-revalidate",
|
201
|
+
"Expires": "Mon, 26 Jul 1997 05:00:00 GMT",
|
202
|
+
".et-Cookie": "PlaxoCS=1274804622353690521; path=/; domain=.plaxo.com",
|
203
|
+
"Vary": "Accept-Encoding",
|
204
|
+
"_eep-Alive": "timeout=45",
|
205
|
+
"_onnection": "Keep-Alive",
|
206
|
+
"Transfer-Encoding": "chunked",
|
207
|
+
"Content-Type": "text/html",
|
208
|
+
"Connection": "close"
|
209
|
+
},
|
210
|
+
"body": ""
|
211
|
+
},
|
212
|
+
{
|
213
|
+
"name": "non-ASCII in status line",
|
214
|
+
"type": "HTTP_RESPONSE",
|
215
|
+
"raw": "HTTP/1.1 500 Oriëntatieprobleem\r\nDate: Fri, 5 Nov 2010 23:07:12 GMT+2\r\nContent-Length: 0\r\nConnection: close\r\n\r\n",
|
216
|
+
"should_keep_alive": false,
|
217
|
+
"message_complete_on_eof": false,
|
218
|
+
"http_major": 1,
|
219
|
+
"http_minor": 1,
|
220
|
+
"status_code": 500,
|
221
|
+
"num_headers": 3,
|
222
|
+
"headers": {
|
223
|
+
"Date": "Fri, 5 Nov 2010 23:07:12 GMT+2",
|
224
|
+
"Content-Length": "0",
|
225
|
+
"Connection": "close"
|
226
|
+
},
|
227
|
+
"body": ""
|
185
228
|
}
|
186
229
|
]
|
data/tasks/compile.rake
CHANGED
data/tasks/fixtures.rake
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
desc "Generate test fixtures"
|
2
|
+
task :fixtures => :submodules do
|
3
|
+
require 'yajl'
|
4
|
+
data = File.read File.expand_path('../../ext/ruby_http_parser/vendor/http-parser/test.c', __FILE__)
|
5
|
+
|
6
|
+
%w[ requests responses ].each do |type|
|
7
|
+
# find test definitions in between requests/responses[]= and .name=NULL
|
8
|
+
tmp = data[/#{type}\[\]\s*=(.+?),\s*\{\s*\.name=\s*NULL/m, 1]
|
9
|
+
|
10
|
+
# replace first { with a [ (parsing an array of test cases)
|
11
|
+
tmp.sub!('{','[')
|
12
|
+
|
13
|
+
# replace booleans
|
14
|
+
tmp.gsub!('TRUE', 'true')
|
15
|
+
tmp.gsub!('FALSE', 'false')
|
16
|
+
|
17
|
+
# remove macros and comments
|
18
|
+
tmp.gsub!(/^#define.+$/,'')
|
19
|
+
tmp.gsub!(/\/\*(.+?)\*\/$/,'')
|
20
|
+
|
21
|
+
# HTTP_* enums become strings
|
22
|
+
tmp.gsub!(/(= )(HTTP_\w+)/){
|
23
|
+
"#{$1}#{$2.dump}"
|
24
|
+
}
|
25
|
+
|
26
|
+
# join multiline strings for body and raw data
|
27
|
+
tmp.gsub!(/((body|raw)\s*=)(.+?)(\n\s+[\},])/m){
|
28
|
+
before, after = $1, $4
|
29
|
+
raw = $3.split("\n").map{ |l| l.strip[1..-2] }.join('')
|
30
|
+
"#{before} \"#{raw}\" #{after}"
|
31
|
+
}
|
32
|
+
|
33
|
+
# make headers an array of array tuples
|
34
|
+
tmp.gsub!(/(\.headers\s*=)(.+?)(\s*,\.)/m){
|
35
|
+
before, after = $1, $3
|
36
|
+
raw = $2.gsub('{', '[').gsub('}', ']')
|
37
|
+
"#{before} #{raw} #{after}"
|
38
|
+
}
|
39
|
+
|
40
|
+
# .name= becomes "name":
|
41
|
+
tmp.gsub!(/^(.{2,5})\.(\w+)\s*=/){
|
42
|
+
"#{$1}#{$2.dump}: "
|
43
|
+
}
|
44
|
+
|
45
|
+
# evaluate addition expressions
|
46
|
+
tmp.gsub!(/(body_size\":\s*)(\d+)\+(\d+)/){
|
47
|
+
"#{$1}#{$2.to_i+$3.to_i}"
|
48
|
+
}
|
49
|
+
|
50
|
+
# end result array
|
51
|
+
tmp << ']'
|
52
|
+
|
53
|
+
# normalize data
|
54
|
+
results = Yajl.load(tmp, :symbolize_keys => true)
|
55
|
+
results.map{ |res|
|
56
|
+
res[:headers] and res[:headers] = Hash[*res[:headers].flatten]
|
57
|
+
res[:method] and res[:method].gsub!(/^HTTP_/, '')
|
58
|
+
}
|
59
|
+
|
60
|
+
# write to a file
|
61
|
+
File.open("spec/support/#{type}.json", 'w'){ |f|
|
62
|
+
f.write Yajl.dump(results, :pretty => true)
|
63
|
+
}
|
64
|
+
end
|
65
|
+
end
|
metadata
CHANGED
@@ -1,12 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: http_parser.rb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
5
|
-
segments:
|
6
|
-
- 0
|
7
|
-
- 5
|
8
|
-
- 0
|
9
|
-
version: 0.5.0
|
4
|
+
version: 0.5.1
|
10
5
|
platform: x86-mswin32-60
|
11
6
|
authors:
|
12
7
|
- Marc-Andre Cournoyer
|
@@ -15,10 +10,39 @@ autorequire:
|
|
15
10
|
bindir: bin
|
16
11
|
cert_chain: []
|
17
12
|
|
18
|
-
date:
|
13
|
+
date: 2011-01-25 00:00:00 -08:00
|
19
14
|
default_executable:
|
20
|
-
dependencies:
|
21
|
-
|
15
|
+
dependencies:
|
16
|
+
- !ruby/object:Gem::Dependency
|
17
|
+
name: rake-compiler
|
18
|
+
type: :development
|
19
|
+
version_requirement:
|
20
|
+
version_requirements: !ruby/object:Gem::Requirement
|
21
|
+
requirements:
|
22
|
+
- - ">="
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: 0.7.5
|
25
|
+
version:
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: rspec
|
28
|
+
type: :development
|
29
|
+
version_requirement:
|
30
|
+
version_requirements: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - ">="
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: 2.0.1
|
35
|
+
version:
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: json
|
38
|
+
type: :development
|
39
|
+
version_requirement:
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
requirements:
|
42
|
+
- - ">="
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: 1.4.6
|
45
|
+
version:
|
22
46
|
description: Ruby bindings to http://github.com/ry/http-parser and http://github.com/a2800276/http-parser.java
|
23
47
|
email:
|
24
48
|
- macournoyer@gmail.com
|
@@ -32,6 +56,8 @@ extra_rdoc_files: []
|
|
32
56
|
files:
|
33
57
|
- .gitignore
|
34
58
|
- .gitmodules
|
59
|
+
- Gemfile
|
60
|
+
- Gemfile.lock
|
35
61
|
- README.md
|
36
62
|
- Rakefile
|
37
63
|
- bench/thin.rb
|
@@ -50,6 +76,7 @@ files:
|
|
50
76
|
- spec/support/requests.json
|
51
77
|
- spec/support/responses.json
|
52
78
|
- tasks/compile.rake
|
79
|
+
- tasks/fixtures.rake
|
53
80
|
- tasks/spec.rake
|
54
81
|
- tasks/submodules.rake
|
55
82
|
- ext/ruby_http_parser/vendor/http-parser/CONTRIBUTIONS
|
@@ -103,20 +130,18 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
103
130
|
requirements:
|
104
131
|
- - ">="
|
105
132
|
- !ruby/object:Gem::Version
|
106
|
-
segments:
|
107
|
-
- 0
|
108
133
|
version: "0"
|
134
|
+
version:
|
109
135
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
110
136
|
requirements:
|
111
137
|
- - ">="
|
112
138
|
- !ruby/object:Gem::Version
|
113
|
-
segments:
|
114
|
-
- 0
|
115
139
|
version: "0"
|
140
|
+
version:
|
116
141
|
requirements: []
|
117
142
|
|
118
143
|
rubyforge_project:
|
119
|
-
rubygems_version: 1.3.
|
144
|
+
rubygems_version: 1.3.5
|
120
145
|
signing_key:
|
121
146
|
specification_version: 3
|
122
147
|
summary: Simple callback-based HTTP request/response parser
|