utils/include/http/BaseHTTPClient.h (177 lines of code) (raw):

/** * * 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. */ #pragma once #include <map> #include <memory> #include <optional> #include <string> #include <vector> #include <utility> #include "utils/ByteArrayCallback.h" #include "minifi-cpp/controllers/SSLContextService.h" #include "utils/gsl.h" namespace org::apache::nifi::minifi::http { struct HTTPProxy { std::string host; std::string username; std::string password; int port = 0; }; class HTTPUploadCallback { public: virtual ~HTTPUploadCallback() = default; virtual size_t getDataChunk(char* data, size_t size) = 0; virtual size_t setPosition(int64_t offset) = 0; virtual size_t size() = 0; virtual void requestStop() = 0; virtual void close() = 0; }; class HTTPUploadByteArrayInputCallback : public HTTPUploadCallback, public utils::ByteInputCallback { public: using ByteInputCallback::ByteInputCallback; size_t getDataChunk(char* data, size_t size) override; size_t setPosition(int64_t offset) override; size_t size() override { return getBufferSize(); } void requestStop() override { stop = true; } void close() override { utils::ByteInputCallback::close(); } std::atomic<bool> stop = false; std::atomic<size_t> pos = 0; }; class HTTPUploadStreamContentsCallback : public HTTPUploadCallback { public: explicit HTTPUploadStreamContentsCallback(std::shared_ptr<io::InputStream> input_stream) : input_stream_{std::move(input_stream)} {} size_t getDataChunk(char* data, size_t size) override; size_t setPosition(int64_t offset) override; private: size_t size() override { return input_stream_->size(); } void requestStop() override {} void close() override {} std::shared_ptr<io::InputStream> input_stream_; std::shared_ptr<core::logging::Logger> logger_ = core::logging::LoggerFactory<HTTPUploadStreamContentsCallback>::getLogger(); }; class HTTPReadCallback : public utils::ByteOutputCallback { public: using ByteOutputCallback::ByteOutputCallback; std::atomic<bool> stop = false; std::atomic<size_t> pos = 0; }; enum class SSLVersion : uint8_t { TLSv1_0, TLSv1_1, TLSv1_2, TLSv1_3 }; struct HTTPHeaderResponse { public: HTTPHeaderResponse() = default; static size_t receive_headers(void *buffer, size_t size, size_t nmemb, void *userp) { auto *pHeaders = static_cast<HTTPHeaderResponse*>(userp); if (pHeaders == nullptr) { return 0U; } pHeaders->header_tokens_.emplace_back(static_cast<char*>(buffer), size * nmemb); return size * nmemb; } [[nodiscard]] const std::vector<std::string>& getHeaderLines() const { return header_tokens_; } void clear() { parsed = false; header_tokens_.clear(); header_mapping_.clear(); } const std::map<std::string, std::string>& getHeaderMap() { if (!parsed) { std::string last_key; bool got_status_line = false; for (const auto& header_line : header_tokens_) { if (header_line.empty()) { /* This should not happen */ continue; } if (!got_status_line) { if (header_line.compare(0, 4, "HTTP") == 0) { /* We got a status line now */ got_status_line = true; header_mapping_.clear(); } /* This is probably a chunked encoding trailer */ continue; } if (header_line == "\r\n") { /* This is the end of the header */ got_status_line = false; continue; } size_t separator_pos = header_line.find(':'); if (separator_pos == std::string::npos) { if (!last_key.empty() && (header_line[0] == ' ' || header_line[0] == '\t')) { // This is a "folded header", which is deprecated (https://www.ietf.org/rfc/rfc7230.txt) but here we are header_mapping_[last_key].append(" " + utils::string::trim(header_line)); } continue; } auto key = header_line.substr(0, separator_pos); /* This will remove leading and trailing LWS and the ending CRLF from the value */ auto value = utils::string::trim(header_line.substr(separator_pos + 1)); header_mapping_[key] = value; last_key = key; } parsed = true; } return header_mapping_; } private: std::vector<std::string> header_tokens_; std::map<std::string, std::string> header_mapping_; bool parsed{false}; }; namespace HTTPRequestResponse { const size_t CALLBACK_ABORT = 0x10000000; const int SEEKFUNC_OK = 0; const int SEEKFUNC_FAIL = 1; size_t receiveWrite(char * data, size_t size, size_t nmemb, void * p); size_t send_write(char * data, size_t size, size_t nmemb, void * p); int seek_callback(void *p, int64_t offset, int); } #undef DELETE // this is a macro in winnt.h enum class HttpRequestMethod { GET, POST, PUT, PATCH, DELETE, CONNECT, HEAD, OPTIONS, TRACE }; class BaseHTTPClient { public: BaseHTTPClient() = default; virtual ~BaseHTTPClient() = default; virtual void setVerbose(bool use_stderr) = 0; virtual void initialize(HttpRequestMethod method, std::string url, std::shared_ptr<minifi::controllers::SSLContextService> ssl_context_service) = 0; virtual void setConnectionTimeout(std::chrono::milliseconds timeout) = 0; virtual void setReadTimeout(std::chrono::milliseconds timeout) = 0; virtual void setUploadCallback(std::unique_ptr<HTTPUploadCallback> callbackObj) = 0; virtual void setContentType(std::string content_type) = 0; virtual std::string escape(std::string string_to_escape) = 0; virtual void setPostFields(const std::string& input) = 0; virtual bool submit() = 0; [[nodiscard]] virtual int64_t getResponseCode() const = 0; virtual const char *getContentType() = 0; virtual void setRequestHeader(std::string key, std::optional<std::string> value) = 0; virtual void set_request_method(HttpRequestMethod method) = 0; virtual void setHTTPProxy(const HTTPProxy &proxy) = 0; virtual void setBasicAuth(const std::string& username, const std::string& password) = 0; virtual void clearBasicAuth() = 0; virtual bool setSpecificSSLVersion(SSLVersion specific_version) = 0; virtual bool setMinimumSSLVersion(SSLVersion minimum_version) = 0; virtual const std::vector<char>& getResponseBody() = 0; virtual const std::vector<std::string>& getResponseHeaders() = 0; virtual const std::map<std::string, std::string>& getResponseHeaderMap() = 0; }; std::string get_token(BaseHTTPClient *client, const std::string& username, const std::string& password); class URL { public: explicit URL(const std::string& url_input); [[nodiscard]] bool isValid() const { return is_valid_; } [[nodiscard]] std::string protocol() const { return protocol_; } [[nodiscard]] std::string host() const { return host_; } [[nodiscard]] int port() const; [[nodiscard]] std::string hostPort() const; [[nodiscard]] std::string toString() const; private: std::string protocol_; std::string host_; std::optional<int> port_; std::optional<std::string> path_; bool is_valid_ = false; std::shared_ptr<core::logging::Logger> logger_ = core::logging::LoggerFactory<URL>::getLogger(); }; } // namespace org::apache::nifi::minifi::http