diff --git a/CHANGES b/CHANGES index a3ceb533c..320923b9a 100644 --- a/CHANGES +++ b/CHANGES @@ -6,6 +6,10 @@ Changelog +Daniel Stenberg (6 Mar 2010) +- Ben Greear brought a patch that fixed the rate limiting logic for TFTP when + the easy interface was used. + Daniel Stenberg (5 Mar 2010) - Daniel Johnson provided fixes for building curl with the clang compiler. diff --git a/RELEASE-NOTES b/RELEASE-NOTES index 5321f86f3..2ecaf6aa0 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -30,6 +30,7 @@ This release includes the following bugfixes: o configure fixes for GSSAPI o threaded resolver double free when closing curl handle o configure fixes for building with the clang compiler + o easy interix rate limiting logic This release includes the following known bugs: diff --git a/lib/tftp.c b/lib/tftp.c index 2c6da5a9a..6884b880c 100644 --- a/lib/tftp.c +++ b/lib/tftp.c @@ -1173,6 +1173,34 @@ static long tftp_state_timeout(struct connectdata *conn, tftp_event_t *event) } } +static curl_off_t sleep_time(curl_off_t rate_bps, curl_off_t cur_rate_bps, + int pkt_size) +{ + curl_off_t min_sleep = 0; + curl_off_t rv = 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; + } + + rv = ((curl_off_t)((pkt_size * 8) * 1000) / rate_bps); + + if (rv < min_sleep) + rv = min_sleep; + + return rv; +} + + /********************************************************** * * tftp_easy_statemach @@ -1187,15 +1215,64 @@ 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; + curl_off_t 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->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->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 +1280,42 @@ 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? */ + long rtms = state->retry_time * 1000; + if (Curl_tvdiff(k->now, transaction_start) > rtms) { + 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(); + + if(k->bytecountp) + *k->bytecountp = k->bytecount; /* read count */ + if(k->writebytecountp) + *k->writebytecountp = k->writebytecount; /* write count */ + } } - /* 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(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 */