1
0
mirror of https://github.com/VCMP-SqMod/SqMod.git synced 2025-12-20 06:57:19 +01:00

Initial preparations for CURL and Discord integration.

This commit is contained in:
Sandu Liviu Catalin
2021-01-27 07:27:48 +02:00
parent 8257eb61d6
commit 95705e87c8
1751 changed files with 440547 additions and 854 deletions

View File

@@ -0,0 +1,308 @@
/*
* Copyright (c) 2014, Peter Thorson. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the WebSocket++ Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef HTTP_CONSTANTS_HPP
#define HTTP_CONSTANTS_HPP
#include <exception>
#include <map>
#include <string>
#include <vector>
#include <utility>
namespace websocketpp {
/// HTTP handling support
namespace http {
/// The type of an HTTP attribute list
/**
* The attribute list is an unordered key/value map. Encoded attribute
* values are delimited by semicolons.
*/
typedef std::map<std::string,std::string> attribute_list;
/// The type of an HTTP parameter list
/**
* The parameter list is an ordered pairing of a parameter and its
* associated attribute list. Encoded parameter values are delimited by
* commas.
*/
typedef std::vector< std::pair<std::string,attribute_list> > parameter_list;
/// Literal value of the HTTP header delimiter
static char const header_delimiter[] = "\r\n";
/// Literal value of the HTTP header separator
static char const header_separator[] = ":";
/// Literal value of an empty header
static std::string const empty_header;
/// Maximum size in bytes before rejecting an HTTP header as too big.
size_t const max_header_size = 16000;
/// Default Maximum size in bytes for HTTP message bodies.
size_t const max_body_size = 32000000;
/// Number of bytes to use for temporary istream read buffers
size_t const istream_buffer = 512;
/// invalid HTTP token characters
/**
* 0x00 - 0x32, 0x7f-0xff
* ( ) < > @ , ; : \ " / [ ] ? = { }
*/
static char const header_token[] = {
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 00..0f
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 10..1f
0,1,0,1,1,1,1,1,0,0,1,1,0,1,1,0, // 20..2f
1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0, // 30..3f
0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 40..4f
1,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1, // 50..5f
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 60..6f
1,1,1,1,1,1,1,1,1,1,1,0,1,0,1,0, // 70..7f
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 80..8f
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 90..9f
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // a0..af
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // b0..bf
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // c0..cf
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // d0..df
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // e0..ef
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // f0..ff
};
/// Is the character a token
inline bool is_token_char(unsigned char c) {
return (header_token[c] == 1);
}
/// Is the character a non-token
inline bool is_not_token_char(unsigned char c) {
return !header_token[c];
}
/// Is the character whitespace
/**
* whitespace is space (32) or horizontal tab (9)
*/
inline bool is_whitespace_char(unsigned char c) {
return (c == 9 || c == 32);
}
/// Is the character non-whitespace
inline bool is_not_whitespace_char(unsigned char c) {
return (c != 9 && c != 32);
}
/// HTTP Status codes
namespace status_code {
enum value {
uninitialized = 0,
continue_code = 100,
switching_protocols = 101,
ok = 200,
created = 201,
accepted = 202,
non_authoritative_information = 203,
no_content = 204,
reset_content = 205,
partial_content = 206,
multiple_choices = 300,
moved_permanently = 301,
found = 302,
see_other = 303,
not_modified = 304,
use_proxy = 305,
temporary_redirect = 307,
bad_request = 400,
unauthorized = 401,
payment_required = 402,
forbidden = 403,
not_found = 404,
method_not_allowed = 405,
not_acceptable = 406,
proxy_authentication_required = 407,
request_timeout = 408,
conflict = 409,
gone = 410,
length_required = 411,
precondition_failed = 412,
request_entity_too_large = 413,
request_uri_too_long = 414,
unsupported_media_type = 415,
request_range_not_satisfiable = 416,
expectation_failed = 417,
im_a_teapot = 418,
upgrade_required = 426,
precondition_required = 428,
too_many_requests = 429,
request_header_fields_too_large = 431,
internal_server_error = 500,
not_implemented = 501,
bad_gateway = 502,
service_unavailable = 503,
gateway_timeout = 504,
http_version_not_supported = 505,
not_extended = 510,
network_authentication_required = 511
};
// TODO: should this be inline?
inline std::string get_string(value c) {
switch (c) {
case uninitialized:
return "Uninitialized";
case continue_code:
return "Continue";
case switching_protocols:
return "Switching Protocols";
case ok:
return "OK";
case created:
return "Created";
case accepted:
return "Accepted";
case non_authoritative_information:
return "Non Authoritative Information";
case no_content:
return "No Content";
case reset_content:
return "Reset Content";
case partial_content:
return "Partial Content";
case multiple_choices:
return "Multiple Choices";
case moved_permanently:
return "Moved Permanently";
case found:
return "Found";
case see_other:
return "See Other";
case not_modified:
return "Not Modified";
case use_proxy:
return "Use Proxy";
case temporary_redirect:
return "Temporary Redirect";
case bad_request:
return "Bad Request";
case unauthorized:
return "Unauthorized";
case payment_required:
return "Payment Required";
case forbidden:
return "Forbidden";
case not_found:
return "Not Found";
case method_not_allowed:
return "Method Not Allowed";
case not_acceptable:
return "Not Acceptable";
case proxy_authentication_required:
return "Proxy Authentication Required";
case request_timeout:
return "Request Timeout";
case conflict:
return "Conflict";
case gone:
return "Gone";
case length_required:
return "Length Required";
case precondition_failed:
return "Precondition Failed";
case request_entity_too_large:
return "Request Entity Too Large";
case request_uri_too_long:
return "Request-URI Too Long";
case unsupported_media_type:
return "Unsupported Media Type";
case request_range_not_satisfiable:
return "Requested Range Not Satisfiable";
case expectation_failed:
return "Expectation Failed";
case im_a_teapot:
return "I'm a teapot";
case upgrade_required:
return "Upgrade Required";
case precondition_required:
return "Precondition Required";
case too_many_requests:
return "Too Many Requests";
case request_header_fields_too_large:
return "Request Header Fields Too Large";
case internal_server_error:
return "Internal Server Error";
case not_implemented:
return "Not Implemented";
case bad_gateway:
return "Bad Gateway";
case service_unavailable:
return "Service Unavailable";
case gateway_timeout:
return "Gateway Timeout";
case http_version_not_supported:
return "HTTP Version Not Supported";
case not_extended:
return "Not Extended";
case network_authentication_required:
return "Network Authentication Required";
default:
return "Unknown";
}
}
}
class exception : public std::exception {
public:
exception(const std::string& log_msg,
status_code::value error_code,
const std::string& error_msg = std::string(),
const std::string& body = std::string())
: m_msg(log_msg)
, m_error_msg(error_msg)
, m_body(body)
, m_error_code(error_code) {}
~exception() throw() {}
virtual const char* what() const throw() {
return m_msg.c_str();
}
std::string m_msg;
std::string m_error_msg;
std::string m_body;
status_code::value m_error_code;
};
}
}
#endif // HTTP_CONSTANTS_HPP

