Attached is a patch that implements tftp rate limitation logic.

The change to transfer.c is just a white-space indentation fixup.

This has been lightly tested and could use some review.


[gree...@ben-dt2 curl]$ ./src/curl --limit-rate 8k -o /dev/null 
tftp://192.168.100.6/bthelper
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0 5304k    0 46080    0     0   7544      0  0:12:00  0:00:06  0:11:54  8147^C
[gree...@ben-dt2 curl]$ ./src/curl --limit-rate 80k -o /dev/null 
tftp://192.168.100.6/bthelper
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  5 5304k    5  293k    0     0  80874      0  0:01:07  0:00:03  0:01:04 80871^C
[gree...@ben-dt2 curl]$ ./src/curl --limit-rate 800k -o /dev/null 
tftp://192.168.100.6/bthelper
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
 44 5304k   44 2347k    0     0   798k      0  0:00:06  0:00:02  0:00:04  798k^C
[gree...@ben-dt2 curl]$ ./src/curl -o /dev/null tftp://192.168.100.6/bthelper
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 5304k  100 5304k    0     0  2482k      0  0:00:02  0:00:02 --:--:-- 2482k


[gree...@ben-dt2 curl]$ ./src/curl --limit-rate 8k -T "config.log" 
tftp://192.168.100.6/bthelper
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0 10.3M    0     0    0 34304      0   7485  0:12:05  0:00:04  0:12:01  7488^C
[gree...@ben-dt2 curl]$ ./src/curl --limit-rate 80k -T "config.log" 
tftp://192.168.100.6/bthelper
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  2 10.3M    0     0    5  273k      0  80964  0:01:07  0:00:03  0:01:04 80966^C
[gree...@ben-dt2 curl]$ ./src/curl --limit-rate 800k -T "config.log" 
tftp://192.168.100.6/bthelper
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
 26 10.3M    0     0   52 2806k      0   799k  0:00:06  0:00:03  0:00:03  799k^C
[gree...@ben-dt2 curl]$ ./src/curl -T "config.log" tftp://192.168.100.6/bthelper
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
 50 10.3M    0     0  100 5304k      0  2458k  0:00:02  0:00:02 --:--:-- 2458k



Thanks,
Ben

--
Ben Greear <[email protected]>
Candela Technologies Inc  http://www.candelatech.com

diff --git a/lib/tftp.c b/lib/tftp.c
index f2d4a51..a286077 100644
--- a/lib/tftp.c
+++ b/lib/tftp.c
@@ -1173,6 +1173,32 @@ static long tftp_state_timeout(struct connectdata *conn, 
tftp_event_t *event)
   }
 }
 
+long sleep_time(struct SessionHandle *data, long rate_bps, long cur_rate_bps, 
int pkt_size) {
+   long min_sleep = 0;
+   
+   if (rate_bps == 0) {
+      return 0;
+   }
+
+   if (cur_rate_bps > (rate_bps + (rate_bps >> 10))) {
+      // running too fast
+      rate_bps -= rate_bps >> 6;
+      min_sleep = 1;
+   }
+   else if (cur_rate_bps < (rate_bps - (rate_bps >> 10))) {
+      // running too slow
+      rate_bps += rate_bps >> 6;
+   }
+
+   long rv = ((long)((pkt_size * 8) * 1000) / rate_bps);
+
+   if (rv < min_sleep) {
+      rv = min_sleep;
+   }
+   return rv;
+}
+   
+
 /**********************************************************
  *
  * tftp_easy_statemach
@@ -1187,15 +1213,62 @@ static CURLcode tftp_easy_statemach(struct connectdata 
*conn)
   CURLcode              result = CURLE_OK;
   struct SessionHandle  *data = conn->data;
   tftp_state_data_t     *state = (tftp_state_data_t *)conn->proto.tftpc;
-
+  int                   fd_read;
+  int                   timeout_ms;
+  struct SingleRequest  *k = &data->req;
+  struct timeval        transaction_start = Curl_tvnow();
+  
+  k->start = transaction_start;
+  k->now = transaction_start;
+  
   /* Run the TFTP State Machine */
