/** * @brief Get token in a string separated by a delimiter (Single Char) * Spec ref R[15.1] * * @param pp_token * @param token_len * @param input_string * @param input_str_len * @param delim_char * @param next_ptr * * @return */ static inline rtsp_error_t rtsp_get_token(const char **pp_token, int32_t *token_len, const char *input_string, int32_t input_str_len, char delim_char, const char **next_ptr){ /* * TODO - $EN - delim_char can be changed to an array of characters which may be delimiters for a token */ int32_t scan_index = 0; rtsp_error_t e_ret_val = RTSP_E_FAILURE; if(!pp_token || !token_len || !input_string || input_str_len<=0){ return (RTSP_E_INVALID_ARG); } while(scan_index < input_str_len){ if(input_string[scan_index] == delim_char){ /* Token end reached * Calling function has to ensure reliable delim_char as per spec * */ *pp_token = input_string; *token_len = scan_index+1; if (next_ptr) *next_ptr = &input_string[scan_index+1]; e_ret_val = RTSP_E_SUCCESS; break; } else if(iscntrl(input_string[scan_index]) || is_tspecial(input_string[scan_index])){ /* * Validates Token as per R[15.1] */ e_ret_val = RTSP_E_FAILURE; break; } else { scan_index++; } } return (e_ret_val); }
boost::tribool& RequestParser::consume(Request& req, Buffer::iterator it) { static boost::tribool False(false); static boost::tribool True(true); static boost::tribool Indeterminate(boost::indeterminate); const char input = *it; if (++requestSize_ > MAX_REQUEST_HEADER_SIZE) return False; switch (httpState_) { case method_start: if (input == '\r') { /* * allow a new line before a request -- this seems to be * accepted practice when dealing with multiple requests * in one connection, separated by a CRLF. */ httpState_ = expecting_newline_0; return Indeterminate; } else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) { return False; } else { httpState_ = method; consumeToString(req.method, MAX_METHOD_SIZE); consumeChar(it); return Indeterminate; } case expecting_newline_0: if (input == '\n') { httpState_ = method_start; return Indeterminate; } else { return False; } case method: if (input == ' ') { consumeComplete(it); httpState_ = uri_start; return Indeterminate; } else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) { return False; } else { if (consumeChar(it)) return Indeterminate; else return False; } case uri_start: if (is_ctl(input)) { return False; } else { httpState_ = uri; consumeToString(req.uri, MAX_URI_SIZE); consumeChar(it); return Indeterminate; } case uri: if (input == ' ') { consumeComplete(it); httpState_ = http_version_h; return Indeterminate; } else if (is_ctl(input)) { return False; } else { if (consumeChar(it)) return Indeterminate; else return False; } case http_version_h: if (input == 'H') { httpState_ = http_version_t_1; return Indeterminate; } else { return False; } case http_version_t_1: if (input == 'T') { httpState_ = http_version_t_2; return Indeterminate; } else { return False; } case http_version_t_2: if (input == 'T') { httpState_ = http_version_p; return Indeterminate; } else { return False; } case http_version_p: if (input == 'P') { httpState_ = http_version_slash; return Indeterminate; } else { return False; } case http_version_slash: if (input == '/') { req.http_version_major = 0; req.http_version_minor = 0; httpState_ = http_version_major_start; return Indeterminate; } else { return False; } case http_version_major_start: if (is_digit(input)) { req.http_version_major = req.http_version_major * 10 + input - '0'; httpState_ = http_version_major; return Indeterminate; } else { return False; } case http_version_major: if (input == '.') { httpState_ = http_version_minor_start; return Indeterminate; } else if (is_digit(input)) { req.http_version_major = req.http_version_major * 10 + input - '0'; return Indeterminate; } else { return False; } case http_version_minor_start: if (is_digit(input)) { req.http_version_minor = req.http_version_minor * 10 + input - '0'; httpState_ = http_version_minor; return Indeterminate; } else { return False; } case http_version_minor: if (input == '\r') { httpState_ = expecting_newline_1; return Indeterminate; } else if (is_digit(input)) { req.http_version_minor = req.http_version_minor * 10 + input - '0'; return Indeterminate; } else { return False; } case expecting_newline_1: if (input == '\n') { httpState_ = header_line_start; return Indeterminate; } else { return False; } case header_line_start: if (input == '\r') { httpState_ = expecting_newline_3; return Indeterminate; } else if ((input == ' ' || input == '\t') && haveHeader_) { // continuation of previous header httpState_ = header_lws; return Indeterminate; } else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) { return False; } else { req.headers.push_back(Request::Header()); haveHeader_ = true; consumeToString(req.headers.back().name, MAX_FIELD_NAME_SIZE); consumeChar(it); httpState_ = header_name; return Indeterminate; } case header_lws: if (input == '\r') { httpState_ = expecting_newline_2; return Indeterminate; } else if (input == ' ' || input == '\t') { return Indeterminate; } else if (is_ctl(input)) { return False; } else { httpState_ = header_value; consumeChar(it); return Indeterminate; } case header_name: if (input == ':') { consumeComplete(it); httpState_ = space_before_header_value; return Indeterminate; } else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) { return False; } else { if (consumeChar(it)) return Indeterminate; else return False; } case space_before_header_value: if (input == ' ') { consumeToString(req.headers.back().value, MAX_FIELD_VALUE_SIZE); httpState_ = header_value; return Indeterminate; } else { consumeToString(req.headers.back().value, MAX_FIELD_VALUE_SIZE); httpState_ = header_value; /* fall through */ } case header_value: if (input == '\r') { consumeComplete(it); httpState_ = expecting_newline_2; return Indeterminate; } else if (is_ctl(input)) { return False; } else { if (consumeChar(it)) return Indeterminate; else return False; } case expecting_newline_2: if (input == '\n') { httpState_ = header_line_start; return Indeterminate; } else { return False; } case expecting_newline_3: if (input == '\n') return True; else return False; default: return False; } }
bool parse_http_headers(Iterator begin, Iterator end, std::string& content_type, std::size_t& content_length, std::string& location) { enum { first_header_line_start, header_line_start, header_lws, header_name, space_before_header_value, header_value, linefeed, final_linefeed, fail } state = first_header_line_start; Iterator iter = begin; std::string reason; std::string name; std::string value; while (iter != end && state != fail) { char c = *iter++; switch (state) { case first_header_line_start: if (c == '\r') state = final_linefeed; else if (!is_char(c) || is_ctl(c) || is_tspecial(c)) state = fail; else { name.push_back(c); state = header_name; } break; case header_line_start: if (c == '\r') { check_header(name, value, content_type, content_length, location); name.clear(); value.clear(); state = final_linefeed; } else if (c == ' ' || c == '\t') state = header_lws; else if (!is_char(c) || is_ctl(c) || is_tspecial(c)) state = fail; else { check_header(name, value, content_type, content_length, location); name.clear(); value.clear(); name.push_back(c); state = header_name; } break; case header_lws: if (c == '\r') state = linefeed; else if (c == ' ' || c == '\t') ; // Discard character. else if (is_ctl(c)) state = fail; else { state = header_value; value.push_back(c); } break; case header_name: if (c == ':') state = space_before_header_value; else if (!is_char(c) || is_ctl(c) || is_tspecial(c)) state = fail; else name.push_back(c); break; case space_before_header_value: state = (c == ' ') ? header_value : fail; break; case header_value: if (c == '\r') state = linefeed; else if (is_ctl(c)) state = fail; else value.push_back(c); break; case linefeed: state = (c == '\n') ? header_line_start : fail; break; case final_linefeed: return (c == '\n'); default: return false; } } return false; }
/// Handle the next character of input. result_type consume(char input) { switch (state_) { case METHOD_START: if (!is_char(input) || is_ctl(input) || is_tspecial(input)) { return bad; } else { state_ = METHOD; method_.push_back(input); return indeterminate; } case METHOD: if (input == ' ') { state_ = URI; return indeterminate; } else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) { return bad; } else { method_.push_back(input); return indeterminate; } case URI: if (input == ' ') { state_ = HTTP_VERSION_H; return indeterminate; } else if (is_ctl(input)) { return bad; } else { url_.push_back(input); return indeterminate; } case HTTP_VERSION_H: if (input == 'H') { state_ = HTTP_VERSUIN_T_1; return indeterminate; } else { return bad; } case HTTP_VERSUIN_T_1: if (input == 'T') { state_ = HTTP_VERSION_T_2; return indeterminate; } else { return bad; } case HTTP_VERSION_T_2: if (input == 'T') { state_ = HTTP_VERSION_P; return indeterminate; } else { return bad; } case HTTP_VERSION_P: if (input == 'P') { state_ = HTTP_VERSION_SLASH; return indeterminate; } else { return bad; } case HTTP_VERSION_SLASH: if (input == '/') { version_major_ = 0; version_minor_ = 0; state_ = HTTP_VERSION_MAJOR_START; return indeterminate; } else { return bad; } case HTTP_VERSION_MAJOR_START: if (is_digit(input)) { version_major_ = version_major_ * 10 + input - '0'; state_ = HTTP_VERSION_MAJOR; return indeterminate; } else { return bad; } case HTTP_VERSION_MAJOR: if (input == '.') { state_ = HTTP_VERSION_MINOR_START; return indeterminate; } else if (is_digit(input)) { version_major_ = version_major_ * 10 + input - '0'; return indeterminate; } else { return bad; } case HTTP_VERSION_MINOR_START: if (is_digit(input)) { version_minor_ = version_minor_ * 10 + input - '0'; state_ = HTTP_VERSION_MINOR; return indeterminate; } else { return bad; } case HTTP_VERSION_MINOR: if (input == '\r') { state_ = EXPECTING_NEWLINE_1; return indeterminate; } else if (is_digit(input)) { version_minor_ = version_minor_ * 10 + input - '0'; return indeterminate; } else { return bad; } case EXPECTING_NEWLINE_1: if (input == '\n') { state_ = HEADER_LINE_START; return indeterminate; } else { return bad; } case HEADER_LINE_START: if (input == '\r') { state_ = EXPECTING_NEWLINE_3; return indeterminate; } else if (header_.size() != 0 && (input == ' ' || input == '\t')) { state_ = HEADER_LWS; return indeterminate; } else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) { return bad; } else { current_header_key_.clear(); current_header_val_.clear(); current_header_key_.push_back(input); state_ = HEADER_NAME; return indeterminate; } case HEADER_LWS: if (input == '\r') { state_ = EXPECTING_NEWLINE_2; return indeterminate; } else if (input == ' ' || input == '\t') { return indeterminate; } else if (is_ctl(input)) { return bad; } else { state_ = HEADER_VALUE; current_header_val_.push_back(input); return indeterminate; } case HEADER_NAME: if (input == ':') { state_ = SPACE_BEFORE_HEADER_VALUE; return indeterminate; } else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) { return bad; } else { current_header_key_.push_back(input); return indeterminate; } case SPACE_BEFORE_HEADER_VALUE: if (input == ' ') return indeterminate; else if(is_ctl(input)) return bad; current_header_val_.push_back(input); state_ = HEADER_VALUE; return indeterminate; case HEADER_VALUE: if (input == '\r') { auto tmp = boost::trim_right_copy_if(current_header_val_, [](const char c)->bool{return c == ' ';}); header_.add(current_header_key_, std::move(tmp)); state_ = EXPECTING_NEWLINE_2; return indeterminate; } else if (is_ctl(input)) { return bad; } else { current_header_val_.push_back(input); return indeterminate; } case EXPECTING_NEWLINE_2: if (input == '\n') { state_ = HEADER_LINE_START; return indeterminate; } else { return bad; } case EXPECTING_NEWLINE_3: { if (input != '\n') { return bad; } if (header_.get_count("content-length") !=0 ) { content_length_ = boost::lexical_cast<unsigned int>(header_.get_val("content-length")); if (content_length_ > 0) { state_ = REQUEST_BODY; return indeterminate; } return good; } content_length_ = 0; return good; } case REQUEST_BODY: { body_.push_back(input); if (body_.size() < content_length_) { return indeterminate; } else { return good; } } default: return bad; } }
boost::tribool request_parser::consume(request& req, char input) { switch (state_) { case method_start: if (!is_char(input) || is_ctl(input) || is_tspecial(input)) { return false; } else { state_ = method; req.method.push_back(input); return boost::indeterminate; } case method: if (input == ' ') { state_ = uri; return boost::indeterminate; } else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) { return false; } else { req.method.push_back(input); return boost::indeterminate; } case uri: if (input == ' ') { state_ = http_version_h; return boost::indeterminate; } else if (is_ctl(input)) { return false; } else { req.uri.push_back(input); return boost::indeterminate; } case http_version_h: if (input == 'H') { state_ = http_version_t_1; return boost::indeterminate; } else { return false; } case http_version_t_1: if (input == 'T') { state_ = http_version_t_2; return boost::indeterminate; } else { return false; } case http_version_t_2: if (input == 'T') { state_ = http_version_p; return boost::indeterminate; } else { return false; } case http_version_p: if (input == 'P') { state_ = http_version_slash; return boost::indeterminate; } else { return false; } case http_version_slash: if (input == '/') { req.http_version_major = 0; req.http_version_minor = 0; state_ = http_version_major_start; return boost::indeterminate; } else { return false; } case http_version_major_start: if (is_digit(input)) { req.http_version_major = req.http_version_major * 10 + input - '0'; state_ = http_version_major; return boost::indeterminate; } else { return false; } case http_version_major: if (input == '.') { state_ = http_version_minor_start; return boost::indeterminate; } else if (is_digit(input)) { req.http_version_major = req.http_version_major * 10 + input - '0'; return boost::indeterminate; } else { return false; } case http_version_minor_start: if (is_digit(input)) { req.http_version_minor = req.http_version_minor * 10 + input - '0'; state_ = http_version_minor; return boost::indeterminate; } else { return false; } case http_version_minor: if (input == '\r') { state_ = expecting_newline_1; return boost::indeterminate; } else if (is_digit(input)) { req.http_version_minor = req.http_version_minor * 10 + input - '0'; return boost::indeterminate; } else { return false; } case expecting_newline_1: if (input == '\n') { state_ = header_line_start; return boost::indeterminate; } else { return false; } case header_line_start: if (input == '\r') { state_ = expecting_newline_3; return boost::indeterminate; } else if (!req.headers.empty() && (input == ' ' || input == '\t')) { state_ = header_lws; return boost::indeterminate; } else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) { return false; } else { req.headers.push_back(header()); req.headers.back().name.push_back(input); state_ = header_name; return boost::indeterminate; } case header_lws: if (input == '\r') { state_ = expecting_newline_2; return boost::indeterminate; } else if (input == ' ' || input == '\t') { return boost::indeterminate; } else if (is_ctl(input)) { return false; } else { state_ = header_value; req.headers.back().value.push_back(input); return boost::indeterminate; } case header_name: if (input == ':') { state_ = space_before_header_value; return boost::indeterminate; } else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) { return false; } else { req.headers.back().name.push_back(input); return boost::indeterminate; } case space_before_header_value: if (input == ' ') { state_ = header_value; return boost::indeterminate; } else { return false; } case header_value: if (input == '\r') { state_ = expecting_newline_2; if (req.headers.back().name == content_length_name_) content_length_ = boost::lexical_cast<std::size_t>(req.headers.back().value); return boost::indeterminate; } else if (is_ctl(input)) { return false; } else { req.headers.back().value.push_back(input); return boost::indeterminate; } case expecting_newline_2: if (input == '\n') { state_ = header_line_start; return boost::indeterminate; } else { return false; } case expecting_newline_3: state_ = content; if (content_length_ > 0) return boost::indeterminate; return (input == '\n'); case content: // Content. /*std::cout << state_ << std::endl; std::cout << input << std::endl; std::cout << req.content.size() << std::endl; std::cout << content_length_ << std::endl;*/ req.content.push_back(input); if (req.content.size() == content_length_) return true; return boost::indeterminate; default: return false; } }
boost::tribool request_parser::consume(request& req, char input) { switch (state_) { case method_start: if (!is_char(input) || is_ctl(input) || is_tspecial(input)) { return false; } else { state_ = method; req.method.push_back(input); return boost::indeterminate; } case method: if (input == ' ') { if (req.method != "POST") return false; // only POST method is allowed state_ = uri; return boost::indeterminate; } else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) { return false; } else { req.method.push_back(input); return boost::indeterminate; } case uri: if (input == ' ') { state_ = http_version_h; return boost::indeterminate; } else if (is_ctl(input) || input == '?') { return false; } else { req.uri.push_back(input); return boost::indeterminate; } case http_version_h: if (input == 'H') { state_ = http_version_t_1; return boost::indeterminate; } else { return false; } case http_version_t_1: if (input == 'T') { state_ = http_version_t_2; return boost::indeterminate; } else { return false; } case http_version_t_2: if (input == 'T') { state_ = http_version_p; return boost::indeterminate; } else { return false; } case http_version_p: if (input == 'P') { state_ = http_version_slash; return boost::indeterminate; } else { return false; } case http_version_slash: if (input == '/') { req.http_version_major = 0; req.http_version_minor = 0; state_ = http_version_major_start; return boost::indeterminate; } else { return false; } case http_version_major_start: if (is_digit(input)) { req.http_version_major = input - '0'; state_ = http_version_major; return boost::indeterminate; } else { return false; } case http_version_major: if (input == '.') { state_ = http_version_minor_start; return boost::indeterminate; } else if (is_digit(input)) { req.http_version_major = req.http_version_major * 10 + input - '0'; return boost::indeterminate; } else { return false; } case http_version_minor_start: if (is_digit(input)) { req.http_version_minor = input - '0'; state_ = http_version_minor; return boost::indeterminate; } else { return false; } case http_version_minor: if (input == '\r') { state_ = expecting_newline_before_first_header; return boost::indeterminate; } else if (is_digit(input)) { req.http_version_minor = req.http_version_minor * 10 + input - '0'; return boost::indeterminate; } else { return false; } case expecting_newline_before_first_header: if (input == '\n') { state_ = header_line_start; return boost::indeterminate; } else { return false; } case header_line_start: if (input == '\r') { state_ = expecting_body_start; return boost::indeterminate; } else if (!req.headers.empty() && (input == ' ' || input == '\t')) { state_ = header_lws; return boost::indeterminate; } else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) { return false; } else { req.headers.emplace_back(); header& h = req.headers.back(); h.name.reserve(64); h.value.reserve(64); h.name.push_back(input); state_ = header_name; return boost::indeterminate; } case header_lws: if (input == '\r') { state_ = expecting_newline; return boost::indeterminate; } else if (input == ' ' || input == '\t') { return boost::indeterminate; } else if (is_ctl(input)) { return false; } else { state_ = header_value; req.headers.back().value.push_back(input); return boost::indeterminate; } case header_name: if (input == ':') { state_ = space_before_header_value; return boost::indeterminate; } else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) { return false; } else { req.headers.back().name.push_back(input); return boost::indeterminate; } case space_before_header_value: if (input == ' ') { state_ = header_value; return boost::indeterminate; } else { return false; } case header_value: if (input == '\r') { state_ = expecting_newline; return boost::indeterminate; } else if (is_ctl(input)) { return false; } else { req.headers.back().value.push_back(input); return boost::indeterminate; } case expecting_newline: if (input == '\n') { state_ = header_line_start; return boost::indeterminate; } else { return false; } case expecting_body_start: if (input == '\n') { state_ = expecting_json_start; return boost::indeterminate; } else { return false; } case expecting_json_start: req.body.push_back(input); if (input == '{') { ++nesting_level_; state_ = expecting_json_end; } return boost::indeterminate; case expecting_json_end: req.body.push_back(input); if (input == '{') { ++nesting_level_; } else if (input == '}') { --nesting_level_; if (!nesting_level_) return true; } return boost::indeterminate; default: return false; } }
Response::Parser::state Response::Parser::consume(Response& resp, char input) { switch (state_) { case http_version_h: if (input == 'H') { state_ = http_version_t_1; return indeterminate; } else { return bad; } case http_version_t_1: if (input == 'T') { state_ = http_version_t_2; return indeterminate; } else { return bad; } case http_version_t_2: if (input == 'T') { state_ = http_version_p; return indeterminate; } else { return bad; } case http_version_p: if (input == 'P') { state_ = http_version_slash; return indeterminate; } else { return bad; } case http_version_slash: if (input == '/') { resp.major_ = resp.minor_ = 0; state_ = http_version_major_start; return indeterminate; } else { return bad; } case http_version_major_start: if (is_digit(input)) { resp.major_ = resp.major_ * 10 + (input - '0'); state_ = http_version_major; return indeterminate; } else { return bad; } case http_version_major: if (input == '.') { state_ = http_version_minor_start; return indeterminate; } else if (is_digit(input)) { resp.major_ = resp.major_ * 10 + (input - '0'); return indeterminate; } else { return bad; } case http_version_minor_start: if (is_digit(input)) { resp.minor_ = resp.minor_ * 10 + (input - '0'); state_ = http_version_minor; return indeterminate; } else { return bad; } case http_version_minor: if (input == ' ') { resp.status_code_ = 0; state_ = http_status_code; return indeterminate; } else if (is_digit(input)) { resp.minor_ = resp.minor_ * 10 + (input - '0'); return indeterminate; } else { return bad; } case http_status_code: if (is_digit(input)) { resp.status_code_ = resp.status_code_ * 10 + (input - '0'); return indeterminate; } else if (input == ' ') { state_ = http_status_msg; return indeterminate; } else { return bad; } case http_status_msg: if (input == '\r') { state_ = expecting_newline_1; return indeterminate; } else if (is_char(input)) { resp.desc.push_back(input); return indeterminate; } else { return bad; } case expecting_newline_1: if (input == '\n') { state_ = header_line_start; return indeterminate; } else { return bad; } case header_line_start: if (input == '\r') { state_ = expecting_newline_3; return indeterminate; } else if (!resp.headers.empty() && (input == ' ' || input == '\t')) { state_ = header_lws; return indeterminate; } else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) { return bad; } else { resp.headers.emplace_back(); resp.headers.back().first.push_back(input); state_ = header_name; return indeterminate; } case header_lws: if (input == '\r') { state_ = expecting_newline_2; return indeterminate; } else if (input == ' ' || input == '\t') { return indeterminate; } else if (is_ctl(input)) { return bad; } else { state_ = header_value; resp.headers.back().second.push_back(input); return indeterminate; } case header_name: if (input == ':') { state_ = space_before_header_value; return indeterminate; } else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) { return bad; } else { resp.headers.back().first.push_back(input); return indeterminate; } case space_before_header_value: if (input == ' ') { state_ = header_value; return indeterminate; } else { return bad; } case header_value: if (input == '\r') { state_ = expecting_newline_2; return indeterminate; } else if (is_ctl(input)) { return bad; } else { resp.headers.back().second.push_back(input); return indeterminate; } case expecting_newline_2: if (input == '\n') { state_ = header_line_start; return indeterminate; } else { return bad; } case expecting_newline_3: return (input == '\n') ? good : bad; default: return bad; } }
boost::tribool request_parser::consume(request& req, char input) { switch (state_) { case method_start: if (!is_char(input) || is_ctl(input) || is_tspecial(input)) { return false; } else { state_ = method; req.method.push_back(input); return boost::indeterminate; } case method: if (input == ' ') { state_ = uri; return boost::indeterminate; } else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) { return false; } else { req.method.push_back(input); return boost::indeterminate; } case uri_start: if (is_ctl(input)) { return false; } else { state_ = uri; req.uri.push_back(input); return boost::indeterminate; } case uri: if (input == ' ') { state_ = http_version_h; return boost::indeterminate; } else if (is_ctl(input)) { return false; } else { req.uri.push_back(input); return boost::indeterminate; } case http_version_h: if (input == 'H') { state_ = http_version_t_1; return boost::indeterminate; } else { return false; } case http_version_t_1: if (input == 'T') { state_ = http_version_t_2; return boost::indeterminate; } else { return false; } case http_version_t_2: if (input == 'T') { state_ = http_version_p; return boost::indeterminate; } else { return false; } case http_version_p: if (input == 'P') { state_ = http_version_slash; return boost::indeterminate; } else { return false; } case http_version_slash: if (input == '/') { req.http_version_major = 0; req.http_version_minor = 0; state_ = http_version_major_start; return boost::indeterminate; } else { return false; } case http_version_major_start: if (is_digit(input)) { req.http_version_major = req.http_version_major * 10 + input - '0'; state_ = http_version_major; return boost::indeterminate; } else { return false; } case http_version_major: if (input == '.') { state_ = http_version_minor_start; return boost::indeterminate; } else if (is_digit(input)) { req.http_version_major = req.http_version_major * 10 + input - '0'; return boost::indeterminate; } else { return false; } case http_version_minor_start: if (is_digit(input)) { req.http_version_minor = req.http_version_minor * 10 + input - '0'; state_ = http_version_minor; return boost::indeterminate; } else { return false; } case http_version_minor: if (input == '\r') { state_ = expecting_newline_1; return boost::indeterminate; } else if (is_digit(input)) { req.http_version_minor = req.http_version_minor * 10 + input - '0'; return boost::indeterminate; } else { return false; } case expecting_newline_1: if (input == '\n') { state_ = header_line_start; return boost::indeterminate; } else { return false; } case header_line_start: if (input == '\r') { state_ = expecting_newline_3; return boost::indeterminate; } else if (!req.headers.empty() && (input == ' ' || input == '\t')) { state_ = header_lws; return boost::indeterminate; } else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) { return false; } else { req.headers.push_back(header()); req.headers.back().name.push_back(input); state_ = header_name; return boost::indeterminate; } case header_lws: if (input == '\r') { state_ = expecting_newline_2; return boost::indeterminate; } else if (input == ' ' || input == '\t') { return boost::indeterminate; } else if (is_ctl(input)) { return false; } else { state_ = header_value; req.headers.back().value.push_back(input); return boost::indeterminate; } case header_name: if (input == ':') { state_ = space_before_header_value; return boost::indeterminate; } else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) { return false; } else { req.headers.back().name.push_back(input); return boost::indeterminate; } case space_before_header_value: if (input == ' ') { state_ = header_value; return boost::indeterminate; } else { return false; } case header_value: if (input == '\r') { state_ = expecting_newline_2; return boost::indeterminate; } else if (is_ctl(input)) { return false; } else { req.headers.back().value.push_back(input); return boost::indeterminate; } case expecting_newline_2: if (input == '\n') { state_ = header_line_start; std::vector<header>::const_iterator cit = req.headers.begin(); for (; cit != req.headers.end(); ++cit) { std::string n = (*cit).name; std::transform(n.begin(), n.end(), n.begin(), ::toupper); if (n == UPPER_CONTENT_LENGTH) { try { cl_ = boost::lexical_cast<int>((*cit).value); } catch (const boost::bad_lexical_cast& e) { return false; } } else if (n == UPPER_CONTENT_TYPE) { std::string m = (*cit).value; std::transform(m.begin(), m.end(), m.begin(), ::toupper); if (m != UPPER_MIME_TYPE) { return false; } } } return boost::indeterminate; } else { return false; } case expecting_newline_3: { if (0 == cl_) { return (input == '\n'); } else { state_ = post_data; return boost::indeterminate; } } case post_data: { req.post_data.push_back(input); if (0 == --cl_) return true; else return ((cl_ < 0) ? false : boost::indeterminate); } default: return false; } }
request_parser::result_type request_parser::consume(request& req, char input) { switch (state_) { case method_start: if (!is_char(input) || is_ctl(input) || is_tspecial(input)) { return bad; } else { state_ = method; req.method.push_back(input); return indeterminate; } case method: if (input == ' ') { state_ = uri; return indeterminate; } else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) { return bad; } else { req.method.push_back(input); return indeterminate; } case uri: if (input == ' ') { state_ = http_version_h; return indeterminate; } else if (is_ctl(input)) { return bad; } else { req.uri.push_back(input); return indeterminate; } case http_version_h: if (input == 'H') { state_ = http_version_t_1; return indeterminate; } else { return bad; } case http_version_t_1: if (input == 'T') { state_ = http_version_t_2; return indeterminate; } else { return bad; } case http_version_t_2: if (input == 'T') { state_ = http_version_p; return indeterminate; } else { return bad; } case http_version_p: if (input == 'P') { state_ = http_version_slash; return indeterminate; } else { return bad; } case http_version_slash: if (input == '/') { req.http_version_major = 0; req.http_version_minor = 0; state_ = http_version_major_start; return indeterminate; } else { return bad; } case http_version_major_start: if (is_digit(input)) { req.http_version_major = req.http_version_major * 10 + input - '0'; state_ = http_version_major; return indeterminate; } else { return bad; } case http_version_major: if (input == '.') { state_ = http_version_minor_start; return indeterminate; } else if (is_digit(input)) { req.http_version_major = req.http_version_major * 10 + input - '0'; return indeterminate; } else { return bad; } case http_version_minor_start: if (is_digit(input)) { req.http_version_minor = req.http_version_minor * 10 + input - '0'; state_ = http_version_minor; return indeterminate; } else { return bad; } case http_version_minor: if (input == '\r') { state_ = expecting_newline_1; return indeterminate; } else if (is_digit(input)) { req.http_version_minor = req.http_version_minor * 10 + input - '0'; return indeterminate; } else { return bad; } case expecting_newline_1: if (input == '\n') { state_ = header_line_start; return indeterminate; } else { return bad; } case header_line_start: if (input == '\r') { state_ = expecting_newline_3; return indeterminate; } else if (!req.headers.empty() && (input == ' ' || input == '\t')) { state_ = header_lws; return indeterminate; } else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) { return bad; } else { req.headers.push_back(header()); req.headers.back().name.push_back(input); state_ = header_name; return indeterminate; } case header_lws: if (input == '\r') { state_ = expecting_newline_2; return indeterminate; } else if (input == ' ' || input == '\t') { return indeterminate; } else if (is_ctl(input)) { return bad; } else { state_ = header_value; req.headers.back().value.push_back(input); return indeterminate; } case header_name: if (input == ':') { state_ = space_before_header_value; return indeterminate; } else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) { return bad; } else { req.headers.back().name.push_back(input); return indeterminate; } case space_before_header_value: if (input == ' ') { state_ = header_value; return indeterminate; } else { return bad; } case header_value: if (input == '\r') { if (req.headers.back().name == "Content-Length") { body_content_length_ = std::stoi(req.headers.back().value); } state_ = expecting_newline_2; return indeterminate; } else if (is_ctl(input)) { return bad; } else { req.headers.back().value.push_back(input); return indeterminate; } case expecting_newline_2: if (input == '\n') { state_ = header_line_start; return indeterminate; } else { return bad; } case expecting_newline_3: if (body_content_length_ > 0) { state_ = body; return indeterminate; } return (input == '\n') ? good : bad; case body: req.body.push_back(input); if (req.body.length() < body_content_length_) { return indeterminate; } return good; default: return bad; } }
request_parser::result_type request_parser::consume(request& req, char input) { switch (state_) { case method_start: if (!is_char(input) || is_ctl(input) || is_tspecial(input)) { return bad; } else { state_ = method; req.method.push_back(input); return indeterminate; } case method: if (input == ' ') { state_ = uri; return indeterminate; } else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) { return bad; } else { req.method.push_back(input); return indeterminate; } case uri: if (input == ' ') { state_ = http_version_h; return indeterminate; } else if (is_ctl(input)) { return bad; } else { req.uri.push_back(input); return indeterminate; } case http_version_h: if (input == 'H') { state_ = http_version_t_1; return indeterminate; } else { return bad; } case http_version_t_1: if (input == 'T') { state_ = http_version_t_2; return indeterminate; } else { return bad; } case http_version_t_2: if (input == 'T') { state_ = http_version_p; return indeterminate; } else { return bad; } case http_version_p: if (input == 'P') { state_ = http_version_slash; return indeterminate; } else { return bad; } case http_version_slash: if (input == '/') { req.http_version_major = 0; req.http_version_minor = 0; state_ = http_version_major_start; return indeterminate; } else { return bad; } case http_version_major_start: if (is_digit(input)) { req.http_version_major = req.http_version_major * 10 + input - '0'; state_ = http_version_major; return indeterminate; } else { return bad; } case http_version_major: if (input == '.') { state_ = http_version_minor_start; return indeterminate; } else if (is_digit(input)) { req.http_version_major = req.http_version_major * 10 + input - '0'; return indeterminate; } else { return bad; } case http_version_minor_start: if (is_digit(input)) { req.http_version_minor = req.http_version_minor * 10 + input - '0'; state_ = http_version_minor; return indeterminate; } else { return bad; } case http_version_minor: if (input == '\r') { state_ = expecting_newline_1; return indeterminate; } else if (is_digit(input)) { req.http_version_minor = req.http_version_minor * 10 + input - '0'; return indeterminate; } else { return bad; } case expecting_newline_1: if (input == '\n') { state_ = header_line_start; return indeterminate; } else { return bad; } case header_line_start: if (input == '\r') { state_ = expecting_newline_3; return indeterminate; } else if (!req.headers.empty() && (input == ' ' || input == '\t')) { state_ = header_lws; return indeterminate; } else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) { return bad; } else { req.headers.push_back(header()); req.headers.back().name.push_back(input); state_ = header_name; return indeterminate; } case header_lws: if (input == '\r') { state_ = expecting_newline_2; return indeterminate; } else if (input == ' ' || input == '\t') { return indeterminate; } else if (is_ctl(input)) { return bad; } else { state_ = header_value; req.headers.back().value.push_back(input); return indeterminate; } case header_name: if (input == ':') { state_ = space_before_header_value; return indeterminate; } else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) { return bad; } else { req.headers.back().name.push_back(input); return indeterminate; } case space_before_header_value: if (input == ' ') { state_ = header_value; return indeterminate; } else { return bad; } case header_value: if (input == '\r') { state_ = expecting_newline_2; return indeterminate; } else if (is_ctl(input)) { return bad; } else { req.headers.back().value.push_back(input); return indeterminate; } case expecting_newline_2: if (input == '\n') { state_ = header_line_start; // try parse content-length if(!req.headers.empty() && 0 == stricmp("Content-Length", req.headers.rbegin()->name.c_str() )) { req.content_length = atoi(req.headers.rbegin()->value.c_str()); } return indeterminate; } else { return bad; } case expecting_newline_3: // headers parse complete return (input == '\n') ? good : bad; default: return bad; } }
boost::tribool request_parser::consume(request& req, char input) { switch (state_) { case method_start: if (!is_char(input) || is_ctl(input) || is_tspecial(input)) { return false; } else { state_ = method; req.method.push_back(input); return boost::indeterminate; } case method: if (input == ' ') { state_ = uri; return boost::indeterminate; } else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) { return false; } else { req.method.push_back(input); return boost::indeterminate; } case uri_start: if (is_ctl(input)) { return false; } else { state_ = uri; req.uri.push_back(input); return boost::indeterminate; } case uri: if (input == ' ') { state_ = http_version_h; return boost::indeterminate; } else if (is_ctl(input)) { return false; } else { req.uri.push_back(input); return boost::indeterminate; } case http_version_h: if (input == 'H') { state_ = http_version_t_1; return boost::indeterminate; } else { return false; } case http_version_t_1: if (input == 'T') { state_ = http_version_t_2; return boost::indeterminate; } else { return false; } case http_version_t_2: if (input == 'T') { state_ = http_version_p; return boost::indeterminate; } else { return false; } case http_version_p: if (input == 'P') { state_ = http_version_slash; return boost::indeterminate; } else { return false; } case http_version_slash: if (input == '/') { req.http_version_major = 0; req.http_version_minor = 0; state_ = http_version_major_start; return boost::indeterminate; } else { return false; } case http_version_major_start: if (is_digit(input)) { req.http_version_major = req.http_version_major * 10 + input - '0'; state_ = http_version_major; return boost::indeterminate; } else { return false; } case http_version_major: if (input == '.') { state_ = http_version_minor_start; return boost::indeterminate; } else if (is_digit(input)) { req.http_version_major = req.http_version_major * 10 + input - '0'; return boost::indeterminate; } else { return false; } case http_version_minor_start: if (is_digit(input)) { req.http_version_minor = req.http_version_minor * 10 + input - '0'; state_ = http_version_minor; return boost::indeterminate; } else { return false; } case http_version_minor: if (input == '\r') { state_ = expecting_newline_1; return boost::indeterminate; } else if (is_digit(input)) { req.http_version_minor = req.http_version_minor * 10 + input - '0'; return boost::indeterminate; } else { return false; } case expecting_newline_1: if (input == '\n') { state_ = header_line_start; return boost::indeterminate; } else { return false; } case header_line_start: if (input == '\r') { state_ = expecting_newline_3; return boost::indeterminate; } else if (!req.headers.empty() && (input == ' ' || input == '\t')) { state_ = header_lws; return boost::indeterminate; } else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) { return false; } else { req.headers.push_back(header()); req.headers.back().name.push_back(input); state_ = header_name; return boost::indeterminate; } case header_lws: if (input == '\r') { state_ = expecting_newline_2; return boost::indeterminate; } else if (input == ' ' || input == '\t') { return boost::indeterminate; } else if (is_ctl(input)) { return false; } else { state_ = header_value; req.headers.back().value.push_back(input); return boost::indeterminate; } case header_name: if (input == ':') { state_ = space_before_header_value; return boost::indeterminate; } else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) { return false; } else { req.headers.back().name.push_back(input); return boost::indeterminate; } case space_before_header_value: if (input == ' ') { state_ = header_value; return boost::indeterminate; } else { return false; } case header_value: if (input == '\r') { state_ = expecting_newline_2; return boost::indeterminate; } else if (is_ctl(input)) { return false; } else { req.headers.back().value.push_back(input); return boost::indeterminate; } case expecting_newline_2: if (input == '\n') { state_ = header_line_start; return boost::indeterminate; } else { return false; } case expecting_newline_3: if (input == '\n') { if( req.method != "POST" ) { // finished return true; } else { // this is a post request, so we need to read the content req.content_length = 0; for( std::vector<header>::iterator ph = req.headers.begin(); ph != req.headers.end(); ++ph ) { if( (*ph).name == "Content-Length" ) { req.content_length = atoi( (*ph).value.c_str()); break; } } state_ = reading_content; return boost::indeterminate; } } else { return false; } case reading_content: req.content += input; if( req.content.length() == req.content_length ) { return true; } else { return boost::indeterminate; } default: return false; } }
boost::tribool& RequestParser::consume(Request& req, char input) { static boost::tribool False(false); static boost::tribool True(true); static boost::tribool Indeterminate(boost::indeterminate); if (++requestSize_ > MAX_REQUEST_HEADER_SIZE) return False; switch (httpState_) { case method_start: if (input == '\r') { /* * allow a new line before a request -- this seems to be * accepted practice when dealing with multiple requests * in one connection, separated by a CRLF. */ httpState_ = expecting_newline_0; return Indeterminate; } else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) { return False; } else { httpState_ = method; consumeToString(req.method, MAX_METHOD_SIZE); consumeChar(input); return Indeterminate; } case expecting_newline_0: if (input == '\n') { httpState_ = method_start; return Indeterminate; } else { return False; } case method: if (input == ' ') { consumeComplete(); httpState_ = uri_start; return Indeterminate; } else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) { return False; } else { if (consumeChar(input)) return Indeterminate; else return False; } case uri_start: if (is_ctl(input)) { return False; } else { httpState_ = uri; consumeToString(req.uri, MAX_URI_SIZE); consumeChar(input); return Indeterminate; } case uri: if (input == ' ') { consumeComplete(); httpState_ = http_version_h; return Indeterminate; } else if (is_ctl(input)) { return False; } else { if (consumeChar(input)) return Indeterminate; else return False; } case http_version_h: if (input == 'H') { httpState_ = http_version_t_1; return Indeterminate; } else { return False; } case http_version_t_1: if (input == 'T') { httpState_ = http_version_t_2; return Indeterminate; } else { return False; } case http_version_t_2: if (input == 'T') { httpState_ = http_version_p; return Indeterminate; } else { return False; } case http_version_p: if (input == 'P') { httpState_ = http_version_slash; return Indeterminate; } else { return False; } case http_version_slash: if (input == '/') { req.http_version_major = 0; req.http_version_minor = 0; httpState_ = http_version_major_start; return Indeterminate; } else { return False; } case http_version_major_start: if (is_digit(input)) { req.http_version_major = req.http_version_major * 10 + input - '0'; httpState_ = http_version_major; return Indeterminate; } else { return False; } case http_version_major: if (input == '.') { httpState_ = http_version_minor_start; return Indeterminate; } else if (is_digit(input)) { req.http_version_major = req.http_version_major * 10 + input - '0'; return Indeterminate; } else { return False; } case http_version_minor_start: if (is_digit(input)) { req.http_version_minor = req.http_version_minor * 10 + input - '0'; httpState_ = http_version_minor; return Indeterminate; } else { return False; } case http_version_minor: if (input == '\r') { httpState_ = expecting_newline_1; return Indeterminate; } else if (is_digit(input)) { req.http_version_minor = req.http_version_minor * 10 + input - '0'; return Indeterminate; } else { return False; } case expecting_newline_1: if (input == '\n') { httpState_ = header_line_start; return Indeterminate; } else { return False; } case header_line_start: if (input == '\r') { httpState_ = expecting_newline_3; return Indeterminate; } else if (!req.headerMap.empty() && (input == ' ' || input == '\t')) { // continuation of previous header httpState_ = header_lws; return Indeterminate; } else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) { return False; } else { consumeToString(headerName_, MAX_FIELD_NAME_SIZE); consumeChar(input); httpState_ = header_name; return Indeterminate; } case header_lws: if (input == '\r') { httpState_ = expecting_newline_2; return Indeterminate; } else if (input == ' ' || input == '\t') { return Indeterminate; } else if (is_ctl(input)) { return False; } else { httpState_ = header_value; headerValue_.push_back(input); return Indeterminate; } case header_name: if (input == ':') { consumeComplete(); httpState_ = space_before_header_value; return Indeterminate; } else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) { return False; } else { if (consumeChar(input)) return Indeterminate; else return False; } case space_before_header_value: if (input == ' ') { consumeToString(headerValue_, MAX_FIELD_VALUE_SIZE); httpState_ = header_value; return Indeterminate; } else { consumeToString(headerValue_, MAX_FIELD_VALUE_SIZE); httpState_ = header_value; } case header_value: if (input == '\r') { consumeComplete(); if (req.headerMap.find(headerName_) != req.headerMap.end()) { req.headerMap[headerName_] += ',' + headerValue_; } else { Request::HeaderMap::iterator i = req.headerMap.insert(std::make_pair(headerName_, headerValue_)) .first; req.headerOrder.push_back(i); } httpState_ = expecting_newline_2; return Indeterminate; } else if (is_ctl(input)) { return False; } else { if (consumeChar(input)) return Indeterminate; else return False; } case expecting_newline_2: if (input == '\n') { httpState_ = header_line_start; return Indeterminate; } else { return False; } case expecting_newline_3: if (input == '\n') return True; else return False; default: return False; } }
boost::tribool request_parser::consume(request& req, const char input) { switch(state_) { case command_start: if (!is_char(input) || is_ctl(input) || is_tspecial(input)) { return false; } else { state_ = command; req.command.push_back(input); return boost::indeterminate; } case command: if (':' == input) { state_ = argument; return boost::indeterminate; } else if ('\r' == input) { state_ = newline_1; return boost::indeterminate; } else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) { return false; } else { req.command.push_back(input); return boost::indeterminate; } case argument: if ('\r' == input) { state_ = newline_1; return boost::indeterminate; } else if (!is_char(input)) { return false; } else { req.argument.push_back(input); return boost::indeterminate; } case newline_1: if ('\n' == input) { state_ = command_start; return true; } else { return false; } } }
boost::tribool HTTPRequestParser::consume(HTTPRequest& req, char input) { switch (state_) { case method_start: if (!is_char(input) || is_ctl(input) || is_tspecial(input)) { return false; } else { state_ = method; req.method.push_back(input); return boost::indeterminate; } case method: if (input == ' ') { state_ = uri; return boost::indeterminate; } else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) { return false; } else { req.method.push_back(input); return boost::indeterminate; } case uri_start: if (is_ctl(input)) { return false; } else { state_ = uri; req.uri.push_back(input); return boost::indeterminate; } case uri: if (input == ' ') { state_ = http_version_h; return boost::indeterminate; } else if (is_ctl(input)) { return false; } else { req.uri.push_back(input); return boost::indeterminate; } case http_version_h: if (input == 'H') { state_ = http_version_t_1; return boost::indeterminate; } else { return false; } case http_version_t_1: if (input == 'T') { state_ = http_version_t_2; return boost::indeterminate; } else { return false; } case http_version_t_2: if (input == 'T') { state_ = http_version_p; return boost::indeterminate; } else { return false; } case http_version_p: if (input == 'P') { state_ = http_version_slash; return boost::indeterminate; } else { return false; } case http_version_slash: if (input == '/') { req.http_version_major = 0; req.http_version_minor = 0; state_ = http_version_major_start; return boost::indeterminate; } else { return false; } case http_version_major_start: if (is_digit(input)) { req.http_version_major = req.http_version_major * 10 + input - '0'; state_ = http_version_major; return boost::indeterminate; } else { return false; } case http_version_major: if (input == '.') { state_ = http_version_minor_start; return boost::indeterminate; } else if (is_digit(input)) { req.http_version_major = req.http_version_major * 10 + input - '0'; return boost::indeterminate; } else { return false; } case http_version_minor_start: if (is_digit(input)) { req.http_version_minor = req.http_version_minor * 10 + input - '0'; state_ = http_version_minor; return boost::indeterminate; } else { return false; } case http_version_minor: if (input == '\r') { state_ = expecting_newline_1; return boost::indeterminate; } else if (is_digit(input)) { req.http_version_minor = req.http_version_minor * 10 + input - '0'; return boost::indeterminate; } else { return false; } case expecting_newline_1: if (input == '\n') { state_ = header_line_start; return boost::indeterminate; } else { return false; } case header_line_start: if (input == '\r') { state_ = expecting_newline_3; return boost::indeterminate; } else if (!req.headers.empty() && (input == ' ' || input == '\t')) { state_ = header_lws; return boost::indeterminate; } else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) { return false; } else { _currentHeaderKey.clear(); _currentHeaderValue.clear(); _currentHeaderKey.push_back(input); state_ = header_name; return boost::indeterminate; } case header_lws: if (input == '\r') { state_ = expecting_newline_2; return boost::indeterminate; } else if (input == ' ' || input == '\t') { return boost::indeterminate; } else if (is_ctl(input)) { return false; } else { state_ = header_value; _currentHeaderValue.push_back(input); return boost::indeterminate; } case header_name: if (input == ':') { state_ = space_before_header_value; return boost::indeterminate; } else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) { return false; } else { _currentHeaderKey.push_back(input); return boost::indeterminate; } case space_before_header_value: if (input == ' ') { state_ = header_value; return boost::indeterminate; } else { return false; } case header_value: if (input == '\r') { req.headers.insert(make_pair(_currentHeaderKey, _currentHeaderValue)); state_ = expecting_newline_2; return boost::indeterminate; } else if (is_ctl(input)) { req.headers.insert(make_pair(_currentHeaderKey, _currentHeaderValue)); return false; } else { _currentHeaderValue.push_back(input); return boost::indeterminate; } case expecting_newline_2: if (input == '\n') { state_ = header_line_start; return boost::indeterminate; } else { return false; } case expecting_newline_3: if (input == '\n') { if(req.method != "POST") return true; HTTPRequest::Headers::const_iterator it(req.headers.find("Content-Length")); if(it == req.headers.end()) return false; _postToDownload = lexical_cast<size_t>(it->second); state_ = postData; return boost::indeterminate; } else { return false; } case postData: req.postData.push_back(input); --_postToDownload; if(_postToDownload) return boost::indeterminate; else return true; default: return false; } }
/* ** parse http message: ** http_eoh - end of headers ** {http_header, Key, Value} - Key = atom() | string() ** {http_request, Method,Url,Version} ** {http_response, Version, Status, Message} ** {http_error, Error-Line} */ int packet_parse_http(const char* buf, int len, int* statep, PacketCallbacks* pcb, void* arg) { const char* ptr = buf; const char* p0; int n = len; /* remove trailing CRNL (accept NL as well) */ if ((n >= 2) && (buf[n-2] == '\r')) n -= 2; else if ((n >= 1) && (buf[n-1] == '\n')) n -= 1; if (*statep == 0) { /* start-line = Request-Line | Status-Line */ if (n >= 5 && (strncmp(buf, "HTTP/", 5) == 0)) { int major = 0; int minor = 0; int status = 0; /* Status-Line = HTTP-Version SP * Status-Code SP Reason-Phrase * CRNL * HTTP-Version = "HTTP" "/" 1*DIGIT "." 1*DIGIT */ ptr += 5; n -= 5; p0 = ptr; while (n && isdigit((int) *ptr)) { major = 10*major + (*ptr - '0'); ptr++; n--; } if (ptr==p0 || !n || (*ptr != '.')) return -1; ptr++; n--; p0 = ptr; while (n && isdigit((int) *ptr)) { minor = 10*minor + (*ptr - '0'); ptr++; n--; } if (ptr==p0) return -1; p0 = ptr; while (n && SP(ptr)) { ptr++; n--; } if (ptr==p0) return -1; while (n && isdigit((int) *ptr)) { status = 10*status + (*ptr - '0'); ptr++; n--; } p0 = ptr; while (n && SP(ptr)) { ptr++; n--; } if (ptr==p0 && n>0) return -1; /* NOTE: the syntax allows empty reason phrases */ (*statep) = !0; return pcb->http_response(arg, major, minor, status, ptr, n); } else { /* Request-Line = Method SP Request-URI SP HTTP-Version CRLF */ http_atom_t* meth; const char* meth_ptr = buf; int meth_len; PacketHttpURI uri; const char* uri_ptr; int uri_len; int major = 0; int minor = 0; unsigned long h = 0; while (n && !is_tspecial((unsigned char)*ptr)) { hash_update(h, (int)*ptr); ptr++; n--; } meth_len = ptr - meth_ptr; if (n == 0 || meth_len == 0 || !SP(ptr)) return -1; meth = http_hash_lookup(meth_ptr, meth_len, h, http_meth_hash, HTTP_METH_HASH_SIZE); while (n && SP(ptr)) { ptr++; n--; } uri_ptr = ptr; while (n && !SP(ptr)) { ptr++; n--; } if ((uri_len = (ptr - uri_ptr)) == 0) return -1; while (n && SP(ptr)) { ptr++; n--; } if (n == 0) { (*statep) = !0; http_parse_uri(&uri, uri_ptr, uri_len); return pcb->http_request(arg, meth, meth_ptr, meth_len, &uri, 0, 9); } if (n < 8) return -1; if (strncmp(ptr, "HTTP/", 5) != 0) return -1; ptr += 5; n -= 5; p0 = ptr; while (n && isdigit((int) *ptr)) { major = 10*major + (*ptr - '0'); ptr++; n--; } if (ptr==p0 || !n || (*ptr != '.')) return -1; ptr++; n--; p0 = ptr; while (n && isdigit((int) *ptr)) { minor = 10*minor + (*ptr - '0'); ptr++; n--; } if (ptr==p0) return -1; (*statep) = !0; http_parse_uri(&uri, uri_ptr, uri_len); return pcb->http_request(arg, meth, meth_ptr, meth_len, &uri, major, minor); } } else { int up = 1; /* make next char uppercase */ http_atom_t* name; char name_buf[HTTP_MAX_NAME_LEN]; const char* name_ptr = name_buf; int name_len; unsigned long h; if (n == 0) { /* end of headers */ *statep = 0; /* reset state (for next request) */ return pcb->http_eoh(arg); } h = 0; name_len = 0; while (!is_tspecial((unsigned char)*ptr)) { if (name_len < HTTP_MAX_NAME_LEN) { int c = *ptr; if (up) { if (islower(c)) { c = toupper(c); } up = 0; } else { if (isupper(c)) c = tolower(c); else if (c == '-') up = 1; } name_buf[name_len] = c; hash_update(h, c); } name_len++; ptr++; if (--n == 0) return -1; } while (n && SP(ptr)) { /* Skip white space before ':' */ ptr++; n--; } if (*ptr != ':') { return -1; } if (name_len <= HTTP_MAX_NAME_LEN) { name = http_hash_lookup(name_buf, name_len, h, http_hdr_hash, HTTP_HDR_HASH_SIZE); } else { /* Is it ok to return original name without case adjustments? */ name_ptr = buf; name = NULL; } ptr++; n--; /* Skip white space after ':' */ while (n && SP(ptr)) { ptr++; n--; } return pcb->http_header(arg, name, name_ptr, name_len, ptr, n); } return -1; }
static bool is_token(int ch) { /* Can by antying except a ctl character or a tspecial */ return !is_ctl(ch) && !is_tspecial(ch); }
NPLMsgIn_parser::Consume_Result NPLMsgIn_parser::consume(NPLMsgIn& req, char input) { switch (state_) { case method_start: if (input == ' ' || input == '\r' || input == '\n') { return c_res_indeterminate; } else { // allowing char [0-255] as method name characters, escaping only \r\n and ' '. state_ = method; req.reset(); req.method.push_back(input); return c_res_indeterminate; } case method: if (input == ' ') { // this fix a bug when http url contains the '(', it may be mis-interpreted. state_ = req.IsNPLFileActivation() ? uri : uri_http; return c_res_indeterminate; } else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) { return c_res_false; } else { req.method.push_back(input); return c_res_indeterminate; } case uri_http: if (input == ' ') { state_ = npl_version_n; return c_res_indeterminate; } else if (input == '\n' || input == '\r') { state_ = header_line_start; // version is omitted on first line. return c_res_indeterminate; } else { req.m_n_filename = 0; req.m_filename.push_back(input); return c_res_indeterminate; } case uri: if (is_ctl(input)) { req.npl_version_major = NPL_VERSION_MAJOR; req.npl_version_minor = NPL_VERSION_MINOR; state_ = header_line_start; // version is omitted on first line. return c_res_indeterminate; } else if (input == '(') { state_ = uri_rts_name; return c_res_indeterminate; } else if (is_digit(input)) { req.m_n_filename = req.m_n_filename * 10 + input - '0'; req.m_filename.push_back(input); return c_res_indeterminate; } else if (input == ' ') { state_ = npl_version_n; return c_res_indeterminate; } else { req.m_n_filename = 0; req.m_filename.push_back(input); return c_res_indeterminate; } case uri_rts_name: if (input == ')') { state_ = uri; return c_res_indeterminate; } else if(is_ctl(input)) { return c_res_false; } else { req.m_rts_name.push_back(input); return c_res_indeterminate; } case npl_version_n: if (input == '/') { req.npl_version_major = 0; req.npl_version_minor = 0; state_ = npl_version_major; return c_res_indeterminate; } else if (input == '\n') { state_ = header_line_start; return c_res_indeterminate; } else if (input == '\r') return c_res_indeterminate; else if (is_char(input)) { // any character is accepted, such as "NPL", "HTTP" if(req.m_filename.empty() && req.m_n_filename > 0) { // for http response, this stores the third parameter "OK", "Error" req.m_filename.push_back(input); } return c_res_indeterminate; } else { return c_res_false; } case npl_version_major: if (is_digit(input)) { req.npl_version_major = req.npl_version_major * 10 + input - '0'; return c_res_indeterminate; } else if (input == '.') { state_ = npl_version_minor; return c_res_indeterminate; } else { return c_res_false; } case npl_version_minor: if (is_digit(input)) { req.npl_version_minor = req.npl_version_minor * 10 + input - '0'; return c_res_indeterminate; } else if (input == '\n') { state_ = header_line_start; return c_res_indeterminate; } else if (input == '\r') return c_res_indeterminate; else { return c_res_false; } case header_line_start: if (input == '\n') { // continue to read the code length state_ = code_length; if(req.method.size()>2 && req.method != "npl") { // only do check header if method has at least three characters. int nCount = (int)req.headers.size(); for (int i=0;i <nCount; ++i) { if(req.headers[i].name == "Content-Length") { int nLength = atoi(req.headers[i].value.c_str()); if(nLength>0) { state_ = code_body; req.m_nLength = nLength; req.m_code.reserve(req.m_nLength+1); m_bCompressed = false; return c_res_code_body; } else { break; } } } m_bCompressed = false; reset(); return c_res_true; } return c_res_indeterminate; } else if (input == '\r') return c_res_indeterminate; else if (!req.headers.empty() && (input == ' ' || input == '\t')) { state_ = header_lws; return c_res_indeterminate; } else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) { return c_res_false; } else { req.headers.push_back(NPLMsgHeader()); req.headers.back().name.push_back(input); state_ = header_name; return c_res_indeterminate; } case header_lws: if (input == '\n') { state_ = header_line_start; return c_res_indeterminate; } else if (input == '\r') return c_res_indeterminate; else if (input == ' ' || input == '\t') { return c_res_indeterminate; } else if (is_ctl(input)) { return c_res_false; } else { state_ = header_value; req.headers.back().value.push_back(input); return c_res_indeterminate; } case header_name: if (input == ':') { state_ = header_value; return c_res_indeterminate; } else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) { return c_res_false; } else { req.headers.back().name.push_back(input); return c_res_indeterminate; } case header_value: if (input == '\n') { state_ = header_line_start; return c_res_indeterminate; } else if (input == '\r') return c_res_indeterminate; else if (is_ctl(input)) { return c_res_false; } else { string& value_ = req.headers.back().value; if( !(value_.empty() && (input == ' ' || input == '\t')) ) value_.push_back(input); return c_res_indeterminate; } case code_length: if(is_digit(input)) { req.m_nLength = req.m_nLength * 10 + input - '0'; return c_res_indeterminate; } else if(input == ':' || input == '>') { state_ = code_body; m_bCompressed = (input == '>'); if(req.m_nLength == 0) { reset(); return c_res_true; } else { req.m_code.reserve(req.m_nLength+1); return c_res_code_body; } } case code_body: if(req.m_nLength>(int)req.m_code.size()) { // I used a special return value c_res_code_body to indicate that code body is met, // so that we can read to end or length instead of doing the lexical work by char at a time. req.m_code.push_back(input); if (req.m_nLength >(int)req.m_code.size()) return c_res_code_body; else { reset(); return c_res_true; } } default: return c_res_false; } }
boost::tribool request_parser::consume(request& req, char input) { switch (state_) { case method_start: if (!is_char(input) || is_ctl(input) || is_tspecial(input)) { return false; } else { state_ = method; req.method.push_back(input); return boost::indeterminate; } case method: if (input == ' ') { state_ = uri; return boost::indeterminate; } else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) { return false; } else { req.method.push_back(input); return boost::indeterminate; } case uri: if (input == ' ') { state_ = http_version_h; return boost::indeterminate; } else if (is_ctl(input)) { return false; } else { req.uri.push_back(input); return boost::indeterminate; } case http_version_h: if (input == 'H') { state_ = http_version_t_1; return boost::indeterminate; } else { return false; } case http_version_t_1: if (input == 'T') { state_ = http_version_t_2; return boost::indeterminate; } else { return false; } case http_version_t_2: if (input == 'T') { state_ = http_version_p; return boost::indeterminate; } else { return false; } case http_version_p: if (input == 'P') { state_ = http_version_slash; return boost::indeterminate; } else { return false; } case http_version_slash: if (input == '/') { req.http_version_major = 0; req.http_version_minor = 0; state_ = http_version_major_start; return boost::indeterminate; } else { return false; } case http_version_major_start: if (is_digit(input)) { req.http_version_major = req.http_version_major * 10 + input - '0'; state_ = http_version_major; return boost::indeterminate; } else { return false; } case http_version_major: if (input == '.') { state_ = http_version_minor_start; return boost::indeterminate; } else if (is_digit(input)) { req.http_version_major = req.http_version_major * 10 + input - '0'; return boost::indeterminate; } else { return false; } case http_version_minor_start: if (is_digit(input)) { req.http_version_minor = req.http_version_minor * 10 + input - '0'; state_ = http_version_minor; return boost::indeterminate; } else { return false; } case http_version_minor: if (input == '\r') { state_ = expecting_newline_1; return boost::indeterminate; } else if (is_digit(input)) { req.http_version_minor = req.http_version_minor * 10 + input - '0'; return boost::indeterminate; } else { return false; } case expecting_newline_1: if (input == '\n') { state_ = header_line_start; return boost::indeterminate; } else { return false; } case header_line_start: if (input == '\r') { state_ = expecting_newline_3; return boost::indeterminate; } else if (!req.headers.empty() && (input == ' ' || input == '\t')) { state_ = header_lws; return boost::indeterminate; } else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) { return false; } else { req.headers.push_back(header()); req.headers.back().name.push_back(input); state_ = header_name; return boost::indeterminate; } case header_lws: if (input == '\r') { state_ = expecting_newline_2; return boost::indeterminate; } else if (input == ' ' || input == '\t') { return boost::indeterminate; } else if (is_ctl(input)) { return false; } else { state_ = header_value; req.headers.back().value.push_back(input); return boost::indeterminate; } case header_name: if (input == ':') { state_ = space_before_header_value; return boost::indeterminate; } else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) { return false; } else { req.headers.back().name.push_back(input); return boost::indeterminate; } case space_before_header_value: if (input == ' ') { state_ = header_value; return boost::indeterminate; } else { return false; } case header_value: if (input == '\r') { state_ = expecting_newline_2; return boost::indeterminate; } else if (is_ctl(input)) { return false; } else { req.headers.back().value.push_back(input); return boost::indeterminate; } case expecting_newline_2: if (input == '\n') { state_ = header_line_start; return boost::indeterminate; } else { return false; } case expecting_newline_3: return (input == '\n'); default: return false; } }
boost::tribool form_parser::consume(json_var& variable, char input) {//std::cout << input << "<" << state_ << "\n"; switch (state_) { case var_start: if (!is_char(input)) { return false; } else if(input == '\0') { return true; } else { value_ += input; state_ = var; return boost::indeterminate; } case var: if (input == '=') { (*var_)[value_] = json_var(json_var::string_var); var_ = &(*var_)[value_]; value_.clear(); state_ = value_start; return boost::indeterminate; } else if (input == '[') { state_ = property_start; return boost::indeterminate; } else if (input == '&') { (*var_)[value_] = json_var(true); value_.clear(); state_ = var_start; return boost::indeterminate; } else if(input == '\0') { (*var_)[value_] = json_var(true); return true; } else if (!is_char(input) || is_tspecial(input)) { return false; } else { value_ += input; return boost::indeterminate; } case property_start: if (is_char(input) && !is_tspecial(input)) { if((*var_)[value_].get_type() != json_var::object_var) (*var_)[value_] = json_var(json_var::object_var); var_ = &(*var_)[value_]; value_.clear(); value_ += input; state_ = property; return boost::indeterminate; } else { return false; } case property: if (input == ']') { state_ = property_end; return boost::indeterminate; } else if (is_char(input) && !is_tspecial(input)) { value_ += input; return boost::indeterminate; } else { return false; } case property_end: if (input == '=') { (*var_)[value_] = json_var(json_var::string_var); var_ = &(*var_)[value_]; value_.clear(); state_ = value_start; return boost::indeterminate; } else if (input == '[') { state_ = property_start; return boost::indeterminate; } else if (input == '&') { (*var_)[value_] = json_var("true"); value_.clear(); var_ = &variable; state_ = var_start; return boost::indeterminate; } else if(input == '\0') { (*var_)[value_] = json_var("true"); return true; } else { return false; } case value_start: if (input == '&') { return false; } else { value_ += input; state_ = value; return boost::indeterminate; } case value: if (input == '&') { //std::cout << value_ << '\n'; std::string value; utils::url_decode(value_, value); (*var_) = value; value_.clear(); var_ = &variable; state_ = var_start; return boost::indeterminate; } else if (input == '\0') { //std::cout << value_ << '\n'; std::string value; utils::url_decode(value_, value); (*var_) = value; return true; } else { value_ += input; return boost::indeterminate; } default: return false; } }