-
-
Notifications
You must be signed in to change notification settings - Fork 6.9k
Description
I did this
Using mbedtls 3.6.2 = Mbed-TLS/mbedtls@107ea89daaef and nghttp2 1.50.0 = nghttp2/nghttp2@87fef4ab71be, HTTP/2 requests with large-ish (600kB+) request bodies often fail with HTTP/2 stream 1 was not closed cleanly: PROTOCOL_ERROR (err 1)
. Wireshark traces I have taken show that the data stream ends sooner than the content-length header would suggest, and the server gives up after some inactivity and fails the request. Matching up frames with known data shows that ranges of bytes inside the TLS stream occasionally get lost.
This has prompted me to look at how data is passed to mbedtls:
Lines 1169 to 1194 in 7eb8c04
static ssize_t mbed_send(struct Curl_cfilter *cf, struct Curl_easy *data, | |
const void *mem, size_t len, | |
CURLcode *curlcode) | |
{ | |
struct ssl_connect_data *connssl = cf->ctx; | |
struct mbed_ssl_backend_data *backend = | |
(struct mbed_ssl_backend_data *)connssl->backend; | |
int ret = -1; | |
(void)data; | |
DEBUGASSERT(backend); | |
ret = mbedtls_ssl_write(&backend->ssl, (unsigned char *)mem, len); | |
if(ret < 0) { | |
CURL_TRC_CF(data, cf, "mbedtls_ssl_write(len=%zu) -> -0x%04X", | |
len, -ret); | |
*curlcode = ((ret == MBEDTLS_ERR_SSL_WANT_WRITE) | |
#ifdef TLS13_SUPPORT | |
|| (ret == MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET) | |
#endif | |
)? CURLE_AGAIN : CURLE_SEND_ERROR; | |
ret = -1; | |
} | |
return ret; | |
} |
Given the documentation on mbedtls_ssl_write, which states that when MBEDTLS_ERR_SSL_WANT_WRITE
is returned, the same arguments must be passed the next time, this seems incorrect: simply returning CURLE_AGAIN
allows for more data to be given to this function than the amount given at the previous call.
I have made a change locally that makes sure that when MBEDTLS_ERR_SSL_WANT_WRITE
is returned, the exact same arguments are passed the next time around, even if mbed_send
has more data to send than the last time (and return an appropriate ret
), and indeed, this fixes my issue.
This is not a pull request because the underlying issue seems to be one of design, and I am not willing to tackle fixing it: as far as I can tell, libcurl is designed with write(2)-like behaviour in mind, i.e. ranges of bytes that are reported sent have been sent (or the stream breaks later, etc.), and ranges reported to have been left unsent are not sent, but also not committed anywhere. The issue with mbedtls seems to be that it effectively commits ranges of bytes internally, which is not compatible with this design.
I expected the following
I expected the aforementioned requests to succeed. Excuse me for filling this field with such an abstract answer; the issue I am facing is also abstract.
curl/libcurl version
curl 8.10.1 = 7eb8c04
operating system
Windows 10.0.19045.3570