diff mbox series

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

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

Commit Message

Sava Jakovljev Sept. 9, 2021, 1:34 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    |  8 ++++++--
 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, 69 insertions(+), 17 deletions(-)

Comments

Sava Jakovljev Sept. 9, 2021, 1:36 p.m. UTC | #1
Hi Stefano,

v4 fixed indentation in documentation sources. Everything else is same as 
in v3, where the leak is fixed.

Best regards,
Sava

On Thursday, September 9, 2021 at 3:35:00 PM UTC+2 Sava Jakovljev wrote:

> * 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.ja...@teufel.de>
> ---
> corelib/channel_curl.c | 8 +++++---
> corelib/downloader.c | 3 ++-
> doc/source/swupdate.rst | 8 ++++++--
> 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, 69 insertions(+), 17 deletions(-)
>
> 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..5b0c150 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,10 @@ Mandatory arguments are marked with '\*':
> | | | during this period - adapt this value to |
> | | | your use case! |
>
> +-------------------------+----------+--------------------------------------------+
> +| -a <name> <value> | strings | Custom HTTP header with given name and |
> +| | | value to be sent with every HTTP request |
> +| | | made. |
>
> ++-------------------------+----------+--------------------------------------------+
>
>
> systemd Integration
> @@ -706,7 +710,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;
>
Stefano Babic Sept. 9, 2021, 1:40 p.m. UTC | #2
Hi Sava,

On 09.09.21 15:34, Sava Jakovljev wrote:
> * 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.
> 

...but I do not see that the leak is fixed, in fact:

> Signed-off-by: Sava Jakovljev <sava.jakovljev@teufel.de>
> ---
>   corelib/channel_curl.c     |  8 +++++---
>   corelib/downloader.c       |  3 ++-
>   doc/source/swupdate.rst    |  8 ++++++--
>   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, 69 insertions(+), 17 deletions(-)
> 
> 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..5b0c150 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,10 @@ Mandatory arguments are marked with '\*':
>   |                         |          | during this period - adapt this value to   |
>   |                         |          | your use case!                             |
>   +-------------------------+----------+--------------------------------------------+
> +| -a <name> <value>       | strings  | Custom HTTP header with given name and     |
> +|                         |          | value to be sent with every HTTP request   |
> +|                         |          | made.                                      |
> ++-------------------------+----------+--------------------------------------------+
> 
> 
>   systemd Integration
> @@ -706,7 +710,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;

the pointer is declared here and the scope is in this case:

> +			SETSTRING(name, optarg);

SETSTRING allocates memory and set name

> +			char *value = NULL;
> +			SETSTRING(value, argv[optind++]);

Same thing for value

> +
> +			if (dict_insert_value(&server_general.httpheaders_to_send,
> +						name,
> +						value) < 0)

both are added to the list, but the list reallocates key/value on its own.


> +				return SERVER_EINIT;
> +
> +			break;

End of scope, memory leak...

>   		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;
> 

Best regards,
Stefano Babic
Sava Jakovljev Sept. 9, 2021, 1:43 p.m. UTC | #3
Hm interesting.

I managed to send a wrong patch file - I don't know how it came to the 
point where my changes were only partially applied. Sorry for this.

v5 coming soon...
Sava

On Thursday, September 9, 2021 at 3:40:25 PM UTC+2 Stefano Babic wrote:

> Hi Sava,
>
> On 09.09.21 15:34, Sava Jakovljev wrote:
> > * 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.
> > 
>
> ...but I do not see that the leak is fixed, in fact:
>
> > Signed-off-by: Sava Jakovljev <sava.ja...@teufel.de>
> > ---
> > corelib/channel_curl.c | 8 +++++---
> > corelib/downloader.c | 3 ++-
> > doc/source/swupdate.rst | 8 ++++++--
> > 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, 69 insertions(+), 17 deletions(-)
> > 
> > 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..5b0c150 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,10 @@ Mandatory arguments are marked with '\*':
> > | | | during this period - adapt this value to |
> > | | | your use case! |
> > 
> +-------------------------+----------+--------------------------------------------+
> > +| -a <name> <value> | strings | Custom HTTP header with given name and |
> > +| | | value to be sent with every HTTP request |
> > +| | | made. |
> > 
> ++-------------------------+----------+--------------------------------------------+
> > 
> > 
> > systemd Integration
> > @@ -706,7 +710,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;
>
> the pointer is declared here and the scope is in this case:
>
> > + SETSTRING(name, optarg);
>
> SETSTRING allocates memory and set name
>
> > + char *value = NULL;
> > + SETSTRING(value, argv[optind++]);
>
> Same thing for value
>
> > +
> > + if (dict_insert_value(&server_general.httpheaders_to_send,
> > + name,
> > + value) < 0)
>
> both are added to the list, but the list reallocates key/value on its own.
>
>
> > + return SERVER_EINIT;
> > +
> > + break;
>
> End of scope, memory leak...
>
> > 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;
> > 
>
> Best regards,
> Stefano Babic
>
> -- 
> =====================================================================
> DENX Software Engineering GmbH, Managing Director: Wolfgang Denk
> HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
> Phone: +49-8142-66989-53 <+49%208142%206698953> Fax: +49-8142-66989-80 
> <+49%208142%206698980> Email: sba...@denx.de
> =====================================================================
>
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..5b0c150 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,10 @@  Mandatory arguments are marked with '\*':
 |                         |          | during this period - adapt this value to   |
 |                         |          | your use case!                             |
 +-------------------------+----------+--------------------------------------------+
+| -a <name> <value>       | strings  | Custom HTTP header with given name and     |
+|                         |          | value to be sent with every HTTP request   |
+|                         |          | made.                                      |
++-------------------------+----------+--------------------------------------------+


 systemd Integration
@@ -706,7 +710,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;