2022-03-29 16:50:26 -04:00
|
|
|
/////////////////////////////////////////////////////////
|
|
|
|
|
//
|
|
|
|
|
// HTTP Implementation.
|
|
|
|
|
//
|
|
|
|
|
// This is a fairly limited implementation of HTTP.
|
|
|
|
|
//
|
|
|
|
|
// It only supports HTTP 1.1
|
|
|
|
|
//
|
|
|
|
|
// It only supports GET, POST, and HEAD.
|
|
|
|
|
//
|
|
|
|
|
/////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
#ifndef HTTP_HPP
|
|
|
|
|
#define HTTP_HPP
|
|
|
|
|
|
|
|
|
|
#include "eng-malloc.hpp"
|
|
|
|
|
#include "wrap-string.hpp"
|
|
|
|
|
#include "wrap-vector.hpp"
|
2022-04-15 17:24:07 -04:00
|
|
|
#include "wrap-map.hpp"
|
2022-03-29 16:50:26 -04:00
|
|
|
#include "luastack.hpp"
|
2022-04-15 17:24:07 -04:00
|
|
|
#include "streambuffer.hpp"
|
2022-05-06 13:16:27 -04:00
|
|
|
#include "drivenengine.hpp"
|
2022-05-12 13:48:42 -04:00
|
|
|
#include <ostream>
|
2022-03-29 16:50:26 -04:00
|
|
|
|
2022-04-25 13:43:11 -04:00
|
|
|
using UrlParameters = eng::map<eng::string, eng::string>;
|
|
|
|
|
|
2022-05-06 13:16:27 -04:00
|
|
|
class HttpClientRequest : public eng::nevernew {
|
2022-04-15 17:24:07 -04:00
|
|
|
private:
|
2022-05-03 00:36:11 -04:00
|
|
|
// Request IDs.
|
|
|
|
|
int64_t request_id_;
|
|
|
|
|
int64_t place_id_;
|
|
|
|
|
int64_t thread_id_;
|
|
|
|
|
|
2022-04-15 17:24:07 -04:00
|
|
|
// If the request contains an error, the error
|
|
|
|
|
// message is stored here.
|
|
|
|
|
eng::string error_;
|
2022-03-29 16:50:26 -04:00
|
|
|
|
2022-04-15 17:24:07 -04:00
|
|
|
// If true, verify the server's certificate.
|
|
|
|
|
// True is the default.
|
|
|
|
|
bool verify_certificate_;
|
|
|
|
|
|
2022-05-12 13:48:42 -04:00
|
|
|
// Method: GET, HEAD, or POST. Must be all-caps.
|
2022-04-15 17:24:07 -04:00
|
|
|
eng::string method_;
|
|
|
|
|
|
2022-05-03 00:36:11 -04:00
|
|
|
// The hostname. Not yet url-encoded.
|
2022-03-29 16:50:26 -04:00
|
|
|
eng::string host_;
|
2022-04-15 17:24:07 -04:00
|
|
|
|
|
|
|
|
// Port number.
|
2022-03-29 16:50:26 -04:00
|
|
|
int port_;
|
|
|
|
|
|
2022-05-03 00:36:11 -04:00
|
|
|
// The path. Not yet url-encoded. Can not include URL parameters.
|
2022-04-15 17:24:07 -04:00
|
|
|
eng::string path_;
|
|
|
|
|
|
2022-05-03 00:36:11 -04:00
|
|
|
// URL parameters to append to the path. Not yet url-encoded.
|
2022-04-25 13:43:11 -04:00
|
|
|
UrlParameters params_;
|
2022-05-03 00:36:11 -04:00
|
|
|
|
2022-05-12 13:48:42 -04:00
|
|
|
// The mime type of the content, only for POST requests.
|
|
|
|
|
eng::string mime_type_;
|
|
|
|
|
|
|
|
|
|
// The content as a string, only for POST requests.
|
|
|
|
|
eng::string content_;
|
|
|
|
|
|
2022-04-15 17:24:07 -04:00
|
|
|
private:
|
2022-04-25 13:43:11 -04:00
|
|
|
void fail(std::string_view error);
|
|
|
|
|
void send_internal(StreamBuffer *target, bool debug_string) const;
|
2022-05-03 00:36:11 -04:00
|
|
|
|
2022-03-29 16:50:26 -04:00
|
|
|
public:
|
2022-04-15 17:24:07 -04:00
|
|
|
// Construct an empty HTTP request.
|
|
|
|
|
// All of the fields have empty values.
|
2022-05-06 13:16:27 -04:00
|
|
|
HttpClientRequest();
|
2022-04-15 17:24:07 -04:00
|
|
|
|
2022-05-03 00:36:11 -04:00
|
|
|
// Get request-related fields.
|
2022-05-06 13:16:27 -04:00
|
|
|
//
|
2022-04-15 17:24:07 -04:00
|
|
|
const eng::string &error() const { return error_; }
|
|
|
|
|
bool verify_certificate() const { return verify_certificate_; }
|
|
|
|
|
const eng::string &method() const { return method_; }
|
|
|
|
|
const eng::string &host() const { return host_; }
|
|
|
|
|
int port() const { return port_; }
|
|
|
|
|
const eng::string &path() const { return path_; }
|
2022-05-12 13:48:42 -04:00
|
|
|
const eng::string &mime_type() const { return mime_type_; }
|
|
|
|
|
const eng::string &content() const { return content_; }
|
2022-04-15 17:24:07 -04:00
|
|
|
|
2022-05-06 13:16:27 -04:00
|
|
|
// Populate an request-related fields one piece at a time.
|
2022-04-15 17:24:07 -04:00
|
|
|
// If you pass an invalid value, or if the field is
|
|
|
|
|
// already set, the routine will generate an error message
|
|
|
|
|
// and store it in the error field. In that case, the set
|
|
|
|
|
// will not happen. If there's already an error in the error
|
|
|
|
|
// field, it will not be overwritten.
|
2022-05-06 13:16:27 -04:00
|
|
|
//
|
2022-04-15 17:24:07 -04:00
|
|
|
void set_verify_certificate(bool flag);
|
|
|
|
|
void set_method(const eng::string &method);
|
|
|
|
|
void set_host(const eng::string &host);
|
|
|
|
|
void set_port(int port);
|
2022-04-25 13:43:11 -04:00
|
|
|
void set_path(std::string_view path);
|
2022-04-15 17:24:07 -04:00
|
|
|
void set_param(const eng::string &key, const eng::string &value);
|
2022-04-25 13:43:11 -04:00
|
|
|
void set_url(std::string_view url);
|
2022-05-12 13:48:42 -04:00
|
|
|
void set_mime_type(const eng::string &mime_type);
|
|
|
|
|
void set_content(const eng::string &content);
|
2022-04-25 13:43:11 -04:00
|
|
|
|
2022-04-15 17:24:07 -04:00
|
|
|
void set_verify_certificate(LuaStack &LS, LuaSlot val);
|
|
|
|
|
void set_method(LuaStack &LS, LuaSlot val);
|
|
|
|
|
void set_host(LuaStack &LS, LuaSlot val);
|
|
|
|
|
void set_port(LuaStack &LS, LuaSlot val);
|
2022-04-25 13:43:11 -04:00
|
|
|
void set_path(LuaStack &LS, LuaSlot path);
|
2022-04-15 17:24:07 -04:00
|
|
|
void set_param(LuaStack &LS, LuaSlot key, LuaSlot val);
|
|
|
|
|
void set_params(LuaStack &LS, LuaSlot tab);
|
2022-04-25 13:43:11 -04:00
|
|
|
void set_url(LuaStack &LS, LuaSlot val);
|
2022-05-12 13:48:42 -04:00
|
|
|
void set_mime_type(LuaStack &LS, LuaSlot val);
|
|
|
|
|
void set_content(LuaStack &LS, LuaSlot val);
|
2022-04-15 17:24:07 -04:00
|
|
|
|
2022-05-06 13:16:27 -04:00
|
|
|
// Set default values for method and port.
|
|
|
|
|
// This must be done after setting regular values.
|
2022-04-15 17:24:07 -04:00
|
|
|
void set_defaults();
|
|
|
|
|
|
2022-05-06 13:16:27 -04:00
|
|
|
// Populate request-related fields from a Lua table.
|
|
|
|
|
void set_config(LuaStack &LS0, LuaSlot tab);
|
|
|
|
|
|
|
|
|
|
// Get or Set the request IDs.
|
|
|
|
|
// This class does just stores these.
|
|
|
|
|
int64_t request_id() const { return request_id_; }
|
|
|
|
|
int64_t place_id() const { return place_id_; }
|
|
|
|
|
int64_t thread_id() const { return thread_id_; }
|
|
|
|
|
void set_request_id(int64_t request_id) { request_id_ = request_id; }
|
|
|
|
|
void set_place_id(int64_t place_id) { place_id_ = place_id; }
|
|
|
|
|
void set_thread_id(int64_t thread_id) { thread_id_ = thread_id; }
|
|
|
|
|
|
|
|
|
|
// Get the network target, eg, "cert:host:port"
|
|
|
|
|
eng::string target() const;
|
|
|
|
|
|
2022-04-15 17:24:07 -04:00
|
|
|
// Verify that the request is error free and that
|
|
|
|
|
// defaults have been set.
|
|
|
|
|
eng::string check() const;
|
|
|
|
|
|
|
|
|
|
// Put the request into the stream, assuming HTTP/1.1
|
|
|
|
|
void send(StreamBuffer *target) const { send_internal(target, false); }
|
|
|
|
|
|
2022-05-03 00:36:11 -04:00
|
|
|
// Serialize and deserialize.
|
|
|
|
|
void serialize(StreamBuffer *sb) const;
|
|
|
|
|
void deserialize(StreamBuffer *sb);
|
|
|
|
|
|
2022-04-15 17:24:07 -04:00
|
|
|
// Get the request as a debug string.
|
|
|
|
|
eng::string DebugString();
|
|
|
|
|
};
|
|
|
|
|
|
2022-05-12 13:48:42 -04:00
|
|
|
// HttpParser is used for parsing both requests and responses.
|
|
|
|
|
// Stores a status code, an error message, headers, and content.
|
|
|
|
|
//
|
|
|
|
|
class HttpParser : public eng::nevernew {
|
|
|
|
|
protected:
|
|
|
|
|
// True if this is for parsing a request.
|
|
|
|
|
// This is only used for generating error messages.
|
|
|
|
|
bool is_request_;
|
|
|
|
|
|
|
|
|
|
// The status code, a 3-digit number.
|
|
|
|
|
int status_;
|
|
|
|
|
|
|
|
|
|
// If the communication contains an error, the error can
|
|
|
|
|
// be stored here. In the event of a successful communication,
|
|
|
|
|
// this should always be empty string.
|
2022-04-15 17:24:07 -04:00
|
|
|
eng::string error_;
|
|
|
|
|
|
2022-04-25 13:43:11 -04:00
|
|
|
// Only if content-length header present, otherwise, -1.
|
|
|
|
|
int64_t content_length_;
|
|
|
|
|
|
|
|
|
|
// If empty, it means there was no transfer-encoding header.
|
|
|
|
|
eng::string transfer_encoding_;
|
|
|
|
|
|
|
|
|
|
// If empty, it means there was no content-encoding header.
|
|
|
|
|
eng::string content_encoding_;
|
|
|
|
|
|
|
|
|
|
// Only if location header present.
|
|
|
|
|
eng::string location_;
|
|
|
|
|
|
2022-04-15 17:24:07 -04:00
|
|
|
// MIME type of the content.
|
2022-03-29 16:50:26 -04:00
|
|
|
eng::string mime_type_;
|
2022-04-15 17:24:07 -04:00
|
|
|
|
2022-05-12 13:48:42 -04:00
|
|
|
// Charset of the content before the content was translated to utf-8.
|
2022-04-25 13:43:11 -04:00
|
|
|
eng::string charset_;
|
|
|
|
|
|
2022-05-12 13:48:42 -04:00
|
|
|
// The method, GET, HEAD, or POST. (only when parsing requests)
|
|
|
|
|
eng::string method_;
|
|
|
|
|
|
|
|
|
|
// The URL path, not url-encoded. (only when parsing requests)
|
|
|
|
|
eng::string path_;
|
|
|
|
|
|
|
|
|
|
// The URL parameters, not url-encoded (only when parsing requests)
|
|
|
|
|
UrlParameters params_;
|
|
|
|
|
|
|
|
|
|
// The content as string. If it's text, it's been translated to utf-8.
|
2022-03-29 16:50:26 -04:00
|
|
|
eng::string content_;
|
2022-04-15 17:24:07 -04:00
|
|
|
|
2022-05-12 13:48:42 -04:00
|
|
|
// The length in bytes of the entire communication.
|
|
|
|
|
// If the response is complete, but the comm_length_
|
|
|
|
|
// is zero, it means we couldn't find the end of
|
|
|
|
|
// the request.
|
|
|
|
|
int comm_length_;
|
|
|
|
|
|
|
|
|
|
protected:
|
2022-04-25 13:43:11 -04:00
|
|
|
// Store a message indicating that we haven't received enough
|
|
|
|
|
// bytes yet. If the connection is closed and we still haven't
|
|
|
|
|
// received enough bytes, that's a fatal error.
|
2022-05-12 13:48:42 -04:00
|
|
|
//
|
2022-04-25 13:43:11 -04:00
|
|
|
void incomplete(bool closed);
|
|
|
|
|
|
2022-05-12 13:48:42 -04:00
|
|
|
// Store a message indicating that we couldn't parse because
|
|
|
|
|
// something was syntactically malformed, ie, not valid HTTP.
|
|
|
|
|
//
|
|
|
|
|
void syntax(std::string_view detail);
|
|
|
|
|
|
|
|
|
|
// Store a message indicating that the content was too large
|
|
|
|
|
// and we refused to download it.
|
|
|
|
|
//
|
|
|
|
|
void oversized();
|
|
|
|
|
|
|
|
|
|
// If the status is not in the range 200-299 (OK),
|
|
|
|
|
// then discard the content.
|
|
|
|
|
void clear_content_on_error();
|
|
|
|
|
|
|
|
|
|
// Parse a response status line, such as "HTTP/1.1 200 OK"
|
|
|
|
|
// returns false if we couldn't parse the whole line.
|
|
|
|
|
//
|
|
|
|
|
bool parse_status_line(std::string_view &view, bool closed);
|
|
|
|
|
|
|
|
|
|
// Parse a request line, such as "GET /index.html HTTP/1.1"
|
|
|
|
|
// returns false if we couldn't parse the whole line.
|
|
|
|
|
//
|
|
|
|
|
bool parse_request_line(std::string_view &view, bool closed);
|
2022-04-25 13:43:11 -04:00
|
|
|
|
|
|
|
|
// Parse specific headers.
|
2022-05-12 13:48:42 -04:00
|
|
|
//
|
2022-04-25 13:43:11 -04:00
|
|
|
void parse_content_encoding(std::string_view value);
|
|
|
|
|
void parse_content_length(std::string_view value);
|
|
|
|
|
void parse_content_type(std::string_view value);
|
|
|
|
|
void parse_location(std::string_view value);
|
|
|
|
|
void parse_transfer_encoding(std::string_view value);
|
|
|
|
|
|
2022-05-12 13:48:42 -04:00
|
|
|
// Parse a single response header. Most headers are ignored.
|
|
|
|
|
// If the header contains an error, the error is stored.
|
|
|
|
|
//
|
|
|
|
|
void parse_header(std::string_view header, std::string_view value);
|
|
|
|
|
|
|
|
|
|
// Parse all of the headers, including the blank line after
|
|
|
|
|
// the headers. Return true if the view is at the end of the headers.
|
|
|
|
|
//
|
|
|
|
|
bool parse_headers(std::string_view &view, bool closed);
|
|
|
|
|
|
|
|
|
|
// parse the content, for different transfer encodings.
|
|
|
|
|
// Returns true if the view is at the end of the content.
|
|
|
|
|
//
|
2022-04-25 13:43:11 -04:00
|
|
|
bool parse_content_basic(std::string_view &view, bool closed);
|
|
|
|
|
bool parse_content_chunked(std::string_view &view, bool closed);
|
2022-05-12 13:48:42 -04:00
|
|
|
|
|
|
|
|
// Parse the content. Return true if the view is at the end of the
|
|
|
|
|
// content.
|
|
|
|
|
//
|
|
|
|
|
// - Uses the appropriate transfer-encoding specified in the headers.
|
|
|
|
|
// - Decompresses the content using the appropriate content-encoding.
|
|
|
|
|
// - Guesses a mimetype and charset if one was not specified in headers.
|
|
|
|
|
// - If it's text, converts the charset to utf-8.
|
|
|
|
|
//
|
|
|
|
|
bool parse_content(std::string_view &view, bool closed);
|
|
|
|
|
|
|
|
|
|
// Get or set the communication length.
|
|
|
|
|
//
|
|
|
|
|
int comm_length() const { return comm_length_; }
|
|
|
|
|
void set_comm_length(int len) { comm_length_ = len; }
|
|
|
|
|
|
|
|
|
|
// Construct a blank parser.
|
|
|
|
|
//
|
|
|
|
|
HttpParser();
|
|
|
|
|
|
|
|
|
|
// Store the parsed fields into a lua table.
|
|
|
|
|
//
|
|
|
|
|
void store_parsed(LuaStack &LS, LuaSlot tab) const;
|
|
|
|
|
|
|
|
|
|
// Emit all the parser fields as a debug string.
|
|
|
|
|
//
|
|
|
|
|
void parser_generate_debug_string(std::ostream &oss) const;
|
|
|
|
|
|
2022-04-15 17:24:07 -04:00
|
|
|
public:
|
2022-05-12 13:48:42 -04:00
|
|
|
// The parser will not try to parse content longer than this.
|
|
|
|
|
//
|
2022-04-25 13:43:11 -04:00
|
|
|
const int64_t MAX_CONTENT_LENGTH = 1000000;
|
|
|
|
|
|
2022-05-12 13:48:42 -04:00
|
|
|
// Store a status code and an error message, and clear the content.
|
2022-04-25 13:43:11 -04:00
|
|
|
// This is generally used when the client detects an error,
|
2022-04-15 17:24:07 -04:00
|
|
|
// such as a DNS lookup fail, a connection failed, an SSL negotiation
|
2022-04-25 13:43:11 -04:00
|
|
|
// failed, or the like.
|
2022-05-12 13:48:42 -04:00
|
|
|
//
|
|
|
|
|
void fail(int status, std::string_view error);
|
|
|
|
|
|
|
|
|
|
// Return true if the communication was complete.
|
|
|
|
|
//
|
|
|
|
|
bool complete() const { return status_ != 0; }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class HttpClientResponse : public HttpParser {
|
|
|
|
|
private:
|
|
|
|
|
// Most of the data is stored in the HttpParser.
|
|
|
|
|
// That includes the status code, error, headers, and content.
|
|
|
|
|
|
|
|
|
|
// The request ID.
|
|
|
|
|
int64_t request_id_;
|
|
|
|
|
|
|
|
|
|
// The length in bytes of the entire communication.
|
|
|
|
|
// If the response is complete, but the comm_length_
|
|
|
|
|
// is zero, it means we couldn't find the end of
|
|
|
|
|
// the request.
|
|
|
|
|
//
|
|
|
|
|
int comm_length_;
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
// Construct a blank response.
|
|
|
|
|
HttpClientResponse();
|
2022-04-25 13:43:11 -04:00
|
|
|
|
|
|
|
|
// Parse the HTTP response. The closed flag is to be set to true if the
|
|
|
|
|
// remote has closed the connection.
|
|
|
|
|
//
|
2022-05-06 13:16:27 -04:00
|
|
|
// If the request is incomplete, generates a status code of zero. In that
|
2022-04-25 13:43:11 -04:00
|
|
|
// case, loading more data from the server might improve the situation.
|
|
|
|
|
//
|
2022-05-12 13:48:42 -04:00
|
|
|
// If successful, indicates how much of the text was consumed by
|
|
|
|
|
// setting comm_length_.
|
2022-04-25 13:43:11 -04:00
|
|
|
//
|
2022-05-12 13:48:42 -04:00
|
|
|
void parse(std::string_view text, bool closed);
|
2022-05-06 13:16:27 -04:00
|
|
|
|
|
|
|
|
// Get or Set the request ID.
|
|
|
|
|
// This class does nothing with the request ID, it just stores it.
|
2022-05-12 13:48:42 -04:00
|
|
|
//
|
2022-05-06 13:16:27 -04:00
|
|
|
int64_t request_id() const { return request_id_; }
|
|
|
|
|
void set_request_id(int64_t v) { request_id_ = v; }
|
|
|
|
|
|
2022-04-15 17:24:07 -04:00
|
|
|
// Convert the HTTP response to a lua table.
|
2022-05-12 13:48:42 -04:00
|
|
|
//
|
2022-05-03 00:36:11 -04:00
|
|
|
void store(LuaStack &LS, LuaSlot tab) const;
|
2022-04-25 13:43:11 -04:00
|
|
|
|
|
|
|
|
// Convert to a debug string.
|
2022-05-12 13:48:42 -04:00
|
|
|
//
|
2022-04-25 13:43:11 -04:00
|
|
|
eng::string DebugString() const;
|
2022-05-03 00:36:11 -04:00
|
|
|
|
|
|
|
|
// Synthesize an error response and store it in lua.
|
2022-05-12 13:48:42 -04:00
|
|
|
//
|
|
|
|
|
static void store_fail(LuaStack &LS, LuaSlot tab, int status, std::string_view error);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class HttpServerRequest : public HttpParser {
|
|
|
|
|
private:
|
|
|
|
|
// The length in bytes of the entire communication.
|
|
|
|
|
// If the request is complete, but the comm_length_
|
|
|
|
|
// is zero, it means we couldn't find the end of
|
|
|
|
|
// the request.
|
|
|
|
|
//
|
|
|
|
|
int comm_length_;
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
// Construct a blank request.
|
|
|
|
|
//
|
|
|
|
|
HttpServerRequest();
|
|
|
|
|
|
|
|
|
|
// Parse the HTTP request. The closed flag is to be set to true if the
|
|
|
|
|
// remote has closed the connection.
|
|
|
|
|
//
|
|
|
|
|
// If the request is incomplete, generates a status code of zero. In that
|
|
|
|
|
// case, loading more data from the server might improve the situation.
|
|
|
|
|
//
|
|
|
|
|
// If successful, indicates how much of the text was consumed by
|
|
|
|
|
// setting comm_length.
|
|
|
|
|
//
|
|
|
|
|
void parse(std::string_view text, bool closed);
|
|
|
|
|
|
|
|
|
|
// Convert the HTTP request to a lua table.
|
|
|
|
|
//
|
|
|
|
|
void store(LuaStack &LS, LuaSlot tab) const;
|
|
|
|
|
|
|
|
|
|
// Convert to a debug string.
|
|
|
|
|
//
|
|
|
|
|
eng::string DebugString() const;
|
2022-03-29 16:50:26 -04:00
|
|
|
};
|
|
|
|
|
|
2022-05-06 13:16:27 -04:00
|
|
|
class HttpClientRequestMap : public eng::map<int64_t, HttpClientRequest> {
|
|
|
|
|
public:
|
|
|
|
|
void serialize(StreamBuffer *sb) const;
|
|
|
|
|
void deserialize(StreamBuffer *sb);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
using HttpClientResponseVec = eng::vector<HttpClientResponse>;
|
|
|
|
|
|
|
|
|
|
// This class associates an HTTP request to an actual communication
|
|
|
|
|
// channel that is executing that request.
|
|
|
|
|
|
|
|
|
|
class HttpClientChannel {
|
|
|
|
|
public:
|
|
|
|
|
SharedChannel channel_;
|
|
|
|
|
int64_t parsed_bytes_;
|
|
|
|
|
|
|
|
|
|
HttpClientChannel() : parsed_bytes_(0) {}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
using HttpClientChannelMap = eng::map<int64_t, HttpClientChannel>;
|
|
|
|
|
|
2022-03-29 16:50:26 -04:00
|
|
|
#endif // HTTP_HPP
|