/** * * @file HttpRequest.h * @author An Tao * * Copyright 2018, An Tao. All rights reserved. * https://github.com/an-tao/drogon * Use of this source code is governed by a MIT license * that can be found in the License file. * * Drogon * */ #pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace drogon { class HttpRequest; using HttpRequestPtr = std::shared_ptr; /** * @brief This template is used to convert a request object to a custom * type object. Users must specialize the template for a particular type. */ template T fromRequest(const HttpRequest &) { LOG_ERROR << "You must specialize the fromRequest template for the type of " << DrClassMap::demangle(typeid(T).name()); exit(1); } /** * @brief This template is used to create a request object from a custom * type object by calling the newCustomHttpRequest(). Users must specialize * the template for a particular type. */ template HttpRequestPtr toRequest(T &&) { LOG_ERROR << "You must specialize the toRequest template for the type of " << DrClassMap::demangle(typeid(T).name()); exit(1); } template <> HttpRequestPtr toRequest(const Json::Value &pJson); template <> HttpRequestPtr toRequest(Json::Value &&pJson); template <> inline HttpRequestPtr toRequest(Json::Value &pJson) { return toRequest((const Json::Value &)pJson); } template <> std::shared_ptr fromRequest(const HttpRequest &req); /// Abstract class for webapp developer to get or set the Http request; class DROGON_EXPORT HttpRequest { public: /** * @brief This template enables implicit type conversion. For using this * template, user must specialize the fromRequest template. For example a * shared_ptr specialization version is available above, so * we can use the following code to get a json object: * @code std::shared_ptr jsonPtr = *requestPtr; @endcode * With this template, user can use their favorite JSON library instead of * the default jsoncpp library or convert the request to an object of any * custom type. */ template operator T() const { return fromRequest(*this); } /** * @brief This template enables explicit type conversion, see the above * template. */ template T as() const { return fromRequest(*this); } /// Return the method string of the request, such as GET, POST, etc. virtual const char *methodString() const = 0; const char *getMethodString() const { return methodString(); } /// Return the enum type method of the request. virtual HttpMethod method() const = 0; HttpMethod getMethod() const { return method(); } /// Get the header string identified by the key parameter. /** * @note * If there is no the header, a empty string is retured. * The key is case insensitive */ virtual const std::string &getHeader(std::string key) const = 0; /** * @brief Set the header string identified by the field parameter * * @param field The field parameter is transformed to lower case before * storing. * @param value The value of the header. */ virtual void addHeader(std::string field, const std::string &value) = 0; virtual void addHeader(std::string field, std::string &&value) = 0; /** * @brief Remove the header identified by the key parameter. * * @param key The key is case insensitive */ virtual void removeHeader(std::string key) = 0; /// Get the cookie string identified by the field parameter virtual const std::string &getCookie(const std::string &field) const = 0; /// Get all headers of the request virtual const std:: unordered_map &headers() const = 0; /// Get all headers of the request const std:: unordered_map &getHeaders() const { return headers(); } /// Get all cookies of the request virtual const std:: unordered_map &cookies() const = 0; /// Get all cookies of the request const std:: unordered_map &getCookies() const { return cookies(); } /// Get the query string of the request. /** * The query string is the substring after the '?' in the URL string. */ virtual const std::string &query() const = 0; /// Get the query string of the request. const std::string &getQuery() const { return query(); } /// Get the content string of the request, which is the body part of the /// request. string_view body() const { return string_view(bodyData(), bodyLength()); } /// Get the content string of the request, which is the body part of the /// request. string_view getBody() const { return body(); } virtual const char *bodyData() const = 0; virtual size_t bodyLength() const = 0; /// Set the content string of the request. virtual void setBody(const std::string &body) = 0; /// Set the content string of the request. virtual void setBody(std::string &&body) = 0; /// Get the path of the request. virtual const std::string &path() const = 0; /// Get the path of the request. const std::string &getPath() const { return path(); } /// Get the matched path pattern after routing string_view getMatchedPathPattern() const { return matchedPathPattern(); } /// Get the matched path pattern after routing string_view matchedPathPattern() const { return string_view(matchedPathPatternData(), matchedPathPatternLength()); } virtual const char *matchedPathPatternData() const = 0; virtual size_t matchedPathPatternLength() const = 0; /// Return the string of http version of request, such as HTTP/1.0, /// HTTP/1.1, etc. virtual const char *versionString() const = 0; const char *getVersionString() const { return versionString(); } /// Return the enum type version of the request. /** * kHttp10 means Http version is 1.0 * kHttp11 means Http verison is 1.1 */ virtual Version version() const = 0; /// Return the enum type version of the request. Version getVersion() const { return version(); } /// Get the session to which the request belongs. virtual const SessionPtr &session() const = 0; /// Get the session to which the request belongs. const SessionPtr &getSession() const { return session(); } /// Get the attributes store, users can add/get any type of data to/from /// this store virtual const AttributesPtr &attributes() const = 0; /// Get the attributes store, users can add/get any type of data to/from /// this store const AttributesPtr &getAttributes() const { return attributes(); } /// Get parameters of the request. virtual const std:: unordered_map ¶meters() const = 0; /// Get parameters of the request. const std:: unordered_map &getParameters() const { return parameters(); } /// Get a parameter identified by the @param key virtual const std::string &getParameter(const std::string &key) const = 0; /** * @brief Get the optional parameter identified by the @param key. if the * parameter doesn't exist, or the original parameter can't be converted to * a T type object, an empty optional object is returned. * * @tparam T * @param key * @return optional */ template optional getOptionalParameter(const std::string &key) { auto ¶ms = getParameters(); auto it = params.find(key); if (it != params.end()) { try { return optional(drogon::utils::fromString(it->second)); } catch (const std::exception &e) { LOG_ERROR << e.what(); return optional{}; } } else { return optional{}; } } /// Return the remote IP address and port virtual const trantor::InetAddress &peerAddr() const = 0; const trantor::InetAddress &getPeerAddr() const { return peerAddr(); } /// Return the local IP address and port virtual const trantor::InetAddress &localAddr() const = 0; const trantor::InetAddress &getLocalAddr() const { return localAddr(); } /// Return the creation timestamp set by the framework. virtual const trantor::Date &creationDate() const = 0; const trantor::Date &getCreationDate() const { return creationDate(); } /// Get the Json object of the request /** * The content type of the request must be 'application/json', * otherwise the method returns an empty shared_ptr object. */ virtual const std::shared_ptr &jsonObject() const = 0; /// Get the Json object of the request const std::shared_ptr &getJsonObject() const { return jsonObject(); } /** * @brief Get the error message of parsing the JSON body received from peer. * This method usually is called after getting a empty shared_ptr object * by the getJsonObject() method. * * @return const std::string& The error message. An empty string is returned * when no error occurs. */ virtual const std::string &getJsonError() const = 0; /// Get the content type virtual ContentType contentType() const = 0; ContentType getContentType() const { return contentType(); } /// Set the Http method virtual void setMethod(const HttpMethod method) = 0; /// Set the path of the request virtual void setPath(const std::string &path) = 0; /** * @brief The default behavior is to encode the value of setPath * using urlEncode. Setting the path encode to false avoid the * value of path will be changed by the library * * @param bool true --> the path will be url encoded * false --> using value of path as it is set */ virtual void setPathEncode(bool) = 0; /// Set the parameter of the request virtual void setParameter(const std::string &key, const std::string &value) = 0; /// Set or get the content type virtual void setContentTypeCode(const ContentType type) = 0; /// Set the content-type string, The string may contain the header name and /// CRLF. Or just the MIME type // /// For example, "content-type: text/plain\r\n" or "text/plain" void setContentTypeString(const string_view &typeString) { setContentTypeString(typeString.data(), typeString.size()); } /// Set the request content-type string, The string /// must contain the header name and CRLF. /// For example, "content-type: text/plain\r\n" virtual void setCustomContentTypeString(const std::string &type) = 0; /// Add a cookie virtual void addCookie(const std::string &key, const std::string &value) = 0; /** * @brief Set the request object to the pass-through mode or not. It's not * by default when a new request object is created. * In pass-through mode, no addtional headers (including user-agent, * connection, etc.) are added to the request. This mode is useful for some * applications such as a proxy. * * @param flag */ virtual void setPassThrough(bool flag) = 0; /// The following methods are a series of factory methods that help users /// create request objects. /// Create a normal request with http method Get and version Http1.1. static HttpRequestPtr newHttpRequest(); /// Create a http request with: /// Method: Get /// Version: Http1.1 /// Content type: application/json, the @param data is serialized into the /// content of the request. static HttpRequestPtr newHttpJsonRequest(const Json::Value &data); /// Create a http request with: /// Method: Post /// Version: Http1.1 /// Content type: application/x-www-form-urlencoded static HttpRequestPtr newHttpFormPostRequest(); /// Create a http file upload request with: /// Method: Post /// Version: Http1.1 /// Content type: multipart/form-data /// The @param files represents pload files which are transferred to the /// server via the multipart/form-data format static HttpRequestPtr newFileUploadRequest( const std::vector &files); /** * @brief Create a custom HTTP request object. For using this template, * users must specialize the toRequest template. */ template static HttpRequestPtr newCustomHttpRequest(T &&obj) { return toRequest(std::forward(obj)); } virtual bool isOnSecureConnection() const noexcept = 0; virtual void setContentTypeString(const char *typeString, size_t typeStringLength) = 0; virtual ~HttpRequest() { } }; template <> inline HttpRequestPtr toRequest(const Json::Value &pJson) { return HttpRequest::newHttpJsonRequest(pJson); } template <> inline HttpRequestPtr toRequest(Json::Value &&pJson) { return HttpRequest::newHttpJsonRequest(std::move(pJson)); } template <> inline std::shared_ptr fromRequest(const HttpRequest &req) { return req.getJsonObject(); } } // namespace drogon