http_parser.rb 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +11 -0
- data/.gitmodules +6 -0
- data/README.md +45 -0
- data/Rakefile +6 -0
- data/bench/thin.rb +57 -0
- data/ext/ruby_http_parser/.gitignore +1 -0
- data/ext/ruby_http_parser/RubyHttpParserService.java +18 -0
- data/ext/ruby_http_parser/ext_help.h +18 -0
- data/ext/ruby_http_parser/extconf.rb +16 -0
- data/ext/ruby_http_parser/org/ruby_http_parser/RubyHttpParser.java +403 -0
- data/ext/ruby_http_parser/ruby_http_parser.c +474 -0
- data/ext/ruby_http_parser/vendor/.gitkeep +0 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/CONTRIBUTIONS +4 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/LICENSE-MIT +19 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/README.md +171 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/TODO +19 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/compile +1 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/http_parser.c +1590 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/http_parser.h +167 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPException.java +7 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPMethod.java +90 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPParser.java +31 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/ParserType.java +13 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/HTTPCallback.java +5 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/HTTPDataCallback.java +25 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/HTTPErrorCallback.java +7 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/HTTPParser.java +1894 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/ParserSettings.java +78 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/Util.java +112 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/TestLoaderNG.java +487 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/UnitTest.java +115 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/test.c +1865 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/test_permutations +1 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/test_unit +1 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/tests.dumped +539 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/tools/byte_constants.rb +6 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/tools/const_char.rb +13 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/tools/lowcase.rb +15 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/tools/parse_tests.rb +33 -0
- data/ext/ruby_http_parser/vendor/http-parser/CONTRIBUTIONS +4 -0
- data/ext/ruby_http_parser/vendor/http-parser/LICENSE-MIT +19 -0
- data/ext/ruby_http_parser/vendor/http-parser/README.md +171 -0
- data/ext/ruby_http_parser/vendor/http-parser/http_parser.c +1590 -0
- data/ext/ruby_http_parser/vendor/http-parser/http_parser.h +167 -0
- data/ext/ruby_http_parser/vendor/http-parser/test.c +1755 -0
- data/http_parser.rb.gemspec +15 -0
- data/lib/http/parser.rb +1 -0
- data/lib/http_parser.rb +4 -0
- data/spec/parser_spec.rb +187 -0
- data/spec/spec_helper.rb +2 -0
- data/spec/support/requests.json +381 -0
- data/spec/support/responses.json +186 -0
- data/tasks/compile.rake +39 -0
- data/tasks/spec.rake +5 -0
- data/tasks/submodules.rake +7 -0
- metadata +121 -0
data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/ParserSettings.java
ADDED
@@ -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
|
+
}
|
data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/TestLoaderNG.java
ADDED
@@ -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
|
+
}
|