View File

@@ -0,0 +1,200 @@
/*
* Copyright (c) 2014, Peter Thorson. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the WebSocket++ Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef HTTP_PARSER_IMPL_HPP
#define HTTP_PARSER_IMPL_HPP
#include <algorithm>
#include <cstdlib>
#include <istream>
#include <sstream>
#include <string>
namespace websocketpp {
namespace http {
namespace parser {
inline void parser::set_version(std::string const & version) {
m_version = version;
}
inline std::string const & parser::get_header(std::string const & key) const {
header_list::const_iterator h = m_headers.find(key);
if (h == m_headers.end()) {
return empty_header;
} else {
return h->second;
}
}
inline bool parser::get_header_as_plist(std::string const & key,
parameter_list & out) const
{
header_list::const_iterator it = m_headers.find(key);
if (it == m_headers.end() || it->second.size() == 0) {
return false;
}
return this->parse_parameter_list(it->second,out);
}
inline void parser::append_header(std::string const & key, std::string const &
val)
{
if (std::find_if(key.begin(),key.end(),is_not_token_char) != key.end()) {
throw exception("Invalid header name",status_code::bad_request);
}
if (this->get_header(key).empty()) {
m_headers[key] = val;
} else {
m_headers[key] += ", " + val;
}
}
inline void parser::replace_header(std::string const & key, std::string const &
val)
{
m_headers[key] = val;
}
inline void parser::remove_header(std::string const & key) {
m_headers.erase(key);
}
inline void parser::set_body(std::string const & value) {
if (value.size() == 0) {
remove_header("Content-Length");
m_body.clear();
return;
}
// TODO: should this method respect the max size? If so how should errors
// be indicated?
std::stringstream len;
len << value.size();
replace_header("Content-Length", len.str());
m_body = value;
}
inline bool parser::parse_parameter_list(std::string const & in,
parameter_list & out) const
{
if (in.size() == 0) {
return false;
}
std::string::const_iterator it;
it = extract_parameters(in.begin(),in.end(),out);
return (it == in.begin());
}
inline bool parser::prepare_body() {
if (!get_header("Content-Length").empty()) {
std::string const & cl_header = get_header("Content-Length");
char * end;
// TODO: not 100% sure what the compatibility of this method is. Also,
// I believe this will only work up to 32bit sizes. Is there a need for
// > 4GiB HTTP payloads?
m_body_bytes_needed = std::strtoul(cl_header.c_str(),&end,10);
if (m_body_bytes_needed > m_body_bytes_max) {
throw exception("HTTP message body too large",
status_code::request_entity_too_large);
}
m_body_encoding = body_encoding::plain;
return true;
} else if (get_header("Transfer-Encoding") == "chunked") {
// TODO
//m_body_encoding = body_encoding::chunked;
return false;
} else {
return false;
}
}
inline size_t parser::process_body(char const * buf, size_t len) {
if (m_body_encoding == body_encoding::plain) {
size_t processed = (std::min)(m_body_bytes_needed,len);
m_body.append(buf,processed);
m_body_bytes_needed -= processed;
return processed;
} else if (m_body_encoding == body_encoding::chunked) {
// TODO:
throw exception("Unexpected body encoding",
status_code::internal_server_error);
} else {
throw exception("Unexpected body encoding",
status_code::internal_server_error);
}
}
inline void parser::process_header(std::string::iterator begin,
std::string::iterator end)
{
std::string::iterator cursor = std::search(
begin,
end,
header_separator,
header_separator + sizeof(header_separator) - 1
);
if (cursor == end) {
throw exception("Invalid header line",status_code::bad_request);
}
append_header(strip_lws(std::string(begin,cursor)),
strip_lws(std::string(cursor+sizeof(header_separator)-1,end)));
}
inline header_list const & parser::get_headers() const {
return m_headers;
}
inline std::string parser::raw_headers() const {
std::stringstream raw;
header_list::const_iterator it;
for (it = m_headers.begin(); it != m_headers.end(); it++) {
raw << it->first << ": " << it->second << "\r\n";
}
return raw.str();
}
} // namespace parser
} // namespace http
} // namespace websocketpp
#endif // HTTP_PARSER_IMPL_HPP

View File

@@ -0,0 +1,191 @@
/*
* Copyright (c) 2014, Peter Thorson. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the WebSocket++ Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef HTTP_PARSER_REQUEST_IMPL_HPP
#define HTTP_PARSER_REQUEST_IMPL_HPP
#include <algorithm>
#include <sstream>
#include <string>
#include <websocketpp/http/parser.hpp>
namespace websocketpp {
namespace http {
namespace parser {
inline size_t request::consume(char const * buf, size_t len) {
size_t bytes_processed;
if (m_ready) {return 0;}
if (m_body_bytes_needed > 0) {
bytes_processed = process_body(buf,len);
if (body_ready()) {
m_ready = true;
}
return bytes_processed;
}
// copy new header bytes into buffer
m_buf->append(buf,len);
// Search for delimiter in buf. If found read until then. If not read all
std::string::iterator begin = m_buf->begin();
std::string::iterator end;
for (;;) {
// search for line delimiter
end = std::search(
begin,
m_buf->end(),
header_delimiter,
header_delimiter+sizeof(header_delimiter)-1
);
m_header_bytes += (end-begin+sizeof(header_delimiter));
if (m_header_bytes > max_header_size) {
// exceeded max header size
throw exception("Maximum header size exceeded.",
status_code::request_header_fields_too_large);
}
if (end == m_buf->end()) {
// we are out of bytes. Discard the processed bytes and copy the
// remaining unprecessed bytes to the beginning of the buffer
std::copy(begin,end,m_buf->begin());
m_buf->resize(static_cast<std::string::size_type>(end-begin));
m_header_bytes -= m_buf->size();
return len;
}
//the range [begin,end) now represents a line to be processed.
if (end-begin == 0) {
// we got a blank line
if (m_method.empty() || get_header("Host").empty()) {
throw exception("Incomplete Request",status_code::bad_request);
}
bytes_processed = (
len - static_cast<std::string::size_type>(m_buf->end()-end)
+ sizeof(header_delimiter) - 1
);
// frees memory used temporarily during request parsing
m_buf.reset();
// if this was not an upgrade request and has a content length
// continue capturing content-length bytes and expose them as a
// request body.
if (prepare_body()) {
bytes_processed += process_body(buf+bytes_processed,len-bytes_processed);
if (body_ready()) {
m_ready = true;
}
return bytes_processed;
} else {
m_ready = true;
// return number of bytes processed (starting bytes - bytes left)
return bytes_processed;
}
} else {
if (m_method.empty()) {
this->process(begin,end);
} else {
this->process_header(begin,end);
}
}
begin = end+(sizeof(header_delimiter)-1);
}
}
inline std::string request::raw() const {
// TODO: validation. Make sure all required fields have been set?
std::stringstream ret;
ret << m_method << " " << m_uri << " " << get_version() << "\r\n";
ret << raw_headers() << "\r\n" << m_body;
return ret.str();
}
inline std::string request::raw_head() const {
// TODO: validation. Make sure all required fields have been set?
std::stringstream ret;
ret << m_method << " " << m_uri << " " << get_version() << "\r\n";
ret << raw_headers() << "\r\n";
return ret.str();
}
inline void request::set_method(std::string const & method) {
if (std::find_if(method.begin(),method.end(),is_not_token_char) != method.end()) {
throw exception("Invalid method token.",status_code::bad_request);
}
m_method = method;
}
inline void request::set_uri(std::string const & uri) {
// TODO: validation?
m_uri = uri;
}
inline void request::process(std::string::iterator begin, std::string::iterator
end)
{
std::string::iterator cursor_start = begin;
std::string::iterator cursor_end = std::find(begin,end,' ');
if (cursor_end == end) {
throw exception("Invalid request line1",status_code::bad_request);
}
set_method(std::string(cursor_start,cursor_end));
cursor_start = cursor_end+1;
cursor_end = std::find(cursor_start,end,' ');
if (cursor_end == end) {
throw exception("Invalid request line2",status_code::bad_request);
}
set_uri(std::string(cursor_start,cursor_end));
set_version(std::string(cursor_end+1,end));
}
} // namespace parser
} // namespace http
} // namespace websocketpp
#endif // HTTP_PARSER_REQUEST_IMPL_HPP

View File

@@ -0,0 +1,266 @@
/*
* Copyright (c) 2014, Peter Thorson. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the WebSocket++ Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef HTTP_PARSER_RESPONSE_IMPL_HPP
#define HTTP_PARSER_RESPONSE_IMPL_HPP
#include <algorithm>
#include <istream>
#include <sstream>
#include <string>
#include <websocketpp/http/parser.hpp>
namespace websocketpp {
namespace http {
namespace parser {
inline size_t response::consume(char const * buf, size_t len) {
if (m_state == DONE) {return 0;}
if (m_state == BODY) {
return this->process_body(buf,len);
}
// copy new header bytes into buffer
m_buf->append(buf,len);
// Search for delimiter in buf. If found read until then. If not read all
std::string::iterator begin = m_buf->begin();
std::string::iterator end = begin;
for (;;) {
// search for delimiter
end = std::search(
begin,
m_buf->end(),
header_delimiter,
header_delimiter + sizeof(header_delimiter) - 1
);
m_header_bytes += (end-begin+sizeof(header_delimiter));
if (m_header_bytes > max_header_size) {
// exceeded max header size
throw exception("Maximum header size exceeded.",
status_code::request_header_fields_too_large);
}
if (end == m_buf->end()) {
// we are out of bytes. Discard the processed bytes and copy the
// remaining unprecessed bytes to the beginning of the buffer
std::copy(begin,end,m_buf->begin());
m_buf->resize(static_cast<std::string::size_type>(end-begin));
m_read += len;
m_header_bytes -= m_buf->size();
return len;
}
//the range [begin,end) now represents a line to be processed.
if (end-begin == 0) {
// we got a blank line
if (m_state == RESPONSE_LINE) {
throw exception("Incomplete Request",status_code::bad_request);
}
// TODO: grab content-length
std::string length = get_header("Content-Length");
if (length.empty()) {
// no content length found, read indefinitely
m_read = 0;
} else {
std::istringstream ss(length);
if ((ss >> m_read).fail()) {
throw exception("Unable to parse Content-Length header",
status_code::bad_request);
}
}
m_state = BODY;
// calc header bytes processed (starting bytes - bytes left)
size_t read = (
len - static_cast<std::string::size_type>(m_buf->end() - end)
+ sizeof(header_delimiter) - 1
);
// if there were bytes left process them as body bytes
if (read < len) {
read += this->process_body(buf+read,(len-read));
}
// frees memory used temporarily during header parsing
m_buf.reset();
return read;
} else {
if (m_state == RESPONSE_LINE) {
this->process(begin,end);
m_state = HEADERS;
} else {
this->process_header(begin,end);
}
}
begin = end+(sizeof(header_delimiter) - 1);
}
}
inline size_t response::consume(std::istream & s) {
char buf[istream_buffer];
size_t bytes_read;
size_t bytes_processed;
size_t total = 0;
while (s.good()) {
s.getline(buf,istream_buffer);
bytes_read = static_cast<size_t>(s.gcount());
if (s.fail() || s.eof()) {
bytes_processed = this->consume(buf,bytes_read);
total += bytes_processed;
if (bytes_processed != bytes_read) {
// problem
break;
}
} else if (s.bad()) {
// problem
break;
} else {
// the delimiting newline was found. Replace the trailing null with
// the newline that was discarded, since our raw consume function
// expects the newline to be be there.
buf[bytes_read-1] = '\n';
bytes_processed = this->consume(buf,bytes_read);
total += bytes_processed;
if (bytes_processed != bytes_read) {
// problem
break;
}
}
}
return total;
}
inline std::string response::raw() const {
// TODO: validation. Make sure all required fields have been set?
std::stringstream ret;
ret << get_version() << " " << m_status_code << " " << m_status_msg;
ret << "\r\n" << raw_headers() << "\r\n";
ret << m_body;
return ret.str();
}
inline void response::set_status(status_code::value code) {
// TODO: validation?
m_status_code = code;
m_status_msg = get_string(code);
}
inline void response::set_status(status_code::value code, std::string const &
msg)
{
// TODO: validation?
m_status_code = code;
m_status_msg = msg;
}
inline void response::process(std::string::iterator begin,
std::string::iterator end)
{
std::string::iterator cursor_start = begin;
std::string::iterator cursor_end = std::find(begin,end,' ');
if (cursor_end == end) {
throw exception("Invalid response line",status_code::bad_request);
}
set_version(std::string(cursor_start,cursor_end));
cursor_start = cursor_end+1;
cursor_end = std::find(cursor_start,end,' ');
if (cursor_end == end) {
throw exception("Invalid request line",status_code::bad_request);
}
int code;
std::istringstream ss(std::string(cursor_start,cursor_end));
if ((ss >> code).fail()) {
throw exception("Unable to parse response code",status_code::bad_request);
}
set_status(status_code::value(code),std::string(cursor_end+1,end));
}
inline size_t response::process_body(char const * buf, size_t len) {
// If no content length was set then we read forever and never set m_ready
if (m_read == 0) {
//m_body.append(buf,len);
//return len;
m_state = DONE;
return 0;
}
// Otherwise m_read is the number of bytes left.
size_t to_read;
if (len >= m_read) {
// if we have more bytes than we need read, read only the amount needed
// then set done state
to_read = m_read;
m_state = DONE;
} else {
// we need more bytes than are available, read them all
to_read = len;
}
m_body.append(buf,to_read);
m_read -= to_read;
return to_read;
}
} // namespace parser
} // namespace http
} // namespace websocketpp
#endif // HTTP_PARSER_RESPONSE_IMPL_HPP

View File

@@ -0,0 +1,629 @@
/*
* Copyright (c) 2014, Peter Thorson. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the WebSocket++ Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef HTTP_PARSER_HPP
#define HTTP_PARSER_HPP
#include <algorithm>
#include <map>
#include <string>
#include <utility>
#include <websocketpp/utilities.hpp>
#include <websocketpp/http/constants.hpp>
namespace websocketpp {
namespace http {
namespace parser {
namespace state {
enum value {
method,
resource,
version,
headers
};
}
namespace body_encoding {
enum value {
unknown,
plain,
chunked
};
}
typedef std::map<std::string, std::string, utility::ci_less > header_list;
/// Read and return the next token in the stream
/**
* Read until a non-token character is found and then return the token and
* iterator to the next character to read
*
* @param begin An iterator to the beginning of the sequence
* @param end An iterator to the end of the sequence
* @return A pair containing the token and an iterator to the next character in
* the stream
*/
template <typename InputIterator>
std::pair<std::string,InputIterator> extract_token(InputIterator begin,
InputIterator end)
{
InputIterator it = std::find_if(begin,end,&is_not_token_char);
return std::make_pair(std::string(begin,it),it);
}
/// Read and return the next quoted string in the stream
/**
* Read a double quoted string starting at `begin`. The quotes themselves are
* stripped. The quoted value is returned along with an iterator to the next
* character to read
*
* @param begin An iterator to the beginning of the sequence
* @param end An iterator to the end of the sequence
* @return A pair containing the string read and an iterator to the next
* character in the stream
*/
template <typename InputIterator>
std::pair<std::string,InputIterator> extract_quoted_string(InputIterator begin,
InputIterator end)
{
std::string s;
if (end == begin) {
return std::make_pair(s,begin);
}
if (*begin != '"') {
return std::make_pair(s,begin);
}
InputIterator cursor = begin+1;
InputIterator marker = cursor;
cursor = std::find(cursor,end,'"');
while (cursor != end) {
// either this is the end or a quoted string
if (*(cursor-1) == '\\') {
s.append(marker,cursor-1);
s.append(1,'"');
++cursor;
marker = cursor;
} else {
s.append(marker,cursor);
++cursor;
return std::make_pair(s,cursor);
}
cursor = std::find(cursor,end,'"');
}
return std::make_pair("",begin);
}
/// Read and discard one unit of linear whitespace
/**
* Read one unit of linear white space and return the iterator to the character
* afterwards. If `begin` is returned, no whitespace was extracted.
*
* @param begin An iterator to the beginning of the sequence
* @param end An iterator to the end of the sequence
* @return An iterator to the character after the linear whitespace read
*/
template <typename InputIterator>
InputIterator extract_lws(InputIterator begin, InputIterator end) {
InputIterator it = begin;
// strip leading CRLF
if (end-begin > 2 && *begin == '\r' && *(begin+1) == '\n' &&
is_whitespace_char(static_cast<unsigned char>(*(begin+2))))
{
it+=3;
}
it = std::find_if(it,end,&is_not_whitespace_char);
return it;
}
/// Read and discard linear whitespace
/**
* Read linear white space until a non-lws character is read and return an
* iterator to that character. If `begin` is returned, no whitespace was
* extracted.
*
* @param begin An iterator to the beginning of the sequence
* @param end An iterator to the end of the sequence
* @return An iterator to the character after the linear whitespace read
*/
template <typename InputIterator>
InputIterator extract_all_lws(InputIterator begin, InputIterator end) {
InputIterator old_it;
InputIterator new_it = begin;
do {
// Pull value from previous iteration
old_it = new_it;
// look ahead another pass
new_it = extract_lws(old_it,end);
} while (new_it != end && old_it != new_it);
return new_it;
}
/// Extract HTTP attributes
/**
* An http attributes list is a semicolon delimited list of key value pairs in
* the format: *( ";" attribute "=" value ) where attribute is a token and value
* is a token or quoted string.
*
* Attributes extracted are appended to the supplied attributes list
* `attributes`.
*
* @param [in] begin An iterator to the beginning of the sequence
* @param [in] end An iterator to the end of the sequence
* @param [out] attributes A reference to the attributes list to append
* attribute/value pairs extracted to
* @return An iterator to the character after the last atribute read
*/
template <typename InputIterator>
InputIterator extract_attributes(InputIterator begin, InputIterator end,
attribute_list & attributes)
{
InputIterator cursor;
bool first = true;
if (begin == end) {
return begin;
}
cursor = begin;
std::pair<std::string,InputIterator> ret;
while (cursor != end) {
std::string name;
cursor = http::parser::extract_all_lws(cursor,end);
if (cursor == end) {
break;
}
if (first) {
// ignore this check for the very first pass
first = false;
} else {
if (*cursor == ';') {
// advance past the ';'
++cursor;
} else {
// non-semicolon in this position indicates end end of the
// attribute list, break and return.
break;
}
}
cursor = http::parser::extract_all_lws(cursor,end);
ret = http::parser::extract_token(cursor,end);
if (ret.first.empty()) {
// error: expected a token
return begin;
} else {
name = ret.first;
cursor = ret.second;
}
cursor = http::parser::extract_all_lws(cursor,end);
if (cursor == end || *cursor != '=') {
// if there is an equals sign, read the attribute value. Otherwise
// record a blank value and continue
attributes[name].clear();
continue;
}
// advance past the '='
++cursor;
cursor = http::parser::extract_all_lws(cursor,end);
if (cursor == end) {
// error: expected a token or quoted string
return begin;
}
ret = http::parser::extract_quoted_string(cursor,end);
if (ret.second != cursor) {
attributes[name] = ret.first;
cursor = ret.second;
continue;
}
ret = http::parser::extract_token(cursor,end);
if (ret.first.empty()) {
// error : expected token or quoted string
return begin;
} else {
attributes[name] = ret.first;
cursor = ret.second;
}
}
return cursor;
}
/// Extract HTTP parameters
/**
* An http parameters list is a comma delimited list of tokens followed by
* optional semicolon delimited attributes lists.
*
* Parameters extracted are appended to the supplied parameters list
* `parameters`.
*
* @param [in] begin An iterator to the beginning of the sequence
* @param [in] end An iterator to the end of the sequence
* @param [out] parameters A reference to the parameters list to append
* paramter values extracted to
* @return An iterator to the character after the last parameter read
*/
template <typename InputIterator>
InputIterator extract_parameters(InputIterator begin, InputIterator end,
parameter_list &parameters)
{
InputIterator cursor;
if (begin == end) {
// error: expected non-zero length range
return begin;
}
cursor = begin;
std::pair<std::string,InputIterator> ret;
/**
* LWS
* token
* LWS
* *(";" method-param)
* LWS
* ,=loop again
*/
while (cursor != end) {
std::string parameter_name;
attribute_list attributes;
// extract any stray whitespace
cursor = http::parser::extract_all_lws(cursor,end);
if (cursor == end) {break;}
ret = http::parser::extract_token(cursor,end);
if (ret.first.empty()) {
// error: expected a token
return begin;
} else {
parameter_name = ret.first;
cursor = ret.second;
}
// Safe break point, insert parameter with blank attributes and exit
cursor = http::parser::extract_all_lws(cursor,end);
if (cursor == end) {
//parameters[parameter_name] = attributes;
parameters.push_back(std::make_pair(parameter_name,attributes));
break;
}
// If there is an attribute list, read it in
if (*cursor == ';') {
InputIterator acursor;
++cursor;
acursor = http::parser::extract_attributes(cursor,end,attributes);
if (acursor == cursor) {
// attribute extraction ended in syntax error
return begin;
}
cursor = acursor;
}
// insert parameter into output list
//parameters[parameter_name] = attributes;
parameters.push_back(std::make_pair(parameter_name,attributes));
cursor = http::parser::extract_all_lws(cursor,end);
if (cursor == end) {break;}
// if next char is ',' then read another parameter, else stop
if (*cursor != ',') {
break;
}
// advance past comma
++cursor;
if (cursor == end) {
// expected more bytes after a comma
return begin;
}
}
return cursor;
}
inline std::string strip_lws(std::string const & input) {
std::string::const_iterator begin = extract_all_lws(input.begin(),input.end());
if (begin == input.end()) {
return std::string();
}
std::string::const_reverse_iterator rbegin = extract_all_lws(input.rbegin(),input.rend());
if (rbegin == input.rend()) {
return std::string();
}
return std::string(begin,rbegin.base());
}
/// Base HTTP parser
/**
* Includes methods and data elements common to all types of HTTP messages such
* as headers, versions, bodies, etc.
*/
class parser {
public:
parser()
: m_header_bytes(0)
, m_body_bytes_needed(0)
, m_body_bytes_max(max_body_size)
, m_body_encoding(body_encoding::unknown) {}
/// Get the HTTP version string
/**
* @return The version string for this parser
*/
std::string const & get_version() const {
return m_version;
}
/// Set HTTP parser Version
/**
* Input should be in format: HTTP/x.y where x and y are positive integers.
* @todo Does this method need any validation?
*
* @param [in] version The value to set the HTTP version to.
*/
void set_version(std::string const & version);
/// Get the value of an HTTP header
/**
* @todo Make this method case insensitive.
*
* @param [in] key The name/key of the header to get.
* @return The value associated with the given HTTP header key.
*/
std::string const & get_header(std::string const & key) const;
/// Extract an HTTP parameter list from a parser header.
/**
* If the header requested doesn't exist or exists and is empty the
* parameter list is valid (but empty).
*
* @param [in] key The name/key of the HTTP header to use as input.
* @param [out] out The parameter list to store extracted parameters in.
* @return Whether or not the input was a valid parameter list.
*/
bool get_header_as_plist(std::string const & key, parameter_list & out)
const;
/// Return a list of all HTTP headers
/**
* Return a list of all HTTP headers
*
* @since 0.8.0
*
* @return A list of all HTTP headers
*/
header_list const & get_headers() const;
/// Append a value to an existing HTTP header
/**
* This method will set the value of the HTTP header `key` with the
* indicated value. If a header with the name `key` already exists, `val`
* will be appended to the existing value.
*
* @todo Make this method case insensitive.
* @todo Should there be any restrictions on which keys are allowed?
* @todo Exception free varient
*
* @see replace_header
*
* @param [in] key The name/key of the header to append to.
* @param [in] val The value to append.
*/
void append_header(std::string const & key, std::string const & val);
/// Set a value for an HTTP header, replacing an existing value
/**
* This method will set the value of the HTTP header `key` with the
* indicated value. If a header with the name `key` already exists, `val`
* will replace the existing value.
*
* @todo Make this method case insensitive.
* @todo Should there be any restrictions on which keys are allowed?
* @todo Exception free varient
*
* @see append_header
*
* @param [in] key The name/key of the header to append to.
* @param [in] val The value to append.
*/
void replace_header(std::string const & key, std::string const & val);
/// Remove a header from the parser
/**
* Removes the header entirely from the parser. This is different than
* setting the value of the header to blank.
*
* @todo Make this method case insensitive.
*
* @param [in] key The name/key of the header to remove.
*/
void remove_header(std::string const & key);
/// Get HTTP body
/**
* Gets the body of the HTTP object
*
* @return The body of the HTTP message.
*/
std::string const & get_body() const {
return m_body;
}
/// Set body content
/**
* Set the body content of the HTTP response to the parameter string. Note
* set_body will also set the Content-Length HTTP header to the appropriate
* value. If you want the Content-Length header to be something else, do so
* via replace_header("Content-Length") after calling set_body()
*
* @param value String data to include as the body content.
*/
void set_body(std::string const & value);
/// Get body size limit
/**
* Retrieves the maximum number of bytes to parse & buffer before canceling
* a request.
*
* @since 0.5.0
*
* @return The maximum length of a message body.
*/
size_t get_max_body_size() const {
return m_body_bytes_max;
}
/// Set body size limit
/**
* Set the maximum number of bytes to parse and buffer before canceling a
* request.
*
* @since 0.5.0
*
* @param value The size to set the max body length to.
*/
void set_max_body_size(size_t value) {
m_body_bytes_max = value;
}
/// Extract an HTTP parameter list from a string.
/**
* @param [in] in The input string.
* @param [out] out The parameter list to store extracted parameters in.
* @return Whether or not the input was a valid parameter list.
*/
bool parse_parameter_list(std::string const & in, parameter_list & out)
const;
protected:
/// Process a header line
/**
* @todo Update this method to be exception free.
*
* @param [in] begin An iterator to the beginning of the sequence.
* @param [in] end An iterator to the end of the sequence.
*/
void process_header(std::string::iterator begin, std::string::iterator end);
/// Prepare the parser to begin parsing body data
/**
* Inspects headers to determine if the message has a body that needs to be
* read. If so, sets up the necessary state, otherwise returns false. If
* this method returns true and loading the message body is desired call
* `process_body` until it returns zero bytes or an error.
*
* Must not be called until after all headers have been processed.
*
* @since 0.5.0
*
* @return True if more bytes are needed to load the body, false otherwise.
*/
bool prepare_body();
/// Process body data
/**
* Parses body data.
*
* @since 0.5.0
*
* @param [in] begin An iterator to the beginning of the sequence.
* @param [in] end An iterator to the end of the sequence.
* @return The number of bytes processed
*/
size_t process_body(char const * buf, size_t len);
/// Check if the parser is done parsing the body
/**
* Behavior before a call to `prepare_body` is undefined.
*
* @since 0.5.0
*
* @return True if the message body has been completed loaded.
*/
bool body_ready() const {
return (m_body_bytes_needed == 0);
}
/// Generate and return the HTTP headers as a string
/**
* Each headers will be followed by the \r\n sequence including the last one.
* A second \r\n sequence (blank header) is not appended by this method
*
* @return The HTTP headers as a string.
*/
std::string raw_headers() const;
std::string m_version;
header_list m_headers;
size_t m_header_bytes;
std::string m_body;
size_t m_body_bytes_needed;
size_t m_body_bytes_max;
body_encoding::value m_body_encoding;
};
} // namespace parser
} // namespace http
} // namespace websocketpp
#include <websocketpp/http/impl/parser.hpp>
#endif // HTTP_PARSER_HPP

