http_parser.rb 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. data/.gitignore +11 -0
  2. data/.gitmodules +6 -0
  3. data/README.md +45 -0
  4. data/Rakefile +6 -0
  5. data/bench/thin.rb +57 -0
  6. data/ext/ruby_http_parser/.gitignore +1 -0
  7. data/ext/ruby_http_parser/RubyHttpParserService.java +18 -0
  8. data/ext/ruby_http_parser/ext_help.h +18 -0
  9. data/ext/ruby_http_parser/extconf.rb +16 -0
  10. data/ext/ruby_http_parser/org/ruby_http_parser/RubyHttpParser.java +403 -0
  11. data/ext/ruby_http_parser/ruby_http_parser.c +474 -0
  12. data/ext/ruby_http_parser/vendor/.gitkeep +0 -0
  13. data/ext/ruby_http_parser/vendor/http-parser-java/CONTRIBUTIONS +4 -0
  14. data/ext/ruby_http_parser/vendor/http-parser-java/LICENSE-MIT +19 -0
  15. data/ext/ruby_http_parser/vendor/http-parser-java/README.md +171 -0
  16. data/ext/ruby_http_parser/vendor/http-parser-java/TODO +19 -0
  17. data/ext/ruby_http_parser/vendor/http-parser-java/compile +1 -0
  18. data/ext/ruby_http_parser/vendor/http-parser-java/http_parser.c +1590 -0
  19. data/ext/ruby_http_parser/vendor/http-parser-java/http_parser.h +167 -0
  20. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPException.java +7 -0
  21. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPMethod.java +90 -0
  22. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPParser.java +31 -0
  23. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/ParserType.java +13 -0
  24. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/HTTPCallback.java +5 -0
  25. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/HTTPDataCallback.java +25 -0
  26. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/HTTPErrorCallback.java +7 -0
  27. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/HTTPParser.java +1894 -0
  28. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/ParserSettings.java +78 -0
  29. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/Util.java +112 -0
  30. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/TestLoaderNG.java +487 -0
  31. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/UnitTest.java +115 -0
  32. data/ext/ruby_http_parser/vendor/http-parser-java/test.c +1865 -0
  33. data/ext/ruby_http_parser/vendor/http-parser-java/test_permutations +1 -0
  34. data/ext/ruby_http_parser/vendor/http-parser-java/test_unit +1 -0
  35. data/ext/ruby_http_parser/vendor/http-parser-java/tests.dumped +539 -0
  36. data/ext/ruby_http_parser/vendor/http-parser-java/tools/byte_constants.rb +6 -0
  37. data/ext/ruby_http_parser/vendor/http-parser-java/tools/const_char.rb +13 -0
  38. data/ext/ruby_http_parser/vendor/http-parser-java/tools/lowcase.rb +15 -0
  39. data/ext/ruby_http_parser/vendor/http-parser-java/tools/parse_tests.rb +33 -0
  40. data/ext/ruby_http_parser/vendor/http-parser/CONTRIBUTIONS +4 -0
  41. data/ext/ruby_http_parser/vendor/http-parser/LICENSE-MIT +19 -0
  42. data/ext/ruby_http_parser/vendor/http-parser/README.md +171 -0
  43. data/ext/ruby_http_parser/vendor/http-parser/http_parser.c +1590 -0
  44. data/ext/ruby_http_parser/vendor/http-parser/http_parser.h +167 -0
  45. data/ext/ruby_http_parser/vendor/http-parser/test.c +1755 -0
  46. data/http_parser.rb.gemspec +15 -0
  47. data/lib/http/parser.rb +1 -0
  48. data/lib/http_parser.rb +4 -0
  49. data/spec/parser_spec.rb +187 -0
  50. data/spec/spec_helper.rb +2 -0
  51. data/spec/support/requests.json +381 -0
  52. data/spec/support/responses.json +186 -0
  53. data/tasks/compile.rake +39 -0
  54. data/tasks/spec.rake +5 -0
  55. data/tasks/submodules.rake +7 -0
  56. metadata +121 -0