-  for(;
-      (state->state != TFTP_STATE_FIN) && (result == CURLE_OK);
-      result=tftp_state_machine(state, state->event) ) {
+  for(; (state->state != TFTP_STATE_FIN) && (result == CURLE_OK); ) {
+
+    timeout_ms = state->retry_time * 1000;
+     
+    if (data->set.upload) {
+       if (data->set.max_send_speed &&
+           (data->progress.ulspeed > data->set.max_send_speed)) {
+          fd_read = CURL_SOCKET_BAD;
+          timeout_ms = sleep_time(data, data->set.max_send_speed, 
data->progress.ulspeed, state->blksize);
+       }
+       else {
+          fd_read = state->sockfd;
+       }
+    }
+    else {
+       if (data->set.max_recv_speed &&
+           (data->progress.dlspeed > data->set.max_recv_speed)) {
+          fd_read = CURL_SOCKET_BAD;
+          timeout_ms = sleep_time(data, data->set.max_recv_speed, 
data->progress.dlspeed, state->blksize);
+       }
+       else {
+          fd_read = state->sockfd;
+       }
+    }
+
+    if(data->set.timeout) {
+      timeout_ms = data->set.timeout - Curl_tvdiff(k->now, k->start);
+      if (timeout_ms > state->retry_time * 1000)
+        timeout_ms = state->retry_time * 1000;
+      else if(timeout_ms < 0)
+        timeout_ms = 0;
+    }
+
 
     /* Wait until ready to read or timeout occurs */
-    rc=Curl_socket_ready(state->sockfd, CURL_SOCKET_BAD,
-                         state->retry_time * 1000);
+    rc=Curl_socket_ready(fd_read, CURL_SOCKET_BAD, timeout_ms);
+
+    k->now = Curl_tvnow();
+
+    /* Force a progress callback if it's been too long */
+    if (Curl_tvdiff(k->now, k->start) >= data->set.timeout) {
+       if(Curl_pgrsUpdate(conn)) {
+          tftp_state_machine(state, TFTP_EVENT_ERROR);
+          return CURLE_ABORTED_BY_CALLBACK;
+       }
+       k->start = k->now;
+    }
 
     if(rc == -1) {
       /* bail out */
@@ -1203,27 +1276,40 @@ static CURLcode tftp_easy_statemach(struct connectdata 
*conn)
       failf(data, "%s", Curl_strerror(conn, error));
       state->event = TFTP_EVENT_ERROR;
     }
-    else if(rc==0) {
-      /* A timeout occured */
-      state->event = TFTP_EVENT_TIMEOUT;
-
-      /* Force a look at transfer timeouts */
-      check_time = 0;
-
-    }
     else {
+
+      if(rc==0) {
+        /* A timeout occured, but our timeout is variable, so maybe just 
continue? */
+        if (Curl_tvdiff(k->now, transaction_start) > (state->retry_time * 
1000)) {
+          state->event = TFTP_EVENT_TIMEOUT;
+          /* Force a look at transfer timeouts */
+          check_time = 1;
+        }
+        else {
+          continue; /* skip state machine */
+        }
+      }
+      else {
         result = tftp_receive_packet(conn);
-    }
+        if (result == CURLE_OK)
+           transaction_start = Curl_tvnow();
 
-    /* Check for transfer timeout every 10 blocks, or after timeout */
-    if(check_time%10==0) {
-      /* ignore the event here as Curl_socket_ready() handles
-       * retransmission timeouts inside the easy state mach */
+        if(k->bytecountp)
+          *k->bytecountp = k->bytecount; /* read count */
+        if(k->writebytecountp)
+          *k->writebytecountp = k->writebytecount; /* write count */
+      }
+    }
+    
+    if(check_time) {
       tftp_state_timeout(conn, NULL);
+      check_time = 0;
     }
 
     if(result)
       return(result);
+
+    result = tftp_state_machine(state, state->event);
   }
 
   /* Tell curl we're done */
diff --git a/lib/transfer.c b/lib/transfer.c
index d3bd4b3..f3cdb2e 100644
--- a/lib/transfer.c
+++ b/lib/transfer.c
@@ -991,10 +991,10 @@ CURLcode Curl_readwrite(struct connectdata *conn,
   else
     fd_write = CURL_SOCKET_BAD;
 
-   if(!select_res) { /* Call for select()/poll() only, if read/write/error
+  if(!select_res) { /* Call for select()/poll() only, if read/write/error
                          status is not known. */
-       select_res = Curl_socket_ready(fd_read, fd_write, 0);
-   }
+    select_res = Curl_socket_ready(fd_read, fd_write, 0);
+  }
 
   if(select_res == CURL_CSELECT_ERR) {
     failf(data, "select/poll returned error");
-------------------------------------------------------------------
List admin: http://cool.haxx.se/list/listinfo/curl-library
Etiquette:  http://curl.haxx.se/mail/etiquette.html

Reply via email to