View File

@@ -0,0 +1,124 @@
/*
* Copyright (c) 2014, Peter Thorson. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the WebSocket++ Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef HTTP_PARSER_REQUEST_HPP
#define HTTP_PARSER_REQUEST_HPP
#include <string>
#include <websocketpp/common/memory.hpp>
#include <websocketpp/http/parser.hpp>
namespace websocketpp {
namespace http {
namespace parser {
/// Stores, parses, and manipulates HTTP requests
/**
* http::request provides the following functionality for working with HTTP
* requests.
*
* - Initialize request via manually setting each element
* - Initialize request via reading raw bytes and parsing
* - Once initialized, access individual parsed elements
* - Once initialized, read entire request as raw bytes
*/
class request : public parser {
public:
typedef request type;
typedef lib::shared_ptr<type> ptr;
request()
: m_buf(lib::make_shared<std::string>())
, m_ready(false) {}
/// Process bytes in the input buffer
/**
* Process up to len bytes from input buffer buf. Returns the number of
* bytes processed. Bytes left unprocessed means bytes left over after the
* final header delimiters.
*
* Consume is a streaming processor. It may be called multiple times on one
* request and the full headers need not be available before processing can
* begin. If the end of the request was reached during this call to consume
* the ready flag will be set. Further calls to consume once ready will be
* ignored.
*
* Consume will throw an http::exception in the case of an error. Typical
* error reasons include malformed requests, incomplete requests, and max
* header size being reached.
*
* @param buf Pointer to byte buffer
* @param len Size of byte buffer
* @return Number of bytes processed.
*/
size_t consume(char const * buf, size_t len);
/// Returns whether or not the request is ready for reading.
bool ready() const {
return m_ready;
}
/// Returns the full raw request (including the body)
std::string raw() const;
/// Returns the raw request headers only (similar to an HTTP HEAD request)
std::string raw_head() const;
/// Set the HTTP method. Must be a valid HTTP token
void set_method(std::string const & method);
/// Return the request method
std::string const & get_method() const {
return m_method;
}
/// Set the HTTP uri. Must be a valid HTTP uri
void set_uri(std::string const & uri);
/// Return the requested URI
std::string const & get_uri() const {
return m_uri;
}
private:
/// Helper function for message::consume. Process request line
void process(std::string::iterator begin, std::string::iterator end);
lib::shared_ptr<std::string> m_buf;
std::string m_method;
std::string m_uri;
bool m_ready;
};
} // namespace parser
} // namespace http
} // namespace websocketpp
#include <websocketpp/http/impl/request.hpp>
#endif // HTTP_PARSER_REQUEST_HPP

