123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537 |
- /***************************************************************************
- *
- * Project _____ __ ____ _ _
- * ( _ ) /__\ (_ _)_| |_ _| |_
- * )(_)( /(__)\ )( (_ _)(_ _)
- * (_____)(__)(__)(__) |_| |_|
- *
- *
- * Copyright 2018-present, Leonid Stryzhevskyi <lganzzzo@gmail.com>
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- ***************************************************************************/
- #include "FullTest.hpp"
- #include "oatpp/web/app/Client.hpp"
- #include "oatpp/web/app/ControllerWithInterceptors.hpp"
- #include "oatpp/web/app/ControllerWithErrorHandler.hpp"
- #include "oatpp/web/app/Controller.hpp"
- #include "oatpp/web/app/BasicAuthorizationController.hpp"
- #include "oatpp/web/app/BearerAuthorizationController.hpp"
- #include "oatpp/web/client/HttpRequestExecutor.hpp"
- #include "oatpp/web/server/HttpConnectionHandler.hpp"
- #include "oatpp/web/server/HttpRouter.hpp"
- #include "oatpp/parser/json/mapping/ObjectMapper.hpp"
- #include "oatpp/network/tcp/server/ConnectionProvider.hpp"
- #include "oatpp/network/tcp/client/ConnectionProvider.hpp"
- #include "oatpp/network/virtual_/client/ConnectionProvider.hpp"
- #include "oatpp/network/virtual_/server/ConnectionProvider.hpp"
- #include "oatpp/network/virtual_/Interface.hpp"
- #include "oatpp/core/data/resource/InMemoryData.hpp"
- #include "oatpp/core/macro/component.hpp"
- #include "oatpp-test/web/ClientServerTestRunner.hpp"
- namespace oatpp { namespace test { namespace web {
- namespace {
- typedef oatpp::web::mime::multipart::PartList PartList;
- typedef oatpp::web::protocol::http::outgoing::MultipartBody MultipartBody;
- class TestComponent {
- private:
- v_uint16 m_port;
- public:
- TestComponent(v_uint16 port)
- : m_port(port)
- {}
- OATPP_CREATE_COMPONENT(std::shared_ptr<oatpp::network::virtual_::Interface>, virtualInterface)([] {
- return oatpp::network::virtual_::Interface::obtainShared("virtualhost");
- }());
- OATPP_CREATE_COMPONENT(std::shared_ptr<oatpp::network::ServerConnectionProvider>, serverConnectionProvider)([this] {
- if(m_port == 0) { // Use oatpp virtual interface
- OATPP_COMPONENT(std::shared_ptr<oatpp::network::virtual_::Interface>, _interface);
- return std::static_pointer_cast<oatpp::network::ServerConnectionProvider>(
- oatpp::network::virtual_::server::ConnectionProvider::createShared(_interface)
- );
- }
- return std::static_pointer_cast<oatpp::network::ServerConnectionProvider>(
- oatpp::network::tcp::server::ConnectionProvider::createShared({"localhost", m_port})
- );
- }());
- OATPP_CREATE_COMPONENT(std::shared_ptr<oatpp::web::server::HttpRouter>, httpRouter)([] {
- return oatpp::web::server::HttpRouter::createShared();
- }());
- OATPP_CREATE_COMPONENT(std::shared_ptr<oatpp::network::ConnectionHandler>, serverConnectionHandler)([] {
- OATPP_COMPONENT(std::shared_ptr<oatpp::web::server::HttpRouter>, router);
- return oatpp::web::server::HttpConnectionHandler::createShared(router);
- }());
- OATPP_CREATE_COMPONENT(std::shared_ptr<oatpp::data::mapping::ObjectMapper>, objectMapper)([] {
- return oatpp::parser::json::mapping::ObjectMapper::createShared();
- }());
- OATPP_CREATE_COMPONENT(std::shared_ptr<oatpp::network::ClientConnectionProvider>, clientConnectionProvider)([this] {
- if(m_port == 0) {
- OATPP_COMPONENT(std::shared_ptr<oatpp::network::virtual_::Interface>, _interface);
- return std::static_pointer_cast<oatpp::network::ClientConnectionProvider>(
- oatpp::network::virtual_::client::ConnectionProvider::createShared(_interface)
- );
- }
- return std::static_pointer_cast<oatpp::network::ClientConnectionProvider>(
- oatpp::network::tcp::client::ConnectionProvider::createShared({"localhost", m_port})
- );
- }());
- };
- std::shared_ptr<PartList> createMultipart(const std::unordered_map<oatpp::String, oatpp::String>& map) {
- auto multipart = oatpp::web::mime::multipart::PartList::createSharedWithRandomBoundary();
- for(auto& pair : map) {
- oatpp::web::mime::multipart::Headers partHeaders;
- auto part = std::make_shared<oatpp::web::mime::multipart::Part>(partHeaders);
- multipart->writeNextPartSimple(part);
- part->putHeader("Content-Disposition", "form-data; name=\"" + pair.first + "\"");
- part->setPayload(std::make_shared<oatpp::data::resource::InMemoryData>(pair.second));
- }
- return multipart;
- }
- }
-
- void FullTest::onRun() {
- TestComponent component(m_port);
- oatpp::test::web::ClientServerTestRunner runner;
- runner.addController(app::Controller::createShared());
- runner.addController(app::ControllerWithInterceptors::createShared());
- runner.addController(app::ControllerWithErrorHandler::createShared());
- runner.addController(app::DefaultBasicAuthorizationController::createShared());
- runner.addController(app::BasicAuthorizationController::createShared());
- runner.addController(app::BearerAuthorizationController::createShared());
- runner.run([this, &runner] {
- OATPP_COMPONENT(std::shared_ptr<oatpp::network::ClientConnectionProvider>, clientConnectionProvider);
- OATPP_COMPONENT(std::shared_ptr<oatpp::data::mapping::ObjectMapper>, objectMapper);
- auto requestExecutor = oatpp::web::client::HttpRequestExecutor::createShared(clientConnectionProvider);
- auto client = app::Client::createShared(requestExecutor, objectMapper);
- auto connection = client->getConnection();
- OATPP_ASSERT(connection);
- v_int32 iterationsStep = m_iterationsPerStep;
- auto lastTick = oatpp::base::Environment::getMicroTickCount();
- for(v_int32 i = 0; i < iterationsStep * 10; i ++) {
- { // test simple GET
- auto response = client->getRoot(connection);
- OATPP_ASSERT(response->getStatusCode() == 200);
- auto value = response->readBodyToString();
- OATPP_ASSERT(value == "Hello World!!!");
- }
- { // test simple GET with CORS
- auto response = client->getCors(connection);
- OATPP_ASSERT(response->getStatusCode() == 200);
- auto value = response->readBodyToString();
- OATPP_ASSERT(value == "Ping");
- auto header = response->getHeader(oatpp::web::protocol::http::Header::CORS_ORIGIN);
- OATPP_ASSERT(header);
- OATPP_ASSERT(header == "*");
- header = response->getHeader(oatpp::web::protocol::http::Header::CORS_METHODS);
- OATPP_ASSERT(header);
- OATPP_ASSERT(header == "GET, POST, OPTIONS");
- header = response->getHeader(oatpp::web::protocol::http::Header::CORS_HEADERS);
- OATPP_ASSERT(header);
- OATPP_ASSERT(header == "DNT, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Range, Authorization");
- }
- { // test simple OPTIONS with CORS
- auto response = client->optionsCors(connection);
- OATPP_ASSERT(response->getStatusCode() == 204);
- auto header = response->getHeader(oatpp::web::protocol::http::Header::CORS_ORIGIN);
- OATPP_ASSERT(header);
- OATPP_ASSERT(header == "*");
- header = response->getHeader(oatpp::web::protocol::http::Header::CORS_METHODS);
- OATPP_ASSERT(header);
- OATPP_ASSERT(header == "GET, POST, OPTIONS");
- header = response->getHeader(oatpp::web::protocol::http::Header::CORS_HEADERS);
- OATPP_ASSERT(header);
- OATPP_ASSERT(header == "DNT, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Range, Authorization");
- }
- { // test simple GET with CORS
- auto response = client->getCorsOrigin(connection);
- OATPP_ASSERT(response->getStatusCode() == 200);
- auto value = response->readBodyToString();
- OATPP_ASSERT(value == "Pong");
- auto header = response->getHeader(oatpp::web::protocol::http::Header::CORS_ORIGIN);
- OATPP_ASSERT(header);
- OATPP_ASSERT(header == "127.0.0.1");
- header = response->getHeader(oatpp::web::protocol::http::Header::CORS_METHODS);
- OATPP_ASSERT(header);
- OATPP_ASSERT(header == "GET, POST, OPTIONS");
- header = response->getHeader(oatpp::web::protocol::http::Header::CORS_HEADERS);
- OATPP_ASSERT(header);
- OATPP_ASSERT(header == "DNT, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Range, Authorization");
- }
- { // test simple GET with CORS
- auto response = client->getCorsOriginMethods(connection);
- OATPP_ASSERT(response->getStatusCode() == 200);
- auto value = response->readBodyToString();
- OATPP_ASSERT(value == "Ping");
- auto header = response->getHeader(oatpp::web::protocol::http::Header::CORS_ORIGIN);
- OATPP_ASSERT(header);
- OATPP_ASSERT(header == "127.0.0.1");
- header = response->getHeader(oatpp::web::protocol::http::Header::CORS_METHODS);
- OATPP_ASSERT(header);
- OATPP_ASSERT(header == "GET, OPTIONS");
- header = response->getHeader(oatpp::web::protocol::http::Header::CORS_HEADERS);
- OATPP_ASSERT(header);
- OATPP_ASSERT(header == "DNT, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Range, Authorization");
- }
- { // test simple GET with CORS
- auto response = client->getCorsOriginMethodsHeader(connection);
- OATPP_ASSERT(response->getStatusCode() == 200);
- auto value = response->readBodyToString();
- OATPP_ASSERT(value == "Pong");
- auto header = response->getHeader(oatpp::web::protocol::http::Header::CORS_ORIGIN);
- OATPP_ASSERT(header);
- OATPP_ASSERT(header == "127.0.0.1");
- header = response->getHeader(oatpp::web::protocol::http::Header::CORS_METHODS);
- OATPP_ASSERT(header);
- OATPP_ASSERT(header == "GET, OPTIONS");
- header = response->getHeader(oatpp::web::protocol::http::Header::CORS_HEADERS);
- OATPP_ASSERT(header);
- OATPP_ASSERT(header == "X-PWNT");
- }
- { // test GET with path parameter
- auto response = client->getWithParams("my_test_param", connection);
- OATPP_ASSERT(response->getStatusCode() == 200);
- auto dto = response->readBodyToDto<oatpp::Object<app::TestDto>>(objectMapper.get());
- OATPP_ASSERT(dto);
- OATPP_ASSERT(dto->testValue == "my_test_param");
- }
- { // test GET with query parameters
- auto response = client->getWithQueries("oatpp", 1, connection);
- OATPP_ASSERT(response->getStatusCode() == 200);
- auto dto = response->readBodyToDto<oatpp::Object<app::TestDto>>(objectMapper.get());
- OATPP_ASSERT(dto);
- OATPP_ASSERT(dto->testValue == "name=oatpp&age=1");
- }
- { // test GET with optional query parameters
- auto response = client->getWithOptQueries("oatpp", connection);
- OATPP_ASSERT(response->getStatusCode() == 200);
- auto dto = response->readBodyToDto<oatpp::Object<app::TestDto>>(objectMapper.get());
- OATPP_ASSERT(dto);
- OATPP_ASSERT(dto->testValue == "name=oatpp&age=101");
- }
- { // test GET with query parameters
- auto response = client->getWithQueriesMap("value1", 32, 0.32f, connection);
- OATPP_ASSERT(response->getStatusCode() == 200);
- auto dto = response->readBodyToDto<oatpp::Object<app::TestDto>>(objectMapper.get());
- OATPP_ASSERT(dto);
- OATPP_ASSERT(dto->testMap);
- OATPP_ASSERT(dto->testMap->size() == 3);
- OATPP_ASSERT(dto->testMap["key1"] == "value1");
- OATPP_ASSERT(dto->testMap["key2"] == "32");
- OATPP_ASSERT(dto->testMap["key3"] == oatpp::utils::conversion::float32ToStr(0.32f));
- }
- { // test GET with header parameter
- auto response = client->getWithHeaders("my_test_header", connection);
- OATPP_ASSERT(response->getStatusCode() == 200);
- auto dto = response->readBodyToDto<oatpp::Object<app::TestDto>>(objectMapper.get());
- OATPP_ASSERT(dto);
- OATPP_ASSERT(dto->testValue == "my_test_header");
- }
- { // test POST with body
- auto response = client->postBody("my_test_body", connection);
- OATPP_ASSERT(response->getStatusCode() == 200);
- auto dto = response->readBodyToDto<oatpp::Object<app::TestDto>>(objectMapper.get());
- OATPP_ASSERT(dto);
- OATPP_ASSERT(dto->testValue == "my_test_body");
- }
- { // test POST with dto body
- auto dtoIn = app::TestDto::createShared();
- dtoIn->testValueInt = i;
- auto response = client->postBodyDto(dtoIn, connection);
- OATPP_ASSERT(response->getStatusCode() == 200);
- auto dtoOut = response->readBodyToDto<oatpp::Object<app::TestDto>>(objectMapper.get());
- OATPP_ASSERT(dtoOut);
- OATPP_ASSERT(dtoOut->testValueInt == i);
- }
- { // test Enum as String
- OATPP_ASSERT(oatpp::Enum<app::AllowedPathParams>::getEntries().size() == 2);
- oatpp::Enum<app::AllowedPathParams> v = app::AllowedPathParams::HELLO;
- auto response = client->getHeaderEnumAsString(v, connection);
- OATPP_ASSERT(response->getStatusCode() == 200);
- }
- { // test Enum as String
- oatpp::Enum<app::AllowedPathParams> v = app::AllowedPathParams::HELLO;
- auto response = client->getHeaderEnumAsNumber(v, connection);
- OATPP_ASSERT(response->getStatusCode() == 200);
- }
- { // test Big Echo with body
- oatpp::data::stream::BufferOutputStream stream;
- for(v_int32 i = 0; i < oatpp::data::buffer::IOBuffer::BUFFER_SIZE; i++) {
- stream.writeSimple("0123456789", 10);
- }
- auto data = stream.toString();
- auto response = client->echoBody(data, connection);
- OATPP_ASSERT(response->getStatusCode() == 200);
- auto returnedData = response->readBodyToString();
- OATPP_ASSERT(returnedData);
- OATPP_ASSERT(returnedData == data);
- }
- {
- auto response = client->headerValueSet(" VALUE_1, VALUE_2, VALUE_3", connection);
- OATPP_ASSERT(response->getStatusCode() == 200);
- }
- {
- auto response = client->getDefaultHeaders1(connection);
- OATPP_ASSERT(response->getStatusCode() == 200);
- }
- {
- auto response = client->getDefaultHeaders2("some param", connection);
- OATPP_ASSERT(response->getStatusCode() == 200);
- }
- { // test custom authorization handler with custom authorization object
- auto response = client->defaultBasicAuthorization("foo:bar", connection);
- OATPP_ASSERT(response->getStatusCode() == 200);
- }
- { // test call of an endpoint that requiers authorization headers, but we don't send one
- auto response = client->defaultBasicAuthorizationWithoutHeader();
- OATPP_ASSERT(response->getStatusCode() == 401);
- oatpp::String body = response->readBodyToString();
- OATPP_ASSERT(body == "server=oatpp/" OATPP_VERSION "\n"
- "code=401\n"
- "description=Unauthorized\n"
- "message=Authorization Required\n");
- // should also add the WWW-Authenticate header when Authorization is missing
- auto header = response->getHeader(oatpp::web::protocol::http::Header::WWW_AUTHENTICATE);
- OATPP_ASSERT(header);
- OATPP_ASSERT(header == "Basic realm=\"default-test-realm\"");
- }
- { // test custom authorization handler with custom authorization object
- auto response = client->customBasicAuthorization("foo:bar", connection);
- OATPP_ASSERT(response->getStatusCode() == 200);
- }
- { // test call of an endpoint that requiers authorization headers, but we don't send one
- auto response = client->customBasicAuthorizationWithoutHeader();
- OATPP_ASSERT(response->getStatusCode() == 401);
- oatpp::String body = response->readBodyToString();
- OATPP_ASSERT(body == "server=oatpp/" OATPP_VERSION "\n"
- "code=401\n"
- "description=Unauthorized\n"
- "message=Authorization Required\n");
- // should also add the WWW-Authenticate header when Authorization is missing
- auto header = response->getHeader(oatpp::web::protocol::http::Header::WWW_AUTHENTICATE);
- OATPP_ASSERT(header);
- OATPP_ASSERT(header == "Basic realm=\"custom-test-realm\"");
- }
- { // test custom authorization handler with custom authorization object with unknown credentials where the
- // handler returns nullptr
- auto response = client->customBasicAuthorization("john:doe");
- oatpp::String body = response->readBodyToString();
- OATPP_ASSERT(response->getStatusCode() == 401);
- OATPP_ASSERT(body == "server=oatpp/" OATPP_VERSION "\n"
- "code=401\n"
- "description=Unauthorized\n"
- "message=Unauthorized\n");
- // should also add the WWW-Authenticate header when Authorization is missing or wrong
- auto header = response->getHeader(oatpp::web::protocol::http::Header::WWW_AUTHENTICATE);
- OATPP_ASSERT(header);
- OATPP_ASSERT(header == "Basic realm=\"custom-test-realm\"");
- }
- { // test custom authorization handler with custom authorization method
- oatpp::String token = "4e99e8c12de7e01535248d2bac85e732";
- auto response = client->bearerAuthorization(token);
- oatpp::String body = response->readBodyToString();
- OATPP_ASSERT(response->getStatusCode() == 200);
- }
- { // test custom authorization handler with custom authorization object with unknown credentials where the
- // handler returns nullptr
- oatpp::String token = "900150983cd24fb0d6963f7d28e17f72";
- auto response = client->bearerAuthorization(token);
- oatpp::String body = response->readBodyToString();
- OATPP_ASSERT(response->getStatusCode() == 401);
- OATPP_ASSERT(body == "server=oatpp/" OATPP_VERSION "\n"
- "code=401\n"
- "description=Unauthorized\n"
- "message=Unauthorized\n");
- // should also add the WWW-Authenticate header when Authorization is missing or wrong
- auto header = response->getHeader(oatpp::web::protocol::http::Header::WWW_AUTHENTICATE);
- OATPP_ASSERT(header);
- OATPP_ASSERT(header == "Bearer realm=\"custom-bearer-realm\"");
- }
- { // test Chunked body
- oatpp::String sample = "__abcdefghijklmnopqrstuvwxyz-0123456789";
- v_int32 numIterations = 10;
- oatpp::data::stream::BufferOutputStream stream;
- for(v_int32 i = 0; i < numIterations; i++) {
- stream.writeSimple(sample->data(), sample->size());
- }
- auto data = stream.toString();
- auto response = client->getChunked(sample, numIterations, connection);
- OATPP_ASSERT(response->getStatusCode() == 200);
- auto returnedData = response->readBodyToString();
- OATPP_ASSERT(returnedData);
- OATPP_ASSERT(returnedData == data);
- }
- { // Multipart body
- std::unordered_map<oatpp::String, oatpp::String> map;
- map["value1"] = "Hello";
- map["value2"] = "World";
- auto multipart = createMultipart(map);
- auto body = std::make_shared<MultipartBody>(multipart);
- auto response = client->multipartTest(i + 1, body);
- OATPP_ASSERT(response->getStatusCode() == 200);
- multipart = std::make_shared<oatpp::web::mime::multipart::PartList>(response->getHeaders());
- oatpp::web::mime::multipart::Reader multipartReader(multipart.get());
- multipartReader.setPartReader("value1", oatpp::web::mime::multipart::createInMemoryPartReader(10));
- multipartReader.setPartReader("value2", oatpp::web::mime::multipart::createInMemoryPartReader(10));
- response->transferBody(&multipartReader);
- OATPP_ASSERT(multipart->getAllParts().size() == 2);
- auto part1 = multipart->getNamedPart("value1");
- auto part2 = multipart->getNamedPart("value2");
- OATPP_ASSERT(part1);
- OATPP_ASSERT(part1->getPayload());
- OATPP_ASSERT(part2);
- OATPP_ASSERT(part2->getPayload());
- OATPP_ASSERT(part1->getPayload()->getInMemoryData() == "Hello");
- OATPP_ASSERT(part2->getPayload()->getInMemoryData() == "World");
- }
- { // test interceptors
- auto response = client->getInterceptors(connection);
- OATPP_ASSERT(response->getStatusCode() == 200);
- auto value = response->readBodyToString();
- OATPP_ASSERT(value == "Hello World!!!");
- }
- { // test controller's error handler catches
- auto response = client->getCaughtError(connection);
- OATPP_ASSERT(response->getStatusCode() == 418);
- auto value = response->readBodyToString();
- OATPP_ASSERT(value == "Controller With Errors!");
- }
- { // test header replacement
- auto response = client->getInterceptors(connection);
- OATPP_ASSERT(response->getStatusCode() == 200);
- OATPP_ASSERT(response->getHeader("to-be-replaced") == "replaced_value");
- }
- if((i + 1) % iterationsStep == 0) {
- auto ticks = oatpp::base::Environment::getMicroTickCount() - lastTick;
- lastTick = oatpp::base::Environment::getMicroTickCount();
- OATPP_LOGV("i", "%d, tick=%d", i + 1, ticks);
- }
- { // test bundle
- auto response = client->getBundle(connection);
- OATPP_ASSERT(response->getStatusCode() == 200);
- auto dto = response->readBodyToDto<oatpp::Object<app::TestDto>>(objectMapper.get());
- OATPP_ASSERT(dto);
- OATPP_ASSERT(dto->testValue == "str-param");
- OATPP_ASSERT(dto->testValueInt == 32000);
- }
- { // test host header
- auto response = client->getHostHeader(connection);
- OATPP_ASSERT(response->getStatusCode() == 200);
- auto value = response->readBodyToString();
- auto host = clientConnectionProvider->getProperty("host");
- OATPP_ASSERT(host);
- OATPP_ASSERT(value == host.toString() + ":" + oatpp::utils::conversion::int32ToStr(m_port));
- }
- }
- }, std::chrono::minutes(10));
- std::this_thread::sleep_for(std::chrono::seconds(1));
- }
-
- }}}
|