diff mbox series

channel_curl: Add maximum download speed limit

Message ID 20210916153237.17527-1-sava.jakovljev@teufel.de
State Changes Requested
Headers show
Series channel_curl: Add maximum download speed limit | expand

Commit Message

Sava Jakovljev Sept. 16, 2021, 3:32 p.m. UTC
* Extend Suricatta and general server with options to set download speed
  limit.
* Add 'n' short and 'max-download-speed' long command line option for
  setting download speed limit.
* Add 'maximum-download-speed' option in configuration file.

Signed-off-by: Sava Jakovljev <sava.jakovljev@teufel.de>
---
 corelib/channel_curl.c     |  9 +++++++++
 corelib/downloader.c       |  3 ++-
 doc/source/swupdate.rst    |  2 ++
 include/channel_curl.h     |  1 +
 suricatta/common.c         |  3 +++
 suricatta/server_general.c | 11 +++++++++--
 suricatta/server_hawkbit.c | 13 ++++++++++---
 7 files changed, 36 insertions(+), 6 deletions(-)

--
2.31.1

Comments

Stefano Babic Sept. 16, 2021, 3:43 p.m. UTC | #1
Hi Sava,

On 16.09.21 17:32, Sava Jakovljev wrote:
> * Extend Suricatta and general server with options to set download speed
>    limit.
> * Add 'n' short and 'max-download-speed' long command line option for
>    setting download speed limit.
> * Add 'maximum-download-speed' option in configuration file.
> 
> Signed-off-by: Sava Jakovljev <sava.jakovljev@teufel.de>
> ---
>   corelib/channel_curl.c     |  9 +++++++++
>   corelib/downloader.c       |  3 ++-
>   doc/source/swupdate.rst    |  2 ++
>   include/channel_curl.h     |  1 +
>   suricatta/common.c         |  3 +++
>   suricatta/server_general.c | 11 +++++++++--
>   suricatta/server_hawkbit.c | 13 ++++++++++---
>   7 files changed, 36 insertions(+), 6 deletions(-)
> 
> diff --git a/corelib/channel_curl.c b/corelib/channel_curl.c
> index 0761811..4422a02 100644
> --- a/corelib/channel_curl.c
> +++ b/corelib/channel_curl.c
> @@ -1104,6 +1104,15 @@ channel_op_res_t channel_get_file(channel_t *this, void *data)
>   		goto cleanup_header;
>   	}
> 
> +	if (channel_data->max_download_speed &&
> +			curl_easy_setopt(channel_curl->handle,
> +				CURLOPT_MAX_RECV_SPEED_LARGE,
> +				channel_data->max_download_speed * 1024) != CURLE_OK) {

I won't hard-code the unit here. There is in SWUpdate a ustrtoull() 
function that accepts the common modifier (k, M, G). The user can then 
pass "-n 250" (for 250 bytes/sec), or "-n 250k" (for 250Kb/sec), or "-n 
250M" (for 250Mbytes/sec, very optimistic) or "-n 250G (science fiction).

> +		ERROR("Set channel download speed limit failed.");
> +		result = CHANNEL_EINIT;
> +		goto cleanup_header;
> +	}
> +
>   	download_callback_data_t download_data;
>   	if (channel_enable_download_progress_tracking(channel_curl,
>   				channel_data->url,
> diff --git a/corelib/downloader.c b/corelib/downloader.c
> index 3d0a0a5..8596694 100644
> --- a/corelib/downloader.c
> +++ b/corelib/downloader.c
> @@ -105,7 +105,8 @@ static channel_data_t channel_options = {
>   	.debug = false,
>   	.retries = DL_DEFAULT_RETRIES,
>   	.low_speed_timeout = DL_LOWSPEED_TIME,
> -	.headers_to_send = NULL
> +	.headers_to_send = NULL,
> +	.max_download_speed = 0 // Unlimited download speed is default.
>   };
> 
>   int start_download(const char *fname, int argc, char *argv[])
> diff --git a/doc/source/swupdate.rst b/doc/source/swupdate.rst
> index 5af52f1..6704bab 100644
> --- a/doc/source/swupdate.rst
> +++ b/doc/source/swupdate.rst
> @@ -665,6 +665,8 @@ Mandatory arguments are marked with '\*':
>   |                         |          | value to be sent with every HTTP request   |
>   |                         |          | made.                                      |
>   +-------------------------+----------+--------------------------------------------+
> +| -n <kB/s>               | integer  | Maximum download speed in kB/s.            |

Of course, here the list of delimiters should then be included.

> ++-------------------------+----------+--------------------------------------------+
> 
> 
>   systemd Integration
> diff --git a/include/channel_curl.h b/include/channel_curl.h
> index 6dabae6..ade5f9b 100644
> --- a/include/channel_curl.h
> +++ b/include/channel_curl.h
> @@ -74,4 +74,5 @@ typedef struct {
>   	sourcetype source;
>   	struct dict *headers_to_send;
>   	struct dict *received_headers;
> +	unsigned int max_download_speed; // in kB/s
>   } channel_data_t;
> diff --git a/suricatta/common.c b/suricatta/common.c
> index e5a3db2..b59c0a4 100644
> --- a/suricatta/common.c
> +++ b/suricatta/common.c
> @@ -23,6 +23,9 @@ void suricatta_channel_settings(void *elem, channel_data_t *chan)
>   	get_field(LIBCFG_PARSER, elem, "retry",
>   		&chan->retries);
> 
> +	get_field(LIBCFG_PARSER, elem, "max-download-speed",
> +		&chan->max_download_speed);
> +
>   	GET_FIELD_STRING_RESET(LIBCFG_PARSER, elem, "retrywait", tmp);
>   	if (strlen(tmp))
>   		chan->retry_sleep =
> diff --git a/suricatta/server_general.c b/suricatta/server_general.c
> index d9ad50d..548cb1f 100644
> --- a/suricatta/server_general.c
> +++ b/suricatta/server_general.c
> @@ -65,6 +65,7 @@ static struct option long_options[] = {
>       {"retry", required_argument, NULL, 'r'},
>       {"retrywait", required_argument, NULL, 'w'},
>       {"cache", required_argument, NULL, '2'},
> +    {"max-download-speed", required_argument, NULL, 'n'},
>       {NULL, 0, NULL, 0}};
> 
>   static unsigned short mandatory_argument_count = 0;
> @@ -525,7 +526,8 @@ void server_print_help(void)
>   	    "\t  -y, --proxy         Use proxy. Either give proxy URL, else "
>   	    "{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.",
> +	    "appended to every HTTP request being sent."
> +	    "\t  -n, --max-download-speed <limit>	Set download speed limit in kB/s.",
>   	    CHANNEL_DEFAULT_POLLING_INTERVAL, CHANNEL_DEFAULT_RESUME_TRIES,
>   	    CHANNEL_DEFAULT_RESUME_DELAY);
>   }
> @@ -630,7 +632,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:a:",
> +	while ((choice = getopt_long(argc, argv, "u:l:r:w:p:2:a:n",
>   				     long_options, NULL)) != -1) {
>   		switch (choice) {
>   		case 'u':
> @@ -665,6 +667,11 @@ server_op_res_t server_start(char *fname, int argc, char *argv[])
>   				return SERVER_EINIT;
> 
>   			break;
> +		case 'n':
> +			channel_data_defaults.max_download_speed =
> +				(unsigned int)strtoul(optarg, NULL, 10);

And ustrtoull() already reports the valuze converted in bytes.

> +			break;
> +
>   		case '?':
>   		/* Ignore not recognized options, they can be already parsed by the caller */
>   		default:
> diff --git a/suricatta/server_hawkbit.c b/suricatta/server_hawkbit.c
> index 9a338d8..2ece79e 100644
> --- a/suricatta/server_hawkbit.c
> +++ b/suricatta/server_hawkbit.c
> @@ -53,6 +53,7 @@ static struct option long_options[] = {
>       {"initial-report-resend-period", required_argument, NULL, 'm'},
>   	{"connection-timeout", required_argument, NULL, 's'},
>   	{"custom-http-header", required_argument, NULL, 'a'},
> +	{"max-download-speed", required_argument, NULL, 'n'},
>       {NULL, 0, NULL, 0}};
> 
>   static unsigned short mandatory_argument_count = 0;
> @@ -135,7 +136,8 @@ static channel_data_t channel_data_defaults = {.debug = false,
>   					       .strictssl = true,
>   						   .connection_timeout = 0,
>   						   .headers_to_send = NULL,
> -						   .received_headers = NULL
> +						   .received_headers = NULL,
> +						   .max_download_speed = 0 // No download speed limit is default.
>   						};
> 
>   static struct timeval server_time;
> @@ -1611,7 +1613,8 @@ void server_print_help(void)
>   	    "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.",
> +	    "appended to every HTTP request being sent."
> +	    "\t  -n, --max-download-speed <limit>	Set download speed limit in kB/s.",
>   	    CHANNEL_DEFAULT_POLLING_INTERVAL, CHANNEL_DEFAULT_RESUME_TRIES,
>   	    CHANNEL_DEFAULT_RESUME_DELAY,
>   	    INITIAL_STATUS_REPORT_WAIT_DELAY);
> @@ -1702,7 +1705,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:a:",
> +	while ((choice = getopt_long(argc, argv, "t:i:c:u:p:xr:y::w:k:g:f:2:m:s:a:n:",
>   				     long_options, NULL)) != -1) {
>   		switch (choice) {
>   		case 't':
> @@ -1805,6 +1808,10 @@ server_op_res_t server_start(char *fname, int argc, char *argv[])
>   				return SERVER_EINIT;
> 
>   			break;
> +		case 'n':
> +			channel_data_defaults.max_download_speed =
> +				(unsigned int)strtoul(optarg, NULL, 10);
> +			break;
>   		/* Ignore not recognized options, they can be already parsed by the caller */
>   		case '?':
>   			break;
> --
> 2.31.1
> 

Best regards,
Stefano Babic
Sava Jakovljev Sept. 16, 2021, 3:46 p.m. UTC | #2
Hi Stefano,

It indeed does sound much better. Will adapt. Thanks for the quick response.

Best regards,
Sava

On Thursday, September 16, 2021 at 5:43:45 PM UTC+2 Stefano Babic wrote:

> Hi Sava,
>
> On 16.09.21 17:32, Sava Jakovljev wrote:
> > * Extend Suricatta and general server with options to set download speed
> > limit.
> > * Add 'n' short and 'max-download-speed' long command line option for
> > setting download speed limit.
> > * Add 'maximum-download-speed' option in configuration file.
> > 
> > Signed-off-by: Sava Jakovljev <sava.ja...@teufel.de>
> > ---
> > corelib/channel_curl.c | 9 +++++++++
> > corelib/downloader.c | 3 ++-
> > doc/source/swupdate.rst | 2 ++
> > include/channel_curl.h | 1 +
> > suricatta/common.c | 3 +++
> > suricatta/server_general.c | 11 +++++++++--
> > suricatta/server_hawkbit.c | 13 ++++++++++---
> > 7 files changed, 36 insertions(+), 6 deletions(-)
> > 
> > diff --git a/corelib/channel_curl.c b/corelib/channel_curl.c
> > index 0761811..4422a02 100644
> > --- a/corelib/channel_curl.c
> > +++ b/corelib/channel_curl.c
> > @@ -1104,6 +1104,15 @@ channel_op_res_t channel_get_file(channel_t 
> *this, void *data)
> > goto cleanup_header;
> > }
> > 
> > + if (channel_data->max_download_speed &&
> > + curl_easy_setopt(channel_curl->handle,
> > + CURLOPT_MAX_RECV_SPEED_LARGE,
> > + channel_data->max_download_speed * 1024) != CURLE_OK) {
>
> I won't hard-code the unit here. There is in SWUpdate a ustrtoull() 
> function that accepts the common modifier (k, M, G). The user can then 
> pass "-n 250" (for 250 bytes/sec), or "-n 250k" (for 250Kb/sec), or "-n 
> 250M" (for 250Mbytes/sec, very optimistic) or "-n 250G (science fiction).
>
> > + ERROR("Set channel download speed limit failed.");
> > + result = CHANNEL_EINIT;
> > + goto cleanup_header;
> > + }
> > +
> > download_callback_data_t download_data;
> > if (channel_enable_download_progress_tracking(channel_curl,
> > channel_data->url,
> > diff --git a/corelib/downloader.c b/corelib/downloader.c
> > index 3d0a0a5..8596694 100644
> > --- a/corelib/downloader.c
> > +++ b/corelib/downloader.c
> > @@ -105,7 +105,8 @@ static channel_data_t channel_options = {
> > .debug = false,
> > .retries = DL_DEFAULT_RETRIES,
> > .low_speed_timeout = DL_LOWSPEED_TIME,
> > - .headers_to_send = NULL
> > + .headers_to_send = NULL,
> > + .max_download_speed = 0 // Unlimited download speed is default.
> > };
> > 
> > int start_download(const char *fname, int argc, char *argv[])
> > diff --git a/doc/source/swupdate.rst b/doc/source/swupdate.rst
> > index 5af52f1..6704bab 100644
> > --- a/doc/source/swupdate.rst
> > +++ b/doc/source/swupdate.rst
> > @@ -665,6 +665,8 @@ Mandatory arguments are marked with '\*':
> > | | | value to be sent with every HTTP request |
> > | | | made. |
> > 
> +-------------------------+----------+--------------------------------------------+
> > +| -n <kB/s> | integer | Maximum download speed in kB/s. |
>
> Of course, here the list of delimiters should then be included.
>
> > 
> ++-------------------------+----------+--------------------------------------------+
> > 
> > 
> > systemd Integration
> > diff --git a/include/channel_curl.h b/include/channel_curl.h
> > index 6dabae6..ade5f9b 100644
> > --- a/include/channel_curl.h
> > +++ b/include/channel_curl.h
> > @@ -74,4 +74,5 @@ typedef struct {
> > sourcetype source;
> > struct dict *headers_to_send;
> > struct dict *received_headers;
> > + unsigned int max_download_speed; // in kB/s
> > } channel_data_t;
> > diff --git a/suricatta/common.c b/suricatta/common.c
> > index e5a3db2..b59c0a4 100644
> > --- a/suricatta/common.c
> > +++ b/suricatta/common.c
> > @@ -23,6 +23,9 @@ void suricatta_channel_settings(void *elem, 
> channel_data_t *chan)
> > get_field(LIBCFG_PARSER, elem, "retry",
> > &chan->retries);
> > 
> > + get_field(LIBCFG_PARSER, elem, "max-download-speed",
> > + &chan->max_download_speed);
> > +
> > GET_FIELD_STRING_RESET(LIBCFG_PARSER, elem, "retrywait", tmp);
> > if (strlen(tmp))
> > chan->retry_sleep =
> > diff --git a/suricatta/server_general.c b/suricatta/server_general.c
> > index d9ad50d..548cb1f 100644
> > --- a/suricatta/server_general.c
> > +++ b/suricatta/server_general.c
> > @@ -65,6 +65,7 @@ static struct option long_options[] = {
> > {"retry", required_argument, NULL, 'r'},
> > {"retrywait", required_argument, NULL, 'w'},
> > {"cache", required_argument, NULL, '2'},
> > + {"max-download-speed", required_argument, NULL, 'n'},
> > {NULL, 0, NULL, 0}};
> > 
> > static unsigned short mandatory_argument_count = 0;
> > @@ -525,7 +526,8 @@ void server_print_help(void)
> > "\t -y, --proxy Use proxy. Either give proxy URL, else "
> > "{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.",
> > + "appended to every HTTP request being sent."
> > + "\t -n, --max-download-speed <limit> Set download speed limit in 
> kB/s.",
> > CHANNEL_DEFAULT_POLLING_INTERVAL, CHANNEL_DEFAULT_RESUME_TRIES,
> > CHANNEL_DEFAULT_RESUME_DELAY);
> > }
> > @@ -630,7 +632,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:a:",
> > + while ((choice = getopt_long(argc, argv, "u:l:r:w:p:2:a:n",
> > long_options, NULL)) != -1) {
> > switch (choice) {
> > case 'u':
> > @@ -665,6 +667,11 @@ server_op_res_t server_start(char *fname, int argc, 
> char *argv[])
> > return SERVER_EINIT;
> > 
> > break;
> > + case 'n':
> > + channel_data_defaults.max_download_speed =
> > + (unsigned int)strtoul(optarg, NULL, 10);
>
> And ustrtoull() already reports the valuze converted in bytes.
>
> > + break;
> > +
> > case '?':
> > /* Ignore not recognized options, they can be already parsed by the 
> caller */
> > default:
> > diff --git a/suricatta/server_hawkbit.c b/suricatta/server_hawkbit.c
> > index 9a338d8..2ece79e 100644
> > --- a/suricatta/server_hawkbit.c
> > +++ b/suricatta/server_hawkbit.c
> > @@ -53,6 +53,7 @@ static struct option long_options[] = {
> > {"initial-report-resend-period", required_argument, NULL, 'm'},
> > {"connection-timeout", required_argument, NULL, 's'},
> > {"custom-http-header", required_argument, NULL, 'a'},
> > + {"max-download-speed", required_argument, NULL, 'n'},
> > {NULL, 0, NULL, 0}};
> > 
> > static unsigned short mandatory_argument_count = 0;
> > @@ -135,7 +136,8 @@ static channel_data_t channel_data_defaults = 
> {.debug = false,
> > .strictssl = true,
> > .connection_timeout = 0,
> > .headers_to_send = NULL,
> > - .received_headers = NULL
> > + .received_headers = NULL,
> > + .max_download_speed = 0 // No download speed limit is default.
> > };
> > 
> > static struct timeval server_time;
> > @@ -1611,7 +1613,8 @@ void server_print_help(void)
> > "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.",
> > + "appended to every HTTP request being sent."
> > + "\t -n, --max-download-speed <limit> Set download speed limit in 
> kB/s.",
> > CHANNEL_DEFAULT_POLLING_INTERVAL, CHANNEL_DEFAULT_RESUME_TRIES,
> > CHANNEL_DEFAULT_RESUME_DELAY,
> > INITIAL_STATUS_REPORT_WAIT_DELAY);
> > @@ -1702,7 +1705,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:a:",
> > + while ((choice = getopt_long(argc, argv, 
> "t:i:c:u:p:xr:y::w:k:g:f:2:m:s:a:n:",
> > long_options, NULL)) != -1) {
> > switch (choice) {
> > case 't':
> > @@ -1805,6 +1808,10 @@ server_op_res_t server_start(char *fname, int 
> argc, char *argv[])
> > return SERVER_EINIT;
> > 
> > break;
> > + case 'n':
> > + channel_data_defaults.max_download_speed =
> > + (unsigned int)strtoul(optarg, NULL, 10);
> > + break;
> > /* Ignore not recognized options, they can be already parsed by the 
> caller */
> > case '?':
> > break;
> > --
> > 2.31.1
> > 
>
> 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 0761811..4422a02 100644
--- a/corelib/channel_curl.c
+++ b/corelib/channel_curl.c
@@ -1104,6 +1104,15 @@  channel_op_res_t channel_get_file(channel_t *this, void *data)
 		goto cleanup_header;
 	}

+	if (channel_data->max_download_speed &&
+			curl_easy_setopt(channel_curl->handle,
+				CURLOPT_MAX_RECV_SPEED_LARGE,
+				channel_data->max_download_speed * 1024) != CURLE_OK) {
+		ERROR("Set channel download speed limit failed.");
+		result = CHANNEL_EINIT;
+		goto cleanup_header;
+	}
+
 	download_callback_data_t download_data;
 	if (channel_enable_download_progress_tracking(channel_curl,
 				channel_data->url,
diff --git a/corelib/downloader.c b/corelib/downloader.c
index 3d0a0a5..8596694 100644
--- a/corelib/downloader.c
+++ b/corelib/downloader.c
@@ -105,7 +105,8 @@  static channel_data_t channel_options = {
 	.debug = false,
 	.retries = DL_DEFAULT_RETRIES,
 	.low_speed_timeout = DL_LOWSPEED_TIME,
-	.headers_to_send = NULL
+	.headers_to_send = NULL,
+	.max_download_speed = 0 // Unlimited download speed is default.
 };

 int start_download(const char *fname, int argc, char *argv[])
diff --git a/doc/source/swupdate.rst b/doc/source/swupdate.rst
index 5af52f1..6704bab 100644
--- a/doc/source/swupdate.rst
+++ b/doc/source/swupdate.rst
@@ -665,6 +665,8 @@  Mandatory arguments are marked with '\*':
 |                         |          | value to be sent with every HTTP request   |
 |                         |          | made.                                      |
 +-------------------------+----------+--------------------------------------------+
+| -n <kB/s>               | integer  | Maximum download speed in kB/s.            |
++-------------------------+----------+--------------------------------------------+


 systemd Integration
diff --git a/include/channel_curl.h b/include/channel_curl.h
index 6dabae6..ade5f9b 100644
--- a/include/channel_curl.h
+++ b/include/channel_curl.h
@@ -74,4 +74,5 @@  typedef struct {
 	sourcetype source;
 	struct dict *headers_to_send;
 	struct dict *received_headers;
+	unsigned int max_download_speed; // in kB/s
 } channel_data_t;
diff --git a/suricatta/common.c b/suricatta/common.c
index e5a3db2..b59c0a4 100644
--- a/suricatta/common.c
+++ b/suricatta/common.c
@@ -23,6 +23,9 @@  void suricatta_channel_settings(void *elem, channel_data_t *chan)
 	get_field(LIBCFG_PARSER, elem, "retry",
 		&chan->retries);

+	get_field(LIBCFG_PARSER, elem, "max-download-speed",
+		&chan->max_download_speed);
+
 	GET_FIELD_STRING_RESET(LIBCFG_PARSER, elem, "retrywait", tmp);
 	if (strlen(tmp))
 		chan->retry_sleep =
diff --git a/suricatta/server_general.c b/suricatta/server_general.c
index d9ad50d..548cb1f 100644
--- a/suricatta/server_general.c
+++ b/suricatta/server_general.c
@@ -65,6 +65,7 @@  static struct option long_options[] = {
     {"retry", required_argument, NULL, 'r'},
     {"retrywait", required_argument, NULL, 'w'},
     {"cache", required_argument, NULL, '2'},
+    {"max-download-speed", required_argument, NULL, 'n'},
     {NULL, 0, NULL, 0}};

 static unsigned short mandatory_argument_count = 0;
@@ -525,7 +526,8 @@  void server_print_help(void)
 	    "\t  -y, --proxy         Use proxy. Either give proxy URL, else "
 	    "{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.",
+	    "appended to every HTTP request being sent."
+	    "\t  -n, --max-download-speed <limit>	Set download speed limit in kB/s.",
 	    CHANNEL_DEFAULT_POLLING_INTERVAL, CHANNEL_DEFAULT_RESUME_TRIES,
 	    CHANNEL_DEFAULT_RESUME_DELAY);
 }
@@ -630,7 +632,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:a:",
+	while ((choice = getopt_long(argc, argv, "u:l:r:w:p:2:a:n",
 				     long_options, NULL)) != -1) {
 		switch (choice) {
 		case 'u':
@@ -665,6 +667,11 @@  server_op_res_t server_start(char *fname, int argc, char *argv[])
 				return SERVER_EINIT;

 			break;
+		case 'n':
+			channel_data_defaults.max_download_speed =
+				(unsigned int)strtoul(optarg, NULL, 10);
+			break;
+
 		case '?':
 		/* Ignore not recognized options, they can be already parsed by the caller */
 		default:
diff --git a/suricatta/server_hawkbit.c b/suricatta/server_hawkbit.c
index 9a338d8..2ece79e 100644
--- a/suricatta/server_hawkbit.c
+++ b/suricatta/server_hawkbit.c
@@ -53,6 +53,7 @@  static struct option long_options[] = {
     {"initial-report-resend-period", required_argument, NULL, 'm'},
 	{"connection-timeout", required_argument, NULL, 's'},
 	{"custom-http-header", required_argument, NULL, 'a'},
+	{"max-download-speed", required_argument, NULL, 'n'},
     {NULL, 0, NULL, 0}};

 static unsigned short mandatory_argument_count = 0;
@@ -135,7 +136,8 @@  static channel_data_t channel_data_defaults = {.debug = false,
 					       .strictssl = true,
 						   .connection_timeout = 0,
 						   .headers_to_send = NULL,
-						   .received_headers = NULL
+						   .received_headers = NULL,
+						   .max_download_speed = 0 // No download speed limit is default.
 						};

 static struct timeval server_time;
@@ -1611,7 +1613,8 @@  void server_print_help(void)
 	    "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.",
+	    "appended to every HTTP request being sent."
+	    "\t  -n, --max-download-speed <limit>	Set download speed limit in kB/s.",
 	    CHANNEL_DEFAULT_POLLING_INTERVAL, CHANNEL_DEFAULT_RESUME_TRIES,
 	    CHANNEL_DEFAULT_RESUME_DELAY,
 	    INITIAL_STATUS_REPORT_WAIT_DELAY);
@@ -1702,7 +1705,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:a:",
+	while ((choice = getopt_long(argc, argv, "t:i:c:u:p:xr:y::w:k:g:f:2:m:s:a:n:",
 				     long_options, NULL)) != -1) {
 		switch (choice) {
 		case 't':
@@ -1805,6 +1808,10 @@  server_op_res_t server_start(char *fname, int argc, char *argv[])
 				return SERVER_EINIT;

 			break;
+		case 'n':
+			channel_data_defaults.max_download_speed =
+				(unsigned int)strtoul(optarg, NULL, 10);
+			break;
 		/* Ignore not recognized options, they can be already parsed by the caller */
 		case '?':
 			break;