From 7c8432add07d02ca7043c122c8d9d7c1cc5e7f49 Mon Sep 17 00:00:00 2001 From: antao Date: Tue, 22 Jan 2019 23:00:14 +0800 Subject: [PATCH] Support http request in json format --- drogon_ctl/templates/filter_cc.csp | 2 +- examples/simple_example/api_Attachment.cc | 2 +- examples/simple_example/api_v1_ApiTest.cc | 16 +++ examples/simple_example/api_v1_ApiTest.h | 2 + examples/simple_example_test/main.cc | 36 ++++- lib/inc/drogon/HttpRequest.h | 19 +-- lib/inc/drogon/HttpResponse.h | 84 +----------- lib/inc/drogon/HttpTypes.h | 106 ++++++++++++++ lib/src/FileUpload.cc | 1 + lib/src/HttpAppFrameworkImpl.cc | 11 +- lib/src/HttpClientImpl.cc | 9 +- lib/src/HttpControllersRouter.cc | 2 +- lib/src/HttpRequestImpl.cc | 126 +++++++++++++++-- lib/src/HttpRequestImpl.h | 110 +++++++-------- lib/src/HttpRequestParser.cc | 5 +- lib/src/HttpResponseImpl.cc | 146 +------------------- lib/src/HttpResponseImpl.h | 17 +-- lib/src/HttpResponseParser.cc | 6 +- lib/src/HttpServer.cc | 2 +- lib/src/HttpSimpleControllersRouter.cc | 2 +- lib/src/HttpUtils.cc | 160 ++++++++++++++++++++++ lib/src/HttpUtils.h | 25 ++++ lib/src/HttpViewBase.cc | 2 +- lib/src/WebsocketControllersRouter.cc | 2 +- 24 files changed, 562 insertions(+), 331 deletions(-) create mode 100644 lib/inc/drogon/HttpTypes.h create mode 100644 lib/src/HttpUtils.cc create mode 100644 lib/src/HttpUtils.h diff --git a/drogon_ctl/templates/filter_cc.csp b/drogon_ctl/templates/filter_cc.csp index 2762ea35..a0863a00 100644 --- a/drogon_ctl/templates/filter_cc.csp +++ b/drogon_ctl/templates/filter_cc.csp @@ -22,6 +22,6 @@ void {{className}}::doFilter(const HttpRequestPtr &req, } //Check failed auto res = drogon::HttpResponse::newHttpResponse(); - res->setStatusCode(HttpResponse::k500InternalServerError); + res->setStatusCode(k500InternalServerError); fcb(res); } diff --git a/examples/simple_example/api_Attachment.cc b/examples/simple_example/api_Attachment.cc index 805e4574..12b6f8a6 100644 --- a/examples/simple_example/api_Attachment.cc +++ b/examples/simple_example/api_Attachment.cc @@ -32,6 +32,6 @@ void Attachment::upload(const HttpRequestPtr &req, file.saveAs("../xxx"); } auto resp = HttpResponse::newHttpResponse(); - resp->setStatusCode(HttpResponse::k200OK); + resp->setStatusCode(k200OK); callback(resp); } diff --git a/examples/simple_example/api_v1_ApiTest.cc b/examples/simple_example/api_v1_ApiTest.cc index 8c204da6..b0d568f1 100755 --- a/examples/simple_example/api_v1_ApiTest.cc +++ b/examples/simple_example/api_v1_ApiTest.cc @@ -350,3 +350,19 @@ void ApiTest::get2(const HttpRequestPtr &req, const std::functionsetExpiredTime(0); callback(res); } + +void ApiTest::jsonTest(const HttpRequestPtr &req, const std::function &callback) +{ + auto json = req->getJsonObject(); + Json::Value ret; + if(json) + { + ret["result"] = "ok"; + } + else + { + ret["result"] = "bad"; + } + auto resp = HttpResponse::newHttpJsonResponse(ret); + callback(resp); +} diff --git a/examples/simple_example/api_v1_ApiTest.h b/examples/simple_example/api_v1_ApiTest.h index 3f5ed7c4..1ea82711 100755 --- a/examples/simple_example/api_v1_ApiTest.h +++ b/examples/simple_example/api_v1_ApiTest.h @@ -16,6 +16,7 @@ class ApiTest : public drogon::HttpController METHOD_ADD(ApiTest::your_method_name, "/{1}/List?P2={2}", Get); //path will be /api/v1/apitest/{arg1}/list METHOD_ADD(ApiTest::staticApi, "/static", Get, Post); METHOD_ADD(ApiTest::get2, "/get/{1}", Get); + METHOD_ADD(ApiTest::jsonTest, "/json", Post); METHOD_LIST_END //your declaration of processing function maybe like this: void get(const HttpRequestPtr &req, const std::function &callback, int p1, std::string &&p2); @@ -24,6 +25,7 @@ class ApiTest : public drogon::HttpController void get2(const HttpRequestPtr &req, const std::function &callback, std::string &&p1); void rootGet(const HttpRequestPtr &req, const std::function &callback); void rootPost(const HttpRequestPtr &req, const std::function &callback); + void jsonTest(const HttpRequestPtr &req, const std::function &callback); }; } // namespace v1 } // namespace api diff --git a/examples/simple_example_test/main.cc b/examples/simple_example_test/main.cc index a5152c0f..d01ffdce 100644 --- a/examples/simple_example_test/main.cc +++ b/examples/simple_example_test/main.cc @@ -35,8 +35,35 @@ void outputGood(const HttpRequestPtr &req) } void doTest(const HttpClientPtr &client) { + /// Post json + Json::Value json; + json["request"] = "json"; + auto req = HttpRequest::newHttpJsonRequest(json); + req->setMethod(drogon::Post); + req->setPath("/api/v1/apitest/json"); + client->sendRequest(req, [=](ReqResult result, const HttpResponsePtr &resp) { + if (result == ReqResult::Ok) + { + auto ret = resp->getJsonObject(); + if (ret && (*ret)["result"].asString() == "ok") + { + outputGood(req); + } + else + { + LOG_DEBUG << resp->getBody(); + LOG_ERROR << "Error!"; + exit(1); + } + } + else + { + LOG_ERROR << "Error!"; + exit(1); + } + }); /// 1 Get / - auto req = HttpRequest::newHttpRequest(); + req = HttpRequest::newHttpRequest(); req->setMethod(drogon::Get); req->setPath("/"); client->sendRequest(req, [=](ReqResult result, const HttpResponsePtr &resp) { @@ -91,7 +118,7 @@ void doTest(const HttpClientPtr &client) if (result == ReqResult::Ok) { //LOG_DEBUG << resp->getBody(); - if (resp->statusCode() == HttpResponse::k405MethodNotAllowed) + if (resp->statusCode() == k405MethodNotAllowed) { outputGood(req); } @@ -444,7 +471,7 @@ void doTest(const HttpClientPtr &client) client->sendRequest(req, [=](ReqResult result, const HttpResponsePtr &resp) { if (result == ReqResult::Ok) { - if (resp->statusCode() == HttpResponse::k403Forbidden) + if (resp->statusCode() == k403Forbidden) { outputGood(req); } @@ -461,6 +488,7 @@ void doTest(const HttpClientPtr &client) exit(1); } }); + } int main() @@ -468,7 +496,7 @@ int main() trantor::EventLoopThread loop[2]; loop[0].run(); loop[1].run(); - // for (int i = 0; i < 100;i++) + // for (int i = 0; i < 100;i++) { auto client = HttpClient::newHttpClient("http://127.0.0.1:8848", loop[0].getLoop()); doTest(client); diff --git a/lib/inc/drogon/HttpRequest.h b/lib/inc/drogon/HttpRequest.h index f281649c..985991e3 100755 --- a/lib/inc/drogon/HttpRequest.h +++ b/lib/inc/drogon/HttpRequest.h @@ -14,6 +14,7 @@ #pragma once +#include #include #include #include @@ -21,19 +22,13 @@ #include #include #include + namespace drogon { + class HttpRequest; typedef std::shared_ptr HttpRequestPtr; -enum HttpMethod -{ - Get = 0, - Post, - Head, - Put, - Delete, - Invalid -}; + /// Abstract class for webapp developer to get or set the Http request; class HttpRequest @@ -111,8 +106,14 @@ class HttpRequest /// 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(ContentType type) = 0; + virtual void setContentTypeCodeAndCharacterSet(ContentType type, const std::string &charSet = "utf-8") = 0; + virtual ContentType getContentTypeCode() = 0; + /// Create a request object. static HttpRequestPtr newHttpRequest(); + static HttpRequestPtr newHttpJsonRequest(const Json::Value &data); virtual ~HttpRequest() {} }; diff --git a/lib/inc/drogon/HttpResponse.h b/lib/inc/drogon/HttpResponse.h index be2d0896..afd4c36d 100755 --- a/lib/inc/drogon/HttpResponse.h +++ b/lib/inc/drogon/HttpResponse.h @@ -15,33 +15,11 @@ #include #include +#include #include #include #include -using std::string; -#define CT_APPLICATION_JSON 1 -#define CT_TEXT_PLAIN 2 -#define CT_TEXT_HTML 3 -#define CT_APPLICATION_X_JAVASCRIPT 4 -#define CT_TEXT_CSS 5 -#define CT_TEXT_XML 6 -#define CT_APPLICATION_XML 7 -#define CT_TEXT_XSL 8 -#define CT_APPLICATION_OCTET_STREAM 9 -#define CT_APPLICATION_X_FONT_TRUETYPE 10 -#define CT_APPLICATION_X_FONT_OPENTYPE 11 -#define CT_APPLICATION_FONT_WOFF 12 -#define CT_APPLICATION_FONT_WOFF2 13 -#define CT_APPLICATION_VND_MS_FONTOBJ 14 -#define CT_IMAGE_SVG_XML 15 -#define CT_IMAGE_PNG 16 -#define CT_IMAGE_JPG 17 -#define CT_IMAGE_GIF 18 -#define CT_IMAGE_XICON 19 -#define CT_IMAGE_ICNS 20 -#define CT_IMAGE_BMP 21 - namespace drogon { /// Abstract class for webapp developer to get or set the Http response; @@ -50,58 +28,6 @@ typedef std::shared_ptr HttpResponsePtr; class HttpResponse { public: - enum HttpStatusCode - { - //rfc2616-6.1.1 - kUnknown = 0, - k100Continue = 100, - k101SwitchingProtocols = 101, - k200OK = 200, - k201Created = 201, - k202Accepted = 202, - k203NonAuthoritativeInformation = 203, - k204NoContent = 204, - k205ResetContent = 205, - k206PartialContent = 206, - k300MultipleChoices = 300, - k301MovedPermanently = 301, - k302Found = 302, - k303SeeOther = 303, - k304NotModified = 304, - k305UseProxy = 305, - k307TemporaryRedirect = 307, - k400BadRequest = 400, - k401Unauthorized = 401, - k402PaymentRequired = 402, - k403Forbidden = 403, - k404NotFound = 404, - k405MethodNotAllowed = 405, - k406NotAcceptable = 406, - k407ProxyAuthenticationRequired = 407, - k408RequestTimeout = 408, - k409Conflict = 409, - k410Gone = 410, - k411LengthRequired = 411, - k412PreconditionFailed = 412, - k413RequestEntityTooLarge = 413, - k414RequestURITooLarge = 414, - k415UnsupportedMediaType = 415, - k416Requestedrangenotsatisfiable = 416, - k417ExpectationFailed = 417, - k500InternalServerError = 500, - k501NotImplemented = 501, - k502BadGateway = 502, - k503ServiceUnavailable = 503, - k504GatewayTimeout = 504, - k505HTTPVersionnotsupported = 505, - }; - - enum Version - { - kHttp10, - kHttp11 - }; - explicit HttpResponse() { } @@ -109,7 +35,7 @@ class HttpResponse virtual HttpStatusCode statusCode() = 0; virtual const trantor::Date &createDate() const = 0; - + virtual void setStatusCode(HttpStatusCode code) = 0; virtual void setStatusCode(HttpStatusCode code, const std::string &status_message) = 0; @@ -120,11 +46,11 @@ class HttpResponse virtual bool closeConnection() const = 0; - virtual void setContentTypeCode(uint8_t type) = 0; + virtual void setContentTypeCode(ContentType type) = 0; - virtual void setContentTypeCodeAndCharacterSet(uint8_t type, const std::string &charSet = "utf-8") = 0; + virtual void setContentTypeCodeAndCharacterSet(ContentType type, const std::string &charSet = "utf-8") = 0; - virtual uint8_t getContentTypeCode() = 0; + virtual ContentType getContentTypeCode() = 0; virtual const std::string &getHeader(const std::string &key, const std::string &defaultVal = std::string()) const = 0; diff --git a/lib/inc/drogon/HttpTypes.h b/lib/inc/drogon/HttpTypes.h new file mode 100644 index 00000000..dcad9181 --- /dev/null +++ b/lib/inc/drogon/HttpTypes.h @@ -0,0 +1,106 @@ +/** + * HttpTypes.h + * 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 + +namespace drogon +{ + +enum HttpStatusCode +{ + //rfc2616-6.1.1 + kUnknown = 0, + k100Continue = 100, + k101SwitchingProtocols = 101, + k200OK = 200, + k201Created = 201, + k202Accepted = 202, + k203NonAuthoritativeInformation = 203, + k204NoContent = 204, + k205ResetContent = 205, + k206PartialContent = 206, + k300MultipleChoices = 300, + k301MovedPermanently = 301, + k302Found = 302, + k303SeeOther = 303, + k304NotModified = 304, + k305UseProxy = 305, + k307TemporaryRedirect = 307, + k400BadRequest = 400, + k401Unauthorized = 401, + k402PaymentRequired = 402, + k403Forbidden = 403, + k404NotFound = 404, + k405MethodNotAllowed = 405, + k406NotAcceptable = 406, + k407ProxyAuthenticationRequired = 407, + k408RequestTimeout = 408, + k409Conflict = 409, + k410Gone = 410, + k411LengthRequired = 411, + k412PreconditionFailed = 412, + k413RequestEntityTooLarge = 413, + k414RequestURITooLarge = 414, + k415UnsupportedMediaType = 415, + k416Requestedrangenotsatisfiable = 416, + k417ExpectationFailed = 417, + k500InternalServerError = 500, + k501NotImplemented = 501, + k502BadGateway = 502, + k503ServiceUnavailable = 503, + k504GatewayTimeout = 504, + k505HTTPVersionnotsupported = 505, +}; + +enum Version +{ + kHttp10, + kHttp11 +}; + +enum ContentType +{ + CT_APPLICATION_JSON = 0, + CT_TEXT_PLAIN, + CT_TEXT_HTML, + CT_APPLICATION_X_JAVASCRIPT, + CT_TEXT_CSS, + CT_TEXT_XML, + CT_APPLICATION_XML, + CT_TEXT_XSL, + CT_APPLICATION_OCTET_STREAM, + CT_APPLICATION_X_FONT_TRUETYPE, + CT_APPLICATION_X_FONT_OPENTYPE, + CT_APPLICATION_FONT_WOFF, + CT_APPLICATION_FONT_WOFF2, + CT_APPLICATION_VND_MS_FONTOBJ, + CT_IMAGE_SVG_XML, + CT_IMAGE_PNG, + CT_IMAGE_JPG, + CT_IMAGE_GIF, + CT_IMAGE_XICON, + CT_IMAGE_ICNS, + CT_IMAGE_BMP +}; + +enum HttpMethod +{ + Get = 0, + Post, + Head, + Put, + Delete, + Invalid +}; + +} // namespace drogon diff --git a/lib/src/FileUpload.cc b/lib/src/FileUpload.cc index 83ce01a2..fb19917d 100755 --- a/lib/src/FileUpload.cc +++ b/lib/src/FileUpload.cc @@ -13,6 +13,7 @@ */ #include "HttpRequestImpl.h" +#include "HttpUtils.h" #include #include #include diff --git a/lib/src/HttpAppFrameworkImpl.cc b/lib/src/HttpAppFrameworkImpl.cc index 2c80a065..92802900 100755 --- a/lib/src/HttpAppFrameworkImpl.cc +++ b/lib/src/HttpAppFrameworkImpl.cc @@ -15,6 +15,7 @@ #include "HttpAppFrameworkImpl.h" #include "ConfigLoader.h" #include "HttpServer.h" +#include #include #include #include @@ -642,7 +643,7 @@ void HttpAppFrameworkImpl::onAsyncRequest(const HttpRequestImplPtr &req, std::fu { //Downloading files from the parent folder is forbidden. auto resp = HttpResponse::newHttpResponse(); - resp->setStatusCode(HttpResponse::k403Forbidden); + resp->setStatusCode(k403Forbidden); callback(resp); return; } @@ -670,7 +671,7 @@ void HttpAppFrameworkImpl::onAsyncRequest(const HttpRequestImplPtr &req, std::fu { if (std::dynamic_pointer_cast(cachedResp)->getHeaderBy("last-modified") == req->getHeaderBy("if-modified-since")) { - resp->setStatusCode(HttpResponse::k304NotModified); + resp->setStatusCode(k304NotModified); if (needSetJsessionid) { resp->addCookie("JSESSIONID", sessionId); @@ -696,7 +697,7 @@ void HttpAppFrameworkImpl::onAsyncRequest(const HttpRequestImplPtr &req, std::fu if (modiStr == timeStr && !modiStr.empty()) { LOG_TRACE << "not Modified!"; - resp->setStatusCode(HttpResponse::k304NotModified); + resp->setStatusCode(k304NotModified); if (needSetJsessionid) { resp->addCookie("JSESSIONID", sessionId); @@ -798,7 +799,7 @@ void HttpAppFrameworkImpl::readSendFile(const std::string &filePath, const HttpR if (!infile) { - resp->setStatusCode(HttpResponse::k404NotFound); + resp->setStatusCode(k404NotFound); resp->setCloseConnection(true); return; } @@ -822,7 +823,7 @@ void HttpAppFrameworkImpl::readSendFile(const std::string &filePath, const HttpR resp->setBody(std::move(str)); } - resp->setStatusCode(HttpResponse::k200OK); + resp->setStatusCode(k200OK); //cache the response for 5 seconds by default diff --git a/lib/src/HttpClientImpl.cc b/lib/src/HttpClientImpl.cc index 0f41fde2..c2289725 100644 --- a/lib/src/HttpClientImpl.cc +++ b/lib/src/HttpClientImpl.cc @@ -103,6 +103,13 @@ void HttpClientImpl::sendRequestInLoop(const drogon::HttpRequestPtr &req, const drogon::HttpReqCallback &callback) { _loop->assertInLoopThread(); + req->addHeader("Connection", "Keep-Alive"); + // req->addHeader("Accept", "*/*"); + if (!_domain.empty()) + { + req->addHeader("Host", _domain); + } + req->addHeader("User-Agent", "DrogonClient"); if (!_tcpClient) { @@ -230,7 +237,7 @@ void HttpClientImpl::onRecvMessage(const trantor::TcpConnectionPtr &connPtr, tra resp->parseJson(); } - if (resp->getHeaderBy("content-encoding")=="gzip") + if (resp->getHeaderBy("content-encoding") == "gzip") { resp->gunzip(); } diff --git a/lib/src/HttpControllersRouter.cc b/lib/src/HttpControllersRouter.cc index ff8377f2..09f98e5e 100644 --- a/lib/src/HttpControllersRouter.cc +++ b/lib/src/HttpControllersRouter.cc @@ -163,7 +163,7 @@ void HttpControllersRouter::route(const HttpRequestImplPtr &req, { //Invalid Http Method auto res = drogon::HttpResponse::newHttpResponse(); - res->setStatusCode(HttpResponse::k405MethodNotAllowed); + res->setStatusCode(k405MethodNotAllowed); callback(res); return; } diff --git a/lib/src/HttpRequestImpl.cc b/lib/src/HttpRequestImpl.cc index c099e9c4..083a6945 100755 --- a/lib/src/HttpRequestImpl.cc +++ b/lib/src/HttpRequestImpl.cc @@ -13,14 +13,14 @@ */ #include "HttpRequestImpl.h" - #include + using namespace drogon; void HttpRequestImpl::parseParameter() { const std::string &input = query(); - if(input.empty()) + if (input.empty()) return; std::string type = getHeaderBy("content-type"); std::transform(type.begin(), type.end(), type.begin(), tolower); @@ -113,7 +113,7 @@ void HttpRequestImpl::appendToBuffer(MsgBuffer *output) const return; } - if (_path.size() != 0) + if (!_path.empty()) { output->append(_path); } @@ -122,17 +122,31 @@ void HttpRequestImpl::appendToBuffer(MsgBuffer *output) const output->append("/"); } - if (_parameters.size() != 0) + std::string content; + if (!_parameters.empty()) { - output->append("?"); for (auto const &p : _parameters) { - output->append(p.first); - output->append("="); - output->append(p.second); - output->append("&"); + content.append(p.first); + content.append("="); + content.append(p.second); + content.append("&"); + } + content.resize(content.length() - 1); + ///TODO: URL code? + if (_method == Get || _method == Delete) + { + output->append("?"); + output->append(content); + content.clear(); + } + else if (_contentType == CT_APPLICATION_JSON) + { + ///Can't set parameters in content in this case + LOG_ERROR << "You can't set parameters in the query string when the request content type is JSON and http method is POST or PUT"; + LOG_ERROR << "Please put these parameters in the path or the json string"; + content.clear(); } - output->unwrite(1); } output->append(" "); @@ -150,6 +164,20 @@ void HttpRequestImpl::appendToBuffer(MsgBuffer *output) const } output->append("\r\n"); + assert(!(!content.empty() && !_content.empty())); + if (!content.empty() || !_content.empty()) + { + char buf[64]; + snprintf(buf, sizeof buf, "Content-Length: %lu\r\n", static_cast(content.length() + _content.length())); + output->append(buf); + if (_headers.find("Content-Type") == _headers.end()) + { + output->append("Content-Type: "); + output->append(webContentTypeToString(_contentType)); + output->append("\r\n"); + } + } + for (auto it = _headers.begin(); it != _headers.end(); ++it) { output->append(it->first); @@ -172,9 +200,70 @@ void HttpRequestImpl::appendToBuffer(MsgBuffer *output) const } output->append("\r\n"); - //LOG_INFO<<"request(no body):"<peek(); - output->append(_content); + if (!content.empty()) + output->append(content); + if (!_content.empty()) + output->append(_content); + //LOG_INFO << output->peek(); +} + +void HttpRequestImpl::addHeader(const char *start, const char *colon, const char *end) +{ + std::string field(start, colon); + //field name is case-insensitive.so we transform it to lower;(rfc2616-4.2) + std::transform(field.begin(), field.end(), field.begin(), ::tolower); + ++colon; + while (colon < end && isspace(*colon)) + { + ++colon; + } + std::string value(colon, end); + while (!value.empty() && isspace(value[value.size() - 1])) + { + value.resize(value.size() - 1); + } + + if (field == "cookie") + { + LOG_TRACE << "cookies!!!:" << value; + std::string::size_type pos; + while ((pos = value.find(";")) != std::string::npos) + { + std::string coo = value.substr(0, pos); + auto epos = coo.find("="); + if (epos != std::string::npos) + { + std::string cookie_name = coo.substr(0, epos); + std::string::size_type cpos = 0; + while (cpos < cookie_name.length() && isspace(cookie_name[cpos])) + cpos++; + cookie_name = cookie_name.substr(cpos); + std::string cookie_value = coo.substr(epos + 1); + _cookies[std::move(cookie_name)] = std::move(cookie_value); + } + value = value.substr(pos + 1); + } + if (value.length() > 0) + { + std::string &coo = value; + auto epos = coo.find("="); + if (epos != std::string::npos) + { + std::string cookie_name = coo.substr(0, epos); + std::string::size_type cpos = 0; + while (cpos < cookie_name.length() && isspace(cookie_name[cpos])) + cpos++; + cookie_name = cookie_name.substr(cpos); + std::string cookie_value = coo.substr(epos + 1); + _cookies[std::move(cookie_name)] = std::move(cookie_value); + } + } + } + else + { + _headers[std::move(field)] = std::move(value); + } } HttpRequestPtr HttpRequest::newHttpRequest() @@ -184,3 +273,16 @@ HttpRequestPtr HttpRequest::newHttpRequest() req->setVersion(drogon::HttpRequest::kHttp11); return req; } + +HttpRequestPtr HttpRequest::newHttpJsonRequest(const Json::Value &data) +{ + auto req = std::make_shared(); + req->setMethod(drogon::Get); + req->setVersion(drogon::HttpRequest::kHttp11); + req->_contentType = CT_APPLICATION_JSON; + Json::StreamWriterBuilder builder; + builder["commentStyle"] = "None"; + builder["indentation"] = ""; + req->setContent(writeString(builder, data)); + return req; +} diff --git a/lib/src/HttpRequestImpl.h b/lib/src/HttpRequestImpl.h index ae181fba..e1537584 100755 --- a/lib/src/HttpRequestImpl.h +++ b/lib/src/HttpRequestImpl.h @@ -14,8 +14,10 @@ #pragma once +#include "HttpUtils.h" #include #include +#include #include #include @@ -26,14 +28,18 @@ #include #include #include + using std::string; using namespace trantor; + namespace drogon { + class HttpRequestImpl : public HttpRequest { public: friend class HttpRequestParser; + HttpRequestImpl() : _method(Invalid), _version(kUnknown), @@ -50,7 +56,9 @@ class HttpRequestImpl : public HttpRequest { return _version; } + void parseParameter(); + bool setMethod(const char *start, const char *end) { @@ -137,6 +145,7 @@ class HttpRequestImpl : public HttpRequest { _path = urlDecode(start, end); } + virtual void setPath(const std::string &path) override { _path = path; @@ -146,6 +155,7 @@ class HttpRequestImpl : public HttpRequest { return _parameters; } + const std::string &path() const override { return _path; @@ -155,6 +165,7 @@ class HttpRequestImpl : public HttpRequest { _query.assign(start, end); } + void setQuery(const std::string &query) { _query = query; @@ -182,84 +193,33 @@ class HttpRequestImpl : public HttpRequest { return _peer; } + virtual const trantor::InetAddress &localAddr() const override { return _local; } + virtual const trantor::Date &receiveDate() const override { return _date; } + void setReceiveDate(const trantor::Date &date) { _date = date; } + void setPeerAddr(const trantor::InetAddress &peer) { _peer = peer; } + void setLocalAddr(const trantor::InetAddress &local) { _local = local; } - void addHeader(const char *start, const char *colon, const char *end) - { - std::string field(start, colon); - //field name is case-insensitive.so we transform it to lower;(rfc2616-4.2) - std::transform(field.begin(), field.end(), field.begin(), ::tolower); - ++colon; - while (colon < end && isspace(*colon)) - { - ++colon; - } - std::string value(colon, end); - while (!value.empty() && isspace(value[value.size() - 1])) - { - value.resize(value.size() - 1); - } - - if (field == "cookie") - { - LOG_TRACE << "cookies!!!:" << value; - std::string::size_type pos; - while ((pos = value.find(";")) != std::string::npos) - { - std::string coo = value.substr(0, pos); - auto epos = coo.find("="); - if (epos != std::string::npos) - { - std::string cookie_name = coo.substr(0, epos); - std::string::size_type cpos = 0; - while (cpos < cookie_name.length() && isspace(cookie_name[cpos])) - cpos++; - cookie_name = cookie_name.substr(cpos); - std::string cookie_value = coo.substr(epos + 1); - _cookies[std::move(cookie_name)] = std::move(cookie_value); - } - value = value.substr(pos + 1); - } - if (value.length() > 0) - { - std::string &coo = value; - auto epos = coo.find("="); - if (epos != std::string::npos) - { - std::string cookie_name = coo.substr(0, epos); - std::string::size_type cpos = 0; - while (cpos < cookie_name.length() && isspace(cookie_name[cpos])) - cpos++; - cookie_name = cookie_name.substr(cpos); - std::string cookie_value = coo.substr(epos + 1); - _cookies[std::move(cookie_name)] = std::move(cookie_value); - } - } - } - else - { - _headers[std::move(field)] = std::move(value); - } - } + void addHeader(const char *start, const char *colon, const char *end); const std::string &getHeader(const std::string &field, const std::string &defaultVal = std::string()) const override { @@ -293,6 +253,7 @@ class HttpRequestImpl : public HttpRequest } return defaultVal; } + const std::unordered_map &headers() const override { return _headers; @@ -302,14 +263,17 @@ class HttpRequestImpl : public HttpRequest { return _cookies; } + virtual void setParameter(const std::string &key, const std::string &value) override { _parameters[key] = value; } + const std::string &getContent() const { return _content; } + void swap(HttpRequestImpl &that) { std::swap(_method, that._method); @@ -334,10 +298,12 @@ class HttpRequestImpl : public HttpRequest { _content = content; } + virtual void addHeader(const std::string &key, const std::string &value) override { _headers[key] = value; } + void addCookie(const std::string &key, const std::string &value) { _cookies[key] = value; @@ -360,7 +326,35 @@ class HttpRequestImpl : public HttpRequest return _jsonPtr; } + virtual void setContentTypeCode(ContentType type) override + { + _contentType = type; + setContentType(webContentTypeToString(type)); + } + + virtual void setContentTypeCodeAndCharacterSet(ContentType type, const std::string &charSet = "utf-8") override + { + _contentType = type; + setContentType(webContentTypeAndCharsetToString(type, charSet)); + } + + virtual ContentType getContentTypeCode() override + { + return _contentType; + } + + private: + friend class HttpRequest; + void setContentType(const std::string &contentType) + { + addHeader("Content-Type", contentType); + } + void setContentType(std::string &&contentType) + { + addHeader("Content-Type", std::move(contentType)); + } + HttpMethod _method; Version _version; std::string _path; @@ -375,6 +369,7 @@ class HttpRequestImpl : public HttpRequest trantor::InetAddress _peer; trantor::InetAddress _local; trantor::Date _date; + ContentType _contentType = CT_TEXT_PLAIN; protected: std::string _content; @@ -382,4 +377,5 @@ class HttpRequestImpl : public HttpRequest }; typedef std::shared_ptr HttpRequestImplPtr; + } // namespace drogon diff --git a/lib/src/HttpRequestParser.cc b/lib/src/HttpRequestParser.cc index 27601655..c1fd19cd 100755 --- a/lib/src/HttpRequestParser.cc +++ b/lib/src/HttpRequestParser.cc @@ -12,6 +12,7 @@ * */ +#include #include #include #include "HttpRequestParser.h" @@ -130,7 +131,7 @@ bool HttpRequestParser::parseRequest(MsgBuffer *buf) if (connPtr) { auto resp = HttpResponse::newHttpResponse(); - resp->setStatusCode(HttpResponse::k100Continue); + resp->setStatusCode(k100Continue); auto httpString = std::dynamic_pointer_cast(resp)->renderToString(); connPtr->send(httpString); } @@ -142,7 +143,7 @@ bool HttpRequestParser::parseRequest(MsgBuffer *buf) if (connPtr) { auto resp = HttpResponse::newHttpResponse(); - resp->setStatusCode(HttpResponse::k417ExpectationFailed); + resp->setStatusCode(k417ExpectationFailed); MsgBuffer buffer; auto httpString = std::dynamic_pointer_cast(resp)->renderToString(); connPtr->send(httpString); diff --git a/lib/src/HttpResponseImpl.cc b/lib/src/HttpResponseImpl.cc index 12ecd643..4f0dadb4 100755 --- a/lib/src/HttpResponseImpl.cc +++ b/lib/src/HttpResponseImpl.cc @@ -28,7 +28,7 @@ using namespace drogon; HttpResponsePtr HttpResponse::newHttpResponse() { auto res = std::make_shared(); - res->setStatusCode(HttpResponse::k200OK); + res->setStatusCode(k200OK); res->setContentTypeCode(CT_TEXT_HTML); return res; } @@ -36,7 +36,7 @@ HttpResponsePtr HttpResponse::newHttpResponse() HttpResponsePtr HttpResponse::newHttpJsonResponse(const Json::Value &data) { auto res = std::make_shared(); - res->setStatusCode(HttpResponse::k200OK); + res->setStatusCode(k200OK); res->setContentTypeCode(CT_APPLICATION_JSON); Json::StreamWriterBuilder builder; builder["commentStyle"] = "None"; @@ -49,7 +49,7 @@ HttpResponsePtr HttpResponse::newNotFoundResponse() HttpViewData data; data.insert("version", getVersion()); auto res = HttpResponse::newHttpViewResponse("NotFound", data); - res->setStatusCode(HttpResponse::k404NotFound); + res->setStatusCode(k404NotFound); //res->setCloseConnection(true); return res; @@ -57,7 +57,7 @@ HttpResponsePtr HttpResponse::newNotFoundResponse() HttpResponsePtr HttpResponse::newLocationRedirectResponse(const std::string &path) { auto res = std::make_shared(); - res->setStatusCode(HttpResponse::k302Found); + res->setStatusCode(k302Found); res->redirect(path.c_str()); return res; } @@ -66,144 +66,6 @@ HttpResponsePtr HttpResponse::newHttpViewResponse(const std::string &viewName, c { return HttpViewBase::genHttpResponse(viewName, data); } -const std::string HttpResponseImpl::web_content_type_and_charset_to_string(uint8_t contenttype, const std::string &charSet) -{ - switch (contenttype) - { - case CT_TEXT_HTML: - return "text/html; charset=" + charSet; - - case CT_APPLICATION_XML: - return "application/xml; charset=" + charSet; - - case CT_APPLICATION_JSON: - return "application/json; charset=" + charSet; - - case CT_APPLICATION_X_JAVASCRIPT: - return "application/x-javascript; charset=" + charSet; - - case CT_TEXT_CSS: - return "text/css; charset=" + charSet; - - case CT_TEXT_XML: - return "text/xml; charset=" + charSet; - - case CT_TEXT_XSL: - return "text/xsl; charset=" + charSet; - - case CT_APPLICATION_OCTET_STREAM: - return "application/octet-stream"; - - case CT_IMAGE_SVG_XML: - return "image/svg+xml"; - - case CT_APPLICATION_X_FONT_TRUETYPE: - return "application/x-font-truetype"; - - case CT_APPLICATION_X_FONT_OPENTYPE: - return "application/x-font-opentype"; - - case CT_APPLICATION_FONT_WOFF: - return "application/font-woff"; - - case CT_APPLICATION_FONT_WOFF2: - return "application/font-woff2"; - - case CT_APPLICATION_VND_MS_FONTOBJ: - return "application/vnd.ms-fontobject"; - - case CT_IMAGE_PNG: - return "image/png"; - - case CT_IMAGE_JPG: - return "image/jpeg"; - - case CT_IMAGE_GIF: - return "image/gif"; - - case CT_IMAGE_XICON: - return "image/x-icon"; - - case CT_IMAGE_BMP: - return "image/bmp"; - - case CT_IMAGE_ICNS: - return "image/icns"; - - default: - case CT_TEXT_PLAIN: - return "text/plain; charset=" + charSet; - } -} -std::string HttpResponseImpl::web_content_type_to_string(uint8_t contenttype) -{ - switch (contenttype) - { - case CT_TEXT_HTML: - return "text/html; charset=utf-8"; - - case CT_APPLICATION_XML: - return "application/xml; charset=utf-8"; - - case CT_APPLICATION_JSON: - return "application/json; charset=utf-8"; - - case CT_APPLICATION_X_JAVASCRIPT: - return "application/x-javascript; charset=utf-8"; - - case CT_TEXT_CSS: - return "text/css; charset=utf-8"; - - case CT_TEXT_XML: - return "text/xml; charset=utf-8"; - - case CT_TEXT_XSL: - return "text/xsl; charset=utf-8"; - - case CT_APPLICATION_OCTET_STREAM: - return "application/octet-stream"; - - case CT_IMAGE_SVG_XML: - return "image/svg+xml"; - - case CT_APPLICATION_X_FONT_TRUETYPE: - return "application/x-font-truetype"; - - case CT_APPLICATION_X_FONT_OPENTYPE: - return "application/x-font-opentype"; - - case CT_APPLICATION_FONT_WOFF: - return "application/font-woff"; - - case CT_APPLICATION_FONT_WOFF2: - return "application/font-woff2"; - - case CT_APPLICATION_VND_MS_FONTOBJ: - return "application/vnd.ms-fontobject"; - - case CT_IMAGE_PNG: - return "image/png"; - - case CT_IMAGE_JPG: - return "image/jpeg"; - - case CT_IMAGE_GIF: - return "image/gif"; - - case CT_IMAGE_XICON: - return "image/x-icon"; - - case CT_IMAGE_BMP: - return "image/bmp"; - - case CT_IMAGE_ICNS: - return "image/icns"; - - default: - case CT_TEXT_PLAIN: - return "text/plain; charset=utf-8"; - } -} std::string HttpResponseImpl::web_response_code_to_string(int code) { diff --git a/lib/src/HttpResponseImpl.h b/lib/src/HttpResponseImpl.h index de7eb16d..a620036a 100755 --- a/lib/src/HttpResponseImpl.h +++ b/lib/src/HttpResponseImpl.h @@ -14,6 +14,7 @@ #pragma once +#include "HttpUtils.h" #include #include #include @@ -79,19 +80,19 @@ class HttpResponseImpl : public HttpResponse return _closeConnection; } - virtual void setContentTypeCode(uint8_t type) override + virtual void setContentTypeCode(ContentType type) override { _contentType = type; - setContentType(web_content_type_to_string(type)); + setContentType(webContentTypeToString(type)); } - virtual void setContentTypeCodeAndCharacterSet(uint8_t type, const std::string &charSet = "utf-8") override + virtual void setContentTypeCodeAndCharacterSet(ContentType type, const std::string &charSet = "utf-8") override { _contentType = type; - setContentType(web_content_type_and_charset_to_string(type, charSet)); + setContentType(webContentTypeAndCharsetToString(type, charSet)); } - virtual uint8_t getContentTypeCode() override + virtual ContentType getContentTypeCode() override { return _contentType; } @@ -360,12 +361,8 @@ class HttpResponseImpl : public HttpResponse } protected: - static std::string web_content_type_to_string(uint8_t contenttype); - static const std::string web_content_type_and_charset_to_string(uint8_t contenttype, - const std::string &charSet); static std::string web_response_code_to_string(int code); - void makeHeaderString(const std::shared_ptr &headerStringPtr) const; private: @@ -381,7 +378,7 @@ class HttpResponseImpl : public HttpResponse size_t _currentChunkLength; std::shared_ptr _bodyPtr; - uint8_t _contentType = CT_TEXT_HTML; + ContentType _contentType = CT_TEXT_HTML; ssize_t _expriedTime = -1; std::string _sendfileName; diff --git a/lib/src/HttpResponseParser.cc b/lib/src/HttpResponseParser.cc index 1507a9d1..92fcb77d 100755 --- a/lib/src/HttpResponseParser.cc +++ b/lib/src/HttpResponseParser.cc @@ -33,11 +33,11 @@ bool HttpResponseParser::processResponseLine(const char *begin, const char *end) LOG_TRACE << *(space - 1); if (*(space - 1) == '1') { - _response->setVersion(HttpResponse::kHttp11); + _response->setVersion(kHttp11); } else if (*(space - 1) == '0') { - _response->setVersion(HttpResponse::kHttp10); + _response->setVersion(kHttp10); } else { @@ -53,7 +53,7 @@ bool HttpResponseParser::processResponseLine(const char *begin, const char *end) std::string status_message(space + 1, end - space - 1); LOG_TRACE << status_code << " " << status_message; auto code = atoi(status_code.c_str()); - _response->setStatusCode(HttpResponse::HttpStatusCode(code), status_message); + _response->setStatusCode(HttpStatusCode(code), status_message); return true; } diff --git a/lib/src/HttpServer.cc b/lib/src/HttpServer.cc index 88ed8754..c473776e 100755 --- a/lib/src/HttpServer.cc +++ b/lib/src/HttpServer.cc @@ -122,7 +122,7 @@ void HttpServer::onMessage(const TcpConnectionPtr &conn, auto wsConn = std::make_shared(conn); _newWebsocketCallback(requestParser->request(), [=](const HttpResponsePtr &resp) mutable { - if (resp->statusCode() == HttpResponse::k101SwitchingProtocols) + if (resp->statusCode() == k101SwitchingProtocols) { requestParser->setWebsockConnection(wsConn); } diff --git a/lib/src/HttpSimpleControllersRouter.cc b/lib/src/HttpSimpleControllersRouter.cc index 21ecffb1..d0351827 100644 --- a/lib/src/HttpSimpleControllersRouter.cc +++ b/lib/src/HttpSimpleControllersRouter.cc @@ -82,7 +82,7 @@ void HttpSimpleControllersRouter::route(const HttpRequestImplPtr &req, { //Invalid Http Method auto res = drogon::HttpResponse::newHttpResponse(); - res->setStatusCode(HttpResponse::k405MethodNotAllowed); + res->setStatusCode(k405MethodNotAllowed); callback(res); return; } diff --git a/lib/src/HttpUtils.cc b/lib/src/HttpUtils.cc new file mode 100644 index 00000000..ec33cd44 --- /dev/null +++ b/lib/src/HttpUtils.cc @@ -0,0 +1,160 @@ +/** + * + * HttpUtils.h + * 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 + * + */ + +#include "HttpUtils.h" + +namespace drogon +{ + +std::string webContentTypeAndCharsetToString(ContentType contenttype, const std::string &charSet) +{ + switch (contenttype) + { + case CT_TEXT_HTML: + return "text/html; charset=" + charSet; + + case CT_APPLICATION_XML: + return "application/xml; charset=" + charSet; + + case CT_APPLICATION_JSON: + return "application/json; charset=" + charSet; + + case CT_APPLICATION_X_JAVASCRIPT: + return "application/x-javascript; charset=" + charSet; + + case CT_TEXT_CSS: + return "text/css; charset=" + charSet; + + case CT_TEXT_XML: + return "text/xml; charset=" + charSet; + + case CT_TEXT_XSL: + return "text/xsl; charset=" + charSet; + + case CT_APPLICATION_OCTET_STREAM: + return "application/octet-stream"; + + case CT_IMAGE_SVG_XML: + return "image/svg+xml"; + + case CT_APPLICATION_X_FONT_TRUETYPE: + return "application/x-font-truetype"; + + case CT_APPLICATION_X_FONT_OPENTYPE: + return "application/x-font-opentype"; + + case CT_APPLICATION_FONT_WOFF: + return "application/font-woff"; + + case CT_APPLICATION_FONT_WOFF2: + return "application/font-woff2"; + + case CT_APPLICATION_VND_MS_FONTOBJ: + return "application/vnd.ms-fontobject"; + + case CT_IMAGE_PNG: + return "image/png"; + + case CT_IMAGE_JPG: + return "image/jpeg"; + + case CT_IMAGE_GIF: + return "image/gif"; + + case CT_IMAGE_XICON: + return "image/x-icon"; + + case CT_IMAGE_BMP: + return "image/bmp"; + + case CT_IMAGE_ICNS: + return "image/icns"; + + default: + case CT_TEXT_PLAIN: + return "text/plain; charset=" + charSet; + } +} + +std::string webContentTypeToString(ContentType contenttype) +{ + switch (contenttype) + { + case CT_TEXT_HTML: + return "text/html; charset=utf-8"; + + case CT_APPLICATION_XML: + return "application/xml; charset=utf-8"; + + case CT_APPLICATION_JSON: + return "application/json; charset=utf-8"; + + case CT_APPLICATION_X_JAVASCRIPT: + return "application/x-javascript; charset=utf-8"; + + case CT_TEXT_CSS: + return "text/css; charset=utf-8"; + + case CT_TEXT_XML: + return "text/xml; charset=utf-8"; + + case CT_TEXT_XSL: + return "text/xsl; charset=utf-8"; + + case CT_APPLICATION_OCTET_STREAM: + return "application/octet-stream"; + + case CT_IMAGE_SVG_XML: + return "image/svg+xml"; + + case CT_APPLICATION_X_FONT_TRUETYPE: + return "application/x-font-truetype"; + + case CT_APPLICATION_X_FONT_OPENTYPE: + return "application/x-font-opentype"; + + case CT_APPLICATION_FONT_WOFF: + return "application/font-woff"; + + case CT_APPLICATION_FONT_WOFF2: + return "application/font-woff2"; + + case CT_APPLICATION_VND_MS_FONTOBJ: + return "application/vnd.ms-fontobject"; + + case CT_IMAGE_PNG: + return "image/png"; + + case CT_IMAGE_JPG: + return "image/jpeg"; + + case CT_IMAGE_GIF: + return "image/gif"; + + case CT_IMAGE_XICON: + return "image/x-icon"; + + case CT_IMAGE_BMP: + return "image/bmp"; + + case CT_IMAGE_ICNS: + return "image/icns"; + + default: + case CT_TEXT_PLAIN: + return "text/plain; charset=utf-8"; + } +} + +} \ No newline at end of file diff --git a/lib/src/HttpUtils.h b/lib/src/HttpUtils.h new file mode 100644 index 00000000..075172d1 --- /dev/null +++ b/lib/src/HttpUtils.h @@ -0,0 +1,25 @@ +/** + * + * HttpUtils.h + * 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 + +namespace drogon +{ + +std::string webContentTypeToString(ContentType contenttype); +std::string webContentTypeAndCharsetToString(ContentType contenttype, const std::string &charSet); + +} // namespace drogon diff --git a/lib/src/HttpViewBase.cc b/lib/src/HttpViewBase.cc index 9c931bbd..3704dd5b 100755 --- a/lib/src/HttpViewBase.cc +++ b/lib/src/HttpViewBase.cc @@ -35,7 +35,7 @@ HttpResponsePtr HttpViewBase::genHttpResponse(std::string viewName, const HttpVi if (templ) { auto res = HttpResponse::newHttpResponse(); - res->setStatusCode(HttpResponse::k200OK); + res->setStatusCode(k200OK); res->setContentTypeCode(CT_TEXT_HTML); res->setBody(templ->genText(data)); return res; diff --git a/lib/src/WebsocketControllersRouter.cc b/lib/src/WebsocketControllersRouter.cc index abbfa862..36b3a6de 100644 --- a/lib/src/WebsocketControllersRouter.cc +++ b/lib/src/WebsocketControllersRouter.cc @@ -90,7 +90,7 @@ void WebsocketControllersRouter::doControllerHandler(const WebSocketControllerBa SHA1(reinterpret_cast(wsKey.c_str()), wsKey.length(), accKey); auto base64Key = base64Encode(accKey, SHA_DIGEST_LENGTH); auto resp = HttpResponse::newHttpResponse(); - resp->setStatusCode(HttpResponse::k101SwitchingProtocols); + resp->setStatusCode(k101SwitchingProtocols); resp->addHeader("Upgrade", "websocket"); resp->addHeader("Connection", "Upgrade"); resp->addHeader("Sec-WebSocket-Accept", base64Key);