diff mbox series

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

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

Commit Message

Sava Jakovljev Sept. 9, 2021, 3:41 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 | 30 +++++++++++++++++++++++-------
 suricatta/server_general.h |  3 ++-
 suricatta/server_hawkbit.c | 31 ++++++++++++++++++++++++++-----
 suricatta/server_hawkbit.h |  1 +
 8 files changed, 67 insertions(+), 20 deletions(-)

Comments

Sava Jakovljev Sept. 9, 2021, 3:42 p.m. UTC | #1
Should be fine now. 

I also fixed indentation in print_help. 
If there are any other problems or concerns, let me know.

Best regards,
Sava

On Thursday, September 9, 2021 at 5:41:46 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 | 30 +++++++++++++++++++++++-------
> suricatta/server_general.h | 3 ++-
> suricatta/server_hawkbit.c | 31 ++++++++++++++++++++++++++-----
> suricatta/server_hawkbit.h | 1 +
> 8 files changed, 67 insertions(+), 20 deletions(-)
>
> diff --git a/corelib/channel_curl.c b/corelib/channel_curl.c
> index e5a3b0e..0761811 100644
> --- a/corelib/channel_curl.c
> +++ b/corelib/channel_curl.c
> @@ -593,7 +593,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.
> @@ -604,14 +604,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 70375fa..8a60fcc 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..8b76205 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':
> @@ -651,8 +655,18 @@ server_op_res_t server_start(char *fname, int argc, 
> char *argv[])
> case '2':
> 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;
> +
> + if (dict_insert_value(&server_hawkbit.httpheaders,
> + optarg,
> + argv[optind++]) < 0)
> + return SERVER_EINIT;
> +
> + break;
> case '?':
> + /* Ignore not recognized options, they can be already parsed by the 
> caller */
> default:
> break;
> }
> @@ -664,6 +678,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 805158d..9a338d8 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;
> @@ -1604,9 +1607,11 @@ void server_print_help(void)
> "\t -f, --interface Set the network interface to connect to hawkBit.\n"
> "\t --disable-token-for-dwl Do not send authentication header when 
> downlloading SWU.\n"
> "\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 -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 -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);
> @@ -1665,6 +1670,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) {
> @@ -1681,6 +1687,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);
> }
> @@ -1693,7 +1702,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':
> @@ -1786,6 +1795,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;
> @@ -1803,6 +1822,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;
> -- 
> 2.31.1
>
>
Stefano Babic Sept. 15, 2021, 7:50 a.m. UTC | #2
Hi Sava,

On 09.09.21 17:42, Sava Jakovljev wrote:
> Should be fine now.
> 
> I also fixed indentation in print_help.
> If there are any other problems or concerns, let me know.
> 

It does not compile clean, it breaks server-general.

See CI here:

https://source.denx.de/swupdate/swupdate/-/jobs/322334

But also here:

  if (dict_insert_value(&server_general.httpheaders,

because httpheaders is not declared inside derver_general structure. 
Could you take a look and repost, please ?

Best regards,
Stefano

> Best regards,
> Sava
> 
> On Thursday, September 9, 2021 at 5:41:46 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 | 30 +++++++++++++++++++++++-------
>     suricatta/server_general.h | 3 ++-
>     suricatta/server_hawkbit.c | 31 ++++++++++++++++++++++++++-----
>     suricatta/server_hawkbit.h | 1 +
>     8 files changed, 67 insertions(+), 20 deletions(-)
> 
>     diff --git a/corelib/channel_curl.c b/corelib/channel_curl.c
>     index e5a3b0e..0761811 100644
>     --- a/corelib/channel_curl.c
>     +++ b/corelib/channel_curl.c
>     @@ -593,7 +593,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.
>     @@ -604,14 +604,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 70375fa..8a60fcc 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..8b76205 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':
>     @@ -651,8 +655,18 @@ server_op_res_t server_start(char *fname, int
>     argc, char *argv[])
>     case '2':
>     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;
>     +
>     + if (dict_insert_value(&server_hawkbit.httpheaders,
>     + optarg,
>     + argv[optind++]) < 0)
>     + return SERVER_EINIT;
>     +
>     + break;
>     case '?':
>     + /* Ignore not recognized options, they can be already parsed by
>     the caller */
>     default:
>     break;
>     }
>     @@ -664,6 +678,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 805158d..9a338d8 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;
>     @@ -1604,9 +1607,11 @@ void server_print_help(void)
>     "\t -f, --interface Set the network interface to connect to hawkBit.\n"
>     "\t --disable-token-for-dwl Do not send authentication header when
>     downlloading SWU.\n"
>     "\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 -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 -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);
>     @@ -1665,6 +1670,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) {
>     @@ -1681,6 +1687,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);
>     }
>     @@ -1693,7 +1702,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':
>     @@ -1786,6 +1795,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;
>     @@ -1803,6 +1822,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;
>     -- 
>     2.31.1
> 
> -- 
> You received this message because you are subscribed to the Google 
> Groups "swupdate" group.
> To unsubscribe from this group and stop receiving emails from it, send 
> an email to swupdate+unsubscribe@googlegroups.com 
> <mailto:swupdate+unsubscribe@googlegroups.com>.
> To view this discussion on the web visit 
> https://groups.google.com/d/msgid/swupdate/a2f23759-6f5e-4ba8-834e-b83da0b12399n%40googlegroups.com 
> <https://groups.google.com/d/msgid/swupdate/a2f23759-6f5e-4ba8-834e-b83da0b12399n%40googlegroups.com?utm_medium=email&utm_source=footer>.
Stefano Babic Sept. 15, 2021, 3:38 p.m. UTC | #3
Hi Sava,

