aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/main.c138
1 files changed, 133 insertions, 5 deletions
diff --git a/src/main.c b/src/main.c
index 5294f6880..8790de14c 100644
--- a/src/main.c
+++ b/src/main.c
@@ -69,6 +69,11 @@
#ifdef HAVE_SYS_UTIME_H
#include <sys/utime.h>
#endif
+
+#endif
+
+#ifdef HAVE_SYS_POLL_H
+#include <sys/poll.h>
#endif
/* The last #include file should be: */
@@ -361,7 +366,8 @@ static void help(void)
" --interface <interface> Specify the interface to be used\n"
" --krb4 <level> Enable krb4 with specified security level (F)\n"
" -K/--config Specify which config file to read\n"
- " -l/--list-only List only names of an FTP directory (F)");
+ " -l/--list-only List only names of an FTP directory (F)\n"
+ " /--limit-rate <rate> Limit how fast transfers to allow");
puts(" -L/--location Follow Location: hints (H)\n"
" -m/--max-time <seconds> Maximum time allowed for the transfer\n"
" -M/--manual Display huge help text\n"
@@ -497,6 +503,17 @@ struct Configurable {
struct curl_slist *telnet_options;
HttpReq httpreq;
+
+ /* for bandwidth limiting features: */
+
+ size_t sendpersecond; /* send to peer */
+ size_t recvpersecond; /* receive from peer */
+
+ time_t lastsendtime;
+ size_t lastsendsize;
+
+ time_t lastrecvtime;
+ size_t lastrecvsize;
};
static int parseconfig(const char *filename,
@@ -979,6 +996,7 @@ static ParameterError getparameter(char *flag, /* f or -long-flag */
#endif
{"5g", "trace", TRUE},
{"5h", "trace-ascii", TRUE},
+ {"5i", "limit-rate", TRUE},
{"0", "http1.0", FALSE},
{"1", "tlsv1", FALSE},
{"2", "sslv2", FALSE},
@@ -1169,6 +1187,29 @@ static ParameterError getparameter(char *flag, /* f or -long-flag */
GetStr(&config->trace_dump, nextarg);
config->trace_ascii = TRUE;
break;
+ case 'i': /* --limit-rate */
+ {
+ /* We support G, M, K too */
+ char *unit;
+ unsigned long value = strtol(nextarg, &unit, 0);
+ switch(nextarg[strlen(nextarg)-1]) {
+ case 'G':
+ case 'g':
+ value *= 1024*1024*1024;
+ break;
+ case 'M':
+ case 'm':
+ value *= 1024*1024;
+ break;
+ case 'K':
+ case 'k':
+ value *= 1024;
+ break;
+ }
+ config->recvpersecond = value;
+ config->sendpersecond = value;
+ }
+ break;
default: /* the URL! */
{
struct getout *url;
@@ -1835,6 +1876,12 @@ static int parseconfig(const char *filename,
return 0;
}
+static void go_sleep(long ms)
+{
+ /* portable subsecond "sleep" */
+ poll((void *)0, 0, ms);
+}
+
struct OutStruct {
char *filename;
FILE *stream;
@@ -1844,21 +1891,87 @@ struct OutStruct {
int my_fwrite(void *buffer, size_t size, size_t nmemb, void *stream)
{
struct OutStruct *out=(struct OutStruct *)stream;
+ struct Configurable *config = out->config;
if(out && !out->stream) {
/* open file for writing */
out->stream=fopen(out->filename, "wb");
if(!out->stream)
return -1; /* failure */
- if(out->config->nobuffer) {
+ if(config->nobuffer) {
/* disable output buffering */
#ifdef HAVE_SETVBUF
setvbuf(out->stream, NULL, _IONBF, 0);
#endif
}
}
+
+ if(config->recvpersecond) {
+ /*
+ * We know when we received data the previous time. We know how much data
+ * we get now. Make sure that this is not faster than we are told to run.
+ * If we're faster, sleep a while *before* doing the fwrite() here.
+ */
+
+ time_t timediff;
+ time_t now;
+
+ now = time(NULL);
+ timediff = now - config->lastrecvtime;
+ if( size*nmemb > config->recvpersecond*timediff) {
+ /* figure out how many milliseconds to rest */
+ go_sleep ( (size*nmemb)*1000/config->recvpersecond - timediff*1000 );
+ now = time(NULL);
+ }
+ config->lastrecvtime = now;
+ }
+
return fwrite(buffer, size, nmemb, out->stream);
}
+struct InStruct {
+ FILE *stream;
+ struct Configurable *config;
+};
+
+int my_fread(void *buffer, size_t size, size_t nmemb, void *userp)
+{
+ struct InStruct *in=(struct InStruct *)userp;
+
+ struct Configurable *config = in->config;
+
+ if(config->sendpersecond) {
+ /*
+ * We know when we sent data the previous time. We know how much data
+ * we sent. Make sure that this was not faster than we are told to run.
+ * If we're faster, sleep a while *before* doing the fread() here.
+ * Also, make no larger fread() than should be sent this second!
+ */
+
+ time_t timediff;
+ time_t now;
+
+ now = time(NULL);
+ timediff = now - config->lastsendtime;
+ if( config->lastsendsize > config->sendpersecond*timediff) {
+ /* figure out how many milliseconds to rest */
+ go_sleep ( config->lastsendsize*1000/config->sendpersecond -
+ timediff*1000 );
+ now = time(NULL);
+ }
+ config->lastsendtime = now;
+
+ if(size*nmemb > config->sendpersecond) {
+ /* lower the size to actually read */
+ nmemb = config->sendpersecond;
+ size = 1;
+ }
+ config->lastsendsize = size*nmemb;
+ }
+
+
+ return fread(buffer, size, nmemb, in->stream);
+}
+
struct ProgressData {
int calls;
double total;
@@ -2112,6 +2225,7 @@ operate(struct Configurable *config, int argc, char *argv[])
struct OutStruct outs;
struct OutStruct heads;
+ struct InStruct input;
char *url = NULL;
@@ -2511,10 +2625,24 @@ operate(struct Configurable *config, int argc, char *argv[])
curl_easy_setopt(curl, CURLOPT_SSLENGINE, config->engine);
curl_easy_setopt(curl, CURLOPT_SSLENGINE_DEFAULT, 1);
- curl_easy_setopt(curl, CURLOPT_FILE, (FILE *)&outs); /* where to store */
- /* what call to write: */
+ /* where to store */
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, (FILE *)&outs);
+ /* what call to write */
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, my_fwrite);
- curl_easy_setopt(curl, CURLOPT_INFILE, infd); /* for uploads */
+
+ /* for uploads */
+ input.stream = infd;
+ input.config = config;
+ curl_easy_setopt(curl, CURLOPT_READDATA, &input);
+ /* what call to read */
+ curl_easy_setopt(curl, CURLOPT_READFUNCTION, my_fread);
+
+ if(config->recvpersecond) {
+ /* tell libcurl to use a smaller sized buffer as it allows us to
+ make better sleeps! 7.9.9 stuff! */
+ curl_easy_setopt(curl, CURLOPT_BUFFERSIZE, config->recvpersecond);
+ }
+
/* size of uploaded file: */
curl_easy_setopt(curl, CURLOPT_INFILESIZE, infilesize);
curl_easy_setopt(curl, CURLOPT_URL, url); /* what to fetch */