blob: 5d46957875751ad9dea86f1d836ef7bb5437bf67 [file] [log] [blame]
/**
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 <utility>
#include <vector>
#include <string>
#include "TestBase.h"
#include "Catch.h"
#include "client/HTTPClient.h"
#include "CivetServer.h"
#include "ConnectionCountingServer.h"
#include "magic_enum.hpp"
#include "utils/BaseHTTPClient.h"
using namespace std::literals::chrono_literals;
namespace org::apache::nifi::minifi::extensions::curl::testing {
TEST_CASE("HTTPClientTestChunkedResponse", "[basic]") {
LogTestController::getInstance().setDebug<HTTPClient>();
class Responder : public CivetHandler {
public:
static void send_response(struct mg_connection *conn) {
mg_printf(conn, "HTTP/1.1 200 OK\r\n");
mg_printf(conn, "Content-Type: application/octet-stream\r\n");
mg_printf(conn, "Transfer-Encoding: chunked\r\n");
mg_printf(conn, "X-Custom-Test: whatever\r\n");
mg_printf(conn, "\r\n");
mg_send_chunk(conn, "foo", 3U);
mg_send_chunk(conn, "bar\r\n", 5U);
mg_send_chunk(conn, "buzz", 4U);
mg_send_chunk(conn, nullptr, 0U);
}
bool handleGet(CivetServer* /*server*/, struct mg_connection* conn) override {
send_response(conn);
return true;
}
bool handlePost(CivetServer* /*server*/, struct mg_connection* conn) override {
mg_printf(conn, "HTTP/1.1 100 Continue\r\n\r\n");
std::array<uint8_t, 16384U> buf{};
while (mg_read(conn, buf.data(), buf.size()) > 0) {}
send_response(conn);
return true;
}
};
std::vector<std::string> options;
options.emplace_back("enable_keep_alive");
options.emplace_back("yes");
options.emplace_back("keep_alive_timeout_ms");
options.emplace_back("15000");
options.emplace_back("num_threads");
options.emplace_back("1");
options.emplace_back("listening_ports");
options.emplace_back("0");
CivetServer server(options);
Responder responder;
server.addHandler("**", responder);
const auto& vec = server.getListeningPorts();
REQUIRE(1 == vec.size());
const std::string port = std::to_string(vec.at(0));
HTTPClient client;
client.initialize(utils::HttpRequestMethod::GET, "http://localhost:" + port + "/testytesttest", nullptr);
REQUIRE(client.submit());
const auto& headers = client.getResponseHeaderMap();
REQUIRE("whatever" == headers.at("X-Custom-Test"));
const std::vector<char>& response = client.getResponseBody();
REQUIRE("foobar\r\nbuzz" == std::string(response.begin(), response.end()));
LogTestController::getInstance().reset();
}
TEST_CASE("HTTPClient escape test") {
HTTPClient client;
CHECK(client.escape("Hello Günter") == "Hello%20G%C3%BCnter");
CHECK(client.escape("шеллы") == "%D1%88%D0%B5%D0%BB%D0%BB%D1%8B");
}
TEST_CASE("HTTPClient isValidHttpHeaderField test") {
CHECK_FALSE(HTTPClient::isValidHttpHeaderField(""));
CHECK(HTTPClient::isValidHttpHeaderField("valid"));
CHECK_FALSE(HTTPClient::isValidHttpHeaderField(" "));
CHECK_FALSE(HTTPClient::isValidHttpHeaderField(std::string("invalid") + static_cast<char>(11) + "character"));
CHECK_FALSE(HTTPClient::isValidHttpHeaderField(std::string("invalid") + static_cast<char>(128) + "character"));
CHECK_FALSE(HTTPClient::isValidHttpHeaderField("contains:invalid"));
}
TEST_CASE("HTTPClient replaceInvalidCharactersInHttpHeaderFieldName test") {
CHECK(HTTPClient::replaceInvalidCharactersInHttpHeaderFieldName("") == "X-MiNiFi-Empty-Attribute-Name");
CHECK(HTTPClient::replaceInvalidCharactersInHttpHeaderFieldName("valid") == "valid");
CHECK(HTTPClient::replaceInvalidCharactersInHttpHeaderFieldName(" ") == "-");
CHECK(HTTPClient::replaceInvalidCharactersInHttpHeaderFieldName(std::string("invalid") + static_cast<char>(11) + "character") == "invalid-character");
CHECK(HTTPClient::replaceInvalidCharactersInHttpHeaderFieldName(std::string("invalid") + static_cast<char>(128) + "character") == "invalid-character");
CHECK(HTTPClient::replaceInvalidCharactersInHttpHeaderFieldName("contains:invalid") == "contains-invalid");
}
TEST_CASE("HTTPClient should be reusable", "[basic]") {
LogTestController::getInstance().setDebug<HTTPClient>();
ConnectionCountingServer keep_alive_server_;
HTTPClient client;
client.setKeepAliveProbe(KeepAliveProbeData{2s, 2s});
client.initialize(utils::HttpRequestMethod::GET, "http://localhost:" + keep_alive_server_.getPort() + "/method", nullptr);
std::vector<utils::HttpRequestMethod> methods = {
utils::HttpRequestMethod::GET, utils::HttpRequestMethod::GET, utils::HttpRequestMethod::PUT, utils::HttpRequestMethod::GET,
utils::HttpRequestMethod::GET, utils::HttpRequestMethod::POST, utils::HttpRequestMethod::POST, utils::HttpRequestMethod::PUT };
uint64_t request_number = 0;
for (const auto& method: methods) {
client.set_request_method(method);
if (method != utils::HttpRequestMethod::GET) {
auto callback = std::make_unique<org::apache::nifi::minifi::utils::HTTPUploadByteArrayInputCallback>();
std::string content = R"({ "content": "a" })";
callback->write(content);
client.setRequestHeader("Content-Length", std::to_string(content.size()));
client.setContentType("application/json");
client.setUploadCallback(std::move(callback));
} else {
client.setRequestHeader("Content-Length", std::to_string(0));
client.setUploadCallback(nullptr);
}
REQUIRE(client.submit());
const auto& headers = client.getResponseHeaderMap();
REQUIRE(headers.contains("Response-number"));
CHECK(std::to_string(request_number) == headers.at("Response-number"));
const auto& response = client.getResponseBody();
CHECK(std::string(magic_enum::enum_name(method)) + std::to_string(request_number) == std::string(response.begin(), response.end()));
++request_number;
}
CHECK(keep_alive_server_.getConnectionCounter() == 1);
LogTestController::getInstance().reset();
}
#ifdef __linux__
TEST_CASE("SSL without SSLContextService", "[HTTPClient]") {
HTTPClient client;
client.initialize(utils::HttpRequestMethod::GET, "https://apache.org", nullptr);
REQUIRE(client.submit());
}
#endif
} // namespace org::apache::nifi::minifi::extensions::curl::testing