// Copyright (c) 2014-present Tom Zhou package com.iwebpp.node; import java.io.UnsupportedEncodingException; import java.nio.ByteBuffer; import com.iwebpp.SimpleDebug; public abstract class HttpParser extends SimpleDebug { private final static String TAG = "HttpParser"; protected HttpParser(http_parser_type type, Object data) { this.data = data; reset(type); } @SuppressWarnings("unused") private HttpParser() {this.data = null;} public static enum http_parser_type { HTTP_REQUEST, HTTP_RESPONSE, HTTP_BOTH; } private enum State { s_unknown (0) , s_dead (1) /* important that this is > 0 */ , s_start_req_or_res (2) , s_res_or_resp_H (3) , s_start_res (4) , s_res_H (5) , s_res_HT (6) , s_res_HTT (7) , s_res_HTTP (8) , s_res_first_http_major (9) , s_res_http_major (10) , s_res_first_http_minor (11) , s_res_http_minor (12) , s_res_first_status_code (13) , s_res_status_code (14) , s_res_status_start (15) , s_res_status (16) , s_res_line_almost_done (17) , s_start_req (18) , s_req_method (19) , s_req_spaces_before_url (20) , s_req_schema (21) , s_req_schema_slash (22) , s_req_schema_slash_slash (23) , s_req_server_start (24) , s_req_server (25) , s_req_server_with_at (26) , s_req_path (27) , s_req_query_string_start (28) , s_req_query_string (29) , s_req_fragment_start (30) , s_req_fragment (31) , s_req_http_start (32) , s_req_http_H (33) , s_req_http_HT (34) , s_req_http_HTT (35) , s_req_http_HTTP (36) , s_req_first_http_major (37) , s_req_http_major (38) , s_req_first_http_minor (39) , s_req_http_minor (40) , s_req_line_almost_done (41) , s_header_field_start (42) , s_header_field (43) , s_header_value_discard_ws (44) , s_header_value_discard_ws_almost_done (45) , s_header_value_discard_lws (46) , s_header_value_start (47) , s_header_value (48) , s_header_value_lws (49) , s_header_almost_done (50) , s_chunk_size_start (51) , s_chunk_size (52) , s_chunk_parameters (53) , s_chunk_size_almost_done (54) , s_headers_almost_done (55) , s_headers_done (56) /* Important: 's_headers_done' must be the last 'header' State. All * states beyond this must be 'body' states. It is used for overflow * checking. See the PARSING_HEADER() macro. */ , s_chunk_data (57) , s_chunk_data_almost_done (58) , s_chunk_data_done (59) , s_body_identity (60) , s_body_identity_eof (61) , s_message_done (62); private final int state; private State(int state) { this.state = state; } } private enum header_states { h_general (0) , h_C (1) , h_CO (2) , h_CON (3) , h_matching_connection (4) , h_matching_proxy_connection (5) , h_matching_content_length (6) , h_matching_transfer_encoding (7) , h_matching_upgrade (8) , h_connection (9) , h_content_length (10) , h_transfer_encoding (11) , h_upgrade (12) , h_matching_transfer_encoding_chunked (13) , h_matching_connection_keep_alive (14) , h_matching_connection_close (15) , h_transfer_encoding_chunked (16) , h_connection_keep_alive (17) , h_connection_close (18); private header_states(int state){ } }; private enum http_host_state { s_http_host_dead (1) , s_http_userinfo_start (2) , s_http_userinfo (3) , s_http_host_start (4) , s_http_host_v6_start (5) , s_http_host (6) , s_http_host_v6 (7) , s_http_host_v6_end (8) , s_http_host_port_start (9) , s_http_host_port (10); private http_host_state(int state){ } } /* Request Methods */ public static enum http_method { HTTP_DELETE ("DELETE\0"), HTTP_GET ("GET\0"), HTTP_HEAD ("HEAD\0"), HTTP_POST ("POST\0"), HTTP_PUT ("PUT\0"), /* pathological */ HTTP_CONNECT ("CONNECT\0"), HTTP_OPTIONS ("OPTIONS\0"), HTTP_TRACE ("TRACE\0"), /* webdav */ HTTP_COPY ("COPY\0"), HTTP_LOCK ("LOCK\0"), HTTP_MKCOL ("MKCOL\0"), HTTP_MOVE ("MOVE\0"), HTTP_PROPFIND ("PROPFIND\0"), HTTP_PROPPATCH ("PROPPATCH\0"), HTTP_SEARCH ("SEARCH\0"), HTTP_UNLOCK ("UNLOCK\0"), /* subversion */ HTTP_REPORT ("REPORT\0"), HTTP_MKACTIVITY ("MKACTIVITY\0"), HTTP_CHECKOUT ("CHECKOUT\0"), HTTP_MERGE ("MERGE\0"), /* upnp */ HTTP_MSEARCH ("M-SEARCH\0"), HTTP_NOTIFY ("NOTIFY\0"), HTTP_SUBSCRIBE ("SUBSCRIBE\0"), HTTP_UNSUBSCRIBE ("UNSUBSCRIBE\0"), /* RFC-5789 */ HTTP_PATCH ("PATCH\0"), HTTP_PURGE ("PURGE\0"), // invalid HTTP_INVALID ("INVALID\0"); private final String desc; private http_method(String desc) { this.desc = desc; } public String desc() { return desc.substring(0, desc.length()-1); } } /* Define HPE_* values for each errno value above */ public static enum http_errno { /* No error */ ///XX(OK, "success") HPE_OK("success"), /* Callback-related errors */ ///XX(CB_message_begin, "the on_message_begin callback failed") ///XX(CB_url, "the on_url callback failed") ///XX(CB_header_field, "the on_header_field callback failed") ///XX(CB_header_value, "the on_header_value callback failed") ///XX(CB_headers_complete, "the on_headers_complete callback failed") ///XX(CB_body, "the on_body callback failed") ///XX(CB_message_complete, "the on_message_complete callback failed") ///XX(CB_status, "the on_status callback failed") HPE_CB_message_begin("the on_message_begin callback failed"), HPE_CB_url("the on_url callback failed"), HPE_CB_header_field("the on_header_field callback failed" ), HPE_CB_header_value("the on_header_value callback failed"), HPE_CB_headers_complete("the on_headers_complete callback failed"), HPE_CB_body("the on_body callback failed"), HPE_CB_message_complete("the on_message_complete callback failed"), HPE_CB_status("the on_status callback failed"), /* Parsing-related errors */ ///XX(INVALID_EOF_STATE, "stream ended at an unexpected time") ///XX(HEADER_OVERFLOW, /// "too many header bytes seen; overflow detected") ///XX(CLOSED_CONNECTION, /// "data received after completed connection: close message") ///XX(INVALID_VERSION, "invalid http version") ///XX(INVALID_STATUS, "invalid http status code") ///XX(INVALID_METHOD, "invalid http method") ///XX(INVALID_URL, "invalid URL") HPE_INVALID_EOF_STATE("stream ended at an unexpected time"), HPE_HEADER_OVERFLOW("too many header bytes seen; overflow detected"), HPE_CLOSED_CONNECTION("data received after completed connection: close message"), HPE_INVALID_VERSION("invalid http version"), HPE_INVALID_STATUS("invalid http status code"), HPE_INVALID_METHOD("invalid http method"), HPE_INVALID_URL("invalid URL"), ///XX(INVALID_HOST, "invalid host") ///XX(INVALID_PORT, "invalid port") ///XX(INVALID_PATH, "invalid path") ///XX(INVALID_QUERY_STRING, "invalid query string") ///XX(INVALID_FRAGMENT, "invalid fragment") ///XX(LF_EXPECTED, "LF character expected") ///XX(INVALID_HEADER_TOKEN, "invalid character in header") ///XX(INVALID_CONTENT_LENGTH, /// "invalid character in content-length header") ///XX(INVALID_CHUNK_SIZE, /// "invalid character in chunk size header") ///XX(INVALID_CONSTANT, "invalid constant string") ///XX(INVALID_INTERNAL_STATE, "encountered unexpected internal State") ///XX(STRICT, "strict mode assertion failed") ///XX(PAUSED, "parser is paused") ///XX(MAYBE, "an unknown error occurred") HPE_INVALID_HOST("invalid host"), HPE_INVALID_PORT("invalid port"), HPE_INVALID_PATH("invalid path"), HPE_INVALID_QUERY_STRING("invalid query string"), HPE_INVALID_FRAGMENT("invalid fragment"), HPE_LF_EXPECTED("LF character expected"), HPE_INVALID_HEADER_TOKEN("invalid character in header"), HPE_INVALID_CONTENT_LENGTH("invalid character in content-length header"), HPE_INVALID_CHUNK_SIZE("invalid character in chunk size header"), HPE_INVALID_CONSTANT("invalid constant string"), HPE_INVALID_INTERNAL_STATE("encountered unexpected internal State"), HPE_STRICT("strict mode assertion failed"), HPE_PAUSED("parser is paused"), HPE_UNKNOWN("an unknown error occurred"); private final String desc; private http_errno(String desc) { this.desc = desc; } public String desc() { return this.desc; } } /* Get an http_errno value from an http_parser */ protected http_errno HTTP_PARSER_ERRNO() { return this.http_errno; } /* Flag values for http_parser.flags field */ private enum Flags { F_CHUNKED (1 << 0), F_CONNECTION_KEEP_ALIVE (1 << 1), F_CONNECTION_CLOSE (1 << 2), F_TRAILING (1 << 3), F_UPGRADE (1 << 4), F_SKIPBODY (1 << 5); private final int flag; private Flags(int flag) { this.flag= flag; } } private enum http_parser_url_fields { UF_SCHEMA (0), UF_HOST (1), UF_PORT (2), UF_PATH (3), UF_QUERY (4), UF_FRAGMENT (5), UF_USERINFO (6), UF_MAX (7); private final int field; private http_parser_url_fields(int field) { this.field = field; } } private final static String PROXY_CONNECTION = "proxy-connection\0"; private final static String CONNECTION = "connection\0"; private final static String CONTENT_LENGTH = "content-length\0"; private final static String TRANSFER_ENCODING = "transfer-encoding\0"; private final static String UPGRADE = "upgrade\0"; private final static String CHUNKED = "chunked\0"; private final static String KEEP_ALIVE = "keep-alive\0"; private final static String CLOSE = "close\0"; private static long ULLONG_MAX() { return Long.MAX_VALUE; } private static long MIN(long a, long b) { return ((a) < (b) ? (a) : (b)); } private static boolean BIT_AT(int [] a, int i) { i &= 0xff; return (0 != (a[i >> 3] & (1 << (i & 7)))); } private void SET_ERRNO(http_errno e) { this.http_errno = e; } /* Tokens as defined by rfc 2616. Also lowercases them. * token = 1* * separators = "(" | ")" | "<" | ">" | "@" * | "," | ";" | ":" | "\" | <"> * | "/" | "[" | "]" | "?" | "=" * | "{" | "}" | SP | HT */ private final static char tokens[] = { /* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */ 0, 0, 0, 0, 0, 0, 0, 0, /* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */ 0, 0, 0, 0, 0, 0, 0, 0, /* 16 dle 17 dc1 18 dc2 19 dc3 20 dc4 21 nak 22 syn 23 etb */ 0, 0, 0, 0, 0, 0, 0, 0, /* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */ 0, 0, 0, 0, 0, 0, 0, 0, /* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */ 0, '!', 0, '#', '$', '%', '&', '\'', /* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */ 0, 0, '*', '+', 0, '-', '.', 0, /* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */ '0', '1', '2', '3', '4', '5', '6', '7', /* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */ '8', '9', 0, 0, 0, 0, 0, 0, /* 64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G */ 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', /* 72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O */ 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', /* 80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W */ 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', /* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */ 'x', 'y', 'z', 0, 0, 0, '^', '_', /* 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g */ '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', /* 104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o */ 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', /* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */ 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', /* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */ 'x', 'y', 'z', 0, '|', 0, '~', 0 }; private final static char ASCII[] = { /* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */ 0, 0, 0, 0, 0, 0, 0, 0, /* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */ 0, '\t', '\n', 0, '\f', '\r', 0, 0, /* 16 dle 17 dc1 18 dc2 19 dc3 20 dc4 21 nak 22 syn 23 etb */ 0, 0, 0, 0, 0, 0, 0, 0, /* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */ 0, 0, 0, 0, 0, 0, 0, 0, /* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */ ' ', '!', '"', '#', '$', '%', '&', '\'', /* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */ '(', ')', '*', '+', ',', '-', '.', '/', /* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */ '0', '1', '2', '3', '4', '5', '6', '7', /* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */ '8', '9', ':', ';', '<', '=', '>', '?', /* 64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G */ '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', /* 72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O */ 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', /* 80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W */ 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', /* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */ 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', /* 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g */ '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', /* 104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o */ 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', /* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */ 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', /* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */ 'x', 'y', 'z', '{', '|', '}', '~', 0 }; private final static byte unhex[] = { -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 , 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1 ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1 ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1 ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 }; // strict parse mode private final static int normal_url_char[] = { /* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */ 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0, /* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */ 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0, /* 16 dle 17 dc1 18 dc2 19 dc3 20 dc4 21 nak 22 syn 23 etb */ 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0, /* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */ 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0, /* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */ 0 | 2 | 4 | 0 | 16 | 32 | 64 | 128, /* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, /* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, /* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 0, /* 64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G */ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, /* 72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O */ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, /* 80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W */ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, /* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, /* 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g */ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, /* 104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o */ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, /* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, /* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 0, }; private boolean PARSING_HEADER() { return (this.state.state <= State.s_headers_done.state); } /* Does the parser need to see an EOF to find the end of the message? */ private boolean http_message_needs_eof () { if (type == http_parser_type.HTTP_REQUEST) { return false; } /* See RFC 2616 section 4.4 */ if (status_code / 100 == 1 || /* 1xx e.g. Continue */ status_code == 204 || /* No Content */ status_code == 304 || /* Not Modified */ (flags & Flags.F_SKIPBODY.flag)!=0) { /* response to a HEAD request */ return false; } if ((flags & Flags.F_CHUNKED.flag)!=0 || content_length != ULLONG_MAX()) { return false; } return true; } /* Macros for character classes; depends on strict-mode */ /*#define CR '\r' #define LF '\n' #define LOWER(c) (unsigned char)(c | 0x20) #define IS_ALPHA(c) (LOWER(c) >= 'a' && LOWER(c) <= 'z') #define IS_NUM(c) ((c) >= '0' && (c) <= '9') #define IS_ALPHANUM(c) (IS_ALPHA(c) || IS_NUM(c)) #define IS_HEX(c) (IS_NUM(c) || (LOWER(c) >= 'a' && LOWER(c) <= 'f')) #define IS_MARK(c) ((c) == '-' || (c) == '_' || (c) == '.' || \ (c) == '!' || (c) == '~' || (c) == '*' || (c) == '\'' || (c) == '(' || \ (c) == ')') #define IS_USERINFO_CHAR(c) (IS_ALPHANUM(c) || IS_MARK(c) || (c) == '%' || \ (c) == ';' || (c) == ':' || (c) == '&' || (c) == '=' || (c) == '+' || \ (c) == '$' || (c) == ',') #if HTTP_PARSER_STRICT #define TOKEN(c) (tokens[(unsigned char)c]) #define IS_URL_CHAR(c) (BIT_AT(normal_url_char, (unsigned char)c)) #define IS_HOST_CHAR(c) (IS_ALPHANUM(c) || (c) == '.' || (c) == '-') #else #define TOKEN(c) ((c == ' ') ? ' ' : tokens[(unsigned char)c]) #define IS_URL_CHAR(c) \ (BIT_AT(normal_url_char, (unsigned char)c) || ((c) & 0x80)) #define IS_HOST_CHAR(c) \ (IS_ALPHANUM(c) || (c) == '.' || (c) == '-' || (c) == '_') #endif #define start_state (type == HTTP_REQUEST ? s_start_req : s_start_res) */ private static char LOWER(char c) { return Character.toLowerCase(c); } private static boolean IS_ALPHA(char c) { boolean yes = (LOWER(c) >= 'a' && LOWER(c) <= 'z'); if (!yes) debug(TAG, "\n\n\nNot alpha: "+c); return yes; } private static boolean IS_NUM(char c) { boolean yes = ((c) >= '0' && (c) <= '9'); if (!yes) debug(TAG, "\n\n\nNot num: "+c); return yes; } private static boolean IS_ALPHANUM(char c) { return (IS_ALPHA(c) || IS_NUM(c)); } private static boolean IS_HEX(char c) { boolean yes = (IS_NUM(c) || (LOWER(c) >= 'a' && LOWER(c) <= 'f')); if (!yes) debug(TAG, "\n\n\nNot HEX: "+c); return yes; } private static boolean IS_MARK(char c) { return ((c) == '-' || (c) == '_' || (c) == '.' || (c) == '!' || (c) == '~' || (c) == '*' || (c) == '\'' || (c) == '(' || (c) == ')'); } private static boolean IS_USERINFO_CHAR(char c) { return (IS_ALPHANUM(c) || IS_MARK(c) || (c) == '%' || (c) == ';' || (c) == ':' || (c) == '&' || (c) == '=' || (c) == '+' || (c) == '$' || (c) == ','); } private static char TOKEN(int uc) { uc &= 0xff; debug(TAG, "TOKEN uc: "+uc); return (tokens[uc]); } private static boolean IS_URL_CHAR(char c) throws UnsupportedEncodingException { // convert to ASICII / UTF-8 char String cstr = "" + c; int uc = cstr.getBytes("utf-8")[0] & 0xff; debug(TAG, "IS_URL_CHAR cstr: "+cstr+", uc: "+uc); return (BIT_AT(normal_url_char, uc)); } private static boolean IS_HOST_CHAR(char c) { return (IS_ALPHANUM(c) || (c) == '.' || (c) == '-'); } private State start_state() { return (type == http_parser_type.HTTP_REQUEST ? State.s_start_req : State.s_start_res); } private State NEW_MESSAGE() { return http_should_keep_alive() ? start_state() : State.s_dead; } /* Our URL parser. * * This is designed to be shared by http_parser_execute() for URL validation, * hence it has a State transition + byte-for-byte interface. In addition, it * is meant to be embedded in http_parser_parse_url(), which does the dirty * work of turning State transitions URL components for its API. * * This function should only be invoked with non-space characters. It is * assumed that the caller cares about (and can detect) the transition between * URL and non-URL states by looking for these. */ private static State parse_url_char(State s, final char ch) throws UnsupportedEncodingException { if (ch == ' ' || ch == '\r' || ch == '\n') { return State.s_dead; } ///#if HTTP_PARSER_STRICT if (ch == '\t' || ch == '\f') { return State.s_dead; } ///#endif switch (s) { case s_req_spaces_before_url: /* Proxied requests are followed by scheme of an absolute URI (alpha). * All methods except CONNECT are followed by '/' or '*'. */ if (ch == '/' || ch == '*') { return State.s_req_path; } if (IS_ALPHA(ch)) { return State.s_req_schema; } break; case s_req_schema: if (IS_ALPHA(ch)) { return s; } if (ch == ':') { return State.s_req_schema_slash; } break; case s_req_schema_slash: if (ch == '/') { return State.s_req_schema_slash_slash; } break; case s_req_schema_slash_slash: if (ch == '/') { return State.s_req_server_start; } break; case s_req_server_with_at: if (ch == '@') { return State.s_dead; } /* FALLTHROUGH */ case s_req_server_start: case s_req_server: if (ch == '/') { return State.s_req_path; } if (ch == '?') { return State.s_req_query_string_start; } if (ch == '@') { return State.s_req_server_with_at; } if (IS_USERINFO_CHAR(ch) || ch == '[' || ch == ']') { return State.s_req_server; } break; case s_req_path: if (IS_URL_CHAR(ch)) { return s; } switch (ch) { case '?': return State.s_req_query_string_start; case '#': return State.s_req_fragment_start; } break; case s_req_query_string_start: case s_req_query_string: if (IS_URL_CHAR(ch)) { return State.s_req_query_string; } switch (ch) { case '?': /* allow extra '?' in query string */ return State.s_req_query_string; case '#': return State.s_req_fragment_start; } break; case s_req_fragment_start: if (IS_URL_CHAR(ch)) { return State.s_req_fragment; } switch (ch) { case '?': return State.s_req_fragment; case '#': return s; } break; case s_req_fragment: if (IS_URL_CHAR(ch)) { return s; } switch (ch) { case '?': case '#': return s; } break; default: break; } /* We should never fall out of the switch above unless there's an error */ return State.s_dead; } /* Result structure for http_parser_parse_url(). * * Callers should index into field_data[] with UF_* values if field_set * has the relevant (1 << UF_*) bit set. As a courtesy to clients (and * because we probably have padding left over), we convert any port to * a uint16_t. */ protected static class http_parser_url { protected int field_set; /* Bitmask of (1 << UF_*) values */ protected int port; /* Converted UF_PORT string */ protected class field_data_t { int off; /* Offset into buffer in which field starts */ int len; /* Length of run in buffer */ } protected field_data_t field_data[]; protected http_parser_url() { field_data = new field_data_t[http_parser_url_fields.UF_MAX.field]; } } /* * struct http_parser_settings { http_cb on_message_begin; http_data_cb on_url; http_data_cb on_status; http_data_cb on_header_field; http_data_cb on_header_value; http_cb on_headers_complete; http_data_cb on_body; http_cb on_message_complete; }; * */ protected abstract int on_message_begin() throws Exception; protected abstract int on_url(ByteBuffer url) throws Exception; protected abstract int on_status(ByteBuffer status) throws Exception; protected abstract int on_header_field(ByteBuffer field) throws Exception; protected abstract int on_header_value(ByteBuffer vaule) throws Exception; protected abstract int on_headers_complete() throws Exception; protected abstract int on_body(ByteBuffer body) throws Exception; protected abstract int on_message_complete() throws Exception; /* Returns the library version. Bits 16-23 contain the major version number, * bits 8-15 the minor version number and bits 0-7 the patch level. * Usage example: * * unsigned long version = http_parser_version(); * unsigned major = (version >> 16) & 255; * unsigned minor = (version >> 8) & 255; * unsigned patch = version & 255; * printf("http_parser v%u.%u.%u\n", major, minor, version); */ private final static long HTTP_PARSER_VERSION_MAJOR = 2; private final static long HTTP_PARSER_VERSION_MINOR = 3; private final static long HTTP_PARSER_VERSION_PATCH = 0; public long version() { return HTTP_PARSER_VERSION_MAJOR * 0x10000 | HTTP_PARSER_VERSION_MINOR * 0x00100 | HTTP_PARSER_VERSION_PATCH * 0x00001; } /* Maximium header size allowed */ private final static int HTTP_MAX_HEADER_SIZE = (80*1024); ///void http_parser_init(http_parser *parser, enum http_parser_type type); @SuppressWarnings("static-access") public void reset(http_parser_type type) { this.type = type; this.http_errno = http_errno.HPE_OK; this.state = (type == http_parser_type.HTTP_REQUEST ? State.s_start_req : (type == http_parser_type.HTTP_RESPONSE ? State.s_start_res : State.s_start_req_or_res)); // zero fields this.content_length = 0; this.flags = 0; this.http_major = this.http_minor = 0; this.index = 0; this.nread = 0; this.status_code = 0; this.upgrade = false; this.header_state = header_state.h_general; this.method = http_method.HTTP_INVALID; } @SuppressWarnings("static-access") public int execute(ByteBuffer data) throws Exception { char c, ch; int uc; /*int8_t unhex_val; const char *p = data; const char *header_field_mark = 0; const char *header_value_mark = 0; const char *url_mark = 0; const char *body_mark = 0; const char *status_mark = 0; */ byte unhex_val; int p = 0; int header_field_mark = -1; int header_value_mark = -1; int url_mark = -1; int body_mark = -1; int status_mark = -1; int len = (data == null) ? 0 : data.capacity(); int reexecute_byte = 0; debug(TAG, "http_parser_execute ."); /* We're in an error state. Don't bother doing anything. */ if (HTTP_PARSER_ERRNO() != http_errno.HPE_OK) { return 0; } debug(TAG, "http_parser_execute .."); if (len == 0) { switch (state) { case s_body_identity_eof: /* Use of CALLBACK_NOTIFY() here would erroneously return 1 byte read if * we got paused. */ ///CALLBACK_NOTIFY_NOADVANCE(message_complete); { assert(HTTP_PARSER_ERRNO() == http_errno.HPE_OK); ///if (settings->on_##FOR) { if (0 != on_message_complete()) { SET_ERRNO(http_errno.HPE_CB_message_complete); } /* We either errored above or got paused; get out */ if (HTTP_PARSER_ERRNO() != http_errno.HPE_OK) { ///return (ER); return (p); } } } return 0; case s_dead: case s_start_req_or_res: case s_start_res: case s_start_req: return 0; default: SET_ERRNO(http_errno.HPE_INVALID_EOF_STATE); return 1; } } if (state == State.s_header_field) header_field_mark = 0;///data; if (state == State.s_header_value) header_value_mark = 0;///data; switch (state) { case s_req_path: case s_req_schema: case s_req_schema_slash: case s_req_schema_slash_slash: case s_req_server_start: case s_req_server: case s_req_server_with_at: case s_req_query_string_start: case s_req_query_string: case s_req_fragment_start: case s_req_fragment: url_mark = 0;///data; break; case s_res_status: status_mark = 0;///data; break; default: break; } debug(TAG, "http_parser_execute ... state:"+state.state); ///for (p=data; p != data + len; p++) { for (p = 0; p < len; p ++) { ///ch = *p; uc = data.get(p) & 0xff; ch = ASCII[uc]; debug(TAG, "uc: "+uc+", ch: "+ch); ///if (PARSING_HEADER(state)) { if (PARSING_HEADER()) { debug(TAG, "PARSING_HEADER"); ///++nread; nread++; /* Don't allow the total size of the http headers (including the status * line) to exceed HTTP_MAX_HEADER_SIZE. This check is here to protect * embedders against denial-of-service attacks where the attacker feeds * us a never-ending header that the embedder keeps buffering. * * This check is arguably the responsibility of embedders but we're doing * it on the embedder's behalf because most won't bother and this way we * make the web a little safer. HTTP_MAX_HEADER_SIZE is still far bigger * than any reasonable request or response so this should never affect * day-to-day operation. */ ///if (nread > HTTP_MAX_HEADER_SIZE) { if (nread > HTTP_MAX_HEADER_SIZE) { SET_ERRNO(http_errno.HPE_HEADER_OVERFLOW); ///goto error; { if (HTTP_PARSER_ERRNO() == http_errno.HPE_OK) { SET_ERRNO(http_errno.HPE_UNKNOWN); } ///return (p - data); return p; } } } ///reexecute_byte: while (true) { debug(TAG, "reexecute_byte "+(reexecute_byte++)+",err:"+HTTP_PARSER_ERRNO().desc()+",state:"+state.state); switch (state) { case s_dead: /* this state is used after a 'Connection: close' message * the parser will error out if it reads another message */ if (ch == '\r' || ch == '\n') break; SET_ERRNO(http_errno.HPE_CLOSED_CONNECTION); ///goto error; { if (HTTP_PARSER_ERRNO() == http_errno.HPE_OK) { SET_ERRNO(http_errno.HPE_UNKNOWN); } ///return (p - data); return p; } case s_start_req_or_res: { if (ch == '\r' || ch == '\n') break; flags = 0; content_length = ULLONG_MAX(); if (ch == 'H') { state = State.s_res_or_resp_H; ///CALLBACK_NOTIFY(message_begin); { assert(HTTP_PARSER_ERRNO() == http_errno.HPE_OK); ///if (settings->on_##FOR) { if (0 != on_message_begin()) { SET_ERRNO(http_errno.HPE_CB_message_begin); } /* We either errored above or got paused; get out */ if (HTTP_PARSER_ERRNO() != http_errno.HPE_OK) { ///return (ER); return (p + 1); } } } } else { type = http_parser_type.HTTP_REQUEST; state = State.s_start_req; ///goto reexecute_byte; continue; } break; } case s_res_or_resp_H: if (ch == 'T') { type = http_parser_type.HTTP_RESPONSE; state = State.s_res_HT; } else { if (ch != 'E') { SET_ERRNO(http_errno.HPE_INVALID_CONSTANT); ///goto error; { if (HTTP_PARSER_ERRNO() == http_errno.HPE_OK) { SET_ERRNO(http_errno.HPE_UNKNOWN); } ///return (p - data); return p; } } type = http_parser_type.HTTP_REQUEST; method = http_method.HTTP_HEAD; index = 2; state = State.s_req_method; } break; case s_start_res: { flags = 0; content_length = ULLONG_MAX(); switch (ch) { case 'H': state = State.s_res_H; break; case '\r': case '\n': break; default: SET_ERRNO(http_errno.HPE_INVALID_CONSTANT); ///goto error; { if (HTTP_PARSER_ERRNO() == http_errno.HPE_OK) { SET_ERRNO(http_errno.HPE_UNKNOWN); } ///return (p - data); return p; } } ///CALLBACK_NOTIFY(message_begin); { assert(HTTP_PARSER_ERRNO() == http_errno.HPE_OK); ///if (settings->on_##FOR) { if (0 != on_message_begin()) { SET_ERRNO(http_errno.HPE_CB_message_begin); } /* We either errored above or got paused; get out */ if (HTTP_PARSER_ERRNO() != http_errno.HPE_OK) { ///return (ER); return (p + 1); } } } break; } case s_res_H: ///STRICT_CHECK(ch != 'T'); if (ch != 'T') { SET_ERRNO(http_errno.HPE_STRICT); ///goto error; if (HTTP_PARSER_ERRNO() == http_errno.HPE_OK) { SET_ERRNO(http_errno.HPE_UNKNOWN); } ///return (p - data); return p; } state = State.s_res_HT; break; case s_res_HT: ///STRICT_CHECK(ch != 'T'); if (ch != 'T') { SET_ERRNO(http_errno.HPE_STRICT); ///goto error; if (HTTP_PARSER_ERRNO() == http_errno.HPE_OK) { SET_ERRNO(http_errno.HPE_UNKNOWN); } ///return (p - data); return p; } state = State.s_res_HTT; break; case s_res_HTT: ///STRICT_CHECK(ch != 'P'); if (ch != 'P') { SET_ERRNO(http_errno.HPE_STRICT); ///goto error; if (HTTP_PARSER_ERRNO() == http_errno.HPE_OK) { SET_ERRNO(http_errno.HPE_UNKNOWN); } ///return (p - data); return p; } state = State.s_res_HTTP; break; case s_res_HTTP: ///STRICT_CHECK(ch != '/'); if (ch != '/') { SET_ERRNO(http_errno.HPE_STRICT); ///goto error; if (HTTP_PARSER_ERRNO() == http_errno.HPE_OK) { SET_ERRNO(http_errno.HPE_UNKNOWN); } ///return (p - data); return p; } state = State.s_res_first_http_major; break; case s_res_first_http_major: if (ch < '0' || ch > '9') { SET_ERRNO(http_errno.HPE_INVALID_VERSION); ///goto error; { if (HTTP_PARSER_ERRNO() == http_errno.HPE_OK) { SET_ERRNO(http_errno.HPE_UNKNOWN); } ///return (p - data); return p; } } http_major = ch - '0'; state = State.s_res_http_major; break; /* major http version or dot */ case s_res_http_major: { if (ch == '.') { state = State.s_res_first_http_minor; break; } if (!IS_NUM(ch)) { SET_ERRNO(http_errno.HPE_INVALID_VERSION); ///goto error; { if (HTTP_PARSER_ERRNO() == http_errno.HPE_OK) { SET_ERRNO(http_errno.HPE_UNKNOWN); } ///return (p - data); return p; } } http_major *= 10; http_major += ch - '0'; if (http_major > 999) { SET_ERRNO(http_errno.HPE_INVALID_VERSION); ///goto error; { if (HTTP_PARSER_ERRNO() == http_errno.HPE_OK) { SET_ERRNO(http_errno.HPE_UNKNOWN); } ///return (p - data); return p; } } break; } /* first digit of minor http version */ case s_res_first_http_minor: if (!IS_NUM(ch)) { SET_ERRNO(http_errno.HPE_INVALID_VERSION); ///goto error; { if (HTTP_PARSER_ERRNO() == http_errno.HPE_OK) { SET_ERRNO(http_errno.HPE_UNKNOWN); } ///return (p - data); return p; } } http_minor = ch - '0'; state = State.s_res_http_minor; break; /* minor http version or end of request line */ case s_res_http_minor: { if (ch == ' ') { state = State.s_res_first_status_code; break; } if (!IS_NUM(ch)) { SET_ERRNO(http_errno.HPE_INVALID_VERSION); ///goto error; { if (HTTP_PARSER_ERRNO() == http_errno.HPE_OK) { SET_ERRNO(http_errno.HPE_UNKNOWN); } ///return (p - data); return p; } } http_minor *= 10; http_minor += ch - '0'; if (http_minor > 999) { SET_ERRNO(http_errno.HPE_INVALID_VERSION); ///goto error; { if (HTTP_PARSER_ERRNO() == http_errno.HPE_OK) { SET_ERRNO(http_errno.HPE_UNKNOWN); } ///return (p - data); return p; } } break; } case s_res_first_status_code: { if (!IS_NUM(ch)) { if (ch == ' ') { break; } SET_ERRNO(http_errno.HPE_INVALID_STATUS); ////goto error; { if (HTTP_PARSER_ERRNO() == http_errno.HPE_OK) { SET_ERRNO(http_errno.HPE_UNKNOWN); } ///return (p - data); return p; } } status_code = ch - '0'; state = State.s_res_status_code; break; } case s_res_status_code: { if (!IS_NUM(ch)) { switch (ch) { case ' ': state = State.s_res_status_start; break; case '\r': state = State.s_res_line_almost_done; break; case '\n': state = State.s_header_field_start; break; default: SET_ERRNO(http_errno.HPE_INVALID_STATUS); ///goto error; { if (HTTP_PARSER_ERRNO() == http_errno.HPE_OK) { SET_ERRNO(http_errno.HPE_UNKNOWN); } ///return (p - data); return p; } } break; } status_code *= 10; status_code += ch - '0'; if (status_code > 999) { SET_ERRNO(http_errno.HPE_INVALID_STATUS); ///goto error; { if (HTTP_PARSER_ERRNO() == http_errno.HPE_OK) { SET_ERRNO(http_errno.HPE_UNKNOWN); } ///return (p - data); return p; } } break; } case s_res_status_start: { if (ch == '\r') { state = State.s_res_line_almost_done; break; } if (ch == '\n') { state = State.s_header_field_start; break; } ///MARK(status); if (status_mark == -1) status_mark = p; state = State.s_res_status; index = 0; break; } case s_res_status: if (ch == '\r') { state = State.s_res_line_almost_done; ///CALLBACK_DATA(status); { assert(HTTP_PARSER_ERRNO() == http_errno.HPE_OK); if (status_mark != -1) { ///if (settings->on_##FOR) { // slice data ByteBuffer ByteBuffer bb = data; int opos = bb.position(); bb.position(status_mark); int olmt = bb.limit(); bb.limit(p); ByteBuffer rb = bb.slice(); bb.limit(olmt); bb.position(opos); ///if (0 != on_status(FOR##_mark, (LEN))) { if (0 != on_status(rb)) { SET_ERRNO(http_errno.HPE_CB_status); } /* We either errored above or got paused; get out */ if (HTTP_PARSER_ERRNO() != http_errno.HPE_OK) { ///return (ER); return (p + 1); } } ///FOR##_mark = NULL; status_mark = -1; } } break; } if (ch == '\n') { state = State.s_header_field_start; ///CALLBACK_DATA(status); { assert(HTTP_PARSER_ERRNO() == http_errno.HPE_OK); if (status_mark != -1) { ///if (settings->on_##FOR) { // slice data ByteBuffer ByteBuffer bb = data; int opos = bb.position(); bb.position(status_mark); int olmt = bb.limit(); bb.limit(p); ByteBuffer rb = bb.slice(); bb.limit(olmt); bb.position(opos); ///if (0 != on_status(FOR##_mark, (LEN))) { if (0 != on_status(rb)) { SET_ERRNO(http_errno.HPE_CB_status); } /* We either errored above or got paused; get out */ if (HTTP_PARSER_ERRNO() != http_errno.HPE_OK) { ///return (ER); return (p + 1); } } ///FOR##_mark = NULL; status_mark = -1; } } break; } break; case s_res_line_almost_done: ///STRICT_CHECK(ch != '\n'); if (ch != '\n') { SET_ERRNO(http_errno.HPE_STRICT); ///goto error; if (HTTP_PARSER_ERRNO() == http_errno.HPE_OK) { SET_ERRNO(http_errno.HPE_UNKNOWN); } ///return (p - data); return p; } state = State.s_header_field_start; break; case s_start_req: { if (ch == '\r' || ch == '\n') break; flags = 0; content_length = ULLONG_MAX(); if (!IS_ALPHA(ch)) { SET_ERRNO(http_errno.HPE_INVALID_METHOD); ///goto error; { if (HTTP_PARSER_ERRNO() == http_errno.HPE_OK) { SET_ERRNO(http_errno.HPE_UNKNOWN); } ///return (p - data); return p; } } method = http_method.HTTP_INVALID; index = 1; switch (ch) { case 'C': method = http_method.HTTP_CONNECT; /* or COPY, CHECKOUT */ break; case 'D': method = http_method.HTTP_DELETE; break; case 'G': method = http_method.HTTP_GET; break; case 'H': method = http_method.HTTP_HEAD; break; case 'L': method = http_method.HTTP_LOCK; break; case 'M': method = http_method.HTTP_MKCOL; /* or MOVE, MKACTIVITY, MERGE, M-SEARCH */ break; case 'N': method = http_method.HTTP_NOTIFY; break; case 'O': method = http_method.HTTP_OPTIONS; break; case 'P': method = http_method.HTTP_POST; /* or PROPFIND|PROPPATCH|PUT|PATCH|PURGE */ break; case 'R': method = http_method.HTTP_REPORT; break; case 'S': method = http_method.HTTP_SUBSCRIBE; /* or SEARCH */ break; case 'T': method = http_method.HTTP_TRACE; break; case 'U': method = http_method.HTTP_UNLOCK; /* or UNSUBSCRIBE */ break; default: SET_ERRNO(http_errno.HPE_INVALID_METHOD); ///goto error; { if (HTTP_PARSER_ERRNO() == http_errno.HPE_OK) { SET_ERRNO(http_errno.HPE_UNKNOWN); } ///return (p - data); return p; } } state = State.s_req_method; ///CALLBACK_NOTIFY(message_begin); { assert(HTTP_PARSER_ERRNO() == http_errno.HPE_OK); ///if (settings->on_##FOR) { if (0 != on_message_begin()) { SET_ERRNO(http_errno.HPE_CB_message_begin); } /* We either errored above or got paused; get out */ if (HTTP_PARSER_ERRNO() != http_errno.HPE_OK) { ///return (ER); return (p + 1); } } } break; } case s_req_method: { char [] matcher; if (ch == '\0') { SET_ERRNO(http_errno.HPE_INVALID_METHOD); ///goto error; { if (HTTP_PARSER_ERRNO() == http_errno.HPE_OK) { SET_ERRNO(http_errno.HPE_UNKNOWN); } ///return (p - data); return p; } } matcher = method.desc.toCharArray(); if (ch == ' ' && matcher[index] == '\0') { state = State.s_req_spaces_before_url; } else if (ch == matcher[index]) { ; /* nada */ } else if (method == http_method.HTTP_CONNECT) { if (index == 1 && ch == 'H') { method = http_method.HTTP_CHECKOUT; } else if (index == 2 && ch == 'P') { method = http_method.HTTP_COPY; } else { SET_ERRNO(http_errno.HPE_INVALID_METHOD); ///goto error; { if (HTTP_PARSER_ERRNO() == http_errno.HPE_OK) { SET_ERRNO(http_errno.HPE_UNKNOWN); } ///return (p - data); return p; } } } else if (method == http_method.HTTP_MKCOL) { if (index == 1 && ch == 'O') { method = http_method.HTTP_MOVE; } else if (index == 1 && ch == 'E') { method = http_method.HTTP_MERGE; } else if (index == 1 && ch == '-') { method = http_method.HTTP_MSEARCH; } else if (index == 2 && ch == 'A') { method = http_method.HTTP_MKACTIVITY; } else { SET_ERRNO(http_errno.HPE_INVALID_METHOD); ///goto error; { if (HTTP_PARSER_ERRNO() == http_errno.HPE_OK) { SET_ERRNO(http_errno.HPE_UNKNOWN); } ///return (p - data); return p; } } } else if (method == http_method.HTTP_SUBSCRIBE) { if (index == 1 && ch == 'E') { method = http_method.HTTP_SEARCH; } else { SET_ERRNO(http_errno.HPE_INVALID_METHOD); ///goto error; { if (HTTP_PARSER_ERRNO() == http_errno.HPE_OK) { SET_ERRNO(http_errno.HPE_UNKNOWN); } ///return (p - data); return p; } } } else if (index == 1 && method == http_method.HTTP_POST) { if (ch == 'R') { method = http_method.HTTP_PROPFIND; /* or HTTP_PROPPATCH */ } else if (ch == 'U') { method = http_method.HTTP_PUT; /* or HTTP_PURGE */ } else if (ch == 'A') { method = http_method.HTTP_PATCH; } else { SET_ERRNO(http_errno.HPE_INVALID_METHOD); ///goto error; { if (HTTP_PARSER_ERRNO() == http_errno.HPE_OK) { SET_ERRNO(http_errno.HPE_UNKNOWN); } ///return (p - data); return p; } } } else if (index == 2) { if (method == http_method.HTTP_PUT) { if (ch == 'R') { method = http_method.HTTP_PURGE; } else { SET_ERRNO(http_errno.HPE_INVALID_METHOD); ///goto error; { if (HTTP_PARSER_ERRNO() == http_errno.HPE_OK) { SET_ERRNO(http_errno.HPE_UNKNOWN); } ///return (p - data); return p; } } } else if (method == http_method.HTTP_UNLOCK) { if (ch == 'S') { method = http_method.HTTP_UNSUBSCRIBE; } else { SET_ERRNO(http_errno.HPE_INVALID_METHOD); ///goto error; { if (HTTP_PARSER_ERRNO() == http_errno.HPE_OK) { SET_ERRNO(http_errno.HPE_UNKNOWN); } ///return (p - data); return p; } } } else { SET_ERRNO(http_errno.HPE_INVALID_METHOD); ///goto error; { if (HTTP_PARSER_ERRNO() == http_errno.HPE_OK) { SET_ERRNO(http_errno.HPE_UNKNOWN); } ///return (p - data); return p; } } } else if (index == 4 && method == http_method.HTTP_PROPFIND && ch == 'P') { method = http_method.HTTP_PROPPATCH; } else { SET_ERRNO(http_errno.HPE_INVALID_METHOD); ///goto error; { if (HTTP_PARSER_ERRNO() == http_errno.HPE_OK) { SET_ERRNO(http_errno.HPE_UNKNOWN); } ///return (p - data); return p; } } ++index; break; } case s_req_spaces_before_url: { if (ch == ' ') break; ///MARK(url); if (url_mark == -1) url_mark = p; if (method == http_method.HTTP_CONNECT) { state = State.s_req_server_start; } state = parse_url_char(state, ch); if (state == State.s_dead) { SET_ERRNO(http_errno.HPE_INVALID_URL); ///goto error; { if (HTTP_PARSER_ERRNO() == http_errno.HPE_OK) { SET_ERRNO(http_errno.HPE_UNKNOWN); } ///return (p - data); return p; } } break; } case s_req_schema: case s_req_schema_slash: case s_req_schema_slash_slash: case s_req_server_start: { switch (ch) { /* No whitespace allowed here */ case ' ': case '\r': case '\n': SET_ERRNO(http_errno.HPE_INVALID_URL); ///goto error; { if (HTTP_PARSER_ERRNO() == http_errno.HPE_OK) { SET_ERRNO(http_errno.HPE_UNKNOWN); } ///return (p - data); return p; } default: state = parse_url_char(state, ch); if (state == State.s_dead) { SET_ERRNO(http_errno.HPE_INVALID_URL); ///goto error; { if (HTTP_PARSER_ERRNO() == http_errno.HPE_OK) { SET_ERRNO(http_errno.HPE_UNKNOWN); } ///return (p - data); return p; } } } break; } case s_req_server: case s_req_server_with_at: case s_req_path: case s_req_query_string_start: case s_req_query_string: case s_req_fragment_start: case s_req_fragment: { switch (ch) { case ' ': state = State.s_req_http_start; ///CALLBACK_DATA(url); { assert(HTTP_PARSER_ERRNO() == http_errno.HPE_OK); if (url_mark != -1) { ///if (settings->on_##FOR) { // slice data ByteBuffer ByteBuffer bb = data; int opos = bb.position(); bb.position(url_mark); int olmt = bb.limit(); bb.limit(p); ByteBuffer rb = bb.slice(); bb.limit(olmt); bb.position(opos); ///if (0 != on_url(FOR##_mark, (LEN))) { if (0 != on_url(rb)) { SET_ERRNO(http_errno.HPE_CB_url); } /* We either errored above or got paused; get out */ if (HTTP_PARSER_ERRNO() != http_errno.HPE_OK) { ///return (ER); return (p + 1); } } ///FOR##_mark = NULL; url_mark = -1; } } break; case '\r': case '\n': http_major = 0; http_minor = 9; state = (ch == '\r') ? State.s_req_line_almost_done : State.s_header_field_start; ///CALLBACK_DATA(url); { assert(HTTP_PARSER_ERRNO() == http_errno.HPE_OK); if (url_mark != -1) { ///if (settings->on_##FOR) { // slice data ByteBuffer ByteBuffer bb = data; int opos = bb.position(); bb.position(url_mark); int olmt = bb.limit(); bb.limit(p); ByteBuffer rb = bb.slice(); bb.limit(olmt); bb.position(opos); ///if (0 != on_##FOR(FOR##_mark, (LEN))) { if (0 != on_url(rb)) { SET_ERRNO(http_errno.HPE_CB_url); } /* We either errored above or got paused; get out */ if (HTTP_PARSER_ERRNO() != http_errno.HPE_OK) { ///return (ER); return (p + 1); } } ///FOR##_mark = NULL; url_mark = -1; } } break; default: state = parse_url_char(state, ch); if (state == State.s_dead) { SET_ERRNO(http_errno.HPE_INVALID_URL); ///goto error; { if (HTTP_PARSER_ERRNO() == http_errno.HPE_OK) { SET_ERRNO(http_errno.HPE_UNKNOWN); } ///return (p - data); return p; } } } break; } case s_req_http_start: switch (ch) { case 'H': state = State.s_req_http_H; break; case ' ': break; default: SET_ERRNO(http_errno.HPE_INVALID_CONSTANT); ///goto error; { if (HTTP_PARSER_ERRNO() == http_errno.HPE_OK) { SET_ERRNO(http_errno.HPE_UNKNOWN); } ///return (p - data); return p; } } break; case s_req_http_H: ///STRICT_CHECK(ch != 'T'); if (ch != 'T') { SET_ERRNO(http_errno.HPE_STRICT); ///goto error; if (HTTP_PARSER_ERRNO() == http_errno.HPE_OK) { SET_ERRNO(http_errno.HPE_UNKNOWN); } ///return (p - data); return p; } state = State.s_req_http_HT; break; case s_req_http_HT: ///STRICT_CHECK(ch != 'T'); if (ch != 'T') { SET_ERRNO(http_errno.HPE_STRICT); ///goto error; if (HTTP_PARSER_ERRNO() == http_errno.HPE_OK) { SET_ERRNO(http_errno.HPE_UNKNOWN); } ///return (p - data); return p; } state = State.s_req_http_HTT; break; case s_req_http_HTT: ///STRICT_CHECK(ch != 'P'); if (ch != 'P') { SET_ERRNO(http_errno.HPE_STRICT); ///goto error; if (HTTP_PARSER_ERRNO() == http_errno.HPE_OK) { SET_ERRNO(http_errno.HPE_UNKNOWN); } ///return (p - data); return p; } state = State.s_req_http_HTTP; break; case s_req_http_HTTP: ///STRICT_CHECK(ch != '/'); if (ch != '/') { SET_ERRNO(http_errno.HPE_STRICT); ///goto error; if (HTTP_PARSER_ERRNO() == http_errno.HPE_OK) { SET_ERRNO(http_errno.HPE_UNKNOWN); } ///return (p - data); return p; } state = State.s_req_first_http_major; break; /* first digit of major http version */ case s_req_first_http_major: if (ch < '1' || ch > '9') { SET_ERRNO(http_errno.HPE_INVALID_VERSION); ///goto error; { if (HTTP_PARSER_ERRNO() == http_errno.HPE_OK) { SET_ERRNO(http_errno.HPE_UNKNOWN); } ///return (p - data); return p; } } http_major = ch - '0'; state = State.s_req_http_major; break; /* major http version or dot */ case s_req_http_major: { if (ch == '.') { state = State.s_req_first_http_minor; break; } if (!IS_NUM(ch)) { SET_ERRNO(http_errno.HPE_INVALID_VERSION); ///goto error; { if (HTTP_PARSER_ERRNO() == http_errno.HPE_OK) { SET_ERRNO(http_errno.HPE_UNKNOWN); } ///return (p - data); return p; } } http_major *= 10; http_major += ch - '0'; if (http_major > 999) { SET_ERRNO(http_errno.HPE_INVALID_VERSION); ///goto error; { if (HTTP_PARSER_ERRNO() == http_errno.HPE_OK) { SET_ERRNO(http_errno.HPE_UNKNOWN); } ///return (p - data); return p; } } break; } /* first digit of minor http version */ case s_req_first_http_minor: if (!IS_NUM(ch)) { SET_ERRNO(http_errno.HPE_INVALID_VERSION); ///goto error; { if (HTTP_PARSER_ERRNO() == http_errno.HPE_OK) { SET_ERRNO(http_errno.HPE_UNKNOWN); } ///return (p - data); return p; } } http_minor = ch - '0'; state = State.s_req_http_minor; break; /* minor http version or end of request line */ case s_req_http_minor: { if (ch == '\r') { state = State.s_req_line_almost_done; break; } if (ch == '\n') { state = State.s_header_field_start; break; } /* XXX allow spaces after digit? */ if (!IS_NUM(ch)) { SET_ERRNO(http_errno.HPE_INVALID_VERSION); ///goto error; { if (HTTP_PARSER_ERRNO() == http_errno.HPE_OK) { SET_ERRNO(http_errno.HPE_UNKNOWN); } ///return (p - data); return p; } } http_minor *= 10; http_minor += ch - '0'; if (http_minor > 999) { SET_ERRNO(http_errno.HPE_INVALID_VERSION); ///goto error; { if (HTTP_PARSER_ERRNO() == http_errno.HPE_OK) { SET_ERRNO(http_errno.HPE_UNKNOWN); } ///return (p - data); return p; } } break; } /* end of request line */ case s_req_line_almost_done: { if (ch != '\n') { SET_ERRNO(http_errno.HPE_LF_EXPECTED); ///goto error; { if (HTTP_PARSER_ERRNO() == http_errno.HPE_OK) { SET_ERRNO(http_errno.HPE_UNKNOWN); } ///return (p - data); return p; } } state = State.s_header_field_start; break; } case s_header_field_start: { if (ch == '\r') { state = State.s_headers_almost_done; break; } if (ch == '\n') { /* they might be just sending \n instead of \r\n so this would be * the second \n to denote the end of headers*/ state = State.s_headers_almost_done; ///goto reexecute_byte; continue; } // Java char is 16bits, not ASCII/UTF-8 char ///c = TOKEN(ch); c = TOKEN(uc); if (0==c) { SET_ERRNO(http_errno.HPE_INVALID_HEADER_TOKEN); ///goto error; { if (HTTP_PARSER_ERRNO() == http_errno.HPE_OK) { SET_ERRNO(http_errno.HPE_UNKNOWN); } ///return (p - data); return p; } } ///MARK(header_field); if (header_field_mark == -1) header_field_mark = p; index = 0; state = State.s_header_field; switch (c) { case 'c': header_state = header_states.h_C; break; case 'p': header_state = header_states.h_matching_proxy_connection; break; case 't': header_state = header_states.h_matching_transfer_encoding; break; case 'u': header_state = header_states.h_matching_upgrade; break; default: header_state = header_states.h_general; break; } break; } case s_header_field: { // Java char is 16bits, not ASCII/UTF-8 char ///c = TOKEN(ch); c = TOKEN(uc); if (c != 0) { switch (header_state) { case h_general: break; case h_C: index++; header_state = (c == 'o' ? header_states.h_CO : header_states.h_general); break; case h_CO: index++; header_state = (c == 'n' ? header_states.h_CON : header_states.h_general); break; case h_CON: index++; switch (c) { case 'n': header_state = header_states.h_matching_connection; break; case 't': header_state = header_states.h_matching_content_length; break; default: header_state = header_states.h_general; break; } break; /* connection */ case h_matching_connection: index++; ///if (index > sizeof(CONNECTION)-1 if (index > CONNECTION.length()-1 || c != CONNECTION.toCharArray()[index]) { header_state = header_states.h_general; ///} else if (index == sizeof(CONNECTION)-2) { } else if (index == CONNECTION.length()-2) { header_state = header_states.h_connection; } break; /* proxy-connection */ case h_matching_proxy_connection: index++; ///if (index > sizeof(PROXY_CONNECTION)-1 if (index > PROXY_CONNECTION.length()-1 || c != PROXY_CONNECTION.toCharArray()[index]) { header_state = header_states.h_general; ///} else if (index == sizeof(PROXY_CONNECTION)-2) { } else if (index == (PROXY_CONNECTION).length()-2) { header_state = header_states.h_connection; } break; /* content-length */ case h_matching_content_length: index++; if (index > (CONTENT_LENGTH).length()-1 || c != CONTENT_LENGTH.toCharArray()[index]) { header_state = header_states.h_general; } else if (index == (CONTENT_LENGTH).length()-2) { header_state = header_states.h_content_length; } break; /* transfer-encoding */ case h_matching_transfer_encoding: index++; if (index > (TRANSFER_ENCODING).length()-1 || c != TRANSFER_ENCODING.toCharArray()[index]) { header_state = header_states.h_general; } else if (index == (TRANSFER_ENCODING).length()-2) { header_state = header_states.h_transfer_encoding; } break; /* upgrade */ case h_matching_upgrade: index++; if (index > (UPGRADE).length()-1 || c != UPGRADE.toCharArray()[index]) { header_state = header_states.h_general; } else if (index == (UPGRADE).length()-2) { header_state = header_states.h_upgrade; } break; case h_connection: case h_content_length: case h_transfer_encoding: case h_upgrade: if (ch != ' ') header_state = header_states.h_general; break; default: ///assert(0 && "Unknown header_state"); assert("Unknown header_state"==null); break; } break; } if (ch == ':') { state = State.s_header_value_discard_ws; ///CALLBACK_DATA(header_field); { assert(HTTP_PARSER_ERRNO() == http_errno.HPE_OK); if (header_field_mark != -1) { ///if (settings->on_##FOR) { // slice data ByteBuffer ByteBuffer bb = data; int opos = bb.position(); bb.position(header_field_mark); int olmt = bb.limit(); bb.limit(p); ByteBuffer rb = bb.slice(); bb.limit(olmt); bb.position(opos); ///if (0 != on_##FOR(FOR##_mark, (LEN))) { if (0 != on_header_field(rb)) { SET_ERRNO(http_errno.HPE_CB_header_field); } /* We either errored above or got paused; get out */ if (HTTP_PARSER_ERRNO() != http_errno.HPE_OK) { ///return (ER); return (p + 1); } } ///FOR##_mark = NULL; header_field_mark = -1; } } break; } if (ch == '\r') { state = State.s_header_almost_done; ///CALLBACK_DATA(header_field); { assert(HTTP_PARSER_ERRNO() == http_errno.HPE_OK); if (header_field_mark != -1) { ///if (settings->on_##FOR) { // slice data ByteBuffer ByteBuffer bb = data; int opos = bb.position(); bb.position(header_field_mark); int olmt = bb.limit(); bb.limit(p); ByteBuffer rb = bb.slice(); bb.limit(olmt); bb.position(opos); ///if (0 != on_##FOR(FOR##_mark, (LEN))) { if (0 != on_header_field(rb)) { SET_ERRNO(http_errno.HPE_CB_header_field); } /* We either errored above or got paused; get out */ if (HTTP_PARSER_ERRNO() != http_errno.HPE_OK) { ///return (ER); return (p + 1); } } ///FOR##_mark = NULL; header_field_mark = -1; } } break; } if (ch == '\n') { state = State.s_header_field_start; ///CALLBACK_DATA(header_field); { assert(HTTP_PARSER_ERRNO() == http_errno.HPE_OK); if (header_field_mark != -1) { ///if (settings->on_##FOR) { // slice data ByteBuffer ByteBuffer bb = data; int opos = bb.position(); bb.position(header_field_mark); int olmt = bb.limit(); bb.limit(p); ByteBuffer rb = bb.slice(); bb.limit(olmt); bb.position(opos); ///if (0 != on_##FOR(FOR##_mark, (LEN))) { if (0 != on_header_field(rb)) { SET_ERRNO(http_errno.HPE_CB_header_field); } /* We either errored above or got paused; get out */ if (HTTP_PARSER_ERRNO() != http_errno.HPE_OK) { ///return (ER); return (p + 1); } } ///FOR##_mark = NULL; header_field_mark = -1; } } break; } SET_ERRNO(http_errno.HPE_INVALID_HEADER_TOKEN); ///goto error; { if (HTTP_PARSER_ERRNO() == http_errno.HPE_OK) { SET_ERRNO(http_errno.HPE_UNKNOWN); } ///return (p - data); return p; } } case s_header_value_discard_ws: if (ch == ' ' || ch == '\t') break; if (ch == '\r') { state = State.s_header_value_discard_ws_almost_done; break; } if (ch == '\n') { state = State.s_header_value_discard_lws; break; } /* FALLTHROUGH */ case s_header_value_start: { ///MARK(header_value); if (header_value_mark == -1) header_value_mark = p; state = State.s_header_value; index = 0; c = LOWER(ch); switch (header_state) { case h_upgrade: flags |= Flags.F_UPGRADE.flag; header_state = header_states.h_general; break; case h_transfer_encoding: /* looking for 'Transfer-Encoding: chunked' */ if ('c' == c) { header_state = header_states.h_matching_transfer_encoding_chunked; } else { header_state = header_states.h_general; } break; case h_content_length: if (!IS_NUM(ch)) { SET_ERRNO(http_errno.HPE_INVALID_CONTENT_LENGTH); ///goto error; { if (HTTP_PARSER_ERRNO() == http_errno.HPE_OK) { SET_ERRNO(http_errno.HPE_UNKNOWN); } ///return (p - data); return p; } } content_length = ch - '0'; break; case h_connection: /* looking for 'Connection: keep-alive' */ if (c == 'k') { header_state = header_states.h_matching_connection_keep_alive; /* looking for 'Connection: close' */ } else if (c == 'c') { header_state = header_states.h_matching_connection_close; } else { header_state = header_states.h_general; } break; default: header_state = header_states.h_general; break; } break; } case s_header_value: { if (ch == '\r') { state = State.s_header_almost_done; ///CALLBACK_DATA(header_value); { assert(HTTP_PARSER_ERRNO() == http_errno.HPE_OK); if (header_value_mark != -1) { ///if (settings->on_##FOR) { // slice data ByteBuffer ByteBuffer bb = data; int opos = bb.position(); bb.position(header_value_mark); int olmt = bb.limit(); bb.limit(p); ByteBuffer rb = bb.slice(); bb.limit(olmt); bb.position(opos); ///if (0 != on_##FOR(FOR##_mark, (LEN))) { if (0 != on_header_value(rb)) { SET_ERRNO(http_errno.HPE_CB_header_value); } /* We either errored above or got paused; get out */ if (HTTP_PARSER_ERRNO() != http_errno.HPE_OK) { ///return (ER); return (p + 1); } } ///FOR##_mark = NULL; header_value_mark = -1; } } break; } if (ch == '\n') { state = State.s_header_almost_done; ///CALLBACK_DATA_NOADVANCE(header_value); { assert(HTTP_PARSER_ERRNO() == http_errno.HPE_OK); if (header_value_mark != -1) { ///if (settings->on_##FOR) { // slice data ByteBuffer ByteBuffer bb = data; int opos = bb.position(); bb.position(header_value_mark); int olmt = bb.limit(); bb.limit(p); ByteBuffer rb = bb.slice(); bb.limit(olmt); bb.position(opos); ///if (0 != on_##FOR(FOR##_mark, (LEN))) { if (0 != on_header_value(rb)) { SET_ERRNO(http_errno.HPE_CB_header_value); } /* We either errored above or got paused; get out */ if (HTTP_PARSER_ERRNO() != http_errno.HPE_OK) { ///return (ER); return (p); } } ///FOR##_mark = NULL; header_value_mark = -1; } } ///goto reexecute_byte; continue; } c = LOWER(ch); switch (header_state) { case h_general: break; case h_connection: case h_transfer_encoding: ///assert(0 && "Shouldn't get here."); assert("Shouldn't get here."==null); break; case h_content_length: { long t; if (ch == ' ') break; if (!IS_NUM(ch)) { SET_ERRNO(http_errno.HPE_INVALID_CONTENT_LENGTH); ///goto error; { if (HTTP_PARSER_ERRNO() == http_errno.HPE_OK) { SET_ERRNO(http_errno.HPE_UNKNOWN); } ///return (p - data); return p; } } t = content_length; t *= 10; t += ch - '0'; /* Overflow? Test against a conservative limit for simplicity. */ if ((ULLONG_MAX() - 10) / 10 < content_length) { SET_ERRNO(http_errno.HPE_INVALID_CONTENT_LENGTH); ///goto error; { if (HTTP_PARSER_ERRNO() == http_errno.HPE_OK) { SET_ERRNO(http_errno.HPE_UNKNOWN); } ///return (p - data); return p; } } content_length = t; break; } /* Transfer-Encoding: chunked */ case h_matching_transfer_encoding_chunked: index++; if (index > (CHUNKED).length()-1 || c != CHUNKED.toCharArray()[index]) { header_state = header_states.h_general; } else if (index == (CHUNKED).length()-2) { header_state = header_states.h_transfer_encoding_chunked; } break; /* looking for 'Connection: keep-alive' */ case h_matching_connection_keep_alive: index++; if (index > (KEEP_ALIVE).length()-1 || c != KEEP_ALIVE.toCharArray()[index]) { header_state = header_states.h_general; } else if (index == (KEEP_ALIVE).length()-2) { header_state = header_states.h_connection_keep_alive; } break; /* looking for 'Connection: close' */ case h_matching_connection_close: index++; if (index > (CLOSE).length()-1 || c != CLOSE.toCharArray()[index]) { header_state = header_states.h_general; } else if (index == (CLOSE).length()-2) { header_state = header_states.h_connection_close; } break; case h_transfer_encoding_chunked: case h_connection_keep_alive: case h_connection_close: if (ch != ' ') header_state = header_states.h_general; break; default: state = State.s_header_value; header_state = header_states.h_general; break; } break; } case s_header_almost_done: { ///STRICT_CHECK(ch != '\n'); if (ch != '\n') { SET_ERRNO(http_errno.HPE_STRICT); ///goto error; if (HTTP_PARSER_ERRNO() == http_errno.HPE_OK) { SET_ERRNO(http_errno.HPE_UNKNOWN); } ///return (p - data); return p; } state = State.s_header_value_lws; break; } case s_header_value_lws: { if (ch == ' ' || ch == '\t') { state = State.s_header_value_start; ///goto reexecute_byte; continue; } /* finished the header */ switch (header_state) { case h_connection_keep_alive: flags |= Flags.F_CONNECTION_KEEP_ALIVE.flag; break; case h_connection_close: flags |= Flags.F_CONNECTION_CLOSE.flag; break; case h_transfer_encoding_chunked: flags |= Flags.F_CHUNKED.flag; break; default: break; } state = State.s_header_field_start; ///goto reexecute_byte; continue; } case s_header_value_discard_ws_almost_done: { ///STRICT_CHECK(ch != '\n'); if (ch != '\n') { SET_ERRNO(http_errno.HPE_STRICT); ///goto error; if (HTTP_PARSER_ERRNO() == http_errno.HPE_OK) { SET_ERRNO(http_errno.HPE_UNKNOWN); } ///return (p - data); return p; } state = State.s_header_value_discard_lws; break; } case s_header_value_discard_lws: { if (ch == ' ' || ch == '\t') { state = State.s_header_value_discard_ws; break; } else { /* header value was empty */ ///MARK(header_value); if (header_value_mark == -1) header_value_mark = p; state = State.s_header_field_start; ///CALLBACK_DATA_NOADVANCE(header_value); { assert(HTTP_PARSER_ERRNO() == http_errno.HPE_OK); if (header_value_mark != -1) { ///if (settings->on_##FOR) { // slice data ByteBuffer ByteBuffer bb = data; int opos = bb.position(); bb.position(header_value_mark); int olmt = bb.limit(); bb.limit(p); ByteBuffer rb = bb.slice(); bb.limit(olmt); bb.position(opos); ///if (0 != on_##FOR(FOR##_mark, (LEN))) { if (0 != on_header_value(rb)) { SET_ERRNO(http_errno.HPE_CB_header_value); } /* We either errored above or got paused; get out */ if (HTTP_PARSER_ERRNO() != http_errno.HPE_OK) { ///return (ER); return (p); } } ///FOR##_mark = NULL; header_value_mark = -1; } } ///goto reexecute_byte; continue; } } case s_headers_almost_done: { ///STRICT_CHECK(ch != '\n'); if (ch != '\n') { SET_ERRNO(http_errno.HPE_STRICT); ///goto error; if (HTTP_PARSER_ERRNO() == http_errno.HPE_OK) { SET_ERRNO(http_errno.HPE_UNKNOWN); } ///return (p - data); return p; } if (0!=(flags & Flags.F_TRAILING.flag)) { /* End of a chunked request */ state = NEW_MESSAGE(); ///CALLBACK_NOTIFY(message_complete); { assert(HTTP_PARSER_ERRNO() == http_errno.HPE_OK); ///if (settings->on_##FOR) { if (0 != on_message_complete()) { SET_ERRNO(http_errno.HPE_CB_message_complete); } /* We either errored above or got paused; get out */ if (HTTP_PARSER_ERRNO() != http_errno.HPE_OK) { ///return (ER); return (p + 1); } } } break; } state = State.s_headers_done; /* Set this here so that on_headers_complete() callbacks can see it */ upgrade = ((flags & Flags.F_UPGRADE.flag)!=0 || method == http_method.HTTP_CONNECT); /* Here we call the headers_complete callback. This is somewhat * different than other callbacks because if the user returns 1, we * will interpret that as saying that this message has no body. This * is needed for the annoying case of recieving a response to a HEAD * request. * * We'd like to use CALLBACK_NOTIFY_NOADVANCE() here but we cannot, so * we have to simulate it by handling a change in errno below. */ // TBD... ///if (settings->on_headers_complete) { ///switch (settings->on_headers_complete(parser)) { switch (on_headers_complete()) { case 0: break; case 1: flags |= Flags.F_SKIPBODY.flag; break; default: SET_ERRNO(http_errno.HPE_CB_headers_complete); ///return p - data; /* Error */ return p; } } if (HTTP_PARSER_ERRNO() != http_errno.HPE_OK) { ///return p - data; return p; } ///goto reexecute_byte; continue; } case s_headers_done: { ///debug(TAG, "STRICT_CHECK ch:"+ch+",state:"+state.state); ///STRICT_CHECK(ch != '\n'); if (ch != '\n') { SET_ERRNO(http_errno.HPE_STRICT); ///goto error; if (HTTP_PARSER_ERRNO() == http_errno.HPE_OK) { SET_ERRNO(http_errno.HPE_UNKNOWN); } ///return (p - data); return p; } nread = 0; /* Exit, the rest of the connect is in a different protocol. */ if (upgrade) { state = NEW_MESSAGE(); ///CALLBACK_NOTIFY(message_complete); { assert(HTTP_PARSER_ERRNO() == http_errno.HPE_OK); ///if (settings->on_##FOR) { if (0 != on_message_complete()) { SET_ERRNO(http_errno.HPE_CB_message_complete); } /* We either errored above or got paused; get out */ if (HTTP_PARSER_ERRNO() != http_errno.HPE_OK) { ///return (ER); return (p + 1); } } } ///return (p - data) + 1; return p + 1; } if ((flags & Flags.F_SKIPBODY.flag)!=0) { state = NEW_MESSAGE(); ///CALLBACK_NOTIFY(message_complete); { assert(HTTP_PARSER_ERRNO() == http_errno.HPE_OK); ///if (settings->on_##FOR) { if (0 != on_message_complete()) { SET_ERRNO(http_errno.HPE_CB_message_complete); } /* We either errored above or got paused; get out */ if (HTTP_PARSER_ERRNO() != http_errno.HPE_OK) { ///return (ER); return (p + 1); } } } } else if ((flags & Flags.F_CHUNKED.flag)!=0) { /* chunked encoding - ignore Content-Length header */ state = State.s_chunk_size_start; } else { if (content_length == 0) { /* Content-Length header given but zero: Content-Length: 0\r\n */ state = NEW_MESSAGE(); ///CALLBACK_NOTIFY(message_complete); { assert(HTTP_PARSER_ERRNO() == http_errno.HPE_OK); ///if (settings->on_##FOR) { if (0 != on_message_complete()) { SET_ERRNO(http_errno.HPE_CB_message_complete); } /* We either errored above or got paused; get out */ if (HTTP_PARSER_ERRNO() != http_errno.HPE_OK) { ///return (ER); return (p + 1); } } } } else if (content_length != ULLONG_MAX()) { /* Content-Length header given and non-zero */ state = State.s_body_identity; } else { if (type == http_parser_type.HTTP_REQUEST || !http_message_needs_eof()) { /* Assume content-length 0 - read the next */ state = NEW_MESSAGE(); ///CALLBACK_NOTIFY(message_complete); { assert(HTTP_PARSER_ERRNO() == http_errno.HPE_OK); ///if (settings->on_##FOR) { if (0 != on_message_complete()) { SET_ERRNO(http_errno.HPE_CB_message_complete); } /* We either errored above or got paused; get out */ if (HTTP_PARSER_ERRNO() != http_errno.HPE_OK) { ///return (ER); return (p + 1); } } } } else { /* Read body until EOF */ state = State.s_body_identity_eof; } } } break; } case s_body_identity: { ///long to_read = MIN(content_length, /// (long) ((data + len) - p)); long to_read = MIN(content_length, len - p); assert(content_length != 0 && content_length != ULLONG_MAX()); /* The difference between advancing content_length and p is because * the latter will automaticaly advance on the next loop iteration. * Further, if content_length ends up at 0, we want to see the last * byte again for our message complete callback. */ ///MARK(body); if (body_mark == -1) body_mark = p; content_length -= to_read; p += to_read - 1; if (content_length == 0) { state = State.s_message_done; /* Mimic CALLBACK_DATA_NOADVANCE() but with one extra byte. * * The alternative to doing this is to wait for the next byte to * trigger the data callback, just as in every other case. The * problem with this is that this makes it difficult for the test * harness to distinguish between complete-on-EOF and * complete-on-length. It's not clear that this distinction is * important for applications, but let's keep it for now. */ ///CALLBACK_DATA_(body, p - body_mark + 1, p - data); { assert(HTTP_PARSER_ERRNO() == http_errno.HPE_OK); if (body_mark != -1) { ///if (settings->on_##FOR) { // slice data ByteBuffer ByteBuffer bb = data; int opos = bb.position(); bb.position(body_mark); int olmt = bb.limit(); bb.limit(p+1); ByteBuffer rb = bb.slice(); bb.limit(olmt); bb.position(opos); ///if (0 != on_##FOR(FOR##_mark, (LEN))) { if (0 != on_body(rb)) { SET_ERRNO(http_errno.HPE_CB_body); } /* We either errored above or got paused; get out */ if (HTTP_PARSER_ERRNO() != http_errno.HPE_OK) { ///return (ER); return (p); } } ///FOR##_mark = NULL; body_mark = -1; } } ///goto reexecute_byte; continue; } break; } /* read until EOF */ case s_body_identity_eof: ///MARK(body); if (body_mark == -1) body_mark = p; ///p = data + len - 1; p = len - 1; break; case s_message_done: state = NEW_MESSAGE(); ///CALLBACK_NOTIFY(message_complete); { assert(HTTP_PARSER_ERRNO() == http_errno.HPE_OK); ///if (settings->on_##FOR) { if (0 != on_message_complete()) { SET_ERRNO(http_errno.HPE_CB_message_complete); } /* We either errored above or got paused; get out */ if (HTTP_PARSER_ERRNO() != http_errno.HPE_OK) { ///return (ER); return (p + 1); } } } break; case s_chunk_size_start: { assert(nread == 1); assert(0!=(flags & Flags.F_CHUNKED.flag)); ///unhex_val = unhex[ch & 0xffff]; unhex_val = unhex[uc]; if (unhex_val == -1) { SET_ERRNO(http_errno.HPE_INVALID_CHUNK_SIZE); ///goto error; { if (HTTP_PARSER_ERRNO() == http_errno.HPE_OK) { SET_ERRNO(http_errno.HPE_UNKNOWN); } ///return (p - data); return p; } } content_length = unhex_val; state = State.s_chunk_size; break; } case s_chunk_size: { long t; assert(0!=(flags & Flags.F_CHUNKED.flag)); if (ch == '\r') { state = State.s_chunk_size_almost_done; break; } ///unhex_val = unhex[ch & 0xffff]; unhex_val = unhex[uc]; if (unhex_val == -1) { if (ch == ';' || ch == ' ') { state = State.s_chunk_parameters; break; } SET_ERRNO(http_errno.HPE_INVALID_CHUNK_SIZE); ///goto error; { if (HTTP_PARSER_ERRNO() == http_errno.HPE_OK) { SET_ERRNO(http_errno.HPE_UNKNOWN); } ///return (p - data); return p; } } t = content_length; t *= 16; t += unhex_val; /* Overflow? Test against a conservative limit for simplicity. */ if ((ULLONG_MAX() - 16) / 16 < content_length) { SET_ERRNO(http_errno.HPE_INVALID_CONTENT_LENGTH); ///goto error; { if (HTTP_PARSER_ERRNO() == http_errno.HPE_OK) { SET_ERRNO(http_errno.HPE_UNKNOWN); } ///return (p - data); return p; } } content_length = t; break; } case s_chunk_parameters: { assert(0!=(flags & Flags.F_CHUNKED.flag)); /* just ignore this shit. TODO check for overflow */ if (ch == '\r') { state = State.s_chunk_size_almost_done; break; } break; } case s_chunk_size_almost_done: { assert(0!=(flags & Flags.F_CHUNKED.flag)); ///STRICT_CHECK(ch != '\n'); if (ch != '\n') { SET_ERRNO(http_errno.HPE_STRICT); ///goto error; if (HTTP_PARSER_ERRNO() == http_errno.HPE_OK) { SET_ERRNO(http_errno.HPE_UNKNOWN); } ///return (p - data); return p; } nread = 0; if (content_length == 0) { flags |= Flags.F_TRAILING.flag; state = State.s_header_field_start; } else { state = State.s_chunk_data; } break; } case s_chunk_data: { ///long to_read = MIN(content_length, /// (long) ((data + len) - p)); long to_read = MIN(content_length, len - p); assert(0!=(flags & Flags.F_CHUNKED.flag)); assert(content_length != 0 && content_length != ULLONG_MAX()); /* See the explanation in s_body_identity for why the content * length and data pointers are managed this way. */ ///MARK(body); if (body_mark == -1) body_mark = p; content_length -= to_read; p += to_read - 1; if (content_length == 0) { state = State.s_chunk_data_almost_done; } break; } case s_chunk_data_almost_done: assert(0!=(flags & Flags.F_CHUNKED.flag)); assert(content_length == 0); ///STRICT_CHECK(ch != '\r'); if (ch != '\r') { SET_ERRNO(http_errno.HPE_STRICT); ///goto error; if (HTTP_PARSER_ERRNO() == http_errno.HPE_OK) { SET_ERRNO(http_errno.HPE_UNKNOWN); } ///return (p - data); return p; } state = State.s_chunk_data_done; ///CALLBACK_DATA(body); { assert(HTTP_PARSER_ERRNO() == http_errno.HPE_OK); if (body_mark != -1) { ///if (settings->on_##FOR) { // slice data ByteBuffer ByteBuffer bb = data; int opos = bb.position(); bb.position(body_mark); int olmt = bb.limit(); bb.limit(p); ByteBuffer rb = bb.slice(); bb.limit(olmt); bb.position(opos); ///if (0 != on_##FOR(FOR##_mark, (LEN))) { if (0 != on_body(rb)) { SET_ERRNO(http_errno.HPE_CB_body); } /* We either errored above or got paused; get out */ if (HTTP_PARSER_ERRNO() != http_errno.HPE_OK) { ///return (ER); return (p + 1); } } ///FOR##_mark = NULL; body_mark = -1; } } break; case s_chunk_data_done: assert(0!=(flags & Flags.F_CHUNKED.flag)); ///STRICT_CHECK(ch != '\n'); if (ch != '\n') { SET_ERRNO(http_errno.HPE_STRICT); ///goto error; if (HTTP_PARSER_ERRNO() == http_errno.HPE_OK) { SET_ERRNO(http_errno.HPE_UNKNOWN); } ///return (p - data); return p; } nread = 0; state = State.s_chunk_size_start; break; default: ///assert(0 && "unhandled state"); assert("unhandled state"==null); SET_ERRNO(http_errno.HPE_INVALID_INTERNAL_STATE); ///goto error; { if (HTTP_PARSER_ERRNO() == http_errno.HPE_OK) { SET_ERRNO(http_errno.HPE_UNKNOWN); } ///return (p - data); return p; } } // break while loop debug(TAG, "break while loop, reexecute_byte:"+reexecute_byte+",err:"+HTTP_PARSER_ERRNO().desc()); break; } } /* Run callbacks for any marks that we have leftover after we ran our of * bytes. There should be at most one of these set, so it's OK to invoke * them in series (unset marks will not result in callbacks). * * We use the NOADVANCE() variety of callbacks here because 'p' has already * overflowed 'data' and this allows us to correct for the off-by-one that * we'd otherwise have (since CALLBACK_DATA() is meant to be run with a 'p' * value that's in-bounds). */ assert(((header_field_mark!=-1 ? 1 : 0) + (header_value_mark!=-1 ? 1 : 0) + (url_mark!=-1 ? 1 : 0) + (body_mark!=-1 ? 1 : 0) + (status_mark!=-1 ? 1 : 0)) <= 1); ///CALLBACK_DATA_NOADVANCE(header_field); { assert(HTTP_PARSER_ERRNO() == http_errno.HPE_OK); if (header_field_mark != -1) { ///if (settings->on_##FOR) { // slice data ByteBuffer ByteBuffer bb = data; int opos = bb.position(); bb.position(header_field_mark); int olmt = bb.limit(); bb.limit(p); ByteBuffer rb = bb.slice(); bb.limit(olmt); bb.position(opos); ///if (0 != on_##FOR(FOR##_mark, (LEN))) { if (0 != on_header_field(rb)) { SET_ERRNO(http_errno.HPE_CB_header_field); } /* We either errored above or got paused; get out */ if (HTTP_PARSER_ERRNO() != http_errno.HPE_OK) { ///return (ER); return (p); } } ///FOR##_mark = NULL; header_field_mark = -1; } } ///CALLBACK_DATA_NOADVANCE(header_value); { assert(HTTP_PARSER_ERRNO() == http_errno.HPE_OK); if (header_value_mark != -1) { ///if (settings->on_##FOR) { // slice data ByteBuffer ByteBuffer bb = data; int opos = bb.position(); bb.position(header_value_mark); int olmt = bb.limit(); bb.limit(p); ByteBuffer rb = bb.slice(); bb.limit(olmt); bb.position(opos); ///if (0 != on_##FOR(FOR##_mark, (LEN))) { if (0 != on_header_value(rb)) { SET_ERRNO(http_errno.HPE_CB_header_value); } /* We either errored above or got paused; get out */ if (HTTP_PARSER_ERRNO() != http_errno.HPE_OK) { ///return (ER); return (p); } } ///FOR##_mark = NULL; header_value_mark = -1; } } ///CALLBACK_DATA_NOADVANCE(url); { assert(HTTP_PARSER_ERRNO() == http_errno.HPE_OK); if (url_mark != -1) { ///if (settings->on_##FOR) { // slice data ByteBuffer ByteBuffer bb = data; int opos = bb.position(); bb.position(url_mark); int olmt = bb.limit(); bb.limit(p); ByteBuffer rb = bb.slice(); bb.limit(olmt); bb.position(opos); ///if (0 != on_##FOR(FOR##_mark, (LEN))) { if (0 != on_url(rb)) { SET_ERRNO(http_errno.HPE_CB_url); } /* We either errored above or got paused; get out */ if (HTTP_PARSER_ERRNO() != http_errno.HPE_OK) { ///return (ER); return (p); } } ///FOR##_mark = NULL; url_mark = -1; } } ///CALLBACK_DATA_NOADVANCE(body); { assert(HTTP_PARSER_ERRNO() == http_errno.HPE_OK); if (body_mark != -1) { ///if (settings->on_##FOR) { // slice data ByteBuffer ByteBuffer bb = data; int opos = bb.position(); bb.position(body_mark); int olmt = bb.limit(); bb.limit(p); ByteBuffer rb = bb.slice(); bb.limit(olmt); bb.position(opos); ///if (0 != on_##FOR(FOR##_mark, (LEN))) { if (0 != on_body(rb)) { SET_ERRNO(http_errno.HPE_CB_body); } /* We either errored above or got paused; get out */ if (HTTP_PARSER_ERRNO() != http_errno.HPE_OK) { ///return (ER); return (p); } } ///FOR##_mark = NULL; body_mark = -1; } } ///CALLBACK_DATA_NOADVANCE(status); { assert(HTTP_PARSER_ERRNO() == http_errno.HPE_OK); if (status_mark != -1) { ///if (settings->on_##FOR) { // slice data ByteBuffer ByteBuffer bb = data; int opos = bb.position(); bb.position(status_mark); int olmt = bb.limit(); bb.limit(p); ByteBuffer rb = bb.slice(); bb.limit(olmt); bb.position(opos); ///if (0 != on_##FOR(FOR##_mark, (LEN))) { if (0 != on_status(rb)) { SET_ERRNO(http_errno.HPE_CB_status); } /* We either errored above or got paused; get out */ if (HTTP_PARSER_ERRNO() != http_errno.HPE_OK) { ///return (ER); return (p); } } ///FOR##_mark = NULL; status_mark = -1; } } return len; /*error: if (HTTP_PARSER_ERRNO(parser) == HPE_OK) { SET_ERRNO(http_errno.HPE_UNKNOWN); } return (p - data);*/ } /* If http_should_keep_alive() in the on_headers_complete or * on_message_complete callback returns 0, then this should be * the last message on the connection. * If you are the server, respond with the "Connection: close" header. * If you are the client, close the connection. */ ///int http_should_keep_alive(const http_parser *parser); protected boolean http_should_keep_alive() { if (http_major > 0 && http_minor > 0) { /* HTTP/1.1 */ if ((flags & Flags.F_CONNECTION_CLOSE.flag)!=0) { return false; } } else { /* HTTP/1.0 or earlier */ if (0==(flags & Flags.F_CONNECTION_KEEP_ALIVE.flag)) { return false; } } return !http_message_needs_eof(); } /* Returns a string version of the http method. */ ///const char *http_method_str(enum http_method m); protected static String http_method_str(http_method m) { return m.desc; } /* Return a string name of the given error */ ///const char *http_errno_name(enum http_errno err); protected static String http_errno_name(http_errno err) { return err.name(); } /* Return a string description of the given error */ ///const char *http_errno_description(enum http_errno err); protected static String http_errno_description(http_errno err) { return err.desc; } /* Parse a URL; return nonzero on failure */ ///int http_parser_parse_url(const char *buf, size_t buflen, /// int is_connect, /// struct http_parser_url *u); public static int parse_url(char [] buf, boolean is_connect, http_parser_url u) throws UnsupportedEncodingException { State s; ///const char *p; int p = 0; http_parser_url_fields uf, old_uf; boolean found_at = false; int buflen = buf != null ? buf.length : 0; u.port = u.field_set = 0; s = is_connect ? State.s_req_server_start : State.s_req_spaces_before_url; uf = old_uf = http_parser_url_fields.UF_MAX; ///for (p = buf; p < buf + buflen; p++) { for (p = 0; p < buflen; p++) { s = parse_url_char(s, buf[p]); /* Figure out the next field that we're operating on */ switch (s) { case s_dead: return 1; /* Skip delimeters */ case s_req_schema_slash: case s_req_schema_slash_slash: case s_req_server_start: case s_req_query_string_start: case s_req_fragment_start: continue; case s_req_schema: uf = http_parser_url_fields.UF_SCHEMA; break; case s_req_server_with_at: found_at = true; /* FALLTROUGH */ case s_req_server: uf = http_parser_url_fields.UF_HOST; break; case s_req_path: uf = http_parser_url_fields.UF_PATH; break; case s_req_query_string: uf = http_parser_url_fields.UF_QUERY; break; case s_req_fragment: uf = http_parser_url_fields.UF_FRAGMENT; break; default: assert(null=="Unexpected state"); return 1; } /* Nothing's changed; soldier on */ if (uf == old_uf) { u.field_data[uf.field].len++; continue; } u.field_data[uf.field].off = p; ///p - buf; u.field_data[uf.field].len = 1; u.field_set |= (1 << uf.field); old_uf = uf; } /* host must be present if there is a schema */ /* parsing http:///toto will fail */ if ((u.field_set & ((1 << http_parser_url_fields.UF_SCHEMA.field) | (1 << http_parser_url_fields.UF_HOST.field))) != 0) { if (http_parse_host(buf, u, found_at) != 0) { return 1; } } /* CONNECT requests can only contain "hostname:port" */ if (is_connect && u.field_set != ((1 << http_parser_url_fields.UF_HOST.field) | (1 << http_parser_url_fields.UF_PORT.field))) { return 1; } if (0!=(u.field_set & (1 << http_parser_url_fields.UF_PORT.field))) { /* Don't bother with endp; we've already validated the string */ ///unsigned long v = strtoul(buf + u.field_data[UF_PORT].off, NULL, 10); String pstr = new String(buf). substring(u.field_data[http_parser_url_fields.UF_PORT.field].off, u.field_data[http_parser_url_fields.UF_PORT.field].len); int v = Integer.parseInt(pstr, 10); debug(TAG, "port str: "+pstr+", port: "+v); /* Ports have a max value of 2^16 */ if (v > 0xffff) { return 1; } u.port = v; } return 0; } private static http_host_state http_parse_host_char(http_host_state s, final char ch) { switch(s) { case s_http_userinfo: case s_http_userinfo_start: if (ch == '@') { return http_host_state.s_http_host_start; } if (IS_USERINFO_CHAR(ch)) { return http_host_state.s_http_userinfo; } break; case s_http_host_start: if (ch == '[') { return http_host_state.s_http_host_v6_start; } if (IS_HOST_CHAR(ch)) { return http_host_state.s_http_host; } break; case s_http_host: if (IS_HOST_CHAR(ch)) { return http_host_state.s_http_host; } /* FALLTHROUGH */ case s_http_host_v6_end: if (ch == ':') { return http_host_state.s_http_host_port_start; } break; case s_http_host_v6: if (ch == ']') { return http_host_state.s_http_host_v6_end; } /* FALLTHROUGH */ case s_http_host_v6_start: if (IS_HEX(ch) || ch == ':' || ch == '.') { return http_host_state.s_http_host_v6; } break; case s_http_host_port: case s_http_host_port_start: if (IS_NUM(ch)) { return http_host_state.s_http_host_port; } break; default: break; } return http_host_state.s_http_host_dead; } private static int http_parse_host(final char [] buf, http_parser_url u, boolean found_at) { http_host_state s; ///const char *p; int p = 0; ///size_t buflen = u.field_data[UF_HOST].off + u.field_data[UF_HOST].len; int buflen = u.field_data[http_parser_url_fields.UF_HOST.field].off + u.field_data[http_parser_url_fields.UF_HOST.field].len; u.field_data[http_parser_url_fields.UF_HOST.field].len = 0; s = found_at ? http_host_state.s_http_userinfo_start : http_host_state.s_http_host_start; ///for (p = buf + u.field_data[UF_HOST].off; p < buf + buflen; p++) { for (p = u.field_data[http_parser_url_fields.UF_HOST.field].off; p < buflen; p++) { http_host_state new_s = http_parse_host_char(s, buf[p]); if (new_s == http_host_state.s_http_host_dead) { return 1; } switch(new_s) { case s_http_host: if (s != http_host_state.s_http_host) { u.field_data[http_parser_url_fields.UF_HOST.field].off = p;///p - buf; } u.field_data[http_parser_url_fields.UF_HOST.field].len++; break; case s_http_host_v6: if (s != http_host_state.s_http_host_v6) { u.field_data[http_parser_url_fields.UF_HOST.field].off = p;///p - buf; } u.field_data[http_parser_url_fields.UF_HOST.field].len++; break; case s_http_host_port: if (s != http_host_state.s_http_host_port) { u.field_data[http_parser_url_fields.UF_PORT.field].off = p;///p - buf; u.field_data[http_parser_url_fields.UF_PORT.field].len = 0; u.field_set |= (1 << http_parser_url_fields.UF_PORT.field); } u.field_data[http_parser_url_fields.UF_PORT.field].len++; break; case s_http_userinfo: if (s != http_host_state.s_http_userinfo) { u.field_data[http_parser_url_fields.UF_USERINFO.field].off = p;///p - buf ; u.field_data[http_parser_url_fields.UF_USERINFO.field].len = 0; u.field_set |= (1 << http_parser_url_fields.UF_USERINFO.field); } u.field_data[http_parser_url_fields.UF_USERINFO.field].len++; break; default: break; } s = new_s; } /* Make sure we don't end somewhere unexpected */ switch (s) { case s_http_host_start: case s_http_host_v6_start: case s_http_host_v6: case s_http_host_port_start: case s_http_userinfo: case s_http_userinfo_start: return 1; default: break; } return 0; } /* Pause or un-pause the parser; a nonzero value pauses */ ///void http_parser_pause(http_parser *parser, int paused); @SuppressWarnings("static-access") protected void pause(boolean paused) { /* Users should only be pausing/unpausing a parser that is not in an error * state. In non-debug builds, there's not much that we can do about this * other than ignore it. */ if (HTTP_PARSER_ERRNO() == http_errno.HPE_OK || HTTP_PARSER_ERRNO() == http_errno.HPE_PAUSED) { SET_ERRNO((paused) ? http_errno.HPE_PAUSED : http_errno.HPE_OK); } else { assert(null == "Attempting to pause parser in error state"); } } /* Checks if this is the final chunk of the body. */ ///int http_body_is_final(const http_parser *parser); protected boolean http_body_is_final() { return state == State.s_message_done; } ///struct http_parser { /** PRIVATE **/ private http_parser_type type; /* enum http_parser_type */ /** * @return the type */ public http_parser_type getType() { return type; } /** * @return the nread */ public int getNread() { return nread; } private int flags = 0; /* F_* values from 'flags' enum; semi-protected */ private State state; /* enum State from http_parser.c */ private header_states header_state; /* enum header_state from http_parser.c */ private int index = 0; /* index into current matcher */ private int nread = 0; /* # bytes read in various scenarios */ private long content_length = 0; /* # bytes in body (0 if no Content-Length header) */ /** READ-ONLY **/ private int http_major; /** * @return the http_major */ public int getHttp_major() { return http_major; } /** * @return the http_minor */ public int getHttp_minor() { return http_minor; } /** * @return the status_code */ public int getStatus_code() { return status_code; } /** * @return the method */ public http_method getMethod() { return method; } /** * @return the http_errno */ public http_errno getHttp_errno() { return http_errno; } /** * @return the upgrade */ public boolean isUpgrade() { return upgrade; } /** * @return the data */ public Object getData() { return data; } private int http_minor = 0; private int status_code = 0; /* responses only */ private http_method method; /* requests only */ private http_errno http_errno; /* 1 = Upgrade header was present and the parser has exited because of that. * 0 = No upgrade header present. * Should be checked when http_parser_execute() returns in addition to * error checking. */ private boolean upgrade = false; /** PUBLIC **/ private final Object data; /* A pointer to get hook to the "connection" or "socket" object */ ///}; }