introduce HTTPClient wrapper around CURL v_0
authorFrantišek Kučera <franta-hg@frantovo.cz>
Sat, 12 Mar 2022 20:48:25 +0100
branchv_0
changeset 5 165f6162524d
parent 4 602462d04c57
child 6 59c9ca066322
introduce HTTPClient wrapper around CURL
CMakeLists.txt
bash-completion.sh
nbproject/configurations.xml
src/CLIParser.h
src/CMakeLists.txt
src/Configuration.h
src/HTTPClient.cpp
src/HTTPClient.h
src/HTTPHandler.h
src/relpipe-tr-http.cpp
--- a/CMakeLists.txt	Sat Mar 12 02:03:44 2022 +0100
+++ b/CMakeLists.txt	Sat Mar 12 20:48:25 2022 +0100
@@ -1,5 +1,5 @@
 # Relational pipes
-# Copyright © 2020 František Kučera (Frantovo.cz, GlobalCode.info)
+# Copyright © 2022 František Kučera (Frantovo.cz, GlobalCode.info)
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
--- a/bash-completion.sh	Sat Mar 12 02:03:44 2022 +0100
+++ b/bash-completion.sh	Sat Mar 12 20:48:25 2022 +0100
@@ -1,5 +1,5 @@
 # Relational pipes
-# Copyright © 2019 František Kučera (Frantovo.cz, GlobalCode.info)
+# Copyright © 2022 František Kučera (Frantovo.cz, GlobalCode.info)
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
--- a/nbproject/configurations.xml	Sat Mar 12 02:03:44 2022 +0100
+++ b/nbproject/configurations.xml	Sat Mar 12 20:48:25 2022 +0100
@@ -42,6 +42,8 @@
   <logicalFolder name="root" displayName="root" projectFiles="true" kind="ROOT">
     <df root="." name="0">
       <df name="src">
+        <in>HTTPClient.cpp</in>
+        <in>HTTPClient.h</in>
         <in>relpipe-tr-http.cpp</in>
       </df>
     </df>
@@ -94,6 +96,10 @@
           <preBuildFirst>true</preBuildFirst>
         </preBuild>
       </makefileType>
+      <item path="src/HTTPClient.cpp" ex="false" tool="1" flavor2="0">
+      </item>
+      <item path="src/HTTPClient.h" ex="false" tool="3" flavor2="0">
+      </item>
       <item path="src/relpipe-tr-http.cpp" ex="false" tool="1" flavor2="0">
         <ccTool flags="0">
         </ccTool>
@@ -134,6 +140,10 @@
           <preBuildFirst>true</preBuildFirst>
         </preBuild>
       </makefileType>
+      <item path="src/HTTPClient.cpp" ex="false" tool="1" flavor2="0">
+      </item>
+      <item path="src/HTTPClient.h" ex="false" tool="3" flavor2="0">
+      </item>
       <item path="src/relpipe-tr-http.cpp" ex="false" tool="1" flavor2="0">
         <ccTool flags="0">
         </ccTool>