@@ -0,0 +1,78 @@
1
+ package http_parser.lolevel;
2
+ import java.nio.ByteBuffer;
3
+ import http_parser.HTTPException;
4
+ public class ParserSettings {
5
+
6
+ public HTTPCallback on_message_begin;
7
+ public HTTPDataCallback on_path;
8
+ public HTTPDataCallback on_query_string;
9
+ public HTTPDataCallback on_url;
10
+ public HTTPDataCallback on_fragment;
11
+ public HTTPDataCallback on_header_field;
12
+ public HTTPDataCallback on_header_value;
13
+ public HTTPCallback on_headers_complete;
14
+ public HTTPDataCallback on_body;
15
+ public HTTPCallback on_message_complete;
16
+ public HTTPErrorCallback on_error;
17
+
18
+ void call_on_message_begin (HTTPParser p) {
19
+ call_on(on_message_begin, p);
20
+ }
21
+
22
+ void call_on_message_complete (HTTPParser p) {
23
+ call_on(on_message_complete, p);
24
+ }
25
+
26
+ // this one is a little bit different:
27
+ // the current `position` of the buffer is the location of the
28
+ // error, `ini_pos` indicates where the position of
29
+ // the buffer when it was passed to the `execute` method of the parser, i.e.
30
+ // using this information and `limit` we'll know all the valid data
31
+ // in the buffer around the error we can use to print pretty error
32
+ // messages.
33
+ void call_on_error (HTTPParser p, String mes, ByteBuffer buf, int ini_pos) {
34
+ if (null != on_error) {
35
+ on_error.cb(p, mes, buf, ini_pos);
36
+ }
37
+ // if on_error gets called it MUST throw an exception, else the parser
38
+ // will attempt to continue parsing, which it can't because it's
39
+ // in an invalid state.
40
+ throw new HTTPException(mes);
41
+ }
42
+
43
+ void call_on_header_field (HTTPParser p, ByteBuffer buf, int pos, int len) {
44
+ call_on(on_header_field, p, buf, pos, len);
45
+ }
46
+ void call_on_query_string (HTTPParser p, ByteBuffer buf, int pos, int len) {
47
+ call_on(on_query_string, p, buf, pos, len);
48
+ }
49
+ void call_on_fragment (HTTPParser p, ByteBuffer buf, int pos, int len) {
50
+ call_on(on_fragment, p, buf, pos, len);
51
+ }
52
+ void call_on_path (HTTPParser p, ByteBuffer buf, int pos, int len) {
53
+ call_on(on_path, p, buf, pos, len);
54
+ }
55
+ void call_on_header_value (HTTPParser p, ByteBuffer buf, int pos, int len) {
56
+ call_on(on_header_value, p, buf, pos, len);
57
+ }
58
+ void call_on_url (HTTPParser p, ByteBuffer buf, int pos, int len) {
59
+ call_on(on_url, p, buf, pos, len);
60
+ }
61
+ void call_on_body(HTTPParser p, ByteBuffer buf, int pos, int len) {
62
+ call_on(on_body, p, buf, pos, len);
63
+ }
64
+ void call_on_headers_complete(HTTPParser p) {
65
+ call_on(on_headers_complete, p);
66
+ }
67
+ void call_on (HTTPCallback cb, HTTPParser p) {
68
+ // cf. CALLBACK2 macro
69
+ if (null != cb) {
70
+ cb.cb(p);
71
+ }
72
+ }
73
+ void call_on (HTTPDataCallback cb, HTTPParser p, ByteBuffer buf, int pos, int len) {
74
+ if (null != cb && -1 != pos) {
75
+ cb.cb(p,buf,pos,len);
76
+ }
77
+ }
78
+ }
@@ -0,0 +1,112 @@
1
+ package http_parser.lolevel;
2
+
3
+ import java.nio.ByteBuffer;
4
+ import http_parser.HTTPException;
5
+
6
+ public class Util {
7
+ public static String toString(HTTPParser p) {
8
+ StringBuilder builder = new StringBuilder();
9
+
10
+ // the stuff up to the break is ephermeral and only meaningful
11
+ // while the parser is parsing. In general, this method is
12
+ // probably only useful during debugging.
13
+
14
+ builder.append("state :"); builder.append(p.state); builder.append("\n");
15
+ builder.append("header_state :"); builder.append(p.header_state); builder.append("\n");
16
+ builder.append("strict :"); builder.append(p.strict); builder.append("\n");
17
+ builder.append("index :"); builder.append(p.index); builder.append("\n");
18
+ builder.append("flags :"); builder.append(p.flags); builder.append("\n");
19
+ builder.append("nread :"); builder.append(p.nread); builder.append("\n");
20
+ builder.append("content_length :"); builder.append(p.content_length); builder.append("\n");
21
+
22
+
23
+ builder.append("type :"); builder.append(p.type); builder.append("\n");
24
+ builder.append("http_major :"); builder.append(p.http_major); builder.append("\n");
25
+ builder.append("http_minor :"); builder.append(p.http_minor); builder.append("\n");
26
+ builder.append("status_code :"); builder.append(p.status_code); builder.append("\n");
27
+ builder.append("method :"); builder.append(p.method); builder.append("\n");
28
+ builder.append("upgrade :"); builder.append(p.upgrade); builder.append("\n");
29
+
30
+ return builder.toString();
31
+
32
+ }
33
+
34
+ public static String error (String mes, ByteBuffer b, int begining) {
35
+ // the error message should look like this:
36
+ //
37
+ // Bla expected something, but it's not there (mes)
38
+ // GEt / HTTP 1_1
39
+ // ............^.
40
+ //
41
+ // |----------------- 72 -------------------------|
42
+
43
+ // This is ridiculously complicated and probably riddled with
44
+ // off-by-one errors, should be moved into high level interface.
45
+ // TODO.
46
+
47
+ // also: need to keep track of the initial buffer position in
48
+ // execute so that we don't screw up any `mark()` that may have
49
+ // been set outside of our control to be nice.
50
+
51
+ final int mes_width = 72;
52
+ int p = b.position(); // error position
53
+ int end = b.limit(); // this is the end
54
+ int m = end - begining; // max mes length
55
+
56
+ StringBuilder builder = new StringBuilder();
57
+ int p_adj = p;
58
+
59
+ byte [] orig = new byte[0];
60
+ if (m <= mes_width) {
61
+ orig = new byte[m];
62
+ b.position(begining);
63
+ b.get(orig, 0, m);
64
+ p_adj = p-begining;
65
+
66
+
67
+ } else {
68
+ // we'll need to trim bit off the beginning and/or end
69
+ orig = new byte[mes_width];
70
+ // three possibilities:
71
+ // a.) plenty of stuff around p
72
+ // b.) plenty of stuff in front of p
73
+ // c.) plenty of stuff behind p
74
+ // CAN'T be not enough stuff aorund p in total, because
75
+ // m>meswidth (see if to this else)
76
+
77
+ int before = p-begining;
78
+ int after = end - p;
79
+ if ( (before > mes_width/2) && (after > mes_width/2)) {
80
+ // plenty of stuff in front of and behind error
81
+ p_adj = mes_width/2;
82
+ b.position(p - mes_width/2);
83
+ b.get(orig, 0, mes_width);
84
+ } else if (before <= mes_width/2) {
85
+ // take all of the begining.
86
+ b.position(begining);
87
+ // and as much of the rest as possible
88
+
89
+ b.get(orig, 0, mes_width);
90
+
91
+ } else {
92
+ // plenty of stuff before
93
+ before = end-mes_width;
94
+ b.position(before);
95
+ p_adj = p - before;
96
+ b.get(orig, 0, mes_width);
97
+ }
98
+ }
99
+
100
+ builder.append(new String(orig));
101
+ builder.append("\n");
102
+ for (int i = 0; i!= p_adj; ++i) {
103
+ builder.append(".");
104
+ }
105
+ builder.append("^");
106
+
107
+
108
+ b.position(p); // restore position
109
+ return builder.toString();
110
+
111
+ }
112
+ }
@@ -0,0 +1,487 @@
1
+ package http_parser.lolevel;
2
+ // name : 200 trailing space on chunked body
3
+ // raw : "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nTransfer-Encoding: chunked\r\n\r\n25 \r\nThis is the data in the first chunk\r\n\r\n1C\r\nand this is the second one\r\n\r\n0 \r\n\r\n"
4
+ // type : HTTP_RESPONSE
5
+ // method: HTTP_DELETE
6
+ // status code :200
7
+ // request_path:
8
+ // request_url :
9
+ // fragment :
10
+ // query_string:
11
+ // body :"This is the data in the first chunk\r\nand this is the second one\r\n"
12
+ // body_size :65
13
+ // header_0 :{ "Content-Type": "text/plain"}
14
+ // header_1 :{ "Transfer-Encoding": "chunked"}
15
+ // should_keep_alive :1
16
+ // upgrade :0
17
+ // http_major :1
18
+ // http_minor :1
19
+
20
+
21
+ import java.io.FileReader;
22
+ import java.io.BufferedReader;
23
+
24
+ import java.util.*;
25
+ import java.util.regex.*;
26
+
27
+ import java.nio.ByteBuffer;
28
+
29
+ import http_parser.HTTPMethod;
30
+ import http_parser.ParserType;
31
+
32
+ public class TestLoaderNG {
33
+ String fn;
34
+ public TestLoaderNG(String filename) {
35
+ this.fn = filename;
36
+ }
37
+ static void p(Object o) {
38
+ System.out.println(o);
39
+ }
40
+ public List<Test> load () throws Throwable {
41
+ List<Test> list = new LinkedList<Test>();
42
+ BufferedReader buf = new BufferedReader(new FileReader(fn));
43
+ String line = null;
44
+ Test curr = new Test();
45
+ Pattern pattern = Pattern.compile("(\\S+)\\s*:(.*)");
46
+ while (null != (line = buf.readLine()) ){
47
+ if ("".equals(line.trim())) {
48
+ list.add (curr);
49
+ curr = new Test();
50
+ continue;
51
+ }
52
+ Matcher m = pattern.matcher(line);
53
+ if (m.matches()) {
54
+ // you can not be fucking serious!?
55
+ // this has got to be the most retarded regex
56
+ // interface in the history of the world ...
57
+ // (though I'm sure there's worse c++ regexp libs...)
58
+ MatchResult r = m.toMatchResult();
59
+ String key = r.group(1).trim();
60
+ String value = r.group(2).trim();
61
+ if ("name".equals(key)) {curr.name = value;}
62
+ else if ("raw".equals(key)) {curr.raw = toByteArray(value);} //!
63
+ else if ("type".equals(key)) {curr.type = ParserType.parse(value);}
64
+ else if ("method".equals(key)) {curr.method = HTTPMethod.parse(value);}
65
+ else if ("status_code".equals(key)) {curr.status_code = Integer.parseInt(value);}
66
+ else if ("request_path".equals(key)) {curr.request_path = value;}
67
+ else if ("request_url".equals(key)) {curr.request_url = value;}
68
+
69
+ else if ("fragment".equals(key)) {curr.fragment = value;}
70
+ else if ("query_string".equals(key)) {curr.query_string = value;}
71
+ else if ("body".equals(key)) {curr.body = toByteArray(value);} //!
72
+ else if ("body_size".equals(key)) {curr.body_size = Integer.parseInt(value);}
73
+ else if (key.startsWith("header")) {
74
+ String [] h = getHeader(value);
75
+ curr.header.put(h[0], h[1]);
76
+ }
77
+ else if ("should_keep_alive".equals(key))
78
+ {curr.should_keep_alive = (1 == Integer.parseInt(value));}
79
+ else if ("upgrade".equals(key)) {curr.upgrade = (1 == Integer.parseInt(value));}
80
+ else if ("http_major".equals(key)) {curr.http_major = Integer.parseInt(value);}
81
+ else if ("http_minor".equals(key)) {curr.http_minor = Integer.parseInt(value);}
82
+ } else {
83
+ p("WTF?"+line);
84
+ }
85
+
86
+ }
87
+ return list;
88
+ }
89
+
90
+ String [] getHeader(String value) {
91
+ // { "Host": "0.0.0.0=5000"}
92
+ Pattern p = Pattern.compile("\\{ ?\"([^\"]*)\": ?\"([^\"]*)\"}");
93
+ Matcher m = p.matcher(value);
94
+ if (!m.matches()) {
95
+ p(value);
96
+ throw new RuntimeException("something wrong");
97
+ }
98
+ String [] result = new String[2];
99
+ MatchResult r = m.toMatchResult();
100
+ result[0] = r.group(1).trim();
101
+ result[1] = r.group(2).trim();
102
+ return result;
103
+ }
104
+
105
+ static final byte BSLASH = 0x5c;
106
+ static final byte QUOT = 0x22;
107
+ static final byte CR = 0x0d;
108
+ static final byte LF = 0x0a;
109
+ static final byte n = 0x6e;
110
+ static final byte r = 0x72;
111
+
112
+ static final Byte[] JAVA_GENERICS_ROCK_HARD = new Byte[0];
113
+
114
+
115
+ static byte [] toByteArray (String quotedString) {
116
+ ArrayList<Byte> bytes = new ArrayList<Byte>();
117
+ String s = quotedString.substring(1, quotedString.length()-1);
118
+ byte [] byts = s.getBytes(java.nio.charset.Charset.forName("ASCII"));
119
+ boolean escaped = false;
120
+ for (byte b : byts) {
121
+ switch (b) {
122
+ case BSLASH:
123
+ escaped = true;
124
+ break;
125
+ case n:
126
+ if (escaped) {
127
+ bytes.add(LF);
128
+ escaped = false;
129
+ } else {
130
+ bytes.add(b);
131
+ }
132
+ break;
133
+ case r:
134
+ if (escaped) {
135
+ escaped = false;
136
+ bytes.add(CR);
137
+ } else {
138
+ bytes.add(b);
139
+ }
140
+ break;
141
+ case QUOT:
142
+ escaped = false;
143
+ bytes.add(QUOT);
144
+ break;
145
+ default:
146
+ bytes.add(b);
147
+ }
148
+
149
+ }
150
+ //Byte [] fuckyou = bytes.toArray(JAVA_GENERICS_ROCK_HARD);
151
+ //return (byte[])fuckyou;
152
+ byts = new byte[bytes.size()];
153
+ int i = 0;
154
+ for (Byte b : bytes) {
155
+ byts[i++]=b;
156
+ // OMG, WFTBBQ!?
157
+ }
158
+ return byts;
159
+ }
160
+
161
+ public static void main(String [] args) throws Throwable {
162
+ TestLoaderNG l = new TestLoaderNG(args[0]);
163
+ List<Test> ts = l.load();
164
+
165
+ for (Test t : ts) {
166
+ t.execute_permutations();
167
+ // t.execute();
168
+ // System.exit(0);
169
+ }
170
+ }
171
+
172
+
173
+ class Test {
174
+ String name;
175
+ byte [] raw;
176
+ ParserType type;
177
+ HTTPMethod method;
178
+ int status_code;
179
+ String request_path; // byte [] ?
180
+ String request_url;
181
+ String fragment ;
182
+ String query_string;
183
+ byte [] body;
184
+ int body_size;
185
+ Map<String,String> header;
186
+ boolean should_keep_alive;
187
+ boolean upgrade;
188
+ int http_major;
189
+ int http_minor;
190
+
191
+ boolean message_begin_called;
192
+ boolean message_complete_called;
193
+ boolean headers_complete_called;
194
+
195
+
196
+ Map<String,String> parsed_header;
197
+ String currHField;
198
+ String currHValue;
199
+ byte [] pbody;
200
+
201
+ public String toString() {
202
+ StringBuilder b = new StringBuilder();
203
+ b.append("type: "); b.append(type);b.append("\n");
204
+ b.append("method: "); b.append(method);b.append("\n");
205
+ b.append("status_code: "); b.append(status_code);b.append("\n");
206
+ b.append("request_path: "); b.append(request_path);b.append("\n");
207
+ b.append("request_url: "); b.append(request_url);b.append("\n");
208
+ b.append("fragment: "); b.append(fragment);b.append("\n");
209
+ b.append("query_string: "); b.append(query_string);b.append("\n");
210
+ b.append("body:\n"); b.append(new String(body));b.append("\n");
211
+ b.append("should_keep_alive: "); b.append(should_keep_alive);b.append("\n");
212
+ b.append("upgrade: "); b.append(upgrade);b.append("\n");
213
+ b.append("http_major: "); b.append(http_major);b.append("\n");
214
+ b.append("http_minor: "); b.append(http_minor);b.append("\n");
215
+ b.append("message_complete_called: "); b.append(message_complete_called);b.append("\n");
216
+ return b.toString();
217
+ }
218
+
219
+ Test () {
220
+ this.header = new HashMap<String, String>();
221
+ reset();
222
+ }
223
+ /*
224
+ *prepare this Test Instance for reuse.
225
+ * */
226
+ void reset () {
227
+ this.parsed_header = new HashMap<String, String>();
228
+ this.pbody = null;
229
+
230
+ }
231
+ void check (boolean val, String mes) {
232
+ if (!val) {
233
+ //p(name+" : "+mes);
234
+ throw new RuntimeException(name+" : "+mes);
235
+ }
236
+ }
237
+
238
+ String str (ByteBuffer b, int pos, int len) {
239
+ byte [] by = new byte[len];
240
+ int saved = b.position();
241
+ b.position(pos);
242
+ b.get(by);
243
+ b.position(saved);
244
+ return new String(by);
245
+ }
246
+
247
+ HTTPDataCallback getCB (final String value, final String mes, final TestSettings settings) {
248
+ return new HTTPDataCallback() {
249
+ public int cb (HTTPParser p, ByteBuffer b, int pos, int len){
250
+ // if ("url".equals(mes)){
251
+ // p("pos"+pos);
252
+ // p("len"+len);
253
+ // if (8==pos && 5 == len && "connect request".equals(name)) {
254
+ // //throw new RuntimeException(name);
255
+ // }
256
+ // }
257
+ String str = str(b, pos, len);
258
+ String prev_val = settings.map.get(mes);
259
+ settings.map.put(mes, prev_val + str);
260
+ //check(value.equals(str), "incorrect "+mes+": "+str);
261
+ if (-1 == pos) {
262
+ throw new RuntimeException("he?");
263
+ }
264
+ return 0;
265
+ }
266
+ };
267
+ }
268
+
269
+ void execute () {
270
+ p(name);
271
+ ByteBuffer buf = ByteBuffer.wrap(raw);
272
+ HTTPParser p = new HTTPParser();
273
+ TestSettings s = settings();
274
+
275
+
276
+
277
+ p.execute(s, buf);
278
+ if (!s.success) {
279
+ throw new RuntimeException("Test: "+name+"failed");
280
+ }
281
+ } // execute
282
+
283
+ void execute_permutations() {
284
+ /*
285
+ |-|---------------|
286
+ |--|--------------|
287
+ |---|-------------|
288
+ (...)
289
+ |---------------|-|
290
+ |-----------------|
291
+ */
292
+ p(name);
293
+ for (int i = 2; i != raw.length; ++i) {
294
+ // p(i);
295
+ HTTPParser p = new HTTPParser();
296
+ TestSettings s = settings();
297
+ ByteBuffer buf = ByteBuffer.wrap(raw);
298
+ int olimit = buf.limit();
299
+ buf.limit(i);
300
+
301
+ parse(p,s,buf);
302
+
303
+ buf.position(i);
304
+ buf.limit(olimit);
305
+
306
+ parse(p,s,buf);
307
+ parse(p,s,buf);
308
+
309
+ if (!s.success) {
310
+ p(this);
311
+ throw new RuntimeException("Test: "+name+" failed");
312
+ }
313
+ reset();
314
+ }
315
+ //System.exit(0);
316
+ } // execute_permutations
317
+ void parse(HTTPParser p, ParserSettings s, ByteBuffer b) {
318
+ //p("About to parse: "+b.position() + "->" + b.limit());
319
+ p.execute(s, b);
320
+ }
321
+
322
+ TestSettings settings() {
323
+ final TestSettings s = new TestSettings();
324
+ s.on_path = getCB(request_path, "path", s);
325
+ s.on_query_string = getCB(query_string, "query_string", s);
326
+ s.on_url = getCB(request_url, "url", s);
327
+ s.on_fragment = getCB(fragment, "fragment", s);
328
+ s.on_message_begin = new HTTPCallback() {
329
+ public int cb (HTTPParser p) {
330
+ message_begin_called = true;
331
+ return -1;
332
+ }
333
+ };
334
+ s.on_header_field = new HTTPDataCallback() {
335
+ public int cb (HTTPParser p, ByteBuffer b, int pos, int len){
336
+ if (null != currHValue && null == currHField) {
337
+ throw new RuntimeException(name+": shouldn't happen");
338
+ }
339
+ if (null != currHField) {
340
+ if (null == currHValue) {
341
+ currHField += str(b,pos,len);
342
+ return 0;
343
+ } else {
344
+ parsed_header.put(currHField, currHValue);
345
+ currHField = null;
346
+ currHValue = null;
347
+ }
348
+ }
349
+ currHField = str(b,pos,len);
350
+ return 0;
351
+ }
352
+ };
353
+ s.on_header_value = new HTTPDataCallback() {
354
+ public int cb (HTTPParser p, ByteBuffer b, int pos, int len){
355
+ if (null == currHField) {
356
+ throw new RuntimeException(name+" :shouldn't happen field");
357
+ }
358
+ if (null == currHValue) {
359
+ currHValue = str(b,pos,len);
360
+ } else {
361
+ currHValue += str(b, pos, len);
362
+ }
363
+ return 0;
364
+ }
365
+ };
366
+ s.on_headers_complete = new HTTPCallback() {
367
+ public int cb (HTTPParser p) {
368
+ headers_complete_called = true;
369
+ String parsed_path = s.map.get("path");
370
+ String parsed_query = s.map.get("query_string");
371
+ String parsed_url = s.map.get("url");
372
+ String parsed_frag = s.map.get("fragment");
373
+
374
+ if (!request_path.equals(parsed_path)) {
375
+ throw new RuntimeException(name+": invalid path: "+parsed_path+" should be: "+request_path);
376
+ }
377
+ if (!query_string.equals(parsed_query)) {
378
+ throw new RuntimeException(name+": invalid query: "+parsed_query+" should be: "+query_string);
379
+ }
380
+ if (!request_url.equals(parsed_url)) {
381
+ throw new RuntimeException(">"+name+"<: invalid url: >"+parsed_url+"< should be: >"+request_url+"<");
382
+ }
383
+ if (!fragment.equals(parsed_frag)) {
384
+ throw new RuntimeException(name+": invalid fragement: "+parsed_frag+" should be: "+fragment);
385
+ }
386
+ if (null != currHValue || null != currHField) {
387
+ if (null == currHField || null == currHValue) {
388
+ throw new RuntimeException("shouldn't happen");
389
+ }
390
+ }
391
+ if (null != currHField) {
392
+ parsed_header.put(currHField, currHValue);
393
+ currHField = null;
394
+ currHValue = null;
395
+ }
396
+
397
+
398
+ return 0;
399
+ }
400
+ };
401
+ // s.on_headers_complete = new HTTPCallback() {
402
+ // public int cb (HTTPParser p) {
403
+ // p("Complete:"+name);
404
+ // return 0;
405
+ // }
406
+ // };
407
+
408
+ s.on_body = new HTTPDataCallback() {
409
+ public int cb (HTTPParser p, ByteBuffer b, int pos, int len){
410
+ int l = pbody == null ? len : len + pbody.length;
411
+ int off = pbody == null ? 0 : pbody.length;
412
+ byte [] nbody = new byte[l];
413
+
414
+ if (null != pbody) {
415
+ System.arraycopy(pbody, 0, nbody, 0, pbody.length);
416
+ }
417
+
418
+ int saved = b.position();
419
+ b.position(pos);
420
+ b.get(nbody, off, len);
421
+ b.position(saved);
422
+ pbody = nbody;
423
+ return 0;
424
+ }
425
+ };
426
+
427
+ s.on_message_complete = new HTTPCallback() {
428
+ public int cb(HTTPParser p) {
429
+ message_complete_called = true;
430
+ if ( p.http_minor != http_minor
431
+ || p.http_major != http_major
432
+ || p.status_code != status_code ) {
433
+
434
+ throw new RuntimeException("major/minor/status_code mismatch");
435
+ }
436
+
437
+ //check headers
438
+
439
+ if (header.keySet().size() != parsed_header.keySet().size()) {
440
+ p(parsed_header);
441
+ throw new RuntimeException(name+": different amount of headers");
442
+ }
443
+ for (String key : header.keySet()) {
444
+ String pvalue = parsed_header.get(key);
445
+ if (!header.get(key).equals(pvalue)) {
446
+ throw new RuntimeException(name+" : different values for :"+key+" is >"+pvalue+"< should: >"+header.get(key)+"<");
447
+ }
448
+ }
449
+ //check body
450
+ if (null == pbody && (null == body || body.length == 0 || body.length == 1)) {
451
+ s.success = true;
452
+ return 0;
453
+ }
454
+ if (null == pbody) {
455
+ throw new RuntimeException(name+": no body, should be: "+new String(body));
456
+ }
457
+ if (pbody.length != body.length) {
458
+ p(pbody.length);
459
+ p(body.length);
460
+ p(new String(pbody));
461
+ p(new String(body));
462
+ throw new RuntimeException(name+": incorrect body length");
463
+ }
464
+ for (int i = 0 ; i!= body.length; ++i) {
465
+ if (pbody[i] != body[i]) {
466
+ throw new RuntimeException("different body");
467
+ }
468
+ }
469
+ s.success = true;
470
+ return 0;
471
+ }
472
+ };
473
+ return s;
474
+ } // settings
475
+ }
476
+ class TestSettings extends ParserSettings {
477
+ public boolean success;
478
+ Map<String, String> map;
479
+ TestSettings () {
480
+ map = new HashMap<String, String>();
481
+ map.put("path", "");
482
+ map.put("query_string", "");
483
+ map.put("url", "");
484
+ map.put("fragment", "");
485
+ }
486
+ }
487
+ }