diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/main.c | 138 | 
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 */ | 