--- a/src/CLIParser.h	Sat Mar 12 02:03:44 2022 +0100
+++ b/src/CLIParser.h	Sat Mar 12 20:48:25 2022 +0100
@@ -1,6 +1,6 @@
 /**
  * Relational pipes
- * Copyright © 2020 František Kučera (Frantovo.cz, GlobalCode.info)
+ * Copyright © 2022 František Kučera (Frantovo.cz, GlobalCode.info)
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
--- a/src/CMakeLists.txt	Sat Mar 12 02:03:44 2022 +0100
+++ b/src/CMakeLists.txt	Sat Mar 12 20:48:25 2022 +0100
@@ -1,5 +1,5 @@
 # Relational pipes
-# Copyright © 2020 František Kučera (Frantovo.cz, GlobalCode.info)
+# Copyright © 2022 František Kučera (Frantovo.cz, GlobalCode.info)
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -29,6 +29,7 @@
 # Executable output:
 add_executable(
 	${EXECUTABLE_FILE}
+	HTTPClient.cpp
 	relpipe-tr-http.cpp
 )
 
--- a/src/Configuration.h	Sat Mar 12 02:03:44 2022 +0100
+++ b/src/Configuration.h	Sat Mar 12 20:48:25 2022 +0100
@@ -1,6 +1,6 @@
 /**
  * Relational pipes
- * Copyright © 2020 František Kučera (Frantovo.cz, GlobalCode.info)
+ * Copyright © 2022 František Kučera (Frantovo.cz, GlobalCode.info)
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/HTTPClient.cpp	Sat Mar 12 20:48:25 2022 +0100
@@ -0,0 +1,84 @@
+/**
+ * Relational pipes
+ * Copyright © 2022 František Kučera (Frantovo.cz, GlobalCode.info)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <sstream>
+
+#include <curl/curl.h>
+
+#include "HTTPClient.h"
+
+
+namespace relpipe {
+namespace tr {
+namespace http {
+
+class HTTPClient::HTTPClientImpl {
+public:
+	CURL* curl;
+	std::stringstream responseBody;
+	std::vector<std::string> responseHeaders;
+
+	HTTPClientImpl(CURL* curl) : curl(curl) {
+	}
+
+	static size_t curlWriteCallback(char* buffer, size_t size, size_t nmemb, HTTPClient::HTTPClientImpl * impl) {
+		size_t r = size * nmemb;
+		impl->responseBody.write(buffer, r);
+		return r;
+	}
+
+};
+
+HTTPClient* HTTPClient::open() {
+	return new HTTPClient(new HTTPClient::HTTPClientImpl(curl_easy_init()));
+}
+
+HTTPClient::~HTTPClient() {
+	curl_easy_cleanup(impl->curl);
+	delete impl;
+}
+
+const HTTPClient::Response HTTPClient::exchange(const Request& request) {
+	HTTPClient::Response response;
+
+	// TODO: set request headers
+	// TODO: set request method
+	// TODO: get response headers
+
+	curl_easy_setopt(impl->curl, CURLOPT_URL, request.url.c_str());
+
+	curl_easy_setopt(impl->curl, CURLOPT_WRITEDATA, impl);
+	curl_easy_setopt(impl->curl, CURLOPT_WRITEFUNCTION, HTTPClientImpl::curlWriteCallback);
+
+	// curl_easy_setopt(curl, CURLOPT_HEADERDATA, this);
+	// curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, headersCurlCallback);
+
+	curl_easy_perform(impl->curl);
+
+	curl_easy_getinfo(impl->curl, CURLINFO_RESPONSE_CODE, &response.responseCode);
+	response.success = response.responseCode >= 200 && response.responseCode <= 299;
+
+	response.body = impl->responseBody.str();
+	impl->responseBody = std::stringstream();
+
+	return response;
+}
+
+
+}
+}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/HTTPClient.h	Sat Mar 12 20:48:25 2022 +0100
@@ -0,0 +1,82 @@
+/**
+ * Relational pipes
+ * Copyright © 2022 František Kučera (Frantovo.cz, GlobalCode.info)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#pragma once
+
+#include <string>
+#include <vector>
+
+#include <relpipe/common/type/typedefs.h>
+
+
+namespace relpipe {
+namespace tr {
+namespace http {
+
+/**
+ * Simple synchronous client for the HTTP and HTTPS protocol.
+ * Is not thread-safe – must not be called from multiple threads simultaneously.
+ */
+class HTTPClient {
+private:
+	class HTTPClientImpl;
+	HTTPClientImpl* impl;
+
+	HTTPClient(HTTPClientImpl* impl) : impl(impl) {
+	}
+
+
+public:
+
+	enum class Method {
+		GET,
+		HEAD,
+		POST,
+		PUT,
+		DELETE,
+		// CONNECT,
+		// OPTIONS,
+		// TRACE,
+		PATCH,
+	};
+
+	struct Request {
+		Method method = Method::GET;
+		std::string url;
+		std::vector<std::string> headers;
+		std::string body;
+	};
+
+	struct Response {
+		bool success = false;
+		int responseCode = 0;
+		std::vector<std::string> headers;
+		std::string body;
+	};
+
+	virtual ~HTTPClient();
+	HTTPClient(const HTTPClient&) = delete;
+	HTTPClient& operator=(const HTTPClient&) = delete;
+
+	static HTTPClient* open();
+
+	const Response exchange(const Request& request);
+
+};
+
+}
+}
+}
--- a/src/HTTPHandler.h	Sat Mar 12 02:03:44 2022 +0100
+++ b/src/HTTPHandler.h	Sat Mar 12 20:48:25 2022 +0100
@@ -1,6 +1,6 @@
 /**
  * Relational pipes
- * Copyright © 2020 František Kučera (Frantovo.cz, GlobalCode.info)
+ * Copyright © 2022 František Kučera (Frantovo.cz, GlobalCode.info)
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -36,6 +36,7 @@
 #include <relpipe/cli/RelpipeCLIException.h>
 
 #include "Configuration.h"
+#include "HTTPClient.h"
 
 namespace relpipe {
 namespace tr {
@@ -51,29 +52,6 @@
 	size_t currentAttributeIndex = 0;
 	size_t currentRecordNumber = 1;
 
-	void writeCallback(std::string value) {
-		// TODO: write this attribute even if this method was not called
-		// TODO: support also binary data or other encodings, not only UTF-8 text
-		relationalWriter->writeAttribute(convertor.from_bytes(value));
-	}
-
-	static uint writeCurlCallback(char* buffer, size_t size, size_t nmemb, HTTPHandler* instance) {
-		size_t r = size * nmemb;
-		instance->writeCallback(std::string(buffer, r));
-		return r;
-	}
-
-	void headersCallback(std::string value) {
-		// TODO: parse name=value and store for later use
-		std::cerr << std::endl << "HTTP response headers:" << std::endl << ">>>" << value << "<<<" << std::endl << "--------" << std::endl;
-	}
-
-	static uint headersCurlCallback(char* buffer, size_t size, size_t nmemb, HTTPHandler* instance) {
-		size_t r = size * nmemb;
-		instance->headersCallback(std::string(buffer, r));
-		return r;
-	}
-
 public:
 
 	HTTPHandler(shared_ptr<relpipe::writer::RelationalWriter> relationalWriter, Configuration configuration) : relationalWriter(relationalWriter), configuration(configuration) {
@@ -96,34 +74,30 @@
 		relationalWriter->startRelation(name + L"_curl_response",{
 			{L"url", relpipe::writer::TypeId::STRING},
 			{L"body", relpipe::writer::TypeId::STRING},
-			{L"response_code", relpipe::writer::TypeId::INTEGER}
+			{L"response_code", relpipe::writer::TypeId::INTEGER},
+			// {L"success", relpipe::writer::TypeId::BOOLEAN},
 		}, true);
 
 	}
 
 	void attribute(const relpipe::common::type::StringX& value) override {
 
-		relationalWriter->writeAttribute(value);
-
-		std::string url = convertor.to_bytes(value);
-		relpipe::common::type::Integer responseCode = 0;
+		std::shared_ptr<HTTPClient> http(HTTPClient::open());
 
-		CURL* curl = curl_easy_init();
-		curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
-
-		curl_easy_setopt(curl, CURLOPT_WRITEDATA, this);
-		curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeCurlCallback);
+		HTTPClient::Request request;
+		request.method = HTTPClient::Method::GET;
+		request.url = convertor.to_bytes(value);
 
-		curl_easy_setopt(curl, CURLOPT_HEADERDATA, this);
-		curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, headersCurlCallback);
-
-		curl_easy_perform(curl);
+		HTTPClient::Response response = http->exchange(request);
+		relpipe::common::type::Integer responseCode = response.responseCode;
 
-		curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &responseCode);
+		// std::cerr << "url = >>>" << convertor.to_bytes(value) << "<<<" << std::endl;
+		// std::cerr << "body = >>>" << response.body << "<<<" << std::endl;
 
-		curl_easy_cleanup(curl);
-
+		relationalWriter->writeAttribute(value);
+		relationalWriter->writeAttribute(convertor.from_bytes(response.body));
 		relationalWriter->writeAttribute(&responseCode, typeid (responseCode));
+		// relationalWriter->writeAttribute(&response.success, typeid (response.success));
 	}
 
 	void endOfPipe() {
--- a/src/relpipe-tr-http.cpp	Sat Mar 12 02:03:44 2022 +0100
+++ b/src/relpipe-tr-http.cpp	Sat Mar 12 20:48:25 2022 +0100
@@ -1,6 +1,6 @@
 /**
  * Relational pipes
- * Copyright © 2020 František Kučera (Frantovo.cz, GlobalCode.info)
+ * Copyright © 2022 František Kučera (Frantovo.cz, GlobalCode.info)
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by