http_parser.rb 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. data/.gitignore +11 -0
  2. data/.gitmodules +6 -0
  3. data/README.md +45 -0
  4. data/Rakefile +6 -0
  5. data/bench/thin.rb +57 -0
  6. data/ext/ruby_http_parser/.gitignore +1 -0
  7. data/ext/ruby_http_parser/RubyHttpParserService.java +18 -0
  8. data/ext/ruby_http_parser/ext_help.h +18 -0
  9. data/ext/ruby_http_parser/extconf.rb +16 -0
  10. data/ext/ruby_http_parser/org/ruby_http_parser/RubyHttpParser.java +403 -0
  11. data/ext/ruby_http_parser/ruby_http_parser.c +474 -0
  12. data/ext/ruby_http_parser/vendor/.gitkeep +0 -0
  13. data/ext/ruby_http_parser/vendor/http-parser-java/CONTRIBUTIONS +4 -0
  14. data/ext/ruby_http_parser/vendor/http-parser-java/LICENSE-MIT +19 -0
  15. data/ext/ruby_http_parser/vendor/http-parser-java/README.md +171 -0
  16. data/ext/ruby_http_parser/vendor/http-parser-java/TODO +19 -0
  17. data/ext/ruby_http_parser/vendor/http-parser-java/compile +1 -0
  18. data/ext/ruby_http_parser/vendor/http-parser-java/http_parser.c +1590 -0
  19. data/ext/ruby_http_parser/vendor/http-parser-java/http_parser.h +167 -0
  20. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPException.java +7 -0
  21. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPMethod.java +90 -0
  22. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPParser.java +31 -0
  23. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/ParserType.java +13 -0
  24. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/HTTPCallback.java +5 -0
  25. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/HTTPDataCallback.java +25 -0
  26. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/HTTPErrorCallback.java +7 -0
  27. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/HTTPParser.java +1894 -0
  28. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/ParserSettings.java +78 -0
  29. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/Util.java +112 -0
  30. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/TestLoaderNG.java +487 -0
  31. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/UnitTest.java +115 -0
  32. data/ext/ruby_http_parser/vendor/http-parser-java/test.c +1865 -0
  33. data/ext/ruby_http_parser/vendor/http-parser-java/test_permutations +1 -0
  34. data/ext/ruby_http_parser/vendor/http-parser-java/test_unit +1 -0
  35. data/ext/ruby_http_parser/vendor/http-parser-java/tests.dumped +539 -0
  36. data/ext/ruby_http_parser/vendor/http-parser-java/tools/byte_constants.rb +6 -0
  37. data/ext/ruby_http_parser/vendor/http-parser-java/tools/const_char.rb +13 -0
  38. data/ext/ruby_http_parser/vendor/http-parser-java/tools/lowcase.rb +15 -0
  39. data/ext/ruby_http_parser/vendor/http-parser-java/tools/parse_tests.rb +33 -0
  40. data/ext/ruby_http_parser/vendor/http-parser/CONTRIBUTIONS +4 -0
  41. data/ext/ruby_http_parser/vendor/http-parser/LICENSE-MIT +19 -0
  42. data/ext/ruby_http_parser/vendor/http-parser/README.md +171 -0
  43. data/ext/ruby_http_parser/vendor/http-parser/http_parser.c +1590 -0
  44. data/ext/ruby_http_parser/vendor/http-parser/http_parser.h +167 -0
  45. data/ext/ruby_http_parser/vendor/http-parser/test.c +1755 -0
  46. data/http_parser.rb.gemspec +15 -0
  47. data/lib/http/parser.rb +1 -0
  48. data/lib/http_parser.rb +4 -0
  49. data/spec/parser_spec.rb +187 -0
  50. data/spec/spec_helper.rb +2 -0
  51. data/spec/support/requests.json +381 -0
  52. data/spec/support/responses.json +186 -0
  53. data/tasks/compile.rake +39 -0
  54. data/tasks/spec.rake +5 -0
  55. data/tasks/submodules.rake +7 -0
  56. metadata +121 -0
@@ -0,0 +1,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,7 @@
1
+ package http_parser;
2
+
3
+ public class HTTPException extends RuntimeException {
4
+ public HTTPException(String mes) {
5
+ super(mes);
6
+ }
7
+ }
@@ -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
+
@@ -0,0 +1,5 @@
1
+ package http_parser.lolevel;
2
+
3
+ public interface HTTPCallback {
4
+ public int cb (HTTPParser parser);
5
+ }
@@ -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
+ }
@@ -0,0 +1,7 @@
1
+ package http_parser.lolevel;
2
+
3
+ import java.nio.ByteBuffer;
4
+
5
+ public interface HTTPErrorCallback {
6
+ public void cb (HTTPParser parser, String mes, ByteBuffer buf, int initial_position);
7
+ }
@@ -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
+ }