///////////////////////////////////////////////////////// // // 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" #include "wrap-map.hpp" #include "luastack.hpp" #include "streambuffer.hpp" using UrlParameters = eng::map; class HttpOutRequest : public eng::nevernew { private: // Request IDs. int64_t request_id_; int64_t place_id_; int64_t thread_id_; // If the request contains an error, the error // message is stored here. eng::string error_; // If true, verify the server's certificate. // True is the default. bool verify_certificate_; // Method: GET, HEAD, POST, etc. Must be all-caps. eng::string method_; // The hostname. Not yet url-encoded. eng::string host_; // Port number. int port_; // The path. Not yet url-encoded. Can not include URL parameters. eng::string path_; // URL parameters to append to the path. Not yet url-encoded. UrlParameters params_; private: void fail(std::string_view error); void send_internal(StreamBuffer *target, bool debug_string) const; public: // Construct an empty HTTP request. // All of the fields have empty values. HttpOutRequest(); // Get the request IDs. int64_t request_id() const { return request_id_; } int64_t place_id() const { return place_id_; } int64_t thread_id() const { return thread_id_; } // Get request-related fields. 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_; } // Get the network target, eg, "cert:host:port" eng::string target() const; // Set the request IDs. 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; } // Populate an HTTP request a piece at a time. // 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. 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); void set_path(std::string_view path); void set_param(const eng::string &key, const eng::string &value); void set_url(std::string_view url); // Same as above, but using Lua values. 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); void set_path(LuaStack &LS, LuaSlot path); void set_param(LuaStack &LS, LuaSlot key, LuaSlot val); void set_params(LuaStack &LS, LuaSlot tab); void set_url(LuaStack &LS, LuaSlot val); // Set default values for any fields that should have // defaults. This must be done after setting regular // values. void set_defaults(); // Verify that the request is error free and that // defaults have been set. eng::string check() const; // Populate an HTTP request from a Lua table. void set_config(LuaStack &LS0, LuaSlot tab); // Put the request into the stream, assuming HTTP/1.1 void send(StreamBuffer *target) const { send_internal(target, false); } // Serialize and deserialize. void serialize(StreamBuffer *sb) const; void deserialize(StreamBuffer *sb); // Get the request as a debug string. eng::string DebugString(); }; class HttpOutRequestMap : public eng::map { public: void serialize(StreamBuffer *sb) const; void deserialize(StreamBuffer *sb); }; class HttpInResponse { private: // The HTTP response status code. int status_code_; // If the HTTP response contains an error, the // error message is stored here. If the HTTP response // is a success such as "200 OK" or "201 Created", this // is the empty string, not "OK" or "Created". eng::string error_; // 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_; // MIME type of the content. eng::string mime_type_; // Charset of the content. Hopefully utf-8. eng::string charset_; // The content as string. eng::string content_; // The length in bytes of the entire response. // May be zero, which means that the response // was so garbled that we couldn't determine the length. int response_length_; private: // 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. void incomplete(bool closed); // Parse a 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 specific headers. // For several headers, all we do is verify that they aren't // invoking unsupported features. 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); // parse the body bool parse_content_basic(std::string_view &view, bool closed); bool parse_content_chunked(std::string_view &view, bool closed); public: const int64_t MAX_CONTENT_LENGTH = 1000000; // Construct a blank response. HttpInResponse(); // Store a result code and an error message, and clear the content. // This is generally used when the client detects an error, // such as a DNS lookup fail, a connection failed, an SSL negotiation // failed, or the like. void fail(int status_code, std::string_view error); // Parse the HTTP response. The closed flag is to be set to true if the // remote has closed the connection. // // If the request is incomplete, generates a 600 incomplete error. In that // case, loading more data from the server might improve the situation. // // Note that the response is not ever removed from the StreamBuffer, which // is always unmodified. If you want to remove the response from the // StreamBuffer, see response_length. // void parse(const StreamBuffer *sb, bool closed); // Convert the HTTP response to a lua table. void store(LuaStack &LS, LuaSlot tab) const; // Convert to a debug string. eng::string DebugString() const; // Synthesize an error response and store it in lua. static void store_fail(LuaStack &LS, LuaSlot tab, int status_code, std::string_view error); }; #endif // HTTP_HPP