added iOS source code
[wl-app.git] / iOS / Pods / Realm / include / core / realm / util / http.hpp
diff --git a/iOS/Pods/Realm/include/core/realm/util/http.hpp b/iOS/Pods/Realm/include/core/realm/util/http.hpp
new file mode 100644 (file)
index 0000000..6ab6591
--- /dev/null
@@ -0,0 +1,511 @@
+/*************************************************************************
+ *
+ * REALM CONFIDENTIAL
+ * __________________
+ *
+ *  [2011] - [2016] Realm Inc
+ *  All Rights Reserved.
+ *
+ * NOTICE:  All information contained herein is, and remains
+ * the property of Realm Incorporated and its suppliers,
+ * if any.  The intellectual and technical concepts contained
+ * herein are proprietary to Realm Incorporated
+ * and its suppliers and may be covered by U.S. and Foreign Patents,
+ * patents in process, and are protected by trade secret or copyright law.
+ * Dissemination of this information or reproduction of this material
+ * is strictly forbidden unless prior written permission is obtained
+ * from Realm Incorporated.
+ *
+ **************************************************************************/
+
+#ifndef REALM_UTIL_HTTP_HPP
+#define REALM_UTIL_HTTP_HPP
+
+#include <map>
+#include <system_error>
+#include <iosfwd>
+#include <locale>
+
+#include <realm/util/optional.hpp>
+#include <realm/util/network.hpp>
+#include <realm/string_data.hpp>
+
+namespace realm {
+namespace util {
+enum class HTTPParserError {
+    None = 0,
+    ContentTooLong,
+    HeaderLineTooLong,
+    MalformedResponse,
+    MalformedRequest,
+};
+std::error_code make_error_code(HTTPParserError);
+} // namespace util
+} // namespace realm
+
+namespace std {
+    template<>
+    struct is_error_code_enum<realm::util::HTTPParserError>: std::true_type {};
+}
+
+namespace realm {
+namespace util {
+
+/// See: https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
+///
+/// It is guaranteed that the backing integer value of this enum corresponds
+/// to the numerical code representing the status.
+enum class HTTPStatus {
+    Unknown = 0,
+
+    Continue = 100,
+    SwitchingProtocols = 101,
+
+    Ok = 200,
+    Created = 201,
+    Accepted = 202,
+    NonAuthoritative = 203,
+    NoContent = 204,
+    ResetContent = 205,
+    PartialContent = 206,
+
+    MultipleChoices = 300,
+    MovedPermanently = 301,
+    Found = 302,
+    SeeOther = 303,
+    NotModified = 304,
+    UseProxy = 305,
+    SwitchProxy = 306,
+    TemporaryRedirect = 307,
+    PermanentRedirect = 308,
+
+    BadRequest = 400,
+    Unauthorized = 401,
+    PaymentRequired = 402,
+    Forbidden = 403,
+    NotFound = 404,
+    MethodNotAllowed = 405,
+    NotAcceptable = 406,
+    ProxyAuthenticationRequired = 407,
+    RequestTimeout = 408,
+    Conflict = 409,
+    Gone = 410,
+    LengthRequired = 411,
+    PreconditionFailed = 412,
+    PayloadTooLarge = 413,
+    UriTooLong = 414,
+    UnsupportedMediaType = 415,
+    RangeNotSatisfiable = 416,
+    ExpectationFailed = 417,
+    ImATeapot = 418,
+    MisdirectedRequest = 421,
+    UpgradeRequired = 426,
+    PreconditionRequired = 428,
+    TooManyRequests = 429,
+    RequestHeaderFieldsTooLarge = 431,
+    UnavailableForLegalReasons = 451,
+
+    InternalServerError = 500,
+    NotImplemented = 501,
+    BadGateway = 502,
+    ServiceUnavailable = 503,
+    GatewayTimeout = 504,
+    HttpVersionNotSupported = 505,
+    VariantAlsoNegotiates = 506,
+    NotExtended = 510,
+    NetworkAuthenticationRequired = 511,
+};
+
+/// See: https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html
+enum class HTTPMethod {
+    Options,
+    Get,
+    Head,
+    Post,
+    Put,
+    Delete,
+    Trace,
+    Connect,
+};
+
+struct HTTPAuthorization {
+    std::string scheme;
+    std::map<std::string, std::string> values;
+};
+
+HTTPAuthorization parse_authorization(const std::string&);
+
+struct CaseInsensitiveCompare {
+    bool operator()(const std::string& a, const std::string& b) const
+    {
+        auto cmp = [](char lhs, char rhs) {
+            return std::tolower(lhs, std::locale::classic()) <
+                   std::tolower(rhs, std::locale::classic());
+        };
+        return std::lexicographical_compare(begin(a), end(a), begin(b), end(b), cmp);
+    }
+};
+
+/// Case-insensitive map suitable for storing HTTP headers.
+using HTTPHeaders = std::map<std::string, std::string, CaseInsensitiveCompare>;
+
+struct HTTPRequest {
+    HTTPMethod method = HTTPMethod::Get;
+    HTTPHeaders headers;
+    std::string path;
+
+    /// If the request object has a body, the Content-Length header MUST be
+    /// set to a string representation of the number of bytes in the body.
+    /// FIXME: Relax this restriction, and also support Transfer-Encoding
+    /// and other HTTP/1.1 features.
+    Optional<std::string> body;
+};
+
+struct HTTPResponse {
+    HTTPStatus status = HTTPStatus::Unknown;
+    HTTPHeaders headers;
+
+    // A body is only read from the response stream if the server sent the
+    // Content-Length header.
+    // FIXME: Support other transfer methods, including Transfer-Encoding and
+    // HTTP/1.1 features.
+    Optional<std::string> body;
+};
+
+
+/// Serialize HTTP request to output stream.
+std::ostream& operator<<(std::ostream&, const HTTPRequest&);
+/// Serialize HTTP response to output stream.
+std::ostream& operator<<(std::ostream&, const HTTPResponse&);
+/// Serialize HTTP method to output stream ("GET", "POST", etc.).
+std::ostream& operator<<(std::ostream&, HTTPMethod);
+/// Serialize HTTP status to output stream, include reason string ("200 OK" etc.)
+std::ostream& operator<<(std::ostream&, HTTPStatus);
+
+struct HTTPParserBase {
+    // FIXME: Generally useful?
+    struct CallocDeleter {
+        void operator()(void* ptr)
+        {
+            std::free(ptr);
+        }
+    };
+
+    HTTPParserBase()
+    {
+        // Allocating read buffer with calloc to avoid accidentally spilling
+        // data from other sessions in case of a buffer overflow exploit.
+        m_read_buffer.reset(static_cast<char*>(std::calloc(read_buffer_size, 1)));
+    }
+    virtual ~HTTPParserBase() {}
+
+    std::string m_write_buffer;
+    std::unique_ptr<char[], CallocDeleter> m_read_buffer;
+    Optional<size_t> m_found_content_length;
+    static const size_t read_buffer_size = 8192;
+    static const size_t max_header_line_length = read_buffer_size;
+
+    /// Parses the contents of m_read_buffer as a HTTP header line,
+    /// and calls on_header() as appropriate. on_header() will be called at
+    /// most once per invocation.
+    /// Returns false if the contents of m_read_buffer is not a valid HTTP
+    /// header line.
+    bool parse_header_line(size_t len);
+
+    virtual std::error_code on_first_line(StringData line) = 0;
+    virtual void on_header(StringData key, StringData value) = 0;
+    virtual void on_body(StringData body) = 0;
+    virtual void on_complete(std::error_code = std::error_code{}) = 0;
+
+    /// If the input matches a known HTTP method string, return the appropriate
+    /// HTTPMethod enum value. Otherwise, returns none.
+    static Optional<HTTPMethod> parse_method_string(StringData method);
+
+    /// Interpret line as the first line of an HTTP request. If the return value
+    /// is true, out_method and out_uri have been assigned the appropriate
+    /// values found in the request line.
+    static bool parse_first_line_of_request(StringData line, HTTPMethod& out_method,
+                                            StringData& out_uri);
+
+    /// Interpret line as the first line of an HTTP response. If the return
+    /// value is true, out_status and out_reason have been assigned the
+    /// appropriate values found in the response line.
+    static bool parse_first_line_of_response(StringData line, HTTPStatus& out_status,
+                                             StringData& out_reason);
+
+    void set_write_buffer(const HTTPRequest&);
+    void set_write_buffer(const HTTPResponse&);
+};
+
+template<class Socket>
+struct HTTPParser: protected HTTPParserBase {
+    explicit HTTPParser(Socket& socket) : m_socket(socket)
+    {}
+
+    void read_first_line()
+    {
+        auto handler = [this](std::error_code ec, size_t n) {
+            if (ec == error::operation_aborted) {
+                return;
+            }
+            if (ec) {
+                on_complete(ec);
+                return;
+            }
+            ec = on_first_line(StringData(m_read_buffer.get(), n));
+            if (ec) {
+                on_complete(ec);
+                return;
+            }
+            read_headers();
+        };
+        m_socket.async_read_until(m_read_buffer.get(), max_header_line_length, '\n',
+                                  std::move(handler));
+    }
+
+    void read_headers()
+    {
+        auto handler = [this](std::error_code ec, size_t n) {
+            if (ec == error::operation_aborted) {
+                return;
+            }
+            if (ec) {
+                on_complete(ec);
+                return;
+            }
+            if (n <= 2) {
+                read_body();
+                return;
+            }
+            parse_header_line(n);
+            // FIXME: Limit the total size of headers. Apache uses 8K.
+            read_headers();
+        };
+        m_socket.async_read_until(m_read_buffer.get(), max_header_line_length, '\n',
+                                  std::move(handler));
+    }
+
+    void read_body()
+    {
+        if (m_found_content_length) {
+            // FIXME: Support longer bodies.
+            // FIXME: Support multipart and other body types (no body shaming).
+            if (*m_found_content_length > read_buffer_size) {
+                on_complete(HTTPParserError::ContentTooLong);
+                return;
+            }
+
+            auto handler = [this](std::error_code ec, size_t n) {
+                if (ec == error::operation_aborted) {
+                    return;
+                }
+                if (!ec) {
+                    on_body(StringData(m_read_buffer.get(), n));
+                }
+                on_complete(ec);
+            };
+            m_socket.async_read(m_read_buffer.get(), *m_found_content_length,
+                                std::move(handler));
+        }
+        else {
+            // No body, just finish.
+            on_complete();
+        }
+    }
+
+    void write_buffer(std::function<void(std::error_code, size_t)> handler)
+    {
+        m_socket.async_write(m_write_buffer.data(), m_write_buffer.size(),
+                             std::move(handler));
+    }
+
+    Socket& m_socket;
+};
+
+template<class Socket>
+struct HTTPClient: protected HTTPParser<Socket> {
+    using Handler = void(HTTPResponse, std::error_code);
+
+    explicit HTTPClient(Socket& socket) : HTTPParser<Socket>(socket) {}
+
+    /// Serialize and send \a request over the connected socket asynchronously.
+    ///
+    /// When the response has been received, or an error occurs, \a handler will
+    /// be invoked with the appropriate parameters. The HTTPResponse object
+    /// passed to \a handler will only be complete in non-error conditions, but
+    /// may be partially populated.
+    ///
+    /// It is an error to start a request before the \a handler of a previous
+    /// request has been invoked. It is permitted to call async_request() from
+    /// the handler, unless an error has been reported representing a condition
+    /// where the underlying socket is no longer able to communicate (for
+    /// example, if it has been closed).
+    ///
+    /// If a request is already in progress, an exception will be thrown.
+    ///
+    /// This method is *NOT* thread-safe.
+    void async_request(const HTTPRequest& request, std::function<Handler> handler)
+    {
+        if (REALM_UNLIKELY(m_handler)) {
+            throw std::runtime_error("Request already in progress.");
+        }
+        this->set_write_buffer(request);
+        m_handler = std::move(handler);
+        this->write_buffer([this](std::error_code ec, size_t bytes_written) {
+            static_cast<void>(bytes_written);
+            if (ec == error::operation_aborted) {
+                return;
+            }
+            if (ec) {
+                this->on_complete(ec);
+                return;
+            }
+            this->read_first_line();
+        });
+    }
+
+private:
+    std::function<Handler> m_handler;
+    HTTPResponse m_response;
+
+    std::error_code on_first_line(StringData line) override final
+    {
+        HTTPStatus status;
+        StringData reason;
+        if (this->parse_first_line_of_response(line, status, reason)) {
+            m_response.status = status;
+            static_cast<void>(reason); // Ignore for now.
+            return std::error_code{};
+        }
+        return HTTPParserError::MalformedResponse;
+    }
+
+    void on_header(StringData key, StringData value) override final
+    {
+        // FIXME: Multiple headers with the same key should show up as a
+        // comma-separated list of their values, rather than overwriting.
+        m_response.headers[std::string(key)] = std::string(value);
+    }
+
+    void on_body(StringData body) override final
+    {
+        m_response.body = std::string(body);
+    }
+
+    void on_complete(std::error_code ec) override final
+    {
+        auto handler = std::move(m_handler); // Nullifies m_handler
+        handler(std::move(m_response), ec);
+    }
+};
+
+template<class Socket>
+struct HTTPServer: protected HTTPParser<Socket> {
+    using RequestHandler = void(HTTPRequest, std::error_code);
+    using RespondHandler = void(std::error_code);
+
+    explicit HTTPServer(Socket& socket): HTTPParser<Socket>(socket)
+    {}
+
+    /// Receive a request on the underlying socket asynchronously.
+    ///
+    /// This function starts an asynchronous read operation and keeps reading
+    /// until an HTTP request has been received. \a handler is invoked when a
+    /// request has been received, or an error occurs.
+    ///
+    /// After a request is received, callers MUST invoke async_send_response()
+    /// to provide the client with a valid HTTP response, unless the error
+    /// passed to the handler represents a condition where the underlying socket
+    /// is no longer able to communicate (for example, if it has been closed).
+    ///
+    /// It is an error to attempt to receive a request before any previous
+    /// requests have been fully responded to, i.e. the \a handler argument of
+    /// async_send_response() must have been invoked before attempting to
+    /// receive the next request.
+    ///
+    /// This function is *NOT* thread-safe.
+    void async_receive_request(std::function<RequestHandler> handler)
+    {
+        if (REALM_UNLIKELY(m_request_handler)) {
+            throw std::runtime_error("Response already in progress.");
+        }
+        m_request_handler = std::move(handler);
+        this->read_first_line();
+    }
+
+    /// Send an HTTP response to a client asynchronously.
+    ///
+    /// This function starts an asynchronous write operation on the underlying
+    /// socket. \a handler is invoked when the response has been written to the
+    /// socket, or an error occurs.
+    ///
+    /// It is an error to call async_receive_request() again before \a handler
+    /// has been invoked, and it is an error to call async_send_response()
+    /// before the \a handler of a previous invocation has been invoked.
+    ///
+    /// This function is *NOT* thread-safe.
+    void async_send_response(const HTTPResponse& response,
+                             std::function<RespondHandler> handler)
+    {
+        if (REALM_UNLIKELY(!m_request_handler)) {
+            throw std::runtime_error("No request in progress.");
+        }
+        if (m_respond_handler) {
+            // FIXME: Proper exception type.
+            throw std::runtime_error("Already responding to request");
+        }
+        m_respond_handler = std::move(handler);
+        this->set_write_buffer(response);
+        this->write_buffer([this](std::error_code ec, size_t) {
+            if (ec == error::operation_aborted) {
+                return;
+            }
+            m_request_handler = nullptr;
+            auto handler = std::move(m_respond_handler);
+            handler(ec);
+        });;
+    }
+
+private:
+    std::function<RequestHandler> m_request_handler;
+    std::function<RespondHandler> m_respond_handler;
+    HTTPRequest m_request;
+
+    std::error_code on_first_line(StringData line) override final
+    {
+        HTTPMethod method;
+        StringData uri;
+        if (this->parse_first_line_of_request(line, method, uri)) {
+            m_request.method = method;
+            m_request.path = uri;
+            return std::error_code{};
+        }
+        return HTTPParserError::MalformedRequest;
+    }
+
+    void on_header(StringData key, StringData value) override final
+    {
+        // FIXME: Multiple headers with the same key should show up as a
+        // comma-separated list of their values, rather than overwriting.
+        m_request.headers[std::string(key)] = std::string(value);
+    }
+
+    void on_body(StringData body) override final
+    {
+        m_request.body = std::string(body);
+    }
+
+    void on_complete(std::error_code ec) override final
+    {
+        // Deliberately not nullifying m_request_handler so that we can
+        // check for invariants in async_send_response.
+        m_request_handler(std::move(m_request), ec);
+    }
+};
+
+} // namespace util
+} // namespace realm
+
+
+#endif // REALM_UTIL_HTTP_HPP
+