http: cap body data amount during send speed limiting

By making sure never to send off more than the allowed number of bytes
per second the speed limit logic is given more room to actually work.

Reported-by: Fabian Keil
Bug: https://curl.se/mail/lib-2021-03/0042.html
Closes #6797
This commit is contained in:
Daniel Stenberg 2021-03-26 10:06:51 +01:00
parent 95cbcec8f9
commit 24e469f6d6
No known key found for this signature in database
GPG Key ID: 5CC908FDB71E12C2
2 changed files with 40 additions and 11 deletions

View File

@ -1167,7 +1167,12 @@ static size_t readmoredata(char *buffer,
/* make sure that a HTTP request is never sent away chunked! */ /* make sure that a HTTP request is never sent away chunked! */
data->req.forbidchunk = (http->sending == HTTPSEND_REQUEST)?TRUE:FALSE; data->req.forbidchunk = (http->sending == HTTPSEND_REQUEST)?TRUE:FALSE;
if(http->postsize <= (curl_off_t)fullsize) { if((data->set.max_send_speed > 0) &&
(data->set.max_send_speed < http->postsize))
/* speed limit */
fullsize = (size_t)data->set.max_send_speed;
else if(http->postsize <= (curl_off_t)fullsize) {
memcpy(buffer, http->postdata, (size_t)http->postsize); memcpy(buffer, http->postdata, (size_t)http->postsize);
fullsize = (size_t)http->postsize; fullsize = (size_t)http->postsize;
@ -1207,7 +1212,7 @@ CURLcode Curl_buffer_send(struct dynbuf *in,
counter */ counter */
curl_off_t *bytes_written, curl_off_t *bytes_written,
/* how much of the buffer contains body data */ /* how much of the buffer contains body data */
size_t included_body_bytes, curl_off_t included_body_bytes,
int socketindex) int socketindex)
{ {
ssize_t amount; ssize_t amount;
@ -1230,10 +1235,10 @@ CURLcode Curl_buffer_send(struct dynbuf *in,
ptr = Curl_dyn_ptr(in); ptr = Curl_dyn_ptr(in);
size = Curl_dyn_len(in); size = Curl_dyn_len(in);
headersize = size - included_body_bytes; /* the initial part that isn't body headersize = size - (size_t)included_body_bytes; /* the initial part that
is header */ isn't body is header */
DEBUGASSERT(size > included_body_bytes); DEBUGASSERT(size > (size_t)included_body_bytes);
result = Curl_convert_to_network(data, ptr, headersize); result = Curl_convert_to_network(data, ptr, headersize);
/* Curl_convert_to_network calls failf if unsuccessful */ /* Curl_convert_to_network calls failf if unsuccessful */
@ -1249,13 +1254,25 @@ CURLcode Curl_buffer_send(struct dynbuf *in,
#endif #endif
) )
&& conn->httpversion != 20) { && conn->httpversion != 20) {
/* Make sure this doesn't send more body bytes than what the max send
speed says. The request bytes do not count to the max speed.
*/
if(data->set.max_send_speed &&
(included_body_bytes > data->set.max_send_speed)) {
curl_off_t overflow = included_body_bytes - data->set.max_send_speed;
DEBUGASSERT((size_t)overflow < size);
sendsize = size - (size_t)overflow;
}
else
sendsize = size;
/* We never send more than CURL_MAX_WRITE_SIZE bytes in one single chunk /* We never send more than CURL_MAX_WRITE_SIZE bytes in one single chunk
when we speak HTTPS, as if only a fraction of it is sent now, this data when we speak HTTPS, as if only a fraction of it is sent now, this data
needs to fit into the normal read-callback buffer later on and that needs to fit into the normal read-callback buffer later on and that
buffer is using this size. buffer is using this size.
*/ */
if(sendsize > CURL_MAX_WRITE_SIZE)
sendsize = CURLMIN(size, CURL_MAX_WRITE_SIZE); sendsize = CURL_MAX_WRITE_SIZE;
/* OpenSSL is very picky and we must send the SAME buffer pointer to the /* OpenSSL is very picky and we must send the SAME buffer pointer to the
library when we attempt to re-send this buffer. Sending the same data library when we attempt to re-send this buffer. Sending the same data
@ -1287,8 +1304,20 @@ CURLcode Curl_buffer_send(struct dynbuf *in,
} }
else else
#endif #endif
{
/* Make sure this doesn't send more body bytes than what the max send
speed says. The request bytes do not count to the max speed.
*/
if(data->set.max_send_speed &&
(included_body_bytes > data->set.max_send_speed)) {
curl_off_t overflow = included_body_bytes - data->set.max_send_speed;
DEBUGASSERT((size_t)overflow < size);
sendsize = size - (size_t)overflow;
}
else
sendsize = size; sendsize = size;
} }
}
result = Curl_write(data, sockfd, ptr, sendsize, &amount); result = Curl_write(data, sockfd, ptr, sendsize, &amount);
@ -2629,8 +2658,8 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn,
} }
} }
/* issue the request */ /* issue the request */
result = Curl_buffer_send(r, data, &data->info.request_size, result = Curl_buffer_send(r, data, &data->info.request_size, included_body,
(size_t)included_body, FIRSTSOCKET); FIRSTSOCKET);
if(result) if(result)
failf(data, "Failed sending HTTP POST request"); failf(data, "Failed sending HTTP POST request");

View File

@ -58,7 +58,7 @@ char *Curl_checkProxyheaders(struct Curl_easy *data,
CURLcode Curl_buffer_send(struct dynbuf *in, CURLcode Curl_buffer_send(struct dynbuf *in,
struct Curl_easy *data, struct Curl_easy *data,
curl_off_t *bytes_written, curl_off_t *bytes_written,
size_t included_body_bytes, curl_off_t included_body_bytes,
int socketindex); int socketindex);
#else #else
#define Curl_buffer_send(a,b,c,d,e) CURLE_OK #define Curl_buffer_send(a,b,c,d,e) CURLE_OK