audio_trans/lib/tests/integration_test/client/main.cc

1240 lines
51 KiB
C++

/**
*
* @file test.cc
* @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
*
*/
// Make a http client to test the example server app;
#define DROGON_TEST_MAIN
#include <drogon/drogon.h>
#include <trantor/net/EventLoopThread.h>
#include <trantor/net/TcpClient.h>
#include <drogon/HttpAppFramework.h>
#include <drogon/drogon_test.h>
#include <mutex>
#include <algorithm>
#include <atomic>
#include <chrono>
#ifndef _WIN32
#include <unistd.h>
#endif
#include <sys/stat.h>
#define JPG_LEN 44618UL
size_t indexLen;
size_t indexImplicitLen;
using namespace drogon;
Cookie sessionID;
void doTest(const HttpClientPtr &client, std::shared_ptr<test::Case> TEST_CTX)
{
/// Get cookie
if (!sessionID)
{
auto req = HttpRequest::newHttpRequest();
req->setMethod(drogon::Get);
req->setPath("/");
std::promise<int> waitCookie;
auto f = waitCookie.get_future();
client->sendRequest(req,
[client, &waitCookie, TEST_CTX](
ReqResult result, const HttpResponsePtr &resp) {
REQUIRE(result == ReqResult::Ok);
auto &id = resp->getCookie("JSESSIONID");
REQUIRE(id);
sessionID = id;
client->addCookie(id);
waitCookie.set_value(1);
});
f.get();
}
else
client->addCookie(sessionID);
/// Test begin advice
auto req = HttpRequest::newHttpRequest();
req->setMethod(drogon::Get);
req->setPath("/test_begin_advice");
client->sendRequest(req,
[req, client, TEST_CTX](ReqResult result,
const HttpResponsePtr &resp) {
REQUIRE(result == ReqResult::Ok);
CHECK(resp->getBody() == "DrogonReady");
});
/// Test session
req = HttpRequest::newHttpRequest();
req->setMethod(drogon::Get);
req->setPath("/slow");
client->sendRequest(
req,
[req, client, TEST_CTX](ReqResult result, const HttpResponsePtr &resp) {
REQUIRE(result == ReqResult::Ok);
auto req1 = HttpRequest::newHttpRequest();
req1->setMethod(drogon::Get);
req1->setPath("/slow");
client->sendRequest(
req1,
[req1, TEST_CTX](ReqResult result,
const HttpResponsePtr &resp1) {
REQUIRE(result == ReqResult::Ok);
auto &json = resp1->jsonObject();
REQUIRE(json != nullptr);
REQUIRE(json->get("message", std::string("")).asString() ==
"Access interval should be "
"at least 10 "
"seconds");
});
});
/// Test gzip
req = HttpRequest::newHttpRequest();
req->setMethod(drogon::Get);
req->addHeader("accept-encoding", "gzip");
req->setPath("/api/v1/apitest/get/111");
client->sendRequest(req,
[req, TEST_CTX](ReqResult result,
const HttpResponsePtr &resp) {
REQUIRE(result == ReqResult::Ok);
CHECK(resp->getBody().length() == 4994UL);
});
/// Test brotli
#ifdef USE_BROTLI
req = HttpRequest::newHttpRequest();
req->setMethod(drogon::Get);
req->addHeader("accept-encoding", "br");
req->setPath("/api/v1/apitest/get/111");
client->sendRequest(req,
[req, TEST_CTX](ReqResult result,
const HttpResponsePtr &resp) {
REQUIRE(result == ReqResult::Ok);
CHECK(resp->getBody().length() == 4994UL);
});
#endif
/// Post json
Json::Value json;
json["request"] = "json";
req = HttpRequest::newCustomHttpRequest(json);
req->setMethod(drogon::Post);
req->setPath("/api/v1/apitest/json");
client->sendRequest(req,
[req, TEST_CTX](ReqResult result,
const HttpResponsePtr &resp) {
REQUIRE(result == ReqResult::Ok);
std::shared_ptr<Json::Value> ret = *resp;
REQUIRE(ret != nullptr);
CHECK((*ret)["result"].asString() == "ok");
});
// Post json again
req = HttpRequest::newHttpJsonRequest(json);
assert(req->jsonObject());
req->setMethod(drogon::Post);
req->setPath("/api/v1/apitest/json");
client->sendRequest(req,
[req, TEST_CTX](ReqResult result,
const HttpResponsePtr &resp) {
REQUIRE(result == ReqResult::Ok);
std::shared_ptr<Json::Value> ret = *resp;
REQUIRE(ret != nullptr);
CHECK((*ret)["result"].asString() == "ok");
});
// Post json again
req = HttpRequest::newHttpJsonRequest(json);
req->setMethod(drogon::Post);
req->setPath("/api/v1/apitest/json");
client->sendRequest(req,
[req, TEST_CTX](ReqResult result,
const HttpResponsePtr &resp) {
REQUIRE(result == ReqResult::Ok);
std::shared_ptr<Json::Value> ret = *resp;
REQUIRE(ret != nullptr);
CHECK((*ret)["result"].asString() == "ok");
});
// Test 404
req = HttpRequest::newHttpJsonRequest(json);
req->setMethod(drogon::Get);
req->setPath("/api/v1/apitest/notFoundRouting");
client->sendRequest(req,
[req, TEST_CTX](ReqResult result,
const HttpResponsePtr &resp) {
REQUIRE(result == ReqResult::Ok);
CHECK(resp->getStatusCode() == k404NotFound);
});
/// 1 Get /
req = HttpRequest::newHttpRequest();
req->setMethod(drogon::Get);
req->setPath("/");
client->sendRequest(req,
[req, TEST_CTX](ReqResult result,
const HttpResponsePtr &resp) {
REQUIRE(result == ReqResult::Ok);
CHECK(resp->getBody() == "<p>Hello, world!</p>");
// LOG_DEBUG << resp->getBody();
});
/// 3. Post to /tpost to test Http Method constraint
req = HttpRequest::newHttpRequest();
req->setMethod(drogon::Get);
req->setPath("/tpost");
client->sendRequest(
req, [req, TEST_CTX](ReqResult result, const HttpResponsePtr &resp) {
REQUIRE(result == ReqResult::Ok);
CHECK(resp->getStatusCode() == k405MethodNotAllowed);
});
req = HttpRequest::newHttpRequest();
req->setMethod(drogon::Post);
req->setPath("/tpost");
client->sendRequest(req,
[req, TEST_CTX](ReqResult result,
const HttpResponsePtr &resp) {
REQUIRE(result == ReqResult::Ok);
CHECK(resp->getBody() == "<p>Hello, world!</p>");
});
/// 4. Http OPTIONS Method
req = HttpRequest::newHttpRequest();
req->setMethod(drogon::Options);
req->setPath("/tpost");
client->sendRequest(req,
[req, TEST_CTX](ReqResult result,
const HttpResponsePtr &resp) {
REQUIRE(result == ReqResult::Ok);
CHECK(resp->statusCode() == k200OK);
auto allow = resp->getHeader("allow");
CHECK(allow.find("POST") != std::string::npos);
});
req = HttpRequest::newHttpRequest();
req->setMethod(drogon::Options);
req->setPath("/api/v1/apitest");
client->sendRequest(req,
[req, TEST_CTX](ReqResult result,
const HttpResponsePtr &resp) {
REQUIRE(result == ReqResult::Ok);
CHECK(resp->statusCode() == k200OK);
auto allow = resp->getHeader("allow");
CHECK(allow == "OPTIONS,GET,HEAD,POST");
});
req = HttpRequest::newHttpRequest();
req->setMethod(drogon::Options);
req->setPath("/slow");
client->sendRequest(req,
[req, TEST_CTX](ReqResult result,
const HttpResponsePtr &resp) {
REQUIRE(result == ReqResult::Ok);
CHECK(resp->statusCode() == k403Forbidden);
});
req = HttpRequest::newHttpRequest();
req->setMethod(drogon::Options);
req->setPath("/*");
client->sendRequest(
req, [req, TEST_CTX](ReqResult result, const HttpResponsePtr &resp) {
REQUIRE(result == ReqResult::Ok);
CHECK(resp->statusCode() == k200OK);
auto allow = resp->getHeader("allow");
CHECK(allow == "GET,HEAD,POST,PUT,DELETE,OPTIONS,PATCH");
});
req = HttpRequest::newHttpRequest();
req->setMethod(drogon::Options);
req->setPath("/api/v1/apitest/static");
client->sendRequest(req,
[req, TEST_CTX](ReqResult result,
const HttpResponsePtr &resp) {
REQUIRE(result == ReqResult::Ok);
CHECK(resp->statusCode() == k200OK);
auto allow = resp->getHeader("allow");
CHECK(allow == "OPTIONS,GET,HEAD");
});
/// 4. Test HttpController
req = HttpRequest::newHttpRequest();
req->setMethod(drogon::Post);
req->setPath("/api/v1/apitest");
client->sendRequest(req,
[req, TEST_CTX](ReqResult result,
const HttpResponsePtr &resp) {
REQUIRE(result == ReqResult::Ok);
CHECK(resp->getBody() == "ROOT Post!!!");
});
req = HttpRequest::newHttpRequest();
req->setMethod(drogon::Get);
req->setPath("/api/v1/apitest");
client->sendRequest(req,
[req, TEST_CTX](ReqResult result,
const HttpResponsePtr &resp) {
REQUIRE(result == ReqResult::Ok);
CHECK(resp->getBody() == "ROOT Get!!!");
});
req = HttpRequest::newHttpRequest();
req->setMethod(drogon::Get);
req->setPath("/api/v1/apitest/get/abc/123");
client->sendRequest(
req, [req, TEST_CTX](ReqResult result, const HttpResponsePtr &resp) {
REQUIRE(result == ReqResult::Ok);
auto body = resp->getBody();
CHECK(body.find("<td>p1</td>\n <td>123</td>") !=
std::string::npos);
CHECK(body.find("<td>p2</td>\n <td>abc</td>") !=
std::string::npos);
});
req = HttpRequest::newHttpRequest();
req->setMethod(drogon::Get);
req->setPath("/api/v1/apitest/3.14/List");
req->setParameter("P2", "1234");
client->sendRequest(
req, [req, TEST_CTX](ReqResult result, const HttpResponsePtr &resp) {
REQUIRE(result == ReqResult::Ok);
auto body = resp->getBody();
CHECK(body.find("<td>p1</td>\n <td>3.140000</td>") !=
std::string::npos);
CHECK(body.find("<td>p2</td>\n <td>1234</td>") !=
std::string::npos);
});
req = HttpRequest::newHttpRequest();
req->setMethod(drogon::Get);
req->setPath("/reg/123/rest/of/the/path");
client->sendRequest(
req, [req, TEST_CTX](ReqResult result, const HttpResponsePtr &resp) {
REQUIRE(result == ReqResult::Ok);
REQUIRE(resp->getJsonObject() != nullptr);
auto &json = resp->getJsonObject();
CHECK(json->isMember("p1"));
CHECK(json->get("p1", 0).asInt() == 123);
CHECK(json->isMember("p2"));
CHECK(json->get("p2", "").asString() == "rest/of/the/path");
});
req = HttpRequest::newHttpRequest();
req->setMethod(drogon::Get);
req->setPath("/api/v1/apitest/static");
client->sendRequest(req,
[req, TEST_CTX](ReqResult result,
const HttpResponsePtr &resp) {
REQUIRE(result == ReqResult::Ok);
CHECK(resp->getBody() == "staticApi,hello!!");
});
// auto loop = app().loop();
req = HttpRequest::newHttpRequest();
req->setMethod(drogon::Post);
req->setPath("/api/v1/apitest/static");
client->sendRequest(req,
[req, TEST_CTX](ReqResult result,
const HttpResponsePtr &resp) {
REQUIRE(result == ReqResult::Ok);
CHECK(resp->getBody() == "staticApi,hello!!");
});
req = HttpRequest::newHttpRequest();
req->setMethod(drogon::Get);
req->setPath("/api/v1/apitest/get/111");
client->sendRequest(req,
[req, TEST_CTX](ReqResult result,
const HttpResponsePtr &resp) {
REQUIRE(result == ReqResult::Ok);
CHECK(resp->getBody().length() == 4994UL);
});
/// Test method routing, see MethodTest.h
req = HttpRequest::newHttpRequest();
req->setMethod(drogon::Post);
req->setPath("/api/method/regex/drogon/test?test=drogon");
client->sendRequest(req,
[req, TEST_CTX](ReqResult result,
const HttpResponsePtr &resp) {
REQUIRE(result == ReqResult::Ok);
auto jsonPtr = resp->getJsonObject();
CHECK(jsonPtr);
CHECK(jsonPtr->asString() == "POST");
});
req = HttpRequest::newHttpRequest();
req->setMethod(drogon::Get);
req->setPath("/api/method/regex/drogon/test");
client->sendRequest(req,
[req, TEST_CTX](ReqResult result,
const HttpResponsePtr &resp) {
REQUIRE(result == ReqResult::Ok);
auto jsonPtr = resp->getJsonObject();
CHECK(jsonPtr);
CHECK(jsonPtr->asString() == "GET");
});
req = HttpRequest::newHttpRequest();
req->setMethod(drogon::Post);
req->setPath("/api/method/drogon/test?test=drogon");
client->sendRequest(req,
[req, TEST_CTX](ReqResult result,
const HttpResponsePtr &resp) {
REQUIRE(result == ReqResult::Ok);
auto jsonPtr = resp->getJsonObject();
CHECK(jsonPtr);
CHECK(jsonPtr->asString() == "POST");
});
req = HttpRequest::newHttpRequest();
req->setMethod(drogon::Get);
req->setPath("/api/method/drogon/test");
client->sendRequest(req,
[req, TEST_CTX](ReqResult result,
const HttpResponsePtr &resp) {
REQUIRE(result == ReqResult::Ok);
auto jsonPtr = resp->getJsonObject();
CHECK(jsonPtr);
CHECK(jsonPtr->asString() == "GET");
});
req = HttpRequest::newHttpRequest();
req->setMethod(drogon::Post);
req->setPath("/api/method/test?test=drogon");
client->sendRequest(req,
[req, TEST_CTX](ReqResult result,
const HttpResponsePtr &resp) {
REQUIRE(result == ReqResult::Ok);
auto jsonPtr = resp->getJsonObject();
CHECK(jsonPtr);
CHECK(jsonPtr->asString() == "POST");
});
req = HttpRequest::newHttpRequest();
req->setMethod(drogon::Get);
req->setPath("/api/method/test");
client->sendRequest(req,
[req, TEST_CTX](ReqResult result,
const HttpResponsePtr &resp) {
REQUIRE(result == ReqResult::Ok);
auto jsonPtr = resp->getJsonObject();
CHECK(jsonPtr);
CHECK(jsonPtr->asString() == "GET");
});
/// Test static function
req = HttpRequest::newHttpRequest();
req->setMethod(drogon::Get);
req->setPath("/api/v1/handle11/11/2 2/?p3=3 x");
req->setParameter("p4", "44");
client->sendRequest(
req, [req, TEST_CTX](ReqResult result, const HttpResponsePtr &resp) {
REQUIRE(result == ReqResult::Ok);
auto body = resp->getBody();
CHECK(body.find("<td>int p1</td>\n <td>11</td>") !=
std::string::npos);
CHECK(body.find("<td>int p4</td>\n <td>44</td>") !=
std::string::npos);
CHECK(body.find("<td>string p2</td>\n <td>2 2</td>") !=
std::string::npos);
CHECK(body.find("<td>string p3</td>\n <td>3 x</td>") !=
std::string::npos);
});
/// Test Incomplete URL
req = HttpRequest::newHttpRequest();
req->setMethod(drogon::Get);
req->setPath("/api/v1/handle11/11/2 2/");
client->sendRequest(
req, [req, TEST_CTX](ReqResult result, const HttpResponsePtr &resp) {
REQUIRE(result == ReqResult::Ok);
auto body = resp->getBody();
CHECK(body.find("<td>int p1</td>\n <td>11</td>") !=
std::string::npos);
CHECK(body.find("<td>int p4</td>\n <td>0</td>") !=
std::string::npos);
CHECK(body.find("<td>string p2</td>\n <td>2 2</td>") !=
std::string::npos);
CHECK(body.find("<td>string p3</td>\n <td></td>") !=
std::string::npos);
});
/// Test lambda
req = HttpRequest::newHttpRequest();
req->setMethod(drogon::Get);
req->setPath("/api/v1/handle2/111/222");
client->sendRequest(
req, [req, TEST_CTX](ReqResult result, const HttpResponsePtr &resp) {
REQUIRE(result == ReqResult::Ok);
auto body = resp->getBody();
CHECK(body.find("<td>a</td>\n <td>111</td>") !=
std::string::npos);
CHECK(body.find("<td>b</td>\n <td>222.000000</td>") !=
std::string::npos);
});
/// Test std::bind and std::function
req = HttpRequest::newHttpRequest();
req->setMethod(drogon::Get);
req->setPath("/api/v1/handle4/444/333/111");
client->sendRequest(
req, [req, TEST_CTX](ReqResult result, const HttpResponsePtr &resp) {
REQUIRE(result == ReqResult::Ok);
auto body = resp->getBody();
CHECK(body.find("<td>int p1</td>\n <td>111</td>") !=
std::string::npos);
CHECK(body.find("<td>int p4</td>\n <td>444</td>") !=
std::string::npos);
CHECK(body.find("<td>string p2</td>\n <td></td>") !=
std::string::npos);
CHECK(body.find("<td>string p3</td>\n <td>333</td>") !=
std::string::npos);
});
/// Test gzip_static
req = HttpRequest::newHttpRequest();
req->setMethod(drogon::Get);
req->setPath("/index.html");
client->sendRequest(
req, [req, TEST_CTX](ReqResult result, const HttpResponsePtr &resp) {
REQUIRE(result == ReqResult::Ok);
CHECK(resp->getBody().length() == indexLen);
CHECK(resp->contentType() == CT_TEXT_HTML);
CHECK(resp->contentTypeString().find("text/html") == 0);
});
req = HttpRequest::newHttpRequest();
req->setMethod(drogon::Get);
req->setPath("/index.html");
req->addHeader("accept-encoding", "gzip");
client->sendRequest(req,
[req, TEST_CTX](ReqResult result,
const HttpResponsePtr &resp) {
REQUIRE(result == ReqResult::Ok);
CHECK(resp->contentType() == CT_TEXT_HTML);
CHECK(resp->getBody().length() == indexLen);
});
/// Test serving file with non-ASCII files
req = HttpRequest::newHttpRequest();
req->setPath("/中文.txt");
client->sendRequest(req,
[req, TEST_CTX](ReqResult result,
const HttpResponsePtr &resp) {
REQUIRE(result == ReqResult::Ok);
// Only tests for serving a file, not the content
// since no good way to read it on Windows witout
// using the wild-char API
});
/// Test custom content types
req = HttpRequest::newHttpRequest();
req->setPath("/test.md");
client->sendRequest(req,
[req, TEST_CTX](ReqResult result,
const HttpResponsePtr &resp) {
REQUIRE(result == ReqResult::Ok);
CHECK(resp->contentType() == CT_CUSTOM);
CHECK(resp->contentTypeString() == "text/markdown");
});
/// Unknown files shoul be application/octet-stream
req = HttpRequest::newHttpRequest();
req->setPath("/main.cc");
client->sendRequest(
req, [req, TEST_CTX](ReqResult result, const HttpResponsePtr &resp) {
REQUIRE(result == ReqResult::Ok);
REQUIRE(resp->contentType() == CT_APPLICATION_OCTET_STREAM);
});
// Test 405
req = HttpRequest::newHttpRequest();
req->setMethod(drogon::Post);
req->setPath("/drogon.jpg");
client->sendRequest(req,
[req, client, TEST_CTX](ReqResult result,
const HttpResponsePtr &resp) {
REQUIRE(result == ReqResult::Ok);
CHECK(resp->getStatusCode() ==
k405MethodNotAllowed);
});
/// Test file download
req = HttpRequest::newHttpRequest();
req->setMethod(drogon::Get);
req->setPath("/drogon.jpg");
client->sendRequest(
req,
[req, client, TEST_CTX](ReqResult result, const HttpResponsePtr &resp) {
REQUIRE(result == ReqResult::Ok);
REQUIRE(resp->getBody().length() == JPG_LEN);
auto &lastModified = resp->getHeader("last-modified");
// LOG_DEBUG << lastModified;
// Test 'Not Modified'
auto req = HttpRequest::newHttpRequest();
req->setMethod(drogon::Get);
req->setPath("/drogon.jpg");
req->addHeader("If-Modified-Since", lastModified);
client->sendRequest(req,
[req, TEST_CTX](ReqResult result,
const HttpResponsePtr &resp) {
REQUIRE(result == ReqResult::Ok);
REQUIRE(resp->statusCode() ==
k304NotModified);
// pro.set_value(1);
});
});
/// Test file download, It is forbidden to download files from the
/// parent folder
req = HttpRequest::newHttpRequest();
req->setMethod(drogon::Get);
req->setPath("/../../drogon.jpg");
client->sendRequest(req,
[req, TEST_CTX](ReqResult result,
const HttpResponsePtr &resp) {
REQUIRE(result == ReqResult::Ok);
CHECK(resp->statusCode() == k403Forbidden);
});
/// Test controllers created and initialized by users
req = HttpRequest::newHttpRequest();
req->setPath("/customctrl/antao");
req->addHeader("custom_header", "yes");
client->sendRequest(req,
[req, TEST_CTX](ReqResult result,
const HttpResponsePtr &resp) {
REQUIRE(result == ReqResult::Ok);
CHECK(resp->getBody() == "<P>Hi, antao</P>");
});
/// Test controllers created and initialized by users
req = HttpRequest::newHttpRequest();
req->setPath("/absolute/123");
client->sendRequest(req,
[req, TEST_CTX](ReqResult result,
const HttpResponsePtr &resp) {
REQUIRE(result == ReqResult::Ok);
CHECK(resp->statusCode() == k200OK);
});
/// Test synchronous advice
req = HttpRequest::newHttpRequest();
req->setPath("/plaintext");
client->sendRequest(req,
[req, TEST_CTX](ReqResult result,
const HttpResponsePtr &resp) {
REQUIRE(result == ReqResult::Ok);
CHECK(resp->getBody() == "Hello, World!");
});
/// Test form post
req = HttpRequest::newHttpFormPostRequest();
req->setPath("/api/v1/apitest/form");
req->setParameter("k1", "1");
req->setParameter("k2", "");
req->setParameter("k3", "test@example.com");
client->sendRequest(req,
[req, TEST_CTX](ReqResult result,
const HttpResponsePtr &resp) {
REQUIRE(result == ReqResult::Ok);
auto ret = resp->getJsonObject();
REQUIRE(ret != nullptr);
CHECK((*ret)["result"].asString() == "ok");
});
/// Test attributes
req = HttpRequest::newHttpRequest();
req->setMethod(drogon::Get);
req->setPath("/api/v1/apitest/attrs");
client->sendRequest(req,
[req, TEST_CTX](ReqResult result,
const HttpResponsePtr &resp) {
REQUIRE(result == ReqResult::Ok);
auto ret = resp->getJsonObject();
REQUIRE(ret != nullptr);
CHECK((*ret)["result"].asString() == "ok");
});
/// Test attachment download
req = HttpRequest::newHttpRequest();
req->setMethod(drogon::Get);
req->setPath("/api/attachment/download");
client->sendRequest(req,
[req, TEST_CTX](ReqResult result,
const HttpResponsePtr &resp) {
REQUIRE(result == ReqResult::Ok);
CHECK(resp->getBody().length() == JPG_LEN);
});
// Test implicit pages
auto body = std::make_shared<std::string>();
req = HttpRequest::newHttpRequest();
req->setMethod(drogon::Get);
req->setPath("/a-directory");
client->sendRequest(req,
[req, TEST_CTX, body](ReqResult result,
const HttpResponsePtr &resp) {
REQUIRE(result == ReqResult::Ok);
CHECK(resp->getBody().length() == indexImplicitLen);
*body = std::string(resp->getBody().data(),
resp->getBody().length());
});
req = HttpRequest::newHttpRequest();
req->setMethod(drogon::Get);
req->setPath("/a-directory/page.html");
client->sendRequest(req,
[req, TEST_CTX, body](ReqResult result,
const HttpResponsePtr &resp) {
REQUIRE(result == ReqResult::Ok);
CHECK(resp->getBody().length() == indexImplicitLen);
CHECK(std::equal(body->begin(),
body->end(),
resp->getBody().begin()));
});
// Test file upload
UploadFile file1("./中文.txt");
UploadFile file2("./drogon.jpg", "drogon1.jpg");
UploadFile file3("./config.example.json", "config.json", "file3");
req = HttpRequest::newFileUploadRequest({file1, file2, file3});
req->setPath("/api/attachment/upload");
req->setParameter("P1", "upload");
req->setParameter("P2", "test");
client->sendRequest(req,
[req, TEST_CTX](ReqResult result,
const HttpResponsePtr &resp) {
REQUIRE(result == ReqResult::Ok);
auto json = resp->getJsonObject();
REQUIRE(json != nullptr);
CHECK((*json)["result"].asString() == "ok");
CHECK((*json)["P1"] == "upload");
CHECK((*json)["P2"] == "test");
});
// Test file upload, file type and extension interface.
UploadFile image("./drogon.jpg");
req = HttpRequest::newFileUploadRequest({image});
req->setPath("/api/attachment/uploadImage");
req->setParameter("P1", "upload");
req->setParameter("P2", "test");
client->sendRequest(req,
[req, TEST_CTX](ReqResult result,
const HttpResponsePtr &resp) {
REQUIRE(result == ReqResult::Ok);
auto json = resp->getJsonObject();
REQUIRE(json != nullptr);
CHECK((*json)["P1"] == "upload");
CHECK((*json)["P2"] == "test");
});
// Test newFileResponse
req = HttpRequest::newHttpRequest();
req->setPath("/RangeTestController/");
client->sendRequest(
req, [req, TEST_CTX](ReqResult result, const HttpResponsePtr &resp) {
REQUIRE(result == ReqResult::Ok);
CHECK(resp->getStatusCode() == k200OK);
CHECK(resp->getBody().size() == 1'000'000);
CHECK(resp->getHeader("Content-Length") == "1000000");
});
req = HttpRequest::newHttpRequest();
req->setPath("/RangeTestController/999980/0");
client->sendRequest(req,
[req, TEST_CTX](ReqResult result,
const HttpResponsePtr &resp) {
REQUIRE(result == ReqResult::Ok);
CHECK(resp->getStatusCode() == k206PartialContent);
CHECK(resp->getBody() == "01234567890123456789");
});
// Test > 200k
req = HttpRequest::newHttpRequest();
req->setPath("/RangeTestController/1/500000");
client->sendRequest(
req, [req, TEST_CTX](ReqResult result, const HttpResponsePtr &resp) {
REQUIRE(result == ReqResult::Ok);
CHECK(resp->getBody().size() == 500'000);
CHECK(resp->getHeader("Content-Length") == "500000");
CHECK(resp->getHeader("Content-Range") == "bytes 1-500000/1000000");
});
req = HttpRequest::newHttpRequest();
req->setPath("/RangeTestController/10/20");
client->sendRequest(
req, [req, TEST_CTX](ReqResult result, const HttpResponsePtr &resp) {
LOG_DEBUG << "result=" << (int)result;
REQUIRE(result == ReqResult::Ok);
CHECK(resp->getStatusCode() == k206PartialContent);
CHECK(resp->getBody() == "01234567890123456789");
CHECK(resp->getHeader("Content-Length") == "20");
CHECK(resp->getHeader("Content-Range") == "bytes 10-29/1000000");
});
// Test invalid range
req = HttpRequest::newHttpRequest();
req->setPath("/RangeTestController/0/2000000");
client->sendRequest(
req, [req, TEST_CTX](ReqResult result, const HttpResponsePtr &resp) {
REQUIRE(result == ReqResult::Ok);
CHECK(resp->getHeader("Content-Range") == "bytes */1000000");
CHECK(resp->getStatusCode() == k416RequestedRangeNotSatisfiable);
});
//
// Test StaticFileRouter with range header
//
req = HttpRequest::newHttpRequest();
req->setPath("/range-test.txt");
req->setMethod(drogon::Head);
client->sendRequest(
req, [req, TEST_CTX](ReqResult result, const HttpResponsePtr &resp) {
REQUIRE(result == ReqResult::Ok);
CHECK(resp->getStatusCode() == k200OK);
CHECK(resp->getHeader("content-length") == "1000000");
CHECK(resp->getHeader("accept-range") == "bytes");
});
req = HttpRequest::newHttpRequest();
req->setPath("/range-test.txt");
req->addHeader("range", "bytes=0-19");
client->sendRequest(req,
[req, TEST_CTX](ReqResult result,
const HttpResponsePtr &resp) {
REQUIRE(result == ReqResult::Ok);
CHECK(resp->getStatusCode() == k206PartialContent);
CHECK(resp->getBody() == "01234567890123456789");
});
req = HttpRequest::newHttpRequest();
req->setPath("/range-test.txt");
req->addHeader("range", "bytes=-20");
client->sendRequest(req,
[req, TEST_CTX](ReqResult result,
const HttpResponsePtr &resp) {
REQUIRE(result == ReqResult::Ok);
CHECK(resp->getStatusCode() == k206PartialContent);
CHECK(resp->getBody() == "01234567890123456789");
});
req = HttpRequest::newHttpRequest();
req->setPath("/range-test.txt");
req->addHeader("range", "bytes=999980-");
client->sendRequest(req,
[req, TEST_CTX](ReqResult result,
const HttpResponsePtr &resp) {
REQUIRE(result == ReqResult::Ok);
CHECK(resp->getStatusCode() == k206PartialContent);
CHECK(resp->getBody() == "01234567890123456789");
});
// Using .. to access a upper directory should be permitted as long as
// it never leaves the document root
req = HttpRequest::newHttpRequest();
req->setMethod(drogon::Get);
req->setPath("/a-directory/../index.html");
client->sendRequest(req,
[req, TEST_CTX](ReqResult result,
const HttpResponsePtr &resp) {
REQUIRE(result == ReqResult::Ok);
CHECK(resp->getBody().length() == indexLen);
});
// . (current directory) shall also be allowed
req = HttpRequest::newHttpRequest();
req->setMethod(drogon::Get);
req->setPath("/a-directory/./page.html");
client->sendRequest(req,
[req, TEST_CTX, body](ReqResult result,
const HttpResponsePtr &resp) {
REQUIRE(result == ReqResult::Ok);
CHECK(resp->getBody().length() == indexImplicitLen);
CHECK(std::equal(body->begin(),
body->end(),
resp->getBody().begin()));
});
// Test exception handling
req = HttpRequest::newHttpRequest();
req->setMethod(drogon::Get);
req->setPath("/api/v1/this_will_fail");
client->sendRequest(
req, [req, TEST_CTX](ReqResult result, const HttpResponsePtr &resp) {
REQUIRE(result == ReqResult::Ok);
CHECK(resp->getStatusCode() == k500InternalServerError);
});
// The result of this API is cached for (almost) forever. And the endpoint
// increments a internal counter on each invoke. This tests if the respond
// is taken from the cache after the first invoke.
// Try poking the cache test endpoint 3 times. They should all respond 0
// since the first respond is cached by the server.
req = HttpRequest::newHttpRequest();
req->setMethod(drogon::Get);
req->setPath("/api/v1/ApiTest/cacheTest");
client->sendRequest(req,
[req, TEST_CTX](ReqResult result,
const HttpResponsePtr &resp) {
REQUIRE(result == ReqResult::Ok);
CHECK(resp->getStatusCode() == k200OK);
CHECK(resp->body() == "0");
});
req = HttpRequest::newHttpRequest();
req->setMethod(drogon::Get);
req->setPath("/api/v1/ApiTest/cacheTest");
client->sendRequest(req,
[req, TEST_CTX](ReqResult result,
const HttpResponsePtr &resp) {
REQUIRE(result == ReqResult::Ok);
CHECK(resp->getStatusCode() == k200OK);
CHECK(resp->body() == "0");
});
req = HttpRequest::newHttpRequest();
req->setMethod(drogon::Get);
req->setPath("/api/v1/ApiTest/cacheTest");
client->sendRequest(req,
[req, TEST_CTX](ReqResult result,
const HttpResponsePtr &resp) {
REQUIRE(result == ReqResult::Ok);
CHECK(resp->getStatusCode() == k200OK);
CHECK(resp->body() == "0");
});
// This API caches it's result on the third (counting from 1) calls. Thus
// we expect to always see 2 upon the third call. And all previous calls
// should be less than or equal to 2, as another test is also poking the API
req = HttpRequest::newHttpRequest();
req->setMethod(drogon::Get);
req->setPath("/api/v1/ApiTest/cacheTest2");
client->sendRequest(
req, [req, TEST_CTX](ReqResult result, const HttpResponsePtr &resp) {
REQUIRE(result == ReqResult::Ok);
CHECK(resp->getStatusCode() == k200OK);
int n = 100;
CHECK_NOTHROW(n = std::stoi(std::string(resp->body())));
CHECK(n <= 2);
});
req = HttpRequest::newHttpRequest();
req->setMethod(drogon::Get);
req->setPath("/api/v1/ApiTest/cacheTest2");
client->sendRequest(
req, [req, TEST_CTX](ReqResult result, const HttpResponsePtr &resp) {
REQUIRE(result == ReqResult::Ok);
CHECK(resp->getStatusCode() == k200OK);
int n = 100;
CHECK_NOTHROW(n = std::stoi(std::string(resp->body())));
CHECK(n <= 2);
});
req = HttpRequest::newHttpRequest();
req->setMethod(drogon::Get);
req->setPath("/api/v1/ApiTest/cacheTest2");
client->sendRequest(req,
[req, TEST_CTX](ReqResult result,
const HttpResponsePtr &resp) {
REQUIRE(result == ReqResult::Ok);
CHECK(resp->getStatusCode() == k200OK);
CHECK(resp->body() == "2");
});
// Same as cacheTest2. But the server has to handle this API through regex.
// it is intentionally made that the final part of the path can't conatin
// a "z" character
req = HttpRequest::newHttpRequest();
req->setMethod(drogon::Get);
req->setPath("/cacheTestRegex/foobar");
client->sendRequest(
req, [req, TEST_CTX](ReqResult result, const HttpResponsePtr &resp) {
REQUIRE(result == ReqResult::Ok);
CHECK(resp->getStatusCode() == k200OK);
int n = 100;
CHECK_NOTHROW(n = std::stoi(std::string(resp->body())));
CHECK(n <= 2);
});
req = HttpRequest::newHttpRequest();
req->setMethod(drogon::Get);
req->setPath("/cacheTestRegex/deadbeef");
client->sendRequest(
req, [req, TEST_CTX](ReqResult result, const HttpResponsePtr &resp) {
REQUIRE(result == ReqResult::Ok);
CHECK(resp->getStatusCode() == k200OK);
int n = 100;
CHECK_NOTHROW(n = std::stoi(std::string(resp->body())));
CHECK(n <= 2);
});
req = HttpRequest::newHttpRequest();
req->setMethod(drogon::Get);
req->setPath("/cacheTestRegex/leet");
client->sendRequest(req,
[req, TEST_CTX](ReqResult result,
const HttpResponsePtr &resp) {
REQUIRE(result == ReqResult::Ok);
CHECK(resp->getStatusCode() == k200OK);
CHECK(resp->body() == "2");
});
req = HttpRequest::newHttpRequest();
req->setMethod(drogon::Get);
req->setPath("/cacheTestRegex/zebra");
client->sendRequest(req,
[req, TEST_CTX](ReqResult result,
const HttpResponsePtr &resp) {
REQUIRE(result == ReqResult::Ok);
CHECK(resp->getStatusCode() == k404NotFound);
});
// Post compressed data
req = HttpRequest::newHttpRequest();
std::string deadbeef = "deadbeef";
req->setPath("/api/v1/ApiTest/echoBody");
req->addHeader("Content-Encoding", "gzip");
req->setMethod(drogon::Post);
req->setBody(utils::gzipCompress(deadbeef.c_str(), deadbeef.size()));
client->sendRequest(req,
[deadbeef, TEST_CTX](ReqResult result,
const HttpResponsePtr &resp) {
REQUIRE(result == ReqResult::Ok);
CHECK(resp->getStatusCode() == k200OK);
CHECK(resp->body() == deadbeef);
});
std::string largeString(128 * 1024, 'a'); // 128KB of 'a'
req = HttpRequest::newHttpRequest();
req->setPath("/api/v1/ApiTest/echoBody");
req->addHeader("Content-Encoding", "gzip");
req->setMethod(drogon::Post);
req->setBody(utils::gzipCompress(largeString.c_str(), largeString.size()));
client->sendRequest(req,
[largeString, TEST_CTX](ReqResult result,
const HttpResponsePtr &resp) {
REQUIRE(result == ReqResult::Ok);
CHECK(resp->getStatusCode() == k200OK);
CHECK(resp->body() == largeString);
});
#ifdef USE_BROTLI
// Post compressed data
req = HttpRequest::newHttpRequest();
req->setPath("/api/v1/ApiTest/echoBody");
req->addHeader("Content-Encoding", "br");
req->setMethod(drogon::Post);
req->setBody(utils::brotliCompress(deadbeef.c_str(), deadbeef.size()));
client->sendRequest(req,
[deadbeef, TEST_CTX](ReqResult result,
const HttpResponsePtr &resp) {
REQUIRE(result == ReqResult::Ok);
CHECK(resp->getStatusCode() == k200OK);
CHECK(resp->body() == deadbeef);
});
req = HttpRequest::newHttpRequest();
req->setPath("/api/v1/ApiTest/echoBody");
req->addHeader("Content-Encoding", "br");
req->setMethod(drogon::Post);
req->setBody(
utils::brotliCompress(largeString.c_str(), largeString.size()));
client->sendRequest(req,
[largeString, TEST_CTX](ReqResult result,
const HttpResponsePtr &resp) {
REQUIRE(result == ReqResult::Ok);
CHECK(resp->getStatusCode() == k200OK);
CHECK(resp->body() == largeString);
});
#endif
#if defined(__cpp_impl_coroutine)
async_run([client, TEST_CTX]() -> Task<> {
// Test coroutine requests
try
{
auto req = HttpRequest::newHttpRequest();
req->setPath("/api/v1/corotest/get");
auto resp = co_await client->sendRequestCoro(req);
CHECK(resp->getBody() == "DEADBEEF");
}
catch (const std::exception &e)
{
FAIL("Unexpected exception, what()" + std::string(e.what()));
}
// Test coroutine request with co_return
try
{
auto req = HttpRequest::newHttpRequest();
req->setPath("/api/v1/corotest/get2");
auto resp = co_await client->sendRequestCoro(req);
CHECK(resp->getBody() == "BADDBEEF");
}
catch (const std::exception &e)
{
FAIL("Unexpected exception, what()" + std::string(e.what()));
}
// Test Coroutine exception
try
{
auto req = HttpRequest::newHttpRequest();
req->setPath("/api/v1/corotest/this_will_fail");
auto resp = co_await client->sendRequestCoro(req);
CHECK(resp->getStatusCode() != k200OK);
}
catch (const std::exception &e)
{
FAIL("Unexpected exception, what()" + std::string(e.what()));
}
// Test Coroutine exception with co_return
try
{
auto req = HttpRequest::newHttpRequest();
req->setPath("/api/v1/corotest/this_will_fail2");
auto resp = co_await client->sendRequestCoro(req);
CHECK(resp->getStatusCode() != k200OK);
}
catch (const std::exception &e)
{
FAIL("Unexpected exception, what()" + std::string(e.what()));
}
// Test coroutine filter
try
{
auto req = HttpRequest::newHttpRequest();
auto start = std::chrono::system_clock::now();
req->setPath("/api/v1/corotest/delay?secs=2");
auto resp = co_await client->sendRequestCoro(req);
CHECK(resp->getStatusCode() == k200OK);
auto end = std::chrono::system_clock::now();
std::chrono::duration<double, std::milli> duration = end - start;
CHECK(duration.count() >= 2000);
}
catch (const std::exception &e)
{
FAIL("Unexpected exception, what()" + std::string(e.what()));
}
// Test coroutine handler with parameters
try
{
auto req = HttpRequest::newHttpRequest();
req->setPath("/api/v1/corotest/get_with_param/some_data");
auto resp = co_await client->sendRequestCoro(req);
CHECK(resp->getStatusCode() == k200OK);
CHECK(resp->getBody() == "some_data");
}
catch (const std::exception &e)
{
FAIL("Unexpected exception, what()" + std::string(e.what()));
}
try
{
auto req = HttpRequest::newHttpRequest();
req->setPath("/api/v1/corotest/get_with_param2/some_data");
auto resp = co_await client->sendRequestCoro(req);
CHECK(resp->getStatusCode() == k200OK);
CHECK(resp->getBody() == "some_data");
}
catch (const std::exception &e)
{
FAIL("Unexpected exception, what()" + std::string(e.what()));
}
});
#endif
}
void loadFileLengths()
{
struct stat filestat;
if (stat("index.html", &filestat) < 0)
{
LOG_SYSERR << "Unable to retrieve index.html file sizes";
exit(1);
}
indexLen = filestat.st_size;
if (stat("a-directory/page.html", &filestat) < 0)
{
LOG_SYSERR << "Unable to retrieve a-directory/page.html file sizes";
exit(1);
}
indexImplicitLen = filestat.st_size;
}
DROGON_TEST(HttpTest)
{
auto client = HttpClient::newHttpClient("http://127.0.0.1:8848");
client->setPipeliningDepth(10);
REQUIRE(client->secure() == false);
REQUIRE(client->port() == 8848);
REQUIRE(client->host() == "127.0.0.1");
REQUIRE(client->onDefaultPort() == false);
doTest(client, TEST_CTX);
}
DROGON_TEST(HttpsTest)
{
if (!app().supportSSL())
return;
auto client = HttpClient::newHttpClient("https://127.0.0.1:8849",
app().getLoop(),
false,
false);
client->setPipeliningDepth(10);
REQUIRE(client->secure() == true);
REQUIRE(client->port() == 8849);
REQUIRE(client->host() == "127.0.0.1");
REQUIRE(client->onDefaultPort() == false);
doTest(client, TEST_CTX);
}
DROGON_TEST(HttpsTimeoutTest)
{
if (!app().supportSSL())
return;
auto client = HttpClient::newHttpClient("https://127.0.0.1:8849",
app().getLoop(),
false,
false);
auto req = HttpRequest::newHttpRequest();
req->setPath("/api/v1/apitest/static");
req->setMethod(drogon::Get);
auto weakClient = std::weak_ptr<HttpClient>(client);
auto weakReq = std::weak_ptr<HttpRequest>(req);
client->sendRequest(
req,
[weakClient, weakReq, TEST_CTX](ReqResult result,
const HttpResponsePtr &resp) {
REQUIRE(result == ReqResult::Ok);
CHECK(resp->getStatusCode() == k200OK);
app().getLoop()->queueInLoop([weakClient, weakReq, TEST_CTX]() {
CHECK(weakReq.expired());
CHECK(weakClient.expired());
});
},
60);
}
int main(int argc, char **argv)
{
trantor::Logger::setLogLevel(trantor::Logger::LogLevel::kDebug);
loadFileLengths();
std::promise<void> p1;
std::future<void> f1 = p1.get_future();
std::thread thr([&p1]() {
app().getLoop()->queueInLoop([&p1]() { p1.set_value(); });
app().run();
});
f1.get();
int testStatus = test::run(argc, argv);
app().getLoop()->queueInLoop([]() { app().quit(); });
thr.join();
return testStatus;
}