On 15.09.21 09:50, Stefano Babic wrote:
> Hi Sava,
> 
> On 09.09.21 17:42, Sava Jakovljev wrote:
>> Should be fine now.
>>
>> I also fixed indentation in print_help.
>> If there are any other problems or concerns, let me know.
>>
> 
> It does not compile clean, it breaks server-general.
> 
> See CI here:
> 
> https://source.denx.de/swupdate/swupdate/-/jobs/322334
> 
> But also here:
> 
>   if (dict_insert_value(&server_general.httpheaders,
> 
> because httpheaders is not declared inside derver_general structure. 
> Could you take a look and repost, please ?

Anyway, fix is trivial, I fixed my self and merged into master.

Best regards,
Stefano Babic

> 
> Best regards,
> Stefano
> 
>> Best regards,
>> Sava
>>
>> On Thursday, September 9, 2021 at 5:41:46 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 | 30 +++++++++++++++++++++++-------
>>     suricatta/server_general.h | 3 ++-
>>     suricatta/server_hawkbit.c | 31 ++++++++++++++++++++++++++-----
>>     suricatta/server_hawkbit.h | 1 +
>>     8 files changed, 67 insertions(+), 20 deletions(-)
>>
>>     diff --git a/corelib/channel_curl.c b/corelib/channel_curl.c
>>     index e5a3b0e..0761811 100644
>>     --- a/corelib/channel_curl.c
>>     +++ b/corelib/channel_curl.c
>>     @@ -593,7 +593,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.
>>     @@ -604,14 +604,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 70375fa..8a60fcc 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..8b76205 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':
>>     @@ -651,8 +655,18 @@ server_op_res_t server_start(char *fname, int
>>     argc, char *argv[])
>>     case '2':
>>     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;
>>     +
>>     + if (dict_insert_value(&server_hawkbit.httpheaders,
>>     + optarg,
>>     + argv[optind++]) < 0)
>>     + return SERVER_EINIT;
>>     +
>>     + break;
>>     case '?':
>>     + /* Ignore not recognized options, they can be already parsed by
>>     the caller */
>>     default:
>>     break;
>>     }
>>     @@ -664,6 +678,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 805158d..9a338d8 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;
>>     @@ -1604,9 +1607,11 @@ void server_print_help(void)
>>     "\t -f, --interface Set the network interface to connect to 
>> hawkBit.\n"
>>     "\t --disable-token-for-dwl Do not send authentication header when
>>     downlloading SWU.\n"
>>     "\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 -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 -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);
>>     @@ -1665,6 +1670,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) {
>>     @@ -1681,6 +1687,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);
>>     }
>>     @@ -1693,7 +1702,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':
>>     @@ -1786,6 +1795,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;
>>     @@ -1803,6 +1822,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;
>>     --     2.31.1
>>
>> -- 
>> You received this message because you are subscribed to the Google 
>> Groups "swupdate" group.
>> To unsubscribe from this group and stop receiving emails from it, send 
>> an email to swupdate+unsubscribe@googlegroups.com 
>> <mailto:swupdate+unsubscribe@googlegroups.com>.
>> To view this discussion on the web visit 
>> https://groups.google.com/d/msgid/swupdate/a2f23759-6f5e-4ba8-834e-b83da0b12399n%40googlegroups.com 
>> <https://groups.google.com/d/msgid/swupdate/a2f23759-6f5e-4ba8-834e-b83da0b12399n%40googlegroups.com?utm_medium=email&utm_source=footer>. 
>>
> 
>
Sava Jakovljev Sept. 15, 2021, 3:54 p.m. UTC | #4
Hello Stefano,

Thank you and sorry for additional trouble, I'm really sorry. I made a 
mistake by not checking the general HTTP server, as we don't use it (which 
is not an excuse at all). 
I'll make sure this situation does not happen again.

Best regards,
Sava

On Wednesday, September 15, 2021 at 5:38:04 PM UTC+2 Stefano Babic wrote:

> Hi Sava,
>
> On 15.09.21 09:50, Stefano Babic wrote:
> > Hi Sava,
> > 
> > On 09.09.21 17:42, Sava Jakovljev wrote:
> >> Should be fine now.
> >>
> >> I also fixed indentation in print_help.
> >> If there are any other problems or concerns, let me know.
> >>
> > 
> > It does not compile clean, it breaks server-general.
> > 
> > See CI here:
> > 
> > https://source.denx.de/swupdate/swupdate/-/jobs/322334
> > 
> > But also here:
> > 
> >  if (dict_insert_value(&server_general.httpheaders,
> > 
> > because httpheaders is not declared inside derver_general structure. 
> > Could you take a look and repost, please ?
>
> Anyway, fix is trivial, I fixed my self and merged into master.
>
> Best regards,
> Stefano Babic
>
> > 
> > Best regards,
> > Stefano
> > 
> >> Best regards,
> >> Sava
> >>
> >> On Thursday, September 9, 2021 at 5:41:46 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 | 30 +++++++++++++++++++++++-------
> >>     suricatta/server_general.h | 3 ++-
> >>     suricatta/server_hawkbit.c | 31 ++++++++++++++++++++++++++-----
> >>     suricatta/server_hawkbit.h | 1 +
> >>     8 files changed, 67 insertions(+), 20 deletions(-)
> >>
> >>     diff --git a/corelib/channel_curl.c b/corelib/channel_curl.c
> >>     index e5a3b0e..0761811 100644
> >>     --- a/corelib/channel_curl.c
> >>     +++ b/corelib/channel_curl.c
> >>     @@ -593,7 +593,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.
> >>     @@ -604,14 +604,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 70375fa..8a60fcc 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..8b76205 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':
> >>     @@ -651,8 +655,18 @@ server_op_res_t server_start(char *fname, int
> >>     argc, char *argv[])
> >>     case '2':
> >>     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;
> >>     +
> >>     + if (dict_insert_value(&server_hawkbit.httpheaders,
> >>     + optarg,
> >>     + argv[optind++]) < 0)
> >>     + return SERVER_EINIT;
> >>     +
> >>     + break;
> >>     case '?':
> >>     + /* Ignore not recognized options, they can be already parsed by
> >>     the caller */
> >>     default:
> >>     break;
> >>     }
> >>     @@ -664,6 +678,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 805158d..9a338d8 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;
> >>     @@ -1604,9 +1607,11 @@ void server_print_help(void)
> >>     "\t -f, --interface Set the network interface to connect to 
> >> hawkBit.\n"
> >>     "\t --disable-token-for-dwl Do not send authentication header when
> >>     downlloading SWU.\n"
> >>     "\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 -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 -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);
> >>     @@ -1665,6 +1670,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) {
> >>     @@ -1681,6 +1687,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);
> >>     }
> >>     @@ -1693,7 +1702,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':
> >>     @@ -1786,6 +1795,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;
> >>     @@ -1803,6 +1822,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;
> >>     --     2.31.1
> >>
> >> -- 
> >> You received this message because you are subscribed to the Google 
> >> Groups "swupdate" group.
> >> To unsubscribe from this group and stop receiving emails from it, send 
> >> an email to swupdate+u...@googlegroups.com 
> >> <mailto:swupdate+u...@googlegroups.com>.
> >> To view this discussion on the web visit 
> >> 
> https://groups.google.com/d/msgid/swupdate/a2f23759-6f5e-4ba8-834e-b83da0b12399n%40googlegroups.com 
> >> <
> https://groups.google.com/d/msgid/swupdate/a2f23759-6f5e-4ba8-834e-b83da0b12399n%40googlegroups.com?utm_medium=email&utm_source=footer>. 
>
> >>
> > 
> > 
>
>
> -- 
> =====================================================================
> 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 e5a3b0e..0761811 100644
--- a/corelib/channel_curl.c
+++ b/corelib/channel_curl.c
@@ -593,7 +593,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.
@@ -604,14 +604,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 70375fa..8a60fcc 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..8b76205 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':
@@ -651,8 +655,18 @@  server_op_res_t server_start(char *fname, int argc, char *argv[])
 		case '2':
 			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;
+
+			if (dict_insert_value(&server_hawkbit.httpheaders,
+						optarg,
+						argv[optind++]) < 0)
+				return SERVER_EINIT;
+
+			break;
 		case '?':
+		/* Ignore not recognized options, they can be already parsed by the caller */
 		default:
 			break;
 		}
@@ -664,6 +678,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 805158d..9a338d8 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;
@@ -1604,9 +1607,11 @@  void server_print_help(void)
 	    "\t  -f, --interface     Set the network interface to connect to hawkBit.\n"
 	    "\t  --disable-token-for-dwl Do not send authentication header when downlloading SWU.\n"
 	    "\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  -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  -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);
@@ -1665,6 +1670,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) {
@@ -1681,6 +1687,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);
 	}
@@ -1693,7 +1702,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':
@@ -1786,6 +1795,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;
@@ -1803,6 +1822,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;