diff mbox series

[v3] channel_curl: Add ability to define a set of custom HTTP headers

Message ID 20210909130819.329015-1-sava.jakovljev@teufel.de
State Changes Requested
Headers show
Series [v3] channel_curl: Add ability to define a set of custom HTTP headers | expand

Commit Message

Sava Jakovljev Sept. 9, 2021, 1:08 p.m. UTC
* Expand Suricatta/Hawkbit client with ability to define a custom set of
  HTTP requests, defined through command line or configuration file,
  using 'a' short option or "custom-http-header" long option.

Signed-off-by: Sava Jakovljev <sava.jakovljev@teufel.de>
---
 corelib/channel_curl.c     |  8 +++++---
 corelib/downloader.c       |  3 ++-
 doc/source/swupdate.rst    |  7 +++++--
 include/channel_curl.h     |  3 ++-
 suricatta/server_general.c | 33 +++++++++++++++++++++++++++------
 suricatta/server_general.h |  3 ++-
 suricatta/server_hawkbit.c | 27 ++++++++++++++++++++++++---
 suricatta/server_hawkbit.h |  1 +
 8 files changed, 68 insertions(+), 17 deletions(-)
diff mbox series

Patch

diff --git a/corelib/channel_curl.c b/corelib/channel_curl.c
index e910f76..bc387d0 100644
--- a/corelib/channel_curl.c
+++ b/corelib/channel_curl.c
@@ -585,7 +585,7 @@  channel_op_res_t channel_set_options(channel_t *this, channel_data_t *channel_da
 		goto cleanup;
 	}

-	if (channel_data->headers) {
+	if (channel_data->received_headers) {
 		/*
 		 * Setup supply request and receive reply HTTP headers.
 		 * A LIST_INIT()'d dictionary is expected at channel_data->headers.
@@ -596,14 +596,16 @@  channel_op_res_t channel_set_options(channel_t *this, channel_data_t *channel_da
 			      CURLOPT_HEADERFUNCTION,
 			      channel_callback_headers) != CURLE_OK) ||
 		    (curl_easy_setopt(channel_curl->handle, CURLOPT_HEADERDATA,
-			      channel_data->headers) != CURLE_OK)) {
+			      channel_data->received_headers) != CURLE_OK)) {
 			result = CHANNEL_EINIT;
 			goto cleanup;
 		}
+	}

+	if (channel_data->headers_to_send) {
 		struct dict_entry *entry;
 		char *header;
-		LIST_FOREACH(entry, channel_data->headers, next)
+		LIST_FOREACH(entry, channel_data->headers_to_send, next)
 		{
 			if (ENOMEM_ASPRINTF ==
 			    asprintf(&header, "%s: %s",
diff --git a/corelib/downloader.c b/corelib/downloader.c
index 437b36d..3d0a0a5 100644
--- a/corelib/downloader.c
+++ b/corelib/downloader.c
@@ -104,7 +104,8 @@  static channel_data_t channel_options = {
 	.source = SOURCE_DOWNLOADER,
 	.debug = false,
 	.retries = DL_DEFAULT_RETRIES,
-	.low_speed_timeout = DL_LOWSPEED_TIME
+	.low_speed_timeout = DL_LOWSPEED_TIME,
+	.headers_to_send = NULL
 };

 int start_download(const char *fname, int argc, char *argv[])
diff --git a/doc/source/swupdate.rst b/doc/source/swupdate.rst
index 317c54d..71f638c 100644
--- a/doc/source/swupdate.rst
+++ b/doc/source/swupdate.rst
@@ -184,7 +184,7 @@  Images fully streamed
 ---------------------

 In case of remote update, SWUpdate extracts relevant images from the stream
-and copies them into the directory pointed to by the environment variable
+and copies them into the directory pointed to by the environment variable
 ``TMPDIR`` (if unset, to ``/tmp``) before calling the handlers.
 This guarantee that an update is initiated only if all parts are present and
 correct. However, on some systems with less resources, the amount of RAM
@@ -652,6 +652,9 @@  Mandatory arguments are marked with '\*':
 |                         |          | during this period - adapt this value to   |
 |                         |          | your use case!                             |
 +-------------------------+----------+--------------------------------------------+
+| -a <name> <value>       | strings  | Custom HTTP header with given value to be  |
+|						  |			 | sent on every HTTP request made.			  |
++-------------------------+----------+--------------------------------------------+


 systemd Integration
@@ -706,7 +709,7 @@  files are also handed over on a "regular" start of SWUpdate via
 ``systemctl start swupdate.service``.

 Note that the socket paths in the two ``ListenStream=`` directives
-have to match the socket paths ``CONFIG_SOCKET_CTRL_PATH`` and
+have to match the socket paths ``CONFIG_SOCKET_CTRL_PATH`` and
 ``CONFIG_SOCKET_PROGRESS_PATH`` in SWUpdate's configuration.
 Here, the default socket path configuration is depicted.

diff --git a/include/channel_curl.h b/include/channel_curl.h
index 4de098e..6dabae6 100644
--- a/include/channel_curl.h
+++ b/include/channel_curl.h
@@ -72,5 +72,6 @@  typedef struct {
 	struct swupdate_digest *dgst;
 	char sha1hash[SWUPDATE_SHA_DIGEST_LENGTH * 2 + 1];
 	sourcetype source;
-	struct dict *headers;
+	struct dict *headers_to_send;
+	struct dict *received_headers;
 } channel_data_t;
diff --git a/suricatta/server_general.c b/suricatta/server_general.c
index 1d11ada..146f02c 100644
--- a/suricatta/server_general.c
+++ b/suricatta/server_general.c
@@ -453,8 +453,9 @@  static server_op_res_t server_get_deployment_info(channel_t *channel, channel_da
 	 */
 	channel_data->url= server_prepare_query(server_general.url, &server_general.configdata);

-	LIST_INIT(&server_general.httpheaders);
-	channel_data->headers = &server_general.httpheaders;
+	LIST_INIT(&server_general.received_httpheaders);
+	LIST_INIT(&server_general.httpheaders_to_send);
+	channel_data->received_headers = &server_general.received_httpheaders;

 	result = map_http_retcode(channel->get(channel, (void *)channel_data));

@@ -462,12 +463,12 @@  static server_op_res_t server_get_deployment_info(channel_t *channel, channel_da
 		free(channel_data->url);
 	}

-	pollstring = dict_get_value(&server_general.httpheaders, "Retry-After");
+	pollstring = dict_get_value(&server_general.received_httpheaders, "Retry-After");
 	if (pollstring) {
 		server_set_polling_interval(pollstring);
 	}

-	dict_drop_db(&server_general.httpheaders);
+	dict_drop_db(&server_general.received_httpheaders);

 	return result;
 }
@@ -522,7 +523,9 @@  void server_print_help(void)
 	    "\t  -w, --retrywait     Time to wait prior to retry and "
 	    "resume a download (default: %ds).\n"
 	    "\t  -y, --proxy         Use proxy. Either give proxy URL, else "
-	    "{http,all}_proxy env is tried.\n",
+	    "{http,all}_proxy env is tried.\n"
+		"\t  -a, --custom-http-header <name> <value> Set custom HTTP header, "
+		"appended to every HTTP request being sent.",
 	    CHANNEL_DEFAULT_POLLING_INTERVAL, CHANNEL_DEFAULT_RESUME_TRIES,
 	    CHANNEL_DEFAULT_RESUME_DELAY);
 }
