diff --git a/examples/simple_example/api_v1_ApiTest.cc b/examples/simple_example/api_v1_ApiTest.cc index b0d568f1..10a6c1c0 100755 --- a/examples/simple_example/api_v1_ApiTest.cc +++ b/examples/simple_example/api_v1_ApiTest.cc @@ -355,7 +355,7 @@ void ApiTest::jsonTest(const HttpRequestPtr &req, const std::functiongetJsonObject(); Json::Value ret; - if(json) + if (json) { ret["result"] = "ok"; } @@ -366,3 +366,19 @@ void ApiTest::jsonTest(const HttpRequestPtr &req, const std::function &callback) +{ + auto parameters = req->getParameters(); + Json::Value ret; + if (parameters["k1"] == "1" && parameters["k2"] == "安") + { + ret["result"] = "ok"; + } + else + { + ret["result"] = "bad"; + } + auto resp = HttpResponse::newHttpJsonResponse(ret); + callback(resp); +} \ No newline at end of file diff --git a/examples/simple_example/api_v1_ApiTest.h b/examples/simple_example/api_v1_ApiTest.h index 1ea82711..8028df49 100755 --- a/examples/simple_example/api_v1_ApiTest.h +++ b/examples/simple_example/api_v1_ApiTest.h @@ -17,6 +17,7 @@ class ApiTest : public drogon::HttpController METHOD_ADD(ApiTest::staticApi, "/static", Get, Post); METHOD_ADD(ApiTest::get2, "/get/{1}", Get); METHOD_ADD(ApiTest::jsonTest, "/json", Post); + METHOD_ADD(ApiTest::formTest, "/form", 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); @@ -26,6 +27,7 @@ class ApiTest : public drogon::HttpController 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); + void formTest(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 d01ffdce..5fb55700 100644 --- a/examples/simple_example_test/main.cc +++ b/examples/simple_example_test/main.cc @@ -489,6 +489,32 @@ void doTest(const HttpClientPtr &client) } }); + /// Test form post + req = HttpRequest::newHttpFormPostRequest(); + req->setPath("/api/v1/apitest/form"); + req->setParameter("k1", "1"); + req->setParameter("k2", "安"); + 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); + } + }); } int main() diff --git a/lib/inc/drogon/HttpRequest.h b/lib/inc/drogon/HttpRequest.h index 985991e3..c47bc3a7 100755 --- a/lib/inc/drogon/HttpRequest.h +++ b/lib/inc/drogon/HttpRequest.h @@ -29,7 +29,6 @@ namespace drogon class HttpRequest; typedef std::shared_ptr HttpRequestPtr; - /// Abstract class for webapp developer to get or set the Http request; class HttpRequest { @@ -114,7 +113,7 @@ class HttpRequest /// Create a request object. static HttpRequestPtr newHttpRequest(); static HttpRequestPtr newHttpJsonRequest(const Json::Value &data); - + static HttpRequestPtr newHttpFormPostRequest(); virtual ~HttpRequest() {} }; diff --git a/lib/inc/drogon/HttpTypes.h b/lib/inc/drogon/HttpTypes.h index dcad9181..99c02eb3 100644 --- a/lib/inc/drogon/HttpTypes.h +++ b/lib/inc/drogon/HttpTypes.h @@ -15,7 +15,7 @@ namespace drogon { - + enum HttpStatusCode { //rfc2616-6.1.1 @@ -73,6 +73,7 @@ enum ContentType CT_APPLICATION_JSON = 0, CT_TEXT_PLAIN, CT_TEXT_HTML, + CT_APPLICATION_X_FORM, CT_APPLICATION_X_JAVASCRIPT, CT_TEXT_CSS, CT_TEXT_XML, diff --git a/lib/inc/drogon/utils/Utilities.h b/lib/inc/drogon/utils/Utilities.h index ac58007f..0c429552 100755 --- a/lib/inc/drogon/utils/Utilities.h +++ b/lib/inc/drogon/utils/Utilities.h @@ -50,9 +50,10 @@ std::string base64Encode(const unsigned char *bytes_to_encode, unsigned int in_l /// Decode the base64 format string. std::string base64Decode(std::string const &encoded_string); -/// Decode the URL format string send by web browser. +/// Decode from or encode to the URL format string std::string urlDecode(const std::string &szToDecode); std::string urlDecode(const char *begin, const char *end); +std::string urlEncode(const std::string &str); /// Commpress or decompress data using gzip lib. /** diff --git a/lib/src/HttpRequestImpl.cc b/lib/src/HttpRequestImpl.cc index 083a6945..1b0be067 100755 --- a/lib/src/HttpRequestImpl.cc +++ b/lib/src/HttpRequestImpl.cc @@ -13,6 +13,7 @@ */ #include "HttpRequestImpl.h" +#include #include using namespace drogon; @@ -133,7 +134,7 @@ void HttpRequestImpl::appendToBuffer(MsgBuffer *output) const content.append("&"); } content.resize(content.length() - 1); - ///TODO: URL code? + content = urlEncode(content); if (_method == Get || _method == Delete) { output->append("?"); @@ -211,7 +212,7 @@ void HttpRequestImpl::appendToBuffer(MsgBuffer *output) const 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) + //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)) @@ -274,6 +275,15 @@ HttpRequestPtr HttpRequest::newHttpRequest() return req; } +HttpRequestPtr HttpRequest::newHttpFormPostRequest() +{ + auto req = std::make_shared(); + req->setMethod(drogon::Post); + req->setVersion(drogon::HttpRequest::kHttp11); + req->_contentType = CT_APPLICATION_X_FORM; + return req; +} + HttpRequestPtr HttpRequest::newHttpJsonRequest(const Json::Value &data) { auto req = std::make_shared(); diff --git a/lib/src/HttpUtils.cc b/lib/src/HttpUtils.cc index ec33cd44..f39b36d0 100644 --- a/lib/src/HttpUtils.cc +++ b/lib/src/HttpUtils.cc @@ -24,6 +24,9 @@ std::string webContentTypeAndCharsetToString(ContentType contenttype, const std: case CT_TEXT_HTML: return "text/html; charset=" + charSet; + case CT_APPLICATION_X_FORM: + return "application/x-www-form-urlencoded"; + case CT_APPLICATION_XML: return "application/xml; charset=" + charSet; @@ -94,6 +97,9 @@ std::string webContentTypeToString(ContentType contenttype) case CT_TEXT_HTML: return "text/html; charset=utf-8"; + case CT_APPLICATION_X_FORM: + return "application/x-www-form-urlencoded"; + case CT_APPLICATION_XML: return "application/xml; charset=utf-8"; diff --git a/lib/src/Utilities.cc b/lib/src/Utilities.cc index 9e431ee7..ac872e08 100755 --- a/lib/src/Utilities.cc +++ b/lib/src/Utilities.cc @@ -27,6 +27,10 @@ #include #include #include +#include +#include +#include +#include namespace drogon { @@ -272,6 +276,121 @@ std::string base64Decode(std::string const &encoded_string) return ret; } +static std::string charToHex(char c) +{ + std::string result; + char first, second; + + first = (c & 0xF0) / 16; + first += first > 9 ? 'A' - 10 : '0'; + second = c & 0x0F; + second += second > 9 ? 'A' - 10 : '0'; + + result.append(1, first); + result.append(1, second); + + return result; +} + +std::string urlEncode(const std::string &src) +{ + std::string result; + std::string::const_iterator iter; + + for (iter = src.begin(); iter != src.end(); ++iter) + { + switch (*iter) + { + case ' ': + result.append(1, '+'); + break; + // alnum + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + case 'G': + case 'H': + case 'I': + case 'J': + case 'K': + case 'L': + case 'M': + case 'N': + case 'O': + case 'P': + case 'Q': + case 'R': + case 'S': + case 'T': + case 'U': + case 'V': + case 'W': + case 'X': + case 'Y': + case 'Z': + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + case 'g': + case 'h': + case 'i': + case 'j': + case 'k': + case 'l': + case 'm': + case 'n': + case 'o': + case 'p': + case 'q': + case 'r': + case 's': + case 't': + case 'u': + case 'v': + case 'w': + case 'x': + case 'y': + case 'z': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + // mark + case '-': + case '_': + case '.': + case '!': + case '~': + case '*': + case '\'': + case '(': + case ')': + case '&': + case '=': + result.append(1, *iter); + break; + // escape + default: + result.append(1, '%'); + result.append(charToHex(*iter)); + break; + } + } + + return result; +} std::string urlDecode(const std::string &szToDecode) { diff --git a/lib/tests/CMakeLists.txt b/lib/tests/CMakeLists.txt index 228e0eed..10990436 100755 --- a/lib/tests/CMakeLists.txt +++ b/lib/tests/CMakeLists.txt @@ -8,4 +8,5 @@ add_executable(sha1_test Sha1Test.cc) add_executable(view_data_test HttpViewDataTest.cc) add_executable(md5_test Md5Test.cc ../src/ssl_funcs/Md5.cc) add_executable(http_full_date_test HttpFullDateTest.cc) -add_executable(gzip_test GzipTest.cc) \ No newline at end of file +add_executable(gzip_test GzipTest.cc) +add_executable(url_codec_test UrlCodecTest.cc) \ No newline at end of file diff --git a/lib/tests/UrlCodecTest.cc b/lib/tests/UrlCodecTest.cc new file mode 100644 index 00000000..dbff3633 --- /dev/null +++ b/lib/tests/UrlCodecTest.cc @@ -0,0 +1,13 @@ +#include +#include + +int main() +{ + std::string input = "k1=1&k2=安"; + auto output = drogon::urlEncode(input); + std::cout << output << std::endl; + auto output2 = drogon::urlDecode(output); + std::cout << output2 << std::endl; + std::cout << drogon::urlDecode("k2%3D%E5%AE%89&k1%3D1"); + return 0; +} \ No newline at end of file