Added --limit-rate
authorDaniel Stenberg <daniel@haxx.se>
Sat, 15 Jun 2002 21:02:11 +0000 (21:02 +0000)
committerDaniel Stenberg <daniel@haxx.se>
Sat, 15 Jun 2002 21:02:11 +0000 (21:02 +0000)
src/main.c

index 5294f68..8790de1 100644 (file)
 #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 */