View File

@@ -0,0 +1,188 @@
/*
* Copyright (c) 2014, Peter Thorson. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the WebSocket++ Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef HTTP_PARSER_RESPONSE_HPP
#define HTTP_PARSER_RESPONSE_HPP
#include <iostream>
#include <string>
#include <websocketpp/http/parser.hpp>
namespace websocketpp {
namespace http {
namespace parser {
/// Stores, parses, and manipulates HTTP responses
/**
* http::response provides the following functionality for working with HTTP
* responses.
*
* - Initialize response via manually setting each element
* - Initialize response via reading raw bytes and parsing
* - Once initialized, access individual parsed elements
* - Once initialized, read entire response as raw bytes
*
* http::response checks for header completeness separately from the full
* response. Once the header is complete, the Content-Length header is read to
* determine when to stop reading body bytes. If no Content-Length is present
* ready() will never return true. It is the responsibility of the caller to
* consume to determine when the response is complete (ie when the connection
* terminates, or some other metric).
*/
class response : public parser {
public:
typedef response type;
typedef lib::shared_ptr<type> ptr;
response()
: m_read(0)
, m_buf(lib::make_shared<std::string>())
, m_status_code(status_code::uninitialized)
, m_state(RESPONSE_LINE) {}
/// Process bytes in the input buffer
/**
* Process up to len bytes from input buffer buf. Returns the number of
* bytes processed. Bytes left unprocessed means bytes left over after the
* final header delimiters.
*
* Consume is a streaming processor. It may be called multiple times on one
* response and the full headers need not be available before processing can
* begin. If the end of the response was reached during this call to consume
* the ready flag will be set. Further calls to consume once ready will be
* ignored.
*
* Consume will throw an http::exception in the case of an error. Typical
* error reasons include malformed responses, incomplete responses, and max
* header size being reached.
*
* @param buf Pointer to byte buffer
* @param len Size of byte buffer
* @return Number of bytes processed.
*/
size_t consume(char const * buf, size_t len);
/// Process bytes in the input buffer (istream version)
/**
* Process bytes from istream s. Returns the number of bytes processed.
* Bytes left unprocessed means bytes left over after the final header
* delimiters.
*
* Consume is a streaming processor. It may be called multiple times on one
* response and the full headers need not be available before processing can
* begin. If the end of the response was reached during this call to consume
* the ready flag will be set. Further calls to consume once ready will be
* ignored.
*
* Consume will throw an http::exception in the case of an error. Typical
* error reasons include malformed responses, incomplete responses, and max
* header size being reached.
*
* @param buf Pointer to byte buffer
* @param len Size of byte buffer
* @return Number of bytes processed.
*/
size_t consume(std::istream & s);
/// Returns true if the response is ready.
/**
* @note will never return true if the content length header is not present
*/
bool ready() const {
return m_state == DONE;
}
/// Returns true if the response headers are fully parsed.
bool headers_ready() const {
return (m_state == BODY || m_state == DONE);
}
/// Returns the full raw response
std::string raw() const;
/// Set response status code and message
/**
* Sets the response status code to `code` and looks up the corresponding
* message for standard codes. Non-standard codes will be entered as Unknown
* use set_status(status_code::value,std::string) overload to set both
* values explicitly.
*
* @param code Code to set
* @param msg Message to set
*/
void set_status(status_code::value code);
/// Set response status code and message
/**
* Sets the response status code and message to independent custom values.
* use set_status(status_code::value) to set the code and have the standard
* message be automatically set.
*
* @param code Code to set
* @param msg Message to set
*/
void set_status(status_code::value code, std::string const & msg);
/// Return the response status code
status_code::value get_status_code() const {
return m_status_code;
}
/// Return the response status message
const std::string& get_status_msg() const {
return m_status_msg;
}
private:
/// Helper function for consume. Process response line
void process(std::string::iterator begin, std::string::iterator end);
/// Helper function for processing body bytes
size_t process_body(char const * buf, size_t len);
enum state {
RESPONSE_LINE = 0,
HEADERS = 1,
BODY = 2,
DONE = 3
};
std::string m_status_msg;
size_t m_read;
lib::shared_ptr<std::string> m_buf;
status_code::value m_status_code;
state m_state;
};
} // namespace parser
} // namespace http
} // namespace websocketpp
#include <websocketpp/http/impl/response.hpp>
#endif // HTTP_PARSER_RESPONSE_HPP