http_parser.rb 0.5.0-x86-mingw32
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/.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/lib/ruby_http_parser.rb +2 -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 +124 -0
@@ -0,0 +1,167 @@
|
|
1
|
+
/* Copyright 2009,2010 Ryan Dahl <ry@tinyclouds.org>
|
2
|
+
*
|
3
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
* of this software and associated documentation files (the "Software"), to
|
5
|
+
* deal in the Software without restriction, including without limitation the
|
6
|
+
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
7
|
+
* sell copies of the Software, and to permit persons to whom the Software is
|
8
|
+
* furnished to do so, subject to the following conditions:
|
9
|
+
*
|
10
|
+
* The above copyright notice and this permission notice shall be included in
|
11
|
+
* all copies or substantial portions of the Software.
|
12
|
+
*
|
13
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
18
|
+
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
19
|
+
* IN THE SOFTWARE.
|
20
|
+
*/
|
21
|
+
#ifndef http_parser_h
|
22
|
+
#define http_parser_h
|
23
|
+
#ifdef __cplusplus
|
24
|
+
extern "C" {
|
25
|
+
#endif
|
26
|
+
|
27
|
+
|
28
|
+
#include <sys/types.h>
|
29
|
+
#include <stdint.h>
|
30
|
+
|
31
|
+
#ifdef _WIN32
|
32
|
+
typedef unsigned int size_t;
|
33
|
+
typedef int ssize_t;
|
34
|
+
#endif
|
35
|
+
|
36
|
+
/* Compile with -DHTTP_PARSER_STRICT=0 to make less checks, but run
|
37
|
+
* faster
|
38
|
+
*/
|
39
|
+
#ifndef HTTP_PARSER_STRICT
|
40
|
+
# define HTTP_PARSER_STRICT 1
|
41
|
+
#else
|
42
|
+
# define HTTP_PARSER_STRICT 0
|
43
|
+
#endif
|
44
|
+
|
45
|
+
|
46
|
+
/* Maximium header size allowed */
|
47
|
+
#define HTTP_MAX_HEADER_SIZE (80*1024)
|
48
|
+
|
49
|
+
|
50
|
+
typedef struct http_parser http_parser;
|
51
|
+
typedef struct http_parser_settings http_parser_settings;
|
52
|
+
|
53
|
+
|
54
|
+
/* Callbacks should return non-zero to indicate an error. The parser will
|
55
|
+
* then halt execution.
|
56
|
+
*
|
57
|
+
* The one exception is on_headers_complete. In a HTTP_RESPONSE parser
|
58
|
+
* returning '1' from on_headers_complete will tell the parser that it
|
59
|
+
* should not expect a body. This is used when receiving a response to a
|
60
|
+
* HEAD request which may contain 'Content-Length' or 'Transfer-Encoding:
|
61
|
+
* chunked' headers that indicate the presence of a body.
|
62
|
+
*
|
63
|
+
* http_data_cb does not return data chunks. It will be call arbitrarally
|
64
|
+
* many times for each string. E.G. you might get 10 callbacks for "on_path"
|
65
|
+
* each providing just a few characters more data.
|
66
|
+
*/
|
67
|
+
typedef int (*http_data_cb) (http_parser*, const char *at, size_t length);
|
68
|
+
typedef int (*http_cb) (http_parser*);
|
69
|
+
|
70
|
+
|
71
|
+
/* Request Methods */
|
72
|
+
enum http_method
|
73
|
+
{ HTTP_DELETE = 0
|
74
|
+
, HTTP_GET
|
75
|
+
, HTTP_HEAD
|
76
|
+
, HTTP_POST
|
77
|
+
, HTTP_PUT
|
78
|
+
/* pathological */
|
79
|
+
, HTTP_CONNECT
|
80
|
+
, HTTP_OPTIONS
|
81
|
+
, HTTP_TRACE
|
82
|
+
/* webdav */
|
83
|
+
, HTTP_COPY
|
84
|
+
, HTTP_LOCK
|
85
|
+
, HTTP_MKCOL
|
86
|
+
, HTTP_MOVE
|
87
|
+
, HTTP_PROPFIND
|
88
|
+
, HTTP_PROPPATCH
|
89
|
+
, HTTP_UNLOCK
|
90
|
+
/* subversion */
|
91
|
+
, HTTP_REPORT
|
92
|
+
, HTTP_MKACTIVITY
|
93
|
+
, HTTP_CHECKOUT
|
94
|
+
, HTTP_MERGE
|
95
|
+
};
|
96
|
+
|
97
|
+
|
98
|
+
enum http_parser_type { HTTP_REQUEST, HTTP_RESPONSE, HTTP_BOTH };
|
99
|
+
|
100
|
+
|
101
|
+
struct http_parser {
|
102
|
+
/** PRIVATE **/
|
103
|
+
unsigned char type : 2;
|
104
|
+
unsigned char flags : 6;
|
105
|
+
unsigned char state;
|
106
|
+
unsigned char header_state;
|
107
|
+
unsigned char index;
|
108
|
+
|
109
|
+
uint32_t nread;
|
110
|
+
int64_t content_length;
|
111
|
+
|
112
|
+
/** READ-ONLY **/
|
113
|
+
unsigned short http_major;
|
114
|
+
unsigned short http_minor;
|
115
|
+
unsigned short status_code; /* responses only */
|
116
|
+
unsigned char method; /* requests only */
|
117
|
+
|
118
|
+
/* 1 = Upgrade header was present and the parser has exited because of that.
|
119
|
+
* 0 = No upgrade header present.
|
120
|
+
* Should be checked when http_parser_execute() returns in addition to
|
121
|
+
* error checking.
|
122
|
+
*/
|
123
|
+
char upgrade;
|
124
|
+
|
125
|
+
/** PUBLIC **/
|
126
|
+
void *data; /* A pointer to get hook to the "connection" or "socket" object */
|
127
|
+
};
|
128
|
+
|
129
|
+
|
130
|
+
struct http_parser_settings {
|
131
|
+
http_cb on_message_begin;
|
132
|
+
http_data_cb on_path;
|
133
|
+
http_data_cb on_query_string;
|
134
|
+
http_data_cb on_url;
|
135
|
+
http_data_cb on_fragment;
|
136
|
+
http_data_cb on_header_field;
|
137
|
+
http_data_cb on_header_value;
|
138
|
+
http_cb on_headers_complete;
|
139
|
+
http_data_cb on_body;
|
140
|
+
http_cb on_message_complete;
|
141
|
+
};
|
142
|
+
|
143
|
+
|
144
|
+
void http_parser_init(http_parser *parser, enum http_parser_type type);
|
145
|
+
|
146
|
+
|
147
|
+
size_t http_parser_execute(http_parser *parser,
|
148
|
+
const http_parser_settings *settings,
|
149
|
+
const char *data,
|
150
|
+
size_t len);
|
151
|
+
|
152
|
+
|
153
|
+
/* If http_should_keep_alive() in the on_headers_complete or
|
154
|
+
* on_message_complete callback returns true, then this will be should be
|
155
|
+
* the last message on the connection.
|
156
|
+
* If you are the server, respond with the "Connection: close" header.
|
157
|
+
* If you are the client, close the connection.
|
158
|
+
*/
|
159
|
+
int http_should_keep_alive(http_parser *parser);
|
160
|
+
|
161
|
+
/* Returns a string version of the HTTP method. */
|
162
|
+
const char *http_method_str(enum http_method);
|
163
|
+
|
164
|
+
#ifdef __cplusplus
|
165
|
+
}
|
166
|
+
#endif
|
167
|
+
#endif
|
@@ -0,0 +1,90 @@
|
|
1
|
+
package http_parser;
|
2
|
+
|
3
|
+
import java.nio.charset.Charset;
|
4
|
+
|
5
|
+
public enum HTTPMethod {
|
6
|
+
HTTP_DELETE("DELETE")// = 0
|
7
|
+
, HTTP_GET("GET")
|
8
|
+
, HTTP_HEAD("HEAD")
|
9
|
+
, HTTP_POST("POST")
|
10
|
+
, HTTP_PUT("PUT")
|
11
|
+
/* pathological */
|
12
|
+
, HTTP_CONNECT("CONNECT")
|
13
|
+
, HTTP_OPTIONS("OPTIONS")
|
14
|
+
, HTTP_TRACE("TRACE")
|
15
|
+
/* webdav */
|
16
|
+
, HTTP_COPY("COPY")
|
17
|
+
, HTTP_LOCK("LOCK")
|
18
|
+
, HTTP_MKCOL("MKCOL")
|
19
|
+
, HTTP_MOVE("MOVE")
|
20
|
+
, HTTP_PROPFIND("PROPFIND")
|
21
|
+
, HTTP_PROPPATCH("PROPPATCH")
|
22
|
+
, HTTP_UNLOCK("UNLOCK")
|
23
|
+
, HTTP_REPORT("REPORT")
|
24
|
+
, HTTP_MKACTIVITY("MKACTIVITY")
|
25
|
+
, HTTP_CHECKOUT("CHECKOUT")
|
26
|
+
, HTTP_MERGE("MERGE");
|
27
|
+
|
28
|
+
private static Charset ASCII;
|
29
|
+
static {
|
30
|
+
ASCII = Charset.forName("US-ASCII");;
|
31
|
+
}
|
32
|
+
public byte[] bytes;
|
33
|
+
|
34
|
+
HTTPMethod(String name) {
|
35
|
+
// good grief, Charlie Brown, the following is necessary because
|
36
|
+
// java is retarded:
|
37
|
+
// illegal reference to static field from initializer
|
38
|
+
// this.bytes = name.getBytes(ASCII);
|
39
|
+
// yet it's not illegal to reference static fields from
|
40
|
+
// methods called from initializer.
|
41
|
+
init(name);
|
42
|
+
}
|
43
|
+
public static HTTPMethod parse(String s) {
|
44
|
+
if ("HTTP_DELETE".equalsIgnoreCase(s)) {return HTTP_DELETE;}
|
45
|
+
else if ("DELETE".equalsIgnoreCase(s)) {return HTTP_DELETE;}
|
46
|
+
else if ("HTTP_GET".equalsIgnoreCase(s)) {return HTTP_GET;}
|
47
|
+
else if ("GET".equalsIgnoreCase(s)) {return HTTP_GET;}
|
48
|
+
else if ("HTTP_HEAD".equalsIgnoreCase(s)) {return HTTP_HEAD;}
|
49
|
+
else if ("HEAD".equalsIgnoreCase(s)) {return HTTP_HEAD;}
|
50
|
+
else if ("HTTP_POST".equalsIgnoreCase(s)) {return HTTP_POST;}
|
51
|
+
else if ("POST".equalsIgnoreCase(s)) {return HTTP_POST;}
|
52
|
+
else if ("HTTP_PUT".equalsIgnoreCase(s)) {return HTTP_PUT;}
|
53
|
+
else if ("PUT".equalsIgnoreCase(s)) {return HTTP_PUT;}
|
54
|
+
else if ("HTTP_CONNECT".equalsIgnoreCase(s)) {return HTTP_CONNECT;}
|
55
|
+
else if ("CONNECT".equalsIgnoreCase(s)) {return HTTP_CONNECT;}
|
56
|
+
else if ("HTTP_OPTIONS".equalsIgnoreCase(s)) {return HTTP_OPTIONS;}
|
57
|
+
else if ("OPTIONS".equalsIgnoreCase(s)) {return HTTP_OPTIONS;}
|
58
|
+
else if ("HTTP_TRACE".equalsIgnoreCase(s)) {return HTTP_TRACE;}
|
59
|
+
else if ("TRACE".equalsIgnoreCase(s)) {return HTTP_TRACE;}
|
60
|
+
else if ("HTTP_COPY".equalsIgnoreCase(s)) {return HTTP_COPY;}
|
61
|
+
else if ("COPY".equalsIgnoreCase(s)) {return HTTP_COPY;}
|
62
|
+
else if ("HTTP_LOCK".equalsIgnoreCase(s)) {return HTTP_LOCK;}
|
63
|
+
else if ("LOCK".equalsIgnoreCase(s)) {return HTTP_LOCK;}
|
64
|
+
else if ("HTTP_MKCOL".equalsIgnoreCase(s)) {return HTTP_MKCOL;}
|
65
|
+
else if ("MKCOL".equalsIgnoreCase(s)) {return HTTP_MKCOL;}
|
66
|
+
else if ("HTTP_MOVE".equalsIgnoreCase(s)) {return HTTP_MOVE;}
|
67
|
+
else if ("MOVE".equalsIgnoreCase(s)) {return HTTP_MOVE;}
|
68
|
+
else if ("HTTP_PROPFIND".equalsIgnoreCase(s)){return HTTP_PROPFIND;}
|
69
|
+
else if ("PROPFIND".equalsIgnoreCase(s)) {return HTTP_PROPFIND;}
|
70
|
+
else if ("HTTP_PROPPATCH".equalsIgnoreCase(s)){return HTTP_PROPPATCH;}
|
71
|
+
else if ("PROPPATCH".equalsIgnoreCase(s)) {return HTTP_PROPPATCH;}
|
72
|
+
else if ("HTTP_UNLOCK".equalsIgnoreCase(s)) {return HTTP_UNLOCK;}
|
73
|
+
else if ("UNLOCK".equalsIgnoreCase(s)) {return HTTP_UNLOCK;}
|
74
|
+
else if ("HTTP_REPORT".equalsIgnoreCase(s)) {return HTTP_REPORT;}
|
75
|
+
else if ("REPORT".equalsIgnoreCase(s)){return HTTP_REPORT;}
|
76
|
+
else if ("HTTP_MKACTIVITY".equalsIgnoreCase(s)) {return HTTP_MKACTIVITY;}
|
77
|
+
else if ("MKACTIVITY".equalsIgnoreCase(s)){return HTTP_MKACTIVITY;}
|
78
|
+
else if ("HTTP_CHECKOUT".equalsIgnoreCase(s)) {return HTTP_CHECKOUT;}
|
79
|
+
else if ("CHECKOUT".equalsIgnoreCase(s)){return HTTP_CHECKOUT;}
|
80
|
+
else if ("HTTP_MERGE".equalsIgnoreCase(s)) {return HTTP_MERGE;}
|
81
|
+
else if ("MERGE".equalsIgnoreCase(s)){return HTTP_MERGE;}
|
82
|
+
|
83
|
+
else {return null;}
|
84
|
+
}
|
85
|
+
void init (String name) {
|
86
|
+
ASCII = null == ASCII ? Charset.forName("US-ASCII") : ASCII;
|
87
|
+
this.bytes = name.getBytes(ASCII);
|
88
|
+
}
|
89
|
+
|
90
|
+
}
|
@@ -0,0 +1,31 @@
|
|
1
|
+
package http_parser;
|
2
|
+
|
3
|
+
public class HTTPParser extends http_parser.lolevel.HTTPParser {
|
4
|
+
|
5
|
+
public HTTPParser() { super(); }
|
6
|
+
public HTTPParser(ParserType type) { super(type); }
|
7
|
+
|
8
|
+
public int getMajor() {
|
9
|
+
return super.http_major;
|
10
|
+
}
|
11
|
+
|
12
|
+
public int getMinor() {
|
13
|
+
return super.http_minor;
|
14
|
+
}
|
15
|
+
|
16
|
+
public int getStatusCode() {
|
17
|
+
return super.status_code;
|
18
|
+
}
|
19
|
+
|
20
|
+
public HTTPMethod getHTTPMethod() {
|
21
|
+
return super.method;
|
22
|
+
}
|
23
|
+
|
24
|
+
public boolean getUpgrade() {
|
25
|
+
return super.upgrade;
|
26
|
+
}
|
27
|
+
|
28
|
+
public boolean shouldKeepAlive() {
|
29
|
+
return super.http_should_keep_alive();
|
30
|
+
}
|
31
|
+
}
|
@@ -0,0 +1,13 @@
|
|
1
|
+
package http_parser;
|
2
|
+
|
3
|
+
public enum ParserType {
|
4
|
+
HTTP_REQUEST, HTTP_RESPONSE, HTTP_BOTH;
|
5
|
+
|
6
|
+
public static ParserType parse(String s) {
|
7
|
+
if ("HTTP_REQUEST".equalsIgnoreCase(s)) { return HTTP_REQUEST; }
|
8
|
+
else if ("HTTP_RESPONSE".equalsIgnoreCase(s)) { return HTTP_RESPONSE; }
|
9
|
+
else if ("HTTP_BOTH".equalsIgnoreCase(s)) { return HTTP_BOTH; }
|
10
|
+
else { return null; }
|
11
|
+
}
|
12
|
+
}
|
13
|
+
|
data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/HTTPDataCallback.java
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
package http_parser.lolevel;
|
2
|
+
|
3
|
+
import java.nio.ByteBuffer;
|
4
|
+
|
5
|
+
public interface HTTPDataCallback {
|
6
|
+
/*
|
7
|
+
very raw and extremly foolhardy! DANGER!
|
8
|
+
The whole Buffer concept is difficult enough to grasp as it is,
|
9
|
+
we pass in a buffer with an arbitrary position.
|
10
|
+
|
11
|
+
The interesting data is located at position pos and is len
|
12
|
+
bytes long.
|
13
|
+
|
14
|
+
The contract of this callback is that the buffer is
|
15
|
+
returned in the state that it was passed in, so implementing
|
16
|
+
this require good citizenship, you'll need to remember the current
|
17
|
+
position, change the position to get at the data you're interested
|
18
|
+
in and then set the position back to how you found it...
|
19
|
+
|
20
|
+
//TODO: there should be an abstract implementation that implements
|
21
|
+
cb as described above, marks it final an provides a new callback
|
22
|
+
with signature cb(byte[], int, int)
|
23
|
+
*/
|
24
|
+
public int cb(HTTPParser p, ByteBuffer buf, int pos, int len);
|
25
|
+
}
|
data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/HTTPParser.java
ADDED
@@ -0,0 +1,1894 @@
|
|
1
|
+
package http_parser.lolevel;
|
2
|
+
|
3
|
+
import java.nio.ByteBuffer;
|
4
|
+
import http_parser.HTTPException;
|
5
|
+
import http_parser.HTTPMethod;
|
6
|
+
import http_parser.ParserType;
|
7
|
+
import static http_parser.lolevel.HTTPParser.C.*;
|
8
|
+
import static http_parser.lolevel.HTTPParser.State.*;
|
9
|
+
|
10
|
+
public class HTTPParser {
|
11
|
+
/* lots of unsigned chars here, not sure what
|
12
|
+
to about them, `bytes` in java suck... */
|
13
|
+
|
14
|
+
ParserType type;
|
15
|
+
State state;
|
16
|
+
HState header_state;
|
17
|
+
boolean strict;
|
18
|
+
|
19
|
+
int index;
|
20
|
+
int flags; // TODO
|
21
|
+
|
22
|
+
int nread;
|
23
|
+
int content_length;
|
24
|
+
|
25
|
+
|
26
|
+
/** READ-ONLY **/
|
27
|
+
protected int http_major;
|
28
|
+
protected int http_minor;
|
29
|
+
protected int status_code; /* responses only */
|
30
|
+
protected HTTPMethod method; /* requests only */
|
31
|
+
|
32
|
+
/* true = Upgrade header was present and the parser has exited because of that.
|
33
|
+
* false = No upgrade header present.
|
34
|
+
* Should be checked when http_parser_execute() returns in addition to
|
35
|
+
* error checking.
|
36
|
+
*/
|
37
|
+
protected boolean upgrade;
|
38
|
+
|
39
|
+
/** PUBLIC **/
|
40
|
+
// TODO : this is used in c to maintain application state.
|
41
|
+
// is this even necessary? we have state in java ?
|
42
|
+
// consider
|
43
|
+
// Object data; /* A pointer to get hook to the "connection" or "socket" object */
|
44
|
+
|
45
|
+
|
46
|
+
/*
|
47
|
+
* technically we could combine all of these (except for url_mark) into one
|
48
|
+
* variable, saving stack space, but it seems more clear to have them
|
49
|
+
* separated.
|
50
|
+
*/
|
51
|
+
int header_field_mark = -1;
|
52
|
+
int header_value_mark = -1;
|
53
|
+
int fragment_mark = -1;
|
54
|
+
int query_string_mark = -1;
|
55
|
+
int path_mark = -1;
|
56
|
+
int url_mark = -1;
|
57
|
+
|
58
|
+
/**
|
59
|
+
* Construct a Parser for ParserType.HTTP_BOTH, meaning it
|
60
|
+
* determines whether it's parsing a request or a response.
|
61
|
+
*/
|
62
|
+
public HTTPParser() {
|
63
|
+
this(ParserType.HTTP_BOTH);
|
64
|
+
}
|
65
|
+
|
66
|
+
/**
|
67
|
+
* Construct a Parser and initialise it to parse either
|
68
|
+
* requests or responses.
|
69
|
+
*/
|
70
|
+
public HTTPParser(ParserType type) {
|
71
|
+
this.type = type;
|
72
|
+
switch(type) {
|
73
|
+
case HTTP_REQUEST:
|
74
|
+
this.state = State.start_req;
|
75
|
+
break;
|
76
|
+
case HTTP_RESPONSE:
|
77
|
+
this.state = State.start_res;
|
78
|
+
break;
|
79
|
+
case HTTP_BOTH:
|
80
|
+
this.state = State.start_res_or_res;
|
81
|
+
break;
|
82
|
+
default:
|
83
|
+
throw new HTTPException("can't happen, invalid ParserType enum");
|
84
|
+
}
|
85
|
+
}
|
86
|
+
|
87
|
+
/*
|
88
|
+
* Utility to facilitate System.out.println style debugging (the way god intended)
|
89
|
+
*/
|
90
|
+
static void p(Object o) {System.out.println(o);}
|
91
|
+
|
92
|
+
|
93
|
+
/** Execute the parser with the currently available data contained in
|
94
|
+
* the buffer. The buffers position() and limit() need to be set
|
95
|
+
* correctly (obviously) and a will be updated approriately when the
|
96
|
+
* method returns to reflect the consumed data.
|
97
|
+
*/
|
98
|
+
public void execute(ParserSettings settings, ByteBuffer data) {
|
99
|
+
|
100
|
+
int p = data.position();
|
101
|
+
int p_err = p; // this is used for pretty printing errors.
|
102
|
+
|
103
|
+
// In case the headers don't provide information about the content
|
104
|
+
// length, `execute` needs to be called with an empty buffer to
|
105
|
+
// indicate that all the data has been send be the client/server,
|
106
|
+
// else there is no way of knowing the message is complete.
|
107
|
+
int len = (data.limit() - data.position());
|
108
|
+
if (0 == len) {
|
109
|
+
if (State.body_identity_eof == state) {
|
110
|
+
settings.call_on_message_complete(this);
|
111
|
+
}
|
112
|
+
}
|
113
|
+
|
114
|
+
|
115
|
+
// in case the _previous_ call to the parser only has data to get to
|
116
|
+
// the middle of certain fields, we need to update marks to point at
|
117
|
+
// the beginning of the current buffer.
|
118
|
+
switch (state) {
|
119
|
+
case header_field:
|
120
|
+
header_field_mark = p;
|
121
|
+
break;
|
122
|
+
case header_value:
|
123
|
+
header_value_mark = p;
|
124
|
+
break;
|
125
|
+
case req_fragment:
|
126
|
+
fragment_mark = p;
|
127
|
+
url_mark = p;
|
128
|
+
break;
|
129
|
+
case req_query_string:
|
130
|
+
query_string_mark = p;
|
131
|
+
url_mark = p;
|
132
|
+
break;
|
133
|
+
case req_path:
|
134
|
+
path_mark = p;
|
135
|
+
|
136
|
+
case req_host:
|
137
|
+
case req_schema:
|
138
|
+
case req_schema_slash:
|
139
|
+
case req_schema_slash_slash:
|
140
|
+
case req_port:
|
141
|
+
case req_query_string_start:
|
142
|
+
case req_fragment_start:
|
143
|
+
url_mark = p;
|
144
|
+
break;
|
145
|
+
}
|
146
|
+
|
147
|
+
// this is where the work gets done, traverse the available data...
|
148
|
+
while (data.position() != data.limit()) {
|
149
|
+
|
150
|
+
p = data.position();
|
151
|
+
int pe = data.limit();
|
152
|
+
|
153
|
+
byte ch = data.get(); // the current character to process.
|
154
|
+
byte c = -1; // utility variably used for up- and downcasing etc.
|
155
|
+
int to_read = 0; // used to keep track of how much of body, etc. is left to read
|
156
|
+
|
157
|
+
if (parsing_header(state)) {
|
158
|
+
++nread;
|
159
|
+
if (nread > HTTP_MAX_HEADER_SIZE) {
|
160
|
+
settings.call_on_error(this, "possible buffer overflow", data, p_err);
|
161
|
+
}
|
162
|
+
}
|
163
|
+
|
164
|
+
switch (state) {
|
165
|
+
/*
|
166
|
+
* this state is used after a 'Connection: close' message
|
167
|
+
* the parser will error out if it reads another message
|
168
|
+
*/
|
169
|
+
case dead:
|
170
|
+
settings.call_on_error(this, "Connection already closed", data, p_err);
|
171
|
+
|
172
|
+
|
173
|
+
|
174
|
+
case start_res_or_res:
|
175
|
+
if (CR == ch || LF == ch){
|
176
|
+
break;
|
177
|
+
}
|
178
|
+
flags = 0;
|
179
|
+
content_length = -1;
|
180
|
+
|
181
|
+
settings.call_on_message_begin(this);
|
182
|
+
|
183
|
+
if (H == ch) {
|
184
|
+
state = State.res_or_resp_H;
|
185
|
+
} else {
|
186
|
+
type = ParserType.HTTP_REQUEST;
|
187
|
+
method = start_req_method_assign(ch);
|
188
|
+
if (null == method) {
|
189
|
+
settings.call_on_error(this, "invalid method", data, p_err);
|
190
|
+
}
|
191
|
+
index = 1;
|
192
|
+
state = State.req_method;
|
193
|
+
}
|
194
|
+
break;
|
195
|
+
|
196
|
+
|
197
|
+
|
198
|
+
case res_or_resp_H:
|
199
|
+
if (T == ch) {
|
200
|
+
type = ParserType.HTTP_RESPONSE;
|
201
|
+
state = State.res_HT;
|
202
|
+
} else {
|
203
|
+
if (E != ch) {
|
204
|
+
settings.call_on_error(this, "not E", data, p_err);
|
205
|
+
}
|
206
|
+
type = ParserType.HTTP_REQUEST;
|
207
|
+
method = HTTPMethod.HTTP_HEAD;
|
208
|
+
index = 2;
|
209
|
+
state = State.req_method;
|
210
|
+
}
|
211
|
+
break;
|
212
|
+
|
213
|
+
|
214
|
+
|
215
|
+
case start_res:
|
216
|
+
flags = 0;
|
217
|
+
content_length = -1;
|
218
|
+
|
219
|
+
settings.call_on_message_begin(this);
|
220
|
+
|
221
|
+
switch(ch) {
|
222
|
+
case H:
|
223
|
+
state = State.res_H;
|
224
|
+
break;
|
225
|
+
case CR:
|
226
|
+
case LF:
|
227
|
+
break;
|
228
|
+
default:
|
229
|
+
settings.call_on_error(this, "Not H or CR/LF", data, p_err);
|
230
|
+
}
|
231
|
+
break;
|
232
|
+
|
233
|
+
|
234
|
+
|
235
|
+
case res_H:
|
236
|
+
if (strict && T != ch) {
|
237
|
+
settings.call_on_error(this, "Not T", data, p_err);
|
238
|
+
}
|
239
|
+
state = State.res_HT;
|
240
|
+
break;
|
241
|
+
case res_HT:
|
242
|
+
if (strict && T != ch) {
|
243
|
+
settings.call_on_error(this, "Not T2", data, p_err);
|
244
|
+
}
|
245
|
+
state = State.res_HTT;
|
246
|
+
break;
|
247
|
+
case res_HTT:
|
248
|
+
if (strict && P != ch) {
|
249
|
+
settings.call_on_error(this, "Not P", data, p_err);
|
250
|
+
}
|
251
|
+
state = State.res_HTTP;
|
252
|
+
break;
|
253
|
+
case res_HTTP:
|
254
|
+
if (strict && SLASH != ch) {
|
255
|
+
settings.call_on_error(this, "Not '/'", data, p_err);
|
256
|
+
}
|
257
|
+
state = State.res_first_http_major;
|
258
|
+
break;
|
259
|
+
|
260
|
+
|
261
|
+
|
262
|
+
case res_first_http_major:
|
263
|
+
if (!isDigit(ch)) {
|
264
|
+
settings.call_on_error(this, "Not a digit", data, p_err);
|
265
|
+
}
|
266
|
+
http_major = (int) ch - 0x30;
|
267
|
+
state = State.res_http_major;
|
268
|
+
break;
|
269
|
+
|
270
|
+
/* major HTTP version or dot */
|
271
|
+
case res_http_major:
|
272
|
+
if (DOT == ch) {
|
273
|
+
state = State.res_http_minor;
|
274
|
+
break;
|
275
|
+
}
|
276
|
+
if (!isDigit(ch)) {
|
277
|
+
settings.call_on_error(this, "Not a digit", data, p_err);
|
278
|
+
}
|
279
|
+
http_major *= 10;
|
280
|
+
http_major += (ch - 0x30);
|
281
|
+
|
282
|
+
if (http_major > 999) {
|
283
|
+
settings.call_on_error(this, "invalid http major version: "+http_major, data, p_err);
|
284
|
+
}
|
285
|
+
break;
|
286
|
+
|
287
|
+
/* first digit of minor HTTP version */
|
288
|
+
case res_first_http_minor:
|
289
|
+
if (!isDigit(ch)) {
|
290
|
+
settings.call_on_error(this, "Not a digit", data, p_err);
|
291
|
+
}
|
292
|
+
http_minor = (int)ch - 0x30;
|
293
|
+
state = State.res_http_minor;
|
294
|
+
break;
|
295
|
+
|
296
|
+
/* minor HTTP version or end of request line */
|
297
|
+
case res_http_minor:
|
298
|
+
if (SPACE == ch) {
|
299
|
+
state = State.res_first_status_code;
|
300
|
+
break;
|
301
|
+
}
|
302
|
+
if (!isDigit(ch)) {
|
303
|
+
settings.call_on_error(this, "Not a digit", data, p_err);
|
304
|
+
}
|
305
|
+
http_minor *= 10;
|
306
|
+
http_minor += (ch - 0x30);
|
307
|
+
|
308
|
+
if (http_minor > 999) {
|
309
|
+
settings.call_on_error(this, "invalid http minor version: "+http_minor, data, p_err);
|
310
|
+
}
|
311
|
+
break;
|
312
|
+
|
313
|
+
|
314
|
+
|
315
|
+
case res_first_status_code:
|
316
|
+
if (!isDigit(ch)) {
|
317
|
+
if (SPACE == ch) {
|
318
|
+
break;
|
319
|
+
}
|
320
|
+
settings.call_on_error(this, "Not a digit (status code)", data, p_err);
|
321
|
+
}
|
322
|
+
status_code = (int)ch - 0x30;
|
323
|
+
state = State.res_status_code;
|
324
|
+
break;
|
325
|
+
|
326
|
+
case res_status_code:
|
327
|
+
if (!isDigit(ch)) {
|
328
|
+
switch(ch) {
|
329
|
+
case SPACE:
|
330
|
+
state = State.res_status;
|
331
|
+
break;
|
332
|
+
case CR:
|
333
|
+
state = State.res_line_almost_done;
|
334
|
+
break;
|
335
|
+
case LF:
|
336
|
+
state = State.header_field_start;
|
337
|
+
break;
|
338
|
+
default:
|
339
|
+
settings.call_on_error(this, "not a valid status code", data, p_err);
|
340
|
+
}
|
341
|
+
break;
|
342
|
+
}
|
343
|
+
status_code *= 10;
|
344
|
+
status_code += (int)ch - 0x30;
|
345
|
+
if (status_code > 999) {
|
346
|
+
settings.call_on_error(this, "ridiculous status code:"+status_code, data, p_err);
|
347
|
+
}
|
348
|
+
break;
|
349
|
+
|
350
|
+
case res_status:
|
351
|
+
/* the human readable status. e.g. "NOT FOUND"
|
352
|
+
* we are not humans so just ignore this
|
353
|
+
* we are not men, we are devo. */
|
354
|
+
|
355
|
+
if (CR == ch) {
|
356
|
+
state = State.res_line_almost_done;
|
357
|
+
break;
|
358
|
+
}
|
359
|
+
if (LF == ch) {
|
360
|
+
state = State.header_field_start;
|
361
|
+
break;
|
362
|
+
}
|
363
|
+
break;
|
364
|
+
|
365
|
+
case res_line_almost_done:
|
366
|
+
if (strict && LF != ch) {
|
367
|
+
settings.call_on_error(this, "not LF", data, p_err);
|
368
|
+
}
|
369
|
+
state = State.header_field_start;
|
370
|
+
break;
|
371
|
+
|
372
|
+
|
373
|
+
|
374
|
+
case start_req:
|
375
|
+
if (CR==ch || LF == LF) {
|
376
|
+
break;
|
377
|
+
}
|
378
|
+
flags = 0;
|
379
|
+
content_length = -1;
|
380
|
+
settings.call_on_message_begin(this);
|
381
|
+
method = start_req_method_assign(ch);
|
382
|
+
if (null == method) {
|
383
|
+
settings.call_on_error(this, "invalid method", data, p_err);
|
384
|
+
}
|
385
|
+
index = 1;
|
386
|
+
state = State.req_method;
|
387
|
+
break;
|
388
|
+
|
389
|
+
|
390
|
+
|
391
|
+
case req_method:
|
392
|
+
if (0 == ch) {
|
393
|
+
settings.call_on_error(this, "NULL in method", data, p_err);
|
394
|
+
}
|
395
|
+
|
396
|
+
byte [] arr = method.bytes;
|
397
|
+
|
398
|
+
if (SPACE == ch && index == arr.length) {
|
399
|
+
state = State.req_spaces_before_url;
|
400
|
+
} else if (arr[index] == ch) {
|
401
|
+
// wuhu!
|
402
|
+
} else if (HTTPMethod.HTTP_CONNECT == method) {
|
403
|
+
if (1 == index && H == ch) {
|
404
|
+
method = HTTPMethod.HTTP_CHECKOUT;
|
405
|
+
} else if (2 == index && P == ch) {
|
406
|
+
method = HTTPMethod.HTTP_COPY;
|
407
|
+
}
|
408
|
+
} else if (HTTPMethod.HTTP_MKCOL == method) {
|
409
|
+
if (1 == index && O == ch) {
|
410
|
+
method = HTTPMethod.HTTP_MOVE;
|
411
|
+
} else if (1 == index && E == ch) {
|
412
|
+
method = HTTPMethod.HTTP_MERGE;
|
413
|
+
} else if (2 == index && A == ch) {
|
414
|
+
method = HTTPMethod.HTTP_MKACTIVITY;
|
415
|
+
}
|
416
|
+
} else if (1 == index && HTTPMethod.HTTP_POST == method && R == ch) {
|
417
|
+
method = HTTPMethod.HTTP_PROPFIND;
|
418
|
+
} else if (1 == index && HTTPMethod.HTTP_POST == method && U == ch) {
|
419
|
+
method = HTTPMethod.HTTP_PUT;
|
420
|
+
} else if (4 == index && HTTPMethod.HTTP_PROPFIND == method && P == ch) {
|
421
|
+
method = HTTPMethod.HTTP_PROPPATCH;
|
422
|
+
} else {
|
423
|
+
settings.call_on_error(this, "Invalid HTTP method", data, p_err);
|
424
|
+
}
|
425
|
+
|
426
|
+
++index;
|
427
|
+
break;
|
428
|
+
|
429
|
+
|
430
|
+
|
431
|
+
/******************* URL *******************/
|
432
|
+
case req_spaces_before_url:
|
433
|
+
if (SPACE == ch) {
|
434
|
+
break;
|
435
|
+
}
|
436
|
+
if (SLASH == ch) {
|
437
|
+
url_mark = p;
|
438
|
+
path_mark = p;
|
439
|
+
state = State.req_path;
|
440
|
+
break;
|
441
|
+
}
|
442
|
+
if (isAtoZ(ch)) {
|
443
|
+
url_mark = p;
|
444
|
+
state = State.req_schema;
|
445
|
+
break;
|
446
|
+
}
|
447
|
+
settings.call_on_error(this, "Invalid something", data, p_err);
|
448
|
+
|
449
|
+
case req_schema:
|
450
|
+
if (isAtoZ(ch)){
|
451
|
+
break;
|
452
|
+
}
|
453
|
+
if (COLON == ch) {
|
454
|
+
state = State.req_schema_slash;
|
455
|
+
break;
|
456
|
+
} else if (DOT == ch) {
|
457
|
+
state = State.req_host;
|
458
|
+
break;
|
459
|
+
}
|
460
|
+
settings.call_on_error(this, "invalid char in schema: "+ch, data, p_err);
|
461
|
+
|
462
|
+
case req_schema_slash:
|
463
|
+
if (strict && SLASH != ch) {
|
464
|
+
settings.call_on_error(this, "invalid char in schema, not /", data, p_err);
|
465
|
+
}
|
466
|
+
state = State.req_schema_slash_slash;
|
467
|
+
break;
|
468
|
+
|
469
|
+
case req_schema_slash_slash:
|
470
|
+
if (strict && SLASH != ch) {
|
471
|
+
settings.call_on_error(this, "invalid char in schema, not /", data, p_err);
|
472
|
+
}
|
473
|
+
state = State.req_host;
|
474
|
+
break;
|
475
|
+
|
476
|
+
case req_host:
|
477
|
+
if (isAtoZ(ch)) {
|
478
|
+
break;
|
479
|
+
}
|
480
|
+
if (isDigit(ch) || DOT == ch || DASH == ch) break;
|
481
|
+
switch (ch) {
|
482
|
+
case COLON:
|
483
|
+
state = State.req_port;
|
484
|
+
break;
|
485
|
+
case SLASH:
|
486
|
+
path_mark = p;
|
487
|
+
break;
|
488
|
+
case SPACE:
|
489
|
+
/* The request line looks like:
|
490
|
+
* "GET http://foo.bar.com HTTP/1.1"
|
491
|
+
* That is, there is no path.
|
492
|
+
*/
|
493
|
+
settings.call_on_url(this, data, url_mark, p-url_mark);
|
494
|
+
url_mark = -1;
|
495
|
+
state = State.req_http_start;
|
496
|
+
break;
|
497
|
+
default:
|
498
|
+
settings.call_on_error(this, "host error in method line", data, p_err);
|
499
|
+
}
|
500
|
+
break;
|
501
|
+
|
502
|
+
case req_port:
|
503
|
+
if (isDigit(ch)) break;
|
504
|
+
switch (ch) {
|
505
|
+
case SLASH:
|
506
|
+
path_mark = p;
|
507
|
+
state = State.req_path;
|
508
|
+
break;
|
509
|
+
case SPACE:
|
510
|
+
/* The request line looks like:
|
511
|
+
* "GET http://foo.bar.com:1234 HTTP/1.1"
|
512
|
+
* That is, there is no path.
|
513
|
+
*/
|
514
|
+
settings.call_on_url(this,data,url_mark,p-url_mark);
|
515
|
+
url_mark = -1;
|
516
|
+
state = State.req_http_start;
|
517
|
+
break;
|
518
|
+
default:
|
519
|
+
settings.call_on_error(this, "invalid port", data, p_err);
|
520
|
+
}
|
521
|
+
break;
|
522
|
+
|
523
|
+
case req_path:
|
524
|
+
if (normal_url_char[ch]) break;
|
525
|
+
switch (ch) {
|
526
|
+
case SPACE:
|
527
|
+
settings.call_on_url(this,data,url_mark, p-url_mark);
|
528
|
+
url_mark = -1;
|
529
|
+
|
530
|
+
settings.call_on_path(this,data,path_mark, p-path_mark);
|
531
|
+
path_mark = -1;
|
532
|
+
|
533
|
+
state = State.req_http_start;
|
534
|
+
break;
|
535
|
+
|
536
|
+
case CR:
|
537
|
+
settings.call_on_url(this,data,url_mark, p-url_mark);
|
538
|
+
url_mark = -1;
|
539
|
+
|
540
|
+
settings.call_on_path(this,data,path_mark, p-path_mark);
|
541
|
+
path_mark = -1;
|
542
|
+
|
543
|
+
http_minor = 9;
|
544
|
+
state = State.res_line_almost_done;
|
545
|
+
break;
|
546
|
+
|
547
|
+
case LF:
|
548
|
+
settings.call_on_url(this,data,url_mark, p-url_mark);
|
549
|
+
url_mark = -1;
|
550
|
+
|
551
|
+
settings.call_on_path(this,data,path_mark, p-path_mark);
|
552
|
+
path_mark = -1;
|
553
|
+
|
554
|
+
http_minor = 9;
|
555
|
+
state = State.header_field_start;
|
556
|
+
break;
|
557
|
+
|
558
|
+
case QMARK:
|
559
|
+
settings.call_on_path(this,data,path_mark, p-path_mark);
|
560
|
+
path_mark = -1;
|
561
|
+
|
562
|
+
state = State.req_query_string_start;
|
563
|
+
break;
|
564
|
+
|
565
|
+
case HASH:
|
566
|
+
settings.call_on_path(this,data,path_mark, p-path_mark);
|
567
|
+
path_mark = -1;
|
568
|
+
|
569
|
+
state = State.req_fragment_start;
|
570
|
+
break;
|
571
|
+
|
572
|
+
default:
|
573
|
+
settings.call_on_error(this, "unexpected char in path", data, p_err);
|
574
|
+
}
|
575
|
+
break;
|
576
|
+
|
577
|
+
case req_query_string_start:
|
578
|
+
if (normal_url_char[ch]) {
|
579
|
+
query_string_mark = p;
|
580
|
+
state = State.req_query_string;
|
581
|
+
break;
|
582
|
+
}
|
583
|
+
|
584
|
+
switch (ch) {
|
585
|
+
case QMARK: break;
|
586
|
+
case SPACE:
|
587
|
+
settings.call_on_url(this, data, url_mark, p-url_mark);
|
588
|
+
url_mark = -1;
|
589
|
+
state = State.req_http_start;
|
590
|
+
break;
|
591
|
+
case CR:
|
592
|
+
settings.call_on_url(this,data,url_mark, p-url_mark);
|
593
|
+
url_mark = -1;
|
594
|
+
http_minor = 9;
|
595
|
+
state = State.res_line_almost_done;
|
596
|
+
break;
|
597
|
+
case LF:
|
598
|
+
settings.call_on_url(this,data,url_mark, p-url_mark);
|
599
|
+
url_mark = -1;
|
600
|
+
http_minor = 9;
|
601
|
+
state = State.header_field_start;
|
602
|
+
break;
|
603
|
+
case HASH:
|
604
|
+
state = State.req_fragment_start;
|
605
|
+
break;
|
606
|
+
default:
|
607
|
+
settings.call_on_error(this, "unexpected char in path", data, p_err);
|
608
|
+
}
|
609
|
+
break;
|
610
|
+
|
611
|
+
case req_query_string:
|
612
|
+
if (normal_url_char[ch]) {
|
613
|
+
break;
|
614
|
+
}
|
615
|
+
|
616
|
+
switch (ch) {
|
617
|
+
case QMARK: break; // allow extra '?' in query string
|
618
|
+
case SPACE:
|
619
|
+
settings.call_on_url(this, data, url_mark, p-url_mark);
|
620
|
+
url_mark = -1;
|
621
|
+
|
622
|
+
settings.call_on_query_string(this, data, query_string_mark, p-query_string_mark);
|
623
|
+
query_string_mark = -1;
|
624
|
+
|
625
|
+
state = State.req_http_start;
|
626
|
+
break;
|
627
|
+
case CR:
|
628
|
+
settings.call_on_url(this,data,url_mark, p-url_mark);
|
629
|
+
url_mark = -1;
|
630
|
+
|
631
|
+
settings.call_on_query_string(this, data, query_string_mark, p-query_string_mark);
|
632
|
+
query_string_mark = -1;
|
633
|
+
|
634
|
+
http_minor = 9;
|
635
|
+
state = State.res_line_almost_done;
|
636
|
+
break;
|
637
|
+
case LF:
|
638
|
+
settings.call_on_url(this,data,url_mark, p-url_mark);
|
639
|
+
url_mark = -1;
|
640
|
+
|
641
|
+
settings.call_on_query_string(this, data, query_string_mark, p-query_string_mark);
|
642
|
+
query_string_mark = -1;
|
643
|
+
http_minor = 9;
|
644
|
+
|
645
|
+
state = State.header_field_start;
|
646
|
+
break;
|
647
|
+
case HASH:
|
648
|
+
settings.call_on_query_string(this, data, query_string_mark, p-query_string_mark);
|
649
|
+
query_string_mark = -1;
|
650
|
+
|
651
|
+
state = State.req_fragment_start;
|
652
|
+
break;
|
653
|
+
default:
|
654
|
+
settings.call_on_error(this, "unexpected char in path", data, p_err);
|
655
|
+
}
|
656
|
+
break;
|
657
|
+
|
658
|
+
case req_fragment_start:
|
659
|
+
if (normal_url_char[ch]) {
|
660
|
+
fragment_mark = p;
|
661
|
+
state = State.req_fragment;
|
662
|
+
break;
|
663
|
+
}
|
664
|
+
|
665
|
+
switch (ch) {
|
666
|
+
case SPACE:
|
667
|
+
settings.call_on_url(this, data, url_mark, p-url_mark);
|
668
|
+
url_mark = -1;
|
669
|
+
|
670
|
+
state = State.req_http_start;
|
671
|
+
break;
|
672
|
+
case CR:
|
673
|
+
settings.call_on_url(this,data,url_mark, p-url_mark);
|
674
|
+
url_mark = -1;
|
675
|
+
|
676
|
+
http_minor = 9;
|
677
|
+
state = State.res_line_almost_done;
|
678
|
+
break;
|
679
|
+
case LF:
|
680
|
+
settings.call_on_url(this,data,url_mark, p-url_mark);
|
681
|
+
url_mark = -1;
|
682
|
+
|
683
|
+
http_minor = 9;
|
684
|
+
state = State.header_field_start;
|
685
|
+
break;
|
686
|
+
case QMARK:
|
687
|
+
fragment_mark = p;
|
688
|
+
state = State.req_fragment;
|
689
|
+
break;
|
690
|
+
case HASH:
|
691
|
+
break;
|
692
|
+
default:
|
693
|
+
settings.call_on_error(this, "unexpected char in path", data, p_err);
|
694
|
+
}
|
695
|
+
break;
|
696
|
+
|
697
|
+
case req_fragment:
|
698
|
+
if (normal_url_char[ch]) {
|
699
|
+
break;
|
700
|
+
}
|
701
|
+
|
702
|
+
switch (ch) {
|
703
|
+
case SPACE:
|
704
|
+
settings.call_on_url(this, data, url_mark, p-url_mark);
|
705
|
+
url_mark = -1;
|
706
|
+
|
707
|
+
settings.call_on_fragment(this, data, fragment_mark, p-fragment_mark);
|
708
|
+
fragment_mark = -1;
|
709
|
+
|
710
|
+
state = State.req_http_start;
|
711
|
+
break;
|
712
|
+
case CR:
|
713
|
+
settings.call_on_url(this,data,url_mark, p-url_mark);
|
714
|
+
url_mark = -1;
|
715
|
+
|
716
|
+
settings.call_on_fragment(this, data, query_string_mark, p-query_string_mark);
|
717
|
+
fragment_mark = -1;
|
718
|
+
|
719
|
+
http_minor = 9;
|
720
|
+
state = State.res_line_almost_done;
|
721
|
+
break;
|
722
|
+
case LF:
|
723
|
+
settings.call_on_url(this,data,url_mark, p-url_mark);
|
724
|
+
url_mark = -1;
|
725
|
+
|
726
|
+
settings.call_on_fragment(this, data, query_string_mark, p-query_string_mark);
|
727
|
+
fragment_mark = -1;
|
728
|
+
|
729
|
+
http_minor = 9;
|
730
|
+
state = State.header_field_start;
|
731
|
+
break;
|
732
|
+
case QMARK:
|
733
|
+
case HASH:
|
734
|
+
break;
|
735
|
+
default:
|
736
|
+
settings.call_on_error(this, "unexpected char in path", data, p_err);
|
737
|
+
}
|
738
|
+
break;
|
739
|
+
/******************* URL *******************/
|
740
|
+
|
741
|
+
|
742
|
+
|
743
|
+
/******************* HTTP 1.1 *******************/
|
744
|
+
case req_http_start:
|
745
|
+
switch (ch) {
|
746
|
+
case H:
|
747
|
+
state = State.req_http_H;
|
748
|
+
break;
|
749
|
+
case SPACE:
|
750
|
+
break;
|
751
|
+
default:
|
752
|
+
settings.call_on_error(this, "error in req_http_H", data, p_err);
|
753
|
+
}
|
754
|
+
break;
|
755
|
+
|
756
|
+
case req_http_H:
|
757
|
+
if (strict && T != ch) {
|
758
|
+
settings.call_on_error(this, "unexpected char", data, p_err);
|
759
|
+
}
|
760
|
+
state = State.req_http_HT;
|
761
|
+
break;
|
762
|
+
|
763
|
+
case req_http_HT:
|
764
|
+
if (strict && T != ch) {
|
765
|
+
settings.call_on_error(this, "unexpected char", data, p_err);
|
766
|
+
}
|
767
|
+
state = State.req_http_HTT;
|
768
|
+
break;
|
769
|
+
|
770
|
+
case req_http_HTT:
|
771
|
+
if (strict && P != ch) {
|
772
|
+
settings.call_on_error(this, "unexpected char", data, p_err);
|
773
|
+
}
|
774
|
+
state = State.req_http_HTTP;
|
775
|
+
break;
|
776
|
+
|
777
|
+
case req_http_HTTP:
|
778
|
+
if (strict && SLASH != ch) {
|
779
|
+
settings.call_on_error(this, "unexpected char", data, p_err);
|
780
|
+
}
|
781
|
+
state = req_first_http_major;
|
782
|
+
break;
|
783
|
+
|
784
|
+
/* first digit of major HTTP version */
|
785
|
+
case req_first_http_major:
|
786
|
+
if (!isDigit(ch)) {
|
787
|
+
settings.call_on_error(this, "non digit in http major", data, p_err);
|
788
|
+
}
|
789
|
+
http_major = (int)ch - 0x30;
|
790
|
+
state = State.req_http_major;
|
791
|
+
break;
|
792
|
+
|
793
|
+
/* major HTTP version or dot */
|
794
|
+
case req_http_major:
|
795
|
+
if (DOT == ch) {
|
796
|
+
state = State.req_first_http_minor;
|
797
|
+
break;
|
798
|
+
}
|
799
|
+
|
800
|
+
if (!isDigit(ch)) {
|
801
|
+
settings.call_on_error(this, "non digit in http major", data, p_err);
|
802
|
+
}
|
803
|
+
|
804
|
+
http_major *= 10;
|
805
|
+
http_major += (int)ch - 0x30;
|
806
|
+
|
807
|
+
if (http_major > 999) {
|
808
|
+
settings.call_on_error(this, "ridiculous http major", data, p_err);
|
809
|
+
};
|
810
|
+
break;
|
811
|
+
|
812
|
+
/* first digit of minor HTTP version */
|
813
|
+
case req_first_http_minor:
|
814
|
+
if (!isDigit(ch)) {
|
815
|
+
settings.call_on_error(this, "non digit in http minor", data, p_err);
|
816
|
+
}
|
817
|
+
http_minor = (int)ch - 0x30;
|
818
|
+
state = State.req_http_minor;
|
819
|
+
break;
|
820
|
+
|
821
|
+
case req_http_minor:
|
822
|
+
if (ch == CR) {
|
823
|
+
state = State.req_line_almost_done;
|
824
|
+
break;
|
825
|
+
}
|
826
|
+
|
827
|
+
if (ch == LF) {
|
828
|
+
state = State.header_field_start;
|
829
|
+
break;
|
830
|
+
}
|
831
|
+
|
832
|
+
/* XXX allow spaces after digit? */
|
833
|
+
|
834
|
+
if (!isDigit(ch)) {
|
835
|
+
settings.call_on_error(this, "non digit in http minor", data, p_err);
|
836
|
+
}
|
837
|
+
|
838
|
+
http_minor *= 10;
|
839
|
+
http_minor += (int)ch - 0x30;
|
840
|
+
|
841
|
+
|
842
|
+
if (http_minor > 999) {
|
843
|
+
settings.call_on_error(this, "ridiculous http minor", data, p_err);
|
844
|
+
};
|
845
|
+
|
846
|
+
break;
|
847
|
+
|
848
|
+
/* end of request line */
|
849
|
+
case req_line_almost_done:
|
850
|
+
{
|
851
|
+
if (ch != LF) {
|
852
|
+
settings.call_on_error(this, "missing LF after request line", data, p_err);
|
853
|
+
}
|
854
|
+
state = State.header_field_start;
|
855
|
+
break;
|
856
|
+
}
|
857
|
+
|
858
|
+
/******************* HTTP 1.1 *******************/
|
859
|
+
|
860
|
+
|
861
|
+
|
862
|
+
/******************* Header *******************/
|
863
|
+
case header_field_start:
|
864
|
+
{
|
865
|
+
if (ch == CR) {
|
866
|
+
state = State.headers_almost_done;
|
867
|
+
break;
|
868
|
+
}
|
869
|
+
|
870
|
+
if (ch == LF) {
|
871
|
+
/* they might be just sending \n instead of \r\n so this would be
|
872
|
+
* the second \n to denote the end of headers*/
|
873
|
+
state = State.headers_almost_done;
|
874
|
+
if (!headers_almost_done(ch, settings)) {
|
875
|
+
settings.call_on_error(this, "header not properly completed", data, p_err);
|
876
|
+
}
|
877
|
+
break;
|
878
|
+
}
|
879
|
+
|
880
|
+
c = upper(ch);
|
881
|
+
|
882
|
+
if (c < A || Z < c) {
|
883
|
+
settings.call_on_error(this, "invalid char in header", data, p_err);
|
884
|
+
};
|
885
|
+
|
886
|
+
header_field_mark = p;
|
887
|
+
|
888
|
+
index = 0;
|
889
|
+
state = State.header_field;
|
890
|
+
|
891
|
+
switch (c) {
|
892
|
+
case C:
|
893
|
+
header_state = HState.C;
|
894
|
+
break;
|
895
|
+
|
896
|
+
case P:
|
897
|
+
header_state = HState.matching_proxy_connection;
|
898
|
+
break;
|
899
|
+
|
900
|
+
case T:
|
901
|
+
header_state = HState.matching_transfer_encoding;
|
902
|
+
break;
|
903
|
+
|
904
|
+
case U:
|
905
|
+
header_state = HState.matching_upgrade;
|
906
|
+
break;
|
907
|
+
|
908
|
+
default:
|
909
|
+
header_state = HState.general;
|
910
|
+
break;
|
911
|
+
}
|
912
|
+
break;
|
913
|
+
}
|
914
|
+
|
915
|
+
|
916
|
+
|
917
|
+
case header_field:
|
918
|
+
{
|
919
|
+
c = (byte) acceptable_header[ch];
|
920
|
+
if (0 != c) {
|
921
|
+
switch (header_state) {
|
922
|
+
case general:
|
923
|
+
break;
|
924
|
+
|
925
|
+
case C:
|
926
|
+
index++;
|
927
|
+
header_state = (O == c ? HState.CO : HState.general);
|
928
|
+
break;
|
929
|
+
|
930
|
+
case CO:
|
931
|
+
index++;
|
932
|
+
header_state = (N == c ? HState.CON : HState.general);
|
933
|
+
break;
|
934
|
+
|
935
|
+
case CON:
|
936
|
+
index++;
|
937
|
+
switch (c) {
|
938
|
+
case N:
|
939
|
+
header_state = HState.matching_connection;
|
940
|
+
break;
|
941
|
+
case T:
|
942
|
+
header_state = HState.matching_content_length;
|
943
|
+
break;
|
944
|
+
default:
|
945
|
+
header_state = HState.general;
|
946
|
+
break;
|
947
|
+
}
|
948
|
+
break;
|
949
|
+
|
950
|
+
/* connection */
|
951
|
+
|
952
|
+
case matching_connection:
|
953
|
+
index++;
|
954
|
+
if (index > CONNECTION.length || c != CONNECTION[index]) {
|
955
|
+
header_state = HState.general;
|
956
|
+
} else if (index == CONNECTION.length-1) {
|
957
|
+
header_state = HState.connection;
|
958
|
+
}
|
959
|
+
break;
|
960
|
+
|
961
|
+
/* proxy-connection */
|
962
|
+
|
963
|
+
case matching_proxy_connection:
|
964
|
+
index++;
|
965
|
+
if (index > PROXY_CONNECTION.length || c != PROXY_CONNECTION[index]) {
|
966
|
+
header_state = HState.general;
|
967
|
+
} else if (index == PROXY_CONNECTION.length-1) {
|
968
|
+
header_state = HState.connection;
|
969
|
+
}
|
970
|
+
break;
|
971
|
+
|
972
|
+
/* content-length */
|
973
|
+
|
974
|
+
case matching_content_length:
|
975
|
+
index++;
|
976
|
+
if (index > CONTENT_LENGTH.length || c != CONTENT_LENGTH[index]) {
|
977
|
+
header_state = HState.general;
|
978
|
+
} else if (index == CONTENT_LENGTH.length-1) {
|
979
|
+
header_state = HState.content_length;
|
980
|
+
}
|
981
|
+
break;
|
982
|
+
|
983
|
+
/* transfer-encoding */
|
984
|
+
|
985
|
+
case matching_transfer_encoding:
|
986
|
+
index++;
|
987
|
+
if (index > TRANSFER_ENCODING.length || c != TRANSFER_ENCODING[index]) {
|
988
|
+
header_state = HState.general;
|
989
|
+
} else if (index == TRANSFER_ENCODING.length-1) {
|
990
|
+
header_state = HState.transfer_encoding;
|
991
|
+
}
|
992
|
+
break;
|
993
|
+
|
994
|
+
/* upgrade */
|
995
|
+
|
996
|
+
case matching_upgrade:
|
997
|
+
index++;
|
998
|
+
if (index > UPGRADE.length || c != UPGRADE[index]) {
|
999
|
+
header_state = HState.general;
|
1000
|
+
} else if (index == UPGRADE.length-1) {
|
1001
|
+
header_state = HState.upgrade;
|
1002
|
+
}
|
1003
|
+
break;
|
1004
|
+
|
1005
|
+
case connection:
|
1006
|
+
case content_length:
|
1007
|
+
case transfer_encoding:
|
1008
|
+
case upgrade:
|
1009
|
+
if (SPACE != ch) header_state = HState.general;
|
1010
|
+
break;
|
1011
|
+
|
1012
|
+
default:
|
1013
|
+
settings.call_on_error(this, "Unknown Header State", data, p_err);
|
1014
|
+
} // switch: header_state
|
1015
|
+
break;
|
1016
|
+
} // 0 != c
|
1017
|
+
|
1018
|
+
if (COLON == ch) {
|
1019
|
+
settings.call_on_header_field(this, data, header_field_mark, p-header_field_mark);
|
1020
|
+
header_field_mark = -1;
|
1021
|
+
|
1022
|
+
state = State.header_value_start;
|
1023
|
+
break;
|
1024
|
+
}
|
1025
|
+
|
1026
|
+
if (CR == ch) {
|
1027
|
+
state = State.header_almost_done;
|
1028
|
+
settings.call_on_header_field(this, data, header_field_mark, p-header_field_mark);
|
1029
|
+
|
1030
|
+
header_field_mark = -1;
|
1031
|
+
break;
|
1032
|
+
}
|
1033
|
+
|
1034
|
+
if (ch == LF) {
|
1035
|
+
settings.call_on_header_field(this, data, header_field_mark, p-header_field_mark);
|
1036
|
+
header_field_mark = -1;
|
1037
|
+
|
1038
|
+
state = State.header_field_start;
|
1039
|
+
break;
|
1040
|
+
}
|
1041
|
+
|
1042
|
+
settings.call_on_error(this, "invalid header field", data, p_err);
|
1043
|
+
}
|
1044
|
+
|
1045
|
+
|
1046
|
+
|
1047
|
+
case header_value_start:
|
1048
|
+
{
|
1049
|
+
if (SPACE == ch) break;
|
1050
|
+
|
1051
|
+
header_value_mark = p;
|
1052
|
+
|
1053
|
+
state = State.header_value;
|
1054
|
+
index = 0;
|
1055
|
+
|
1056
|
+
c = (byte)acceptable_header[ch];
|
1057
|
+
|
1058
|
+
if (c == 0) {
|
1059
|
+
if (CR == ch) {
|
1060
|
+
settings.call_on_header_value(this, data, header_value_mark, p-header_value_mark);
|
1061
|
+
header_value_mark = -1;
|
1062
|
+
|
1063
|
+
header_state = HState.general;
|
1064
|
+
state = State.header_almost_done;
|
1065
|
+
break;
|
1066
|
+
}
|
1067
|
+
|
1068
|
+
if (LF == ch) {
|
1069
|
+
settings.call_on_header_value(this, data, header_value_mark, p-header_value_mark);
|
1070
|
+
header_value_mark = -1;
|
1071
|
+
|
1072
|
+
state = State.header_field_start;
|
1073
|
+
break;
|
1074
|
+
}
|
1075
|
+
|
1076
|
+
header_state = HState.general;
|
1077
|
+
break;
|
1078
|
+
}
|
1079
|
+
|
1080
|
+
switch (header_state) {
|
1081
|
+
case upgrade:
|
1082
|
+
flags |= F_UPGRADE;
|
1083
|
+
header_state = HState.general;
|
1084
|
+
break;
|
1085
|
+
|
1086
|
+
case transfer_encoding:
|
1087
|
+
/* looking for 'Transfer-Encoding: chunked' */
|
1088
|
+
if (C == c) {
|
1089
|
+
header_state = HState.matching_transfer_encoding_chunked;
|
1090
|
+
} else {
|
1091
|
+
header_state = HState.general;
|
1092
|
+
}
|
1093
|
+
break;
|
1094
|
+
|
1095
|
+
case content_length:
|
1096
|
+
if (!isDigit(ch)) {
|
1097
|
+
settings.call_on_error(this, "Content-Length not numeric", data, p_err);
|
1098
|
+
}
|
1099
|
+
content_length = (int)ch - 0x30;
|
1100
|
+
break;
|
1101
|
+
|
1102
|
+
case connection:
|
1103
|
+
/* looking for 'Connection: keep-alive' */
|
1104
|
+
if (K == c) {
|
1105
|
+
header_state = HState.matching_connection_keep_alive;
|
1106
|
+
/* looking for 'Connection: close' */
|
1107
|
+
} else if (C == c) {
|
1108
|
+
header_state = HState.matching_connection_close;
|
1109
|
+
} else {
|
1110
|
+
header_state = HState.general;
|
1111
|
+
}
|
1112
|
+
break;
|
1113
|
+
|
1114
|
+
default:
|
1115
|
+
header_state = HState.general;
|
1116
|
+
break;
|
1117
|
+
}
|
1118
|
+
break;
|
1119
|
+
} // header value start
|
1120
|
+
|
1121
|
+
|
1122
|
+
|
1123
|
+
case header_value:
|
1124
|
+
{
|
1125
|
+
c = (byte)acceptable_header[ch];
|
1126
|
+
|
1127
|
+
if (c == 0) {
|
1128
|
+
if (CR == ch) {
|
1129
|
+
settings.call_on_header_value(this, data, header_value_mark, p-header_value_mark);
|
1130
|
+
header_value_mark = -1;
|
1131
|
+
|
1132
|
+
state = State.header_almost_done;
|
1133
|
+
break;
|
1134
|
+
}
|
1135
|
+
|
1136
|
+
if (LF == ch) {
|
1137
|
+
settings.call_on_header_value(this, data, header_value_mark, p-header_value_mark);
|
1138
|
+
header_value_mark = -1;
|
1139
|
+
|
1140
|
+
if (!header_almost_done(ch)) {
|
1141
|
+
settings.call_on_error(this,"incorrect header ending, expection LF", data, p_err);
|
1142
|
+
}
|
1143
|
+
break;
|
1144
|
+
}
|
1145
|
+
break;
|
1146
|
+
}
|
1147
|
+
|
1148
|
+
switch (header_state) {
|
1149
|
+
case general:
|
1150
|
+
break;
|
1151
|
+
|
1152
|
+
case connection:
|
1153
|
+
case transfer_encoding:
|
1154
|
+
settings.call_on_error(this, "Shouldn't be here", data, p_err);
|
1155
|
+
|
1156
|
+
case content_length:
|
1157
|
+
if (SPACE == ch) {
|
1158
|
+
break;
|
1159
|
+
}
|
1160
|
+
if (!isDigit(ch)) {
|
1161
|
+
settings.call_on_error(this, "Content-Length not numeric", data, p_err);
|
1162
|
+
}
|
1163
|
+
|
1164
|
+
content_length *= 10;
|
1165
|
+
content_length += (int)ch - 0x30;
|
1166
|
+
break;
|
1167
|
+
|
1168
|
+
/* Transfer-Encoding: chunked */
|
1169
|
+
case matching_transfer_encoding_chunked:
|
1170
|
+
index++;
|
1171
|
+
if (index > CHUNKED.length || c != CHUNKED[index]) {
|
1172
|
+
header_state = HState.general;
|
1173
|
+
} else if (index == CHUNKED.length-1) {
|
1174
|
+
header_state = HState.transfer_encoding_chunked;
|
1175
|
+
}
|
1176
|
+
break;
|
1177
|
+
|
1178
|
+
/* looking for 'Connection: keep-alive' */
|
1179
|
+
case matching_connection_keep_alive:
|
1180
|
+
index++;
|
1181
|
+
if (index > KEEP_ALIVE.length || c != KEEP_ALIVE[index]) {
|
1182
|
+
header_state = HState.general;
|
1183
|
+
} else if (index == KEEP_ALIVE.length-1) {
|
1184
|
+
header_state = HState.connection_keep_alive;
|
1185
|
+
}
|
1186
|
+
break;
|
1187
|
+
|
1188
|
+
/* looking for 'Connection: close' */
|
1189
|
+
case matching_connection_close:
|
1190
|
+
index++;
|
1191
|
+
if (index > CLOSE.length || c != CLOSE[index]) {
|
1192
|
+
header_state = HState.general;
|
1193
|
+
} else if (index == CLOSE.length-1) {
|
1194
|
+
header_state = HState.connection_close;
|
1195
|
+
}
|
1196
|
+
break;
|
1197
|
+
|
1198
|
+
case transfer_encoding_chunked:
|
1199
|
+
case connection_keep_alive:
|
1200
|
+
case connection_close:
|
1201
|
+
if (SPACE != ch) header_state = HState.general;
|
1202
|
+
break;
|
1203
|
+
|
1204
|
+
default:
|
1205
|
+
state = State.header_value;
|
1206
|
+
header_state = HState.general;
|
1207
|
+
break;
|
1208
|
+
}
|
1209
|
+
break;
|
1210
|
+
} // header_value
|
1211
|
+
|
1212
|
+
|
1213
|
+
|
1214
|
+
case header_almost_done:
|
1215
|
+
if (!header_almost_done(ch)) {
|
1216
|
+
settings.call_on_error(this,"incorrect header ending, expection LF", data, p_err);
|
1217
|
+
}
|
1218
|
+
break;
|
1219
|
+
|
1220
|
+
case headers_almost_done:
|
1221
|
+
if (!headers_almost_done(ch, settings)) {
|
1222
|
+
settings.call_on_error(this, "header not properly completed", data, p_err);
|
1223
|
+
}
|
1224
|
+
break;
|
1225
|
+
|
1226
|
+
/******************* Header *******************/
|
1227
|
+
|
1228
|
+
|
1229
|
+
|
1230
|
+
|
1231
|
+
/******************* Body *******************/
|
1232
|
+
case body_identity:
|
1233
|
+
to_read = min(pe - p, content_length); //TODO change to use buffer?
|
1234
|
+
|
1235
|
+
if (to_read > 0) {
|
1236
|
+
settings.call_on_body(this, data, p, to_read);
|
1237
|
+
data.position(p+to_read);
|
1238
|
+
content_length -= to_read;
|
1239
|
+
if (content_length == 0) {
|
1240
|
+
settings.call_on_message_complete(this);
|
1241
|
+
state = new_message();
|
1242
|
+
}
|
1243
|
+
}
|
1244
|
+
break;
|
1245
|
+
|
1246
|
+
|
1247
|
+
|
1248
|
+
case body_identity_eof:
|
1249
|
+
to_read = pe - p; // TODO change to use buffer ?
|
1250
|
+
if (to_read > 0) {
|
1251
|
+
settings.call_on_body(this, data, p, to_read);
|
1252
|
+
data.position(p+to_read);
|
1253
|
+
}
|
1254
|
+
break;
|
1255
|
+
/******************* Body *******************/
|
1256
|
+
|
1257
|
+
|
1258
|
+
|
1259
|
+
/******************* Chunk *******************/
|
1260
|
+
case chunk_size_start:
|
1261
|
+
if (0 == (flags & F_CHUNKED)) {
|
1262
|
+
settings.call_on_error(this, "not chunked", data, p_err);
|
1263
|
+
}
|
1264
|
+
|
1265
|
+
c = UNHEX[ch];
|
1266
|
+
if (c == -1) {
|
1267
|
+
settings.call_on_error(this, "invalid hex char in chunk content length", data, p_err);
|
1268
|
+
}
|
1269
|
+
content_length = c;
|
1270
|
+
state = State.chunk_size;
|
1271
|
+
break;
|
1272
|
+
|
1273
|
+
|
1274
|
+
|
1275
|
+
case chunk_size:
|
1276
|
+
if (0 == (flags & F_CHUNKED)) {
|
1277
|
+
settings.call_on_error(this, "not chunked", data, p_err);
|
1278
|
+
}
|
1279
|
+
|
1280
|
+
if (CR == ch) {
|
1281
|
+
state = State.chunk_size_almost_done;
|
1282
|
+
break;
|
1283
|
+
}
|
1284
|
+
|
1285
|
+
c = UNHEX[ch];
|
1286
|
+
|
1287
|
+
if (c == -1) {
|
1288
|
+
if (SEMI == ch || SPACE == ch) {
|
1289
|
+
state = State.chunk_parameters;
|
1290
|
+
break;
|
1291
|
+
}
|
1292
|
+
settings.call_on_error(this, "invalid hex char in chunk content length", data, p_err);
|
1293
|
+
}
|
1294
|
+
|
1295
|
+
content_length *= 16;
|
1296
|
+
content_length += c;
|
1297
|
+
break;
|
1298
|
+
|
1299
|
+
|
1300
|
+
|
1301
|
+
case chunk_parameters:
|
1302
|
+
if (0 == (flags & F_CHUNKED)) {
|
1303
|
+
settings.call_on_error(this, "not chunked", data, p_err);
|
1304
|
+
}
|
1305
|
+
/* just ignore this shit. TODO check for overflow */
|
1306
|
+
if (CR == ch) {
|
1307
|
+
state = State.chunk_size_almost_done;
|
1308
|
+
break;
|
1309
|
+
}
|
1310
|
+
break;
|
1311
|
+
|
1312
|
+
|
1313
|
+
|
1314
|
+
case chunk_size_almost_done:
|
1315
|
+
if (0 == (flags & F_CHUNKED)) {
|
1316
|
+
settings.call_on_error(this, "not chunked", data, p_err);
|
1317
|
+
}
|
1318
|
+
if (strict && LF != ch) {
|
1319
|
+
settings.call_on_error(this, "expected LF at end of chunk size", data, p_err);
|
1320
|
+
}
|
1321
|
+
|
1322
|
+
if (0 == content_length) {
|
1323
|
+
flags |= F_TRAILING;
|
1324
|
+
state = State.header_field_start;
|
1325
|
+
} else {
|
1326
|
+
state = State.chunk_data;
|
1327
|
+
}
|
1328
|
+
break;
|
1329
|
+
|
1330
|
+
|
1331
|
+
|
1332
|
+
case chunk_data:
|
1333
|
+
{
|
1334
|
+
if (0 == (flags & F_CHUNKED)) {
|
1335
|
+
settings.call_on_error(this, "not chunked", data, p_err);
|
1336
|
+
}
|
1337
|
+
|
1338
|
+
to_read = min(pe-p, content_length);
|
1339
|
+
if (to_read > 0) {
|
1340
|
+
settings.call_on_body(this, data, p, to_read);
|
1341
|
+
data.position(p+to_read);
|
1342
|
+
}
|
1343
|
+
|
1344
|
+
if (to_read == content_length) {
|
1345
|
+
state = State.chunk_data_almost_done;
|
1346
|
+
}
|
1347
|
+
|
1348
|
+
content_length -= to_read;
|
1349
|
+
break;
|
1350
|
+
}
|
1351
|
+
|
1352
|
+
|
1353
|
+
|
1354
|
+
case chunk_data_almost_done:
|
1355
|
+
if (0 == (flags & F_CHUNKED)) {
|
1356
|
+
settings.call_on_error(this, "not chunked", data, p_err);
|
1357
|
+
}
|
1358
|
+
if (strict && CR != ch) {
|
1359
|
+
settings.call_on_error(this, "chunk data terminated incorrectly, expected CR", data, p_err);
|
1360
|
+
}
|
1361
|
+
state = State.chunk_data_done;
|
1362
|
+
break;
|
1363
|
+
|
1364
|
+
|
1365
|
+
|
1366
|
+
case chunk_data_done:
|
1367
|
+
if (0 == (flags & F_CHUNKED)) {
|
1368
|
+
settings.call_on_error(this, "not chunked", data, p_err);
|
1369
|
+
}
|
1370
|
+
if (strict && LF != ch) {
|
1371
|
+
settings.call_on_error(this, "chunk data terminated incorrectly, expected LF", data, p_err);
|
1372
|
+
}
|
1373
|
+
state = State.chunk_size_start;
|
1374
|
+
break;
|
1375
|
+
/******************* Chunk *******************/
|
1376
|
+
|
1377
|
+
|
1378
|
+
|
1379
|
+
default:
|
1380
|
+
settings.call_on_error(this, "unhandled state", data, p_err);
|
1381
|
+
|
1382
|
+
} // switch
|
1383
|
+
} // while
|
1384
|
+
|
1385
|
+
p = data.position();
|
1386
|
+
|
1387
|
+
|
1388
|
+
/* Reaching this point assumes that we only received part of a
|
1389
|
+
* message, inform the callbacks about the progress made so far*/
|
1390
|
+
|
1391
|
+
settings.call_on_header_field(this, data, header_field_mark, p-header_field_mark);
|
1392
|
+
settings.call_on_header_value(this, data, header_value_mark, p-header_value_mark);
|
1393
|
+
settings.call_on_fragment (this, data, fragment_mark, p-fragment_mark);
|
1394
|
+
settings.call_on_query_string(this, data, query_string_mark, p-query_string_mark);
|
1395
|
+
settings.call_on_path (this, data, path_mark, p-path_mark);
|
1396
|
+
settings.call_on_url (this, data, url_mark, p-url_mark);
|
1397
|
+
|
1398
|
+
} // execute
|
1399
|
+
|
1400
|
+
/* If http_should_keep_alive() in the on_headers_complete or
|
1401
|
+
* on_message_complete callback returns true, then this will be should be
|
1402
|
+
* the last message on the connection.
|
1403
|
+
* If you are the server, respond with the "Connection: close" header.
|
1404
|
+
* If you are the client, close the connection.
|
1405
|
+
*/
|
1406
|
+
public boolean http_should_keep_alive() {
|
1407
|
+
if (http_major > 0 && http_minor > 0) {
|
1408
|
+
/* HTTP/1.1 */
|
1409
|
+
if ( 0 != (flags & F_CONNECTION_CLOSE) ) {
|
1410
|
+
return false;
|
1411
|
+
} else {
|
1412
|
+
return true;
|
1413
|
+
}
|
1414
|
+
} else {
|
1415
|
+
/* HTTP/1.0 or earlier */
|
1416
|
+
if ( 0 != (flags & F_CONNECTION_KEEP_ALIVE) ) {
|
1417
|
+
return true;
|
1418
|
+
} else {
|
1419
|
+
return false;
|
1420
|
+
}
|
1421
|
+
}
|
1422
|
+
}
|
1423
|
+
|
1424
|
+
boolean isDigit(byte b) {
|
1425
|
+
if (b >= 0x30 && b <=0x39) {
|
1426
|
+
return true;
|
1427
|
+
}
|
1428
|
+
return false;
|
1429
|
+
}
|
1430
|
+
|
1431
|
+
boolean isAtoZ(byte b) {
|
1432
|
+
byte c = lower(b);
|
1433
|
+
return (c>= 0x61 /*a*/ && c <= 0x7a /*z*/);
|
1434
|
+
}
|
1435
|
+
|
1436
|
+
|
1437
|
+
byte lower (byte b) {
|
1438
|
+
return (byte)(b|0x20);
|
1439
|
+
}
|
1440
|
+
|
1441
|
+
byte upper(byte b) {
|
1442
|
+
char c = (char)(b);
|
1443
|
+
return (byte)Character.toUpperCase(c);
|
1444
|
+
}
|
1445
|
+
|
1446
|
+
|
1447
|
+
HTTPMethod start_req_method_assign(byte c){
|
1448
|
+
switch (c) {
|
1449
|
+
case C: return HTTPMethod.HTTP_CONNECT; /* or COPY, CHECKOUT */
|
1450
|
+
case D: return HTTPMethod.HTTP_DELETE;
|
1451
|
+
case G: return HTTPMethod.HTTP_GET;
|
1452
|
+
case H: return HTTPMethod.HTTP_HEAD;
|
1453
|
+
case L: return HTTPMethod.HTTP_LOCK;
|
1454
|
+
case M: return HTTPMethod.HTTP_MKCOL; /* or MOVE, MKACTIVITY, MERGE */
|
1455
|
+
case O: return HTTPMethod.HTTP_OPTIONS;
|
1456
|
+
case P: return HTTPMethod.HTTP_POST; /* or PROPFIND, PROPPATH, PUT */
|
1457
|
+
case R: return HTTPMethod.HTTP_REPORT;
|
1458
|
+
case T: return HTTPMethod.HTTP_TRACE;
|
1459
|
+
case U: return HTTPMethod.HTTP_UNLOCK;
|
1460
|
+
}
|
1461
|
+
return null; // ugh.
|
1462
|
+
}
|
1463
|
+
|
1464
|
+
boolean header_almost_done(byte ch) {
|
1465
|
+
if (strict && LF != ch) {
|
1466
|
+
return false;
|
1467
|
+
}
|
1468
|
+
|
1469
|
+
state = State.header_field_start;
|
1470
|
+
// TODO java enums support some sort of bitflag mechanism !?
|
1471
|
+
switch (header_state) {
|
1472
|
+
case connection_keep_alive:
|
1473
|
+
flags |= F_CONNECTION_KEEP_ALIVE;
|
1474
|
+
break;
|
1475
|
+
case connection_close:
|
1476
|
+
flags |= F_CONNECTION_CLOSE;
|
1477
|
+
break;
|
1478
|
+
case transfer_encoding_chunked:
|
1479
|
+
flags |= F_CHUNKED;
|
1480
|
+
break;
|
1481
|
+
default:
|
1482
|
+
break;
|
1483
|
+
}
|
1484
|
+
return true;
|
1485
|
+
}
|
1486
|
+
|
1487
|
+
boolean headers_almost_done (byte ch, ParserSettings settings) {
|
1488
|
+
|
1489
|
+
if (LF != ch) {
|
1490
|
+
return false;
|
1491
|
+
}
|
1492
|
+
|
1493
|
+
if (0 != (flags & F_TRAILING)) {
|
1494
|
+
/* End of a chunked request */
|
1495
|
+
|
1496
|
+
settings.call_on_headers_complete(this);
|
1497
|
+
settings.call_on_message_complete(this);
|
1498
|
+
|
1499
|
+
state = new_message();
|
1500
|
+
|
1501
|
+
return true;
|
1502
|
+
}
|
1503
|
+
|
1504
|
+
nread = 0;
|
1505
|
+
|
1506
|
+
if (0 != (flags & F_UPGRADE) || HTTPMethod.HTTP_CONNECT == method) {
|
1507
|
+
upgrade = true;
|
1508
|
+
}
|
1509
|
+
|
1510
|
+
|
1511
|
+
/* Here we call the headers_complete callback. This is somewhat
|
1512
|
+
* different than other callbacks because if the user returns 1, we
|
1513
|
+
* will interpret that as saying that this message has no body. This
|
1514
|
+
* is needed for the annoying case of recieving a response to a HEAD
|
1515
|
+
* request.
|
1516
|
+
*/
|
1517
|
+
|
1518
|
+
/* (responses to HEAD request contain a CONTENT-LENGTH header
|
1519
|
+
* but no content)
|
1520
|
+
*
|
1521
|
+
* Consider what to do here: I don't like the idea of the callback
|
1522
|
+
* interface having a different contract in the case of HEAD
|
1523
|
+
* responses. The alternatives would be either to:
|
1524
|
+
*
|
1525
|
+
* a.) require the header_complete callback to implement a different
|
1526
|
+
* interface or
|
1527
|
+
*
|
1528
|
+
* b.) provide an overridden execute(bla, bla, boolean
|
1529
|
+
* parsingHeader) implementation ...
|
1530
|
+
*/
|
1531
|
+
|
1532
|
+
/*TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO */
|
1533
|
+
if (null != settings.on_headers_complete) {
|
1534
|
+
settings.call_on_headers_complete(this);
|
1535
|
+
//return;
|
1536
|
+
}
|
1537
|
+
|
1538
|
+
// if (null != settings.on_headers_complete) {
|
1539
|
+
// switch (settings.on_headers_complete.cb(parser)) {
|
1540
|
+
// case 0:
|
1541
|
+
// break;
|
1542
|
+
//
|
1543
|
+
// case 1:
|
1544
|
+
// flags |= F_SKIPBODY;
|
1545
|
+
// break;
|
1546
|
+
//
|
1547
|
+
// default:
|
1548
|
+
// return p - data; /* Error */ // TODO // RuntimeException ?
|
1549
|
+
// }
|
1550
|
+
// }
|
1551
|
+
|
1552
|
+
|
1553
|
+
// Exit, the rest of the connect is in a different protocol.
|
1554
|
+
if (upgrade) {
|
1555
|
+
settings.call_on_message_complete(this);
|
1556
|
+
return true;
|
1557
|
+
}
|
1558
|
+
|
1559
|
+
if (0 != (flags & F_SKIPBODY)) {
|
1560
|
+
settings.call_on_message_complete(this);
|
1561
|
+
state = new_message();
|
1562
|
+
} else if (0 != (flags & F_CHUNKED)) {
|
1563
|
+
/* chunked encoding - ignore Content-Length header */
|
1564
|
+
state = State.chunk_size_start;
|
1565
|
+
} else {
|
1566
|
+
if (content_length == 0) {
|
1567
|
+
/* Content-Length header given but zero: Content-Length: 0\r\n */
|
1568
|
+
settings.call_on_message_complete(this);
|
1569
|
+
state = new_message();
|
1570
|
+
} else if (content_length > 0) {
|
1571
|
+
/* Content-Length header given and non-zero */
|
1572
|
+
state = State.body_identity;
|
1573
|
+
} else {
|
1574
|
+
if (type == ParserType.HTTP_REQUEST || http_should_keep_alive()) {
|
1575
|
+
/* Assume content-length 0 - read the next */
|
1576
|
+
settings.call_on_message_complete(this);
|
1577
|
+
state = new_message();
|
1578
|
+
} else {
|
1579
|
+
/* Read body until EOF */
|
1580
|
+
state = State.body_identity_eof;
|
1581
|
+
}
|
1582
|
+
}
|
1583
|
+
}
|
1584
|
+
return true;
|
1585
|
+
} // headers_almost_fone
|
1586
|
+
|
1587
|
+
|
1588
|
+
final int min (int a, int b) {
|
1589
|
+
return a < b ? a : b;
|
1590
|
+
}
|
1591
|
+
|
1592
|
+
/* probably not the best place to hide this ... */
|
1593
|
+
public boolean HTTP_PARSER_STRICT;
|
1594
|
+
State new_message() {
|
1595
|
+
if (HTTP_PARSER_STRICT){
|
1596
|
+
return http_should_keep_alive() ? start_state() : State.dead;
|
1597
|
+
} else {
|
1598
|
+
return start_state();
|
1599
|
+
}
|
1600
|
+
|
1601
|
+
}
|
1602
|
+
|
1603
|
+
State start_state() {
|
1604
|
+
return type == ParserType.HTTP_REQUEST ? State.start_req : State.start_res;
|
1605
|
+
}
|
1606
|
+
|
1607
|
+
|
1608
|
+
boolean parsing_header(State state) {
|
1609
|
+
|
1610
|
+
switch (state) {
|
1611
|
+
case chunk_size_start :
|
1612
|
+
case chunk_size :
|
1613
|
+
case chunk_size_almost_done :
|
1614
|
+
case chunk_parameters :
|
1615
|
+
case chunk_data :
|
1616
|
+
case chunk_data_almost_done :
|
1617
|
+
case chunk_data_done :
|
1618
|
+
case body_identity :
|
1619
|
+
case body_identity_eof :
|
1620
|
+
return false;
|
1621
|
+
|
1622
|
+
}
|
1623
|
+
return (0==(flags & F_TRAILING));
|
1624
|
+
}
|
1625
|
+
|
1626
|
+
/* "Dial C for Constants" */
|
1627
|
+
static class C {
|
1628
|
+
static final int HTTP_MAX_HEADER_SIZE = 80 * 1024;
|
1629
|
+
|
1630
|
+
static final int F_CHUNKED = 1 << 0;
|
1631
|
+
static final int F_CONNECTION_KEEP_ALIVE = 1 << 1;
|
1632
|
+
static final int F_CONNECTION_CLOSE = 1 << 2;
|
1633
|
+
static final int F_TRAILING = 1 << 3;
|
1634
|
+
static final int F_UPGRADE = 1 << 4;
|
1635
|
+
static final int F_SKIPBODY = 1 << 5;
|
1636
|
+
|
1637
|
+
static final byte [] UPCASE = {
|
1638
|
+
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
1639
|
+
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
1640
|
+
0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x2d,0x00,0x2f,
|
1641
|
+
0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37, 0x38,0x39,0x00,0x00,0x00,0x00,0x00,0x00,
|
1642
|
+
0x00,0x41,0x42,0x43,0x44,0x45,0x46,0x47, 0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,
|
1643
|
+
0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57, 0x58,0x59,0x5a,0x00,0x00,0x00,0x00,0x5f,
|
1644
|
+
0x00,0x41,0x42,0x43,0x44,0x45,0x46,0x47, 0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,
|
1645
|
+
0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57, 0x58,0x59,0x5a,0x00,0x00,0x00,0x00,0x00,
|
1646
|
+
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
1647
|
+
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
1648
|
+
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
1649
|
+
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
1650
|
+
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
1651
|
+
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
1652
|
+
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
1653
|
+
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
1654
|
+
};
|
1655
|
+
static final byte [] CONNECTION = {
|
1656
|
+
0x43, 0x4f, 0x4e, 0x4e, 0x45, 0x43, 0x54, 0x49, 0x4f, 0x4e,
|
1657
|
+
};
|
1658
|
+
static final byte [] PROXY_CONNECTION = {
|
1659
|
+
0x50, 0x52, 0x4f, 0x58, 0x59, 0x2d, 0x43, 0x4f, 0x4e, 0x4e, 0x45, 0x43, 0x54, 0x49, 0x4f, 0x4e,
|
1660
|
+
};
|
1661
|
+
static final byte [] CONTENT_LENGTH = {
|
1662
|
+
0x43, 0x4f, 0x4e, 0x54, 0x45, 0x4e, 0x54, 0x2d, 0x4c, 0x45, 0x4e, 0x47, 0x54, 0x48,
|
1663
|
+
};
|
1664
|
+
static final byte [] TRANSFER_ENCODING = {
|
1665
|
+
0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, 0x52, 0x2d, 0x45, 0x4e, 0x43, 0x4f, 0x44, 0x49, 0x4e, 0x47,
|
1666
|
+
};
|
1667
|
+
static final byte [] UPGRADE = {
|
1668
|
+
0x55, 0x50, 0x47, 0x52, 0x41, 0x44, 0x45,
|
1669
|
+
};
|
1670
|
+
static final byte [] CHUNKED = {
|
1671
|
+
0x43, 0x48, 0x55, 0x4e, 0x4b, 0x45, 0x44,
|
1672
|
+
};
|
1673
|
+
static final byte [] KEEP_ALIVE = {
|
1674
|
+
0x4b, 0x45, 0x45, 0x50, 0x2d, 0x41, 0x4c, 0x49, 0x56, 0x45,
|
1675
|
+
};
|
1676
|
+
static final byte [] CLOSE = {
|
1677
|
+
0x43, 0x4c, 0x4f, 0x53, 0x45,
|
1678
|
+
};
|
1679
|
+
/*
|
1680
|
+
* ' ', '_', '-' and all alpha-numeric ascii characters are accepted by acceptable_header.
|
1681
|
+
* The 'A'-'Z' are upper-cased.
|
1682
|
+
*/
|
1683
|
+
|
1684
|
+
static final char [] acceptable_header = {
|
1685
|
+
/* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */
|
1686
|
+
0, 0, 0, 0, 0, 0, 0, 0,
|
1687
|
+
/* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */
|
1688
|
+
0, 0, 0, 0, 0, 0, 0, 0,
|
1689
|
+
/* 16 dle 17 dc1 18 dc2 19 dc3 20 dc4 21 nak 22 syn 23 etb */
|
1690
|
+
0, 0, 0, 0, 0, 0, 0, 0,
|
1691
|
+
/* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */
|
1692
|
+
0, 0, 0, 0, 0, 0, 0, 0,
|
1693
|
+
/* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */
|
1694
|
+
' ', 0, 0, 0, 0, 0, 0, 0,
|
1695
|
+
/* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */
|
1696
|
+
0, 0, 0, 0, 0, '-', 0, 0,
|
1697
|
+
/* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */
|
1698
|
+
'0', '1', '2', '3', '4', '5', '6', '7',
|
1699
|
+
/* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */
|
1700
|
+
'8', '9', 0, 0, 0, 0, 0, 0,
|
1701
|
+
/* 64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G */
|
1702
|
+
0, 'A', 'B', 'C', 'D', 'E', 'F', 'G',
|
1703
|
+
/* 72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O */
|
1704
|
+
'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
|
1705
|
+
/* 80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W */
|
1706
|
+
'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
|
1707
|
+
/* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */
|
1708
|
+
'X', 'Y', 'Z', 0, 0, 0, 0, '_',
|
1709
|
+
/* 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g */
|
1710
|
+
0, 'A', 'B', 'C', 'D', 'E', 'F', 'G',
|
1711
|
+
/* 104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o */
|
1712
|
+
'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
|
1713
|
+
/* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */
|
1714
|
+
'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
|
1715
|
+
/* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */
|
1716
|
+
'X', 'Y', 'Z', 0, 0, 0, 0, 0 };
|
1717
|
+
|
1718
|
+
static final byte [] UNHEX =
|
1719
|
+
{ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
|
1720
|
+
,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
|
1721
|
+
,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
|
1722
|
+
, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1
|
1723
|
+
,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1
|
1724
|
+
,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
|
1725
|
+
,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1
|
1726
|
+
,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
|
1727
|
+
};
|
1728
|
+
|
1729
|
+
static final boolean [] normal_url_char = {
|
1730
|
+
/* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */
|
1731
|
+
false, false, false, false, false, false, false, false,
|
1732
|
+
/* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */
|
1733
|
+
false, false, false, false, false, false, false, false,
|
1734
|
+
/* 16 dle 17 dc1 18 dc2 19 dc3 20 dc4 21 nak 22 syn 23 etb */
|
1735
|
+
false, false, false, false, false, false, false, false,
|
1736
|
+
/* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */
|
1737
|
+
false, false, false, false, false, false, false, false,
|
1738
|
+
/* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */
|
1739
|
+
false, true, true, false, true, true, true, true,
|
1740
|
+
/* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */
|
1741
|
+
true, true, true, true, true, true, true, true,
|
1742
|
+
/* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */
|
1743
|
+
true, true, true, true, true, true, true, true,
|
1744
|
+
/* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */
|
1745
|
+
true, true, true, true, true, true, true, false,
|
1746
|
+
/* 64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G */
|
1747
|
+
true, true, true, true, true, true, true, true,
|
1748
|
+
/* 72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O */
|
1749
|
+
true, true, true, true, true, true, true, true,
|
1750
|
+
/* 80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W */
|
1751
|
+
true, true, true, true, true, true, true, true,
|
1752
|
+
/* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */
|
1753
|
+
true, true, true, true, true, true, true, true,
|
1754
|
+
/* 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g */
|
1755
|
+
true, true, true, true, true, true, true, true,
|
1756
|
+
/* 104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o */
|
1757
|
+
true, true, true, true, true, true, true, true,
|
1758
|
+
/* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */
|
1759
|
+
true, true, true, true, true, true, true, true,
|
1760
|
+
/* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */
|
1761
|
+
true, true, true, true, true, true, true, false };
|
1762
|
+
|
1763
|
+
public static final byte A = 0x41;
|
1764
|
+
public static final byte B = 0x42;
|
1765
|
+
public static final byte C = 0x43;
|
1766
|
+
public static final byte D = 0x44;
|
1767
|
+
public static final byte E = 0x45;
|
1768
|
+
public static final byte F = 0x46;
|
1769
|
+
public static final byte G = 0x47;
|
1770
|
+
public static final byte H = 0x48;
|
1771
|
+
public static final byte I = 0x49;
|
1772
|
+
public static final byte J = 0x4a;
|
1773
|
+
public static final byte K = 0x4b;
|
1774
|
+
public static final byte L = 0x4c;
|
1775
|
+
public static final byte M = 0x4d;
|
1776
|
+
public static final byte N = 0x4e;
|
1777
|
+
public static final byte O = 0x4f;
|
1778
|
+
public static final byte P = 0x50;
|
1779
|
+
public static final byte Q = 0x51;
|
1780
|
+
public static final byte R = 0x52;
|
1781
|
+
public static final byte S = 0x53;
|
1782
|
+
public static final byte T = 0x54;
|
1783
|
+
public static final byte U = 0x55;
|
1784
|
+
public static final byte V = 0x56;
|
1785
|
+
public static final byte W = 0x57;
|
1786
|
+
public static final byte X = 0x58;
|
1787
|
+
public static final byte Y = 0x59;
|
1788
|
+
public static final byte Z = 0x5a;
|
1789
|
+
public static final byte CR = 0x0d;
|
1790
|
+
public static final byte LF = 0x0a;
|
1791
|
+
public static final byte DOT = 0x2e;
|
1792
|
+
public static final byte SPACE = 0x20;
|
1793
|
+
public static final byte SEMI = 0x3b;
|
1794
|
+
public static final byte COLON = 0x3a;
|
1795
|
+
public static final byte HASH = 0x23;
|
1796
|
+
public static final byte QMARK = 0x3f;
|
1797
|
+
public static final byte SLASH = 0x2f;
|
1798
|
+
public static final byte DASH = 0x2d;
|
1799
|
+
public static final byte NULL = 0x00;
|
1800
|
+
}
|
1801
|
+
|
1802
|
+
enum State {
|
1803
|
+
|
1804
|
+
dead
|
1805
|
+
|
1806
|
+
, start_res_or_res
|
1807
|
+
, res_or_resp_H
|
1808
|
+
, start_res
|
1809
|
+
, res_H
|
1810
|
+
, res_HT
|
1811
|
+
, res_HTT
|
1812
|
+
, res_HTTP
|
1813
|
+
, res_first_http_major
|
1814
|
+
, res_http_major
|
1815
|
+
, res_first_http_minor
|
1816
|
+
, res_http_minor
|
1817
|
+
, res_first_status_code
|
1818
|
+
, res_status_code
|
1819
|
+
, res_status
|
1820
|
+
, res_line_almost_done
|
1821
|
+
|
1822
|
+
, start_req
|
1823
|
+
|
1824
|
+
, req_method
|
1825
|
+
, req_spaces_before_url
|
1826
|
+
, req_schema
|
1827
|
+
, req_schema_slash
|
1828
|
+
, req_schema_slash_slash
|
1829
|
+
, req_host
|
1830
|
+
, req_port
|
1831
|
+
, req_path
|
1832
|
+
, req_query_string_start
|
1833
|
+
, req_query_string
|
1834
|
+
, req_fragment_start
|
1835
|
+
, req_fragment
|
1836
|
+
, req_http_start
|
1837
|
+
, req_http_H
|
1838
|
+
, req_http_HT
|
1839
|
+
, req_http_HTT
|
1840
|
+
, req_http_HTTP
|
1841
|
+
, req_first_http_major
|
1842
|
+
, req_http_major
|
1843
|
+
, req_first_http_minor
|
1844
|
+
, req_http_minor
|
1845
|
+
, req_line_almost_done
|
1846
|
+
|
1847
|
+
, header_field_start
|
1848
|
+
, header_field
|
1849
|
+
, header_value_start
|
1850
|
+
, header_value
|
1851
|
+
|
1852
|
+
, header_almost_done
|
1853
|
+
|
1854
|
+
, headers_almost_done
|
1855
|
+
|
1856
|
+
, chunk_size_start
|
1857
|
+
, chunk_size
|
1858
|
+
, chunk_size_almost_done
|
1859
|
+
, chunk_parameters
|
1860
|
+
, chunk_data
|
1861
|
+
, chunk_data_almost_done
|
1862
|
+
, chunk_data_done
|
1863
|
+
|
1864
|
+
, body_identity
|
1865
|
+
, body_identity_eof;
|
1866
|
+
|
1867
|
+
|
1868
|
+
}
|
1869
|
+
enum HState {
|
1870
|
+
general
|
1871
|
+
, C
|
1872
|
+
, CO
|
1873
|
+
, CON
|
1874
|
+
|
1875
|
+
, matching_connection
|
1876
|
+
, matching_proxy_connection
|
1877
|
+
, matching_content_length
|
1878
|
+
, matching_transfer_encoding
|
1879
|
+
, matching_upgrade
|
1880
|
+
|
1881
|
+
, connection
|
1882
|
+
, content_length
|
1883
|
+
, transfer_encoding
|
1884
|
+
, upgrade
|
1885
|
+
|
1886
|
+
, matching_transfer_encoding_chunked
|
1887
|
+
, matching_connection_keep_alive
|
1888
|
+
, matching_connection_close
|
1889
|
+
|
1890
|
+
, transfer_encoding_chunked
|
1891
|
+
, connection_keep_alive
|
1892
|
+
, connection_close
|
1893
|
+
}
|
1894
|
+
}
|