@@ -608,6 +611,7 @@  server_op_res_t server_start(char *fname, int argc, char *argv[])
 	int choice = 0;

 	LIST_INIT(&server_general.configdata);
+	LIST_INIT(&server_general.httpheaders_to_send);

 	if (fname) {
 		swupdate_cfg_handle handle;
@@ -626,7 +630,7 @@  server_op_res_t server_start(char *fname, int argc, char *argv[])
 	/* reset to optind=1 to parse suricatta's argument vector */
 	optind = 1;
 	opterr = 0;
-	while ((choice = getopt_long(argc, argv, "u:l:r:w:p:2:",
+	while ((choice = getopt_long(argc, argv, "u:l:r:w:p:2:a:",
 				     long_options, NULL)) != -1) {
 		switch (choice) {
 		case 'u':
@@ -652,6 +656,21 @@  server_op_res_t server_start(char *fname, int argc, char *argv[])
 			SETSTRING(server_general.cached_file, optarg);
 			break;
 		/* Ignore not recognized options, they can be already parsed by the caller */
+		case 'a':
+			if (optind >= argc)
+				return SERVER_EINIT;
+
+			char *name = NULL;
+			SETSTRING(name, optarg);
+			char *value = NULL;
+			SETSTRING(value, argv[optind++]);
+
+			if (dict_insert_value(&server_general.httpheaders_to_send,
+						name,
+						value) < 0)
+				return SERVER_EINIT;
+
+			break;
 		case '?':
 		default:
 			break;
@@ -664,6 +683,8 @@  server_op_res_t server_start(char *fname, int argc, char *argv[])
 		return SERVER_EINIT;
 	}

+	channel_data_defaults.headers_to_send = &server_general.httpheaders_to_send;
+
 	if (channel_curl_init() != CHANNEL_OK)
 		return SERVER_EINIT;

diff --git a/suricatta/server_general.h b/suricatta/server_general.h
index 69345a1..d5d8d48 100644
--- a/suricatta/server_general.h
+++ b/suricatta/server_general.h
@@ -23,7 +23,8 @@  typedef struct {
 	bool debug;
 	char *cached_file;
 	struct dict configdata;
-	struct dict httpheaders;
+	struct dict received_httpheaders;
+	struct dict httpheaders_to_send;
 	update_state_t update_state;
 	channel_t *channel;
 } server_general_t;
diff --git a/suricatta/server_hawkbit.c b/suricatta/server_hawkbit.c
index 33d2eec..daa00f0 100644
--- a/suricatta/server_hawkbit.c
+++ b/suricatta/server_hawkbit.c
@@ -52,6 +52,7 @@  static struct option long_options[] = {
     {"cache", required_argument, NULL, '2'},
     {"initial-report-resend-period", required_argument, NULL, 'm'},
 	{"connection-timeout", required_argument, NULL, 's'},
+	{"custom-http-header", required_argument, NULL, 'a'},
     {NULL, 0, NULL, 0}};

 static unsigned short mandatory_argument_count = 0;
@@ -132,7 +133,9 @@  static channel_data_t channel_data_defaults = {.debug = false,
 					       .nocheckanswer = false,
 					       .nofollow = false,
 					       .strictssl = true,
-						   .connection_timeout = 0
+						   .connection_timeout = 0,
+						   .headers_to_send = NULL,
+						   .received_headers = NULL
 						};

 static struct timeval server_time;
@@ -1602,7 +1605,9 @@  void server_print_help(void)
 	    "\t  --cache <file>      Use cache file as starting SWU\n"
 		"\t  -m, --initial-report-resend-period <seconds> Time to wait prior to retry "
 		"sending initial state with '-c' option (default: %ds).\n"
-		"\t  -s, --connection-timeout Set the server connection timeout (default: 300s).\n",
+		"\t  -s, --connection-timeout Set the server connection timeout (default: 300s).\n"
+		"\t  -a, --custom-http-header <name> <value> Set custom HTTP header, "
+		"appended to every HTTP request being sent.",
 	    CHANNEL_DEFAULT_POLLING_INTERVAL, CHANNEL_DEFAULT_RESUME_TRIES,
 	    CHANNEL_DEFAULT_RESUME_DELAY,
 	    INITIAL_STATUS_REPORT_WAIT_DELAY);
@@ -1661,6 +1666,7 @@  server_op_res_t server_start(char *fname, int argc, char *argv[])
 	mandatory_argument_count = 0;

 	LIST_INIT(&server_hawkbit.configdata);
+	LIST_INIT(&server_hawkbit.httpheaders);

 	server_hawkbit.initial_report_resend_period = INITIAL_STATUS_REPORT_WAIT_DELAY;
 	if (fname) {
@@ -1677,6 +1683,9 @@  server_op_res_t server_start(char *fname, int argc, char *argv[])
 			 */
 			read_module_settings(&handle, "hawkbit", server_hawkbit_settings, NULL);
 			read_module_settings(&handle, "identify", settings_into_dict, &server_hawkbit.configdata);
+
+			read_module_settings(&handle, "custom-http-headers",
+					settings_into_dict, &server_hawkbit.httpheaders);
 		}
 		swupdate_cfg_destroy(&handle);
 	}
@@ -1689,7 +1698,7 @@  server_op_res_t server_start(char *fname, int argc, char *argv[])
 	/* reset to optind=1 to parse suricatta's argument vector */
 	optind = 1;
 	opterr = 0;
-	while ((choice = getopt_long(argc, argv, "t:i:c:u:p:xr:y::w:k:g:f:2:m:s:",
+	while ((choice = getopt_long(argc, argv, "t:i:c:u:p:xr:y::w:k:g:f:2:m:s:a:",
 				     long_options, NULL)) != -1) {
 		switch (choice) {
 		case 't':
@@ -1782,6 +1791,16 @@  server_op_res_t server_start(char *fname, int argc, char *argv[])
 			channel_data_defaults.connection_timeout =
 				(unsigned int)strtoul(optarg, NULL, 10);
 			break;
+		case 'a':
+			if (optind >= argc)
+				return SERVER_EINIT;
+
+			if (dict_insert_value(&server_hawkbit.httpheaders,
+						optarg,
+						argv[optind++]) < 0)
+				return SERVER_EINIT;
+
+			break;
 		/* Ignore not recognized options, they can be already parsed by the caller */
 		case '?':
 			break;
@@ -1799,6 +1818,8 @@  server_op_res_t server_start(char *fname, int argc, char *argv[])
 		return SERVER_EINIT;
 	}

+	channel_data_defaults.headers_to_send = &server_hawkbit.httpheaders;
+
 	if (channel_curl_init() != CHANNEL_OK)
 		return SERVER_EINIT;

diff --git a/suricatta/server_hawkbit.h b/suricatta/server_hawkbit.h
index aea4bb0..67f1c31 100644
--- a/suricatta/server_hawkbit.h
+++ b/suricatta/server_hawkbit.h
@@ -29,6 +29,7 @@  typedef struct {
 	bool polling_interval_from_server;
 	bool debug;
 	struct dict configdata;
+	struct dict httpheaders;
 	bool has_to_send_configData;
 	char *configData_url;
 	char *cancel_url;