data->req.httpversion_sent = httpversion;
buf = curlx_dyn_ptr(req);
blen = curlx_dyn_len(req);
- if(!Curl_creader_total_length(data)) {
- /* Request without body. Try to send directly from the buf given. */
+ /* if the sendbuf is empty and the request without body and
+ * the length to send fits info a sendbuf chunk, we send it directly.
+ * If `blen` is larger then `chunk_size`, we can not. Because we
+ * might have to retry a blocked send later from sendbuf and that
+ * would result in retry sends with a shrunken length. That is trouble. */
+ if(Curl_bufq_is_empty(&data->req.sendbuf) &&
+ !Curl_creader_total_length(data) &&
+ (blen <= data->req.sendbuf.chunk_size)) {
data->req.eos_read = TRUE;
result = xfer_send(data, buf, blen, blen, &nwritten);
if(result)
connssl->io_need = CURL_SSL_IO_NEED_NONE;
memlen = (len > (size_t)INT_MAX) ? INT_MAX : (int)len;
+ if(octx->blocked_ssl_write_len && (octx->blocked_ssl_write_len != memlen)) {
+ /* The previous SSL_write() call was blocked, using that length.
+ * We need to use that again or OpenSSL will freak out. A shorter
+ * length should not happen and is a bug in libcurl. */
+ if(octx->blocked_ssl_write_len > memlen) {
+ DEBUGASSERT(0);
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ }
+ memlen = octx->blocked_ssl_write_len;
+ }
+ octx->blocked_ssl_write_len = 0;
nwritten = SSL_write(octx->ssl, mem, memlen);
if(nwritten > 0)
switch(err) {
case SSL_ERROR_WANT_READ:
connssl->io_need = CURL_SSL_IO_NEED_RECV;
+ octx->blocked_ssl_write_len = memlen;
result = CURLE_AGAIN;
goto out;
case SSL_ERROR_WANT_WRITE:
result = CURLE_AGAIN;
+ octx->blocked_ssl_write_len = memlen;
goto out;
case SSL_ERROR_SYSCALL:
{
int sockerr = SOCKERRNO;
if(octx->io_result == CURLE_AGAIN) {
+ octx->blocked_ssl_write_len = memlen;
result = CURLE_AGAIN;
goto out;
}
X509* server_cert;
BIO_METHOD *bio_method;
CURLcode io_result; /* result of last BIO cfilter operation */
+ /* blocked writes need to retry with same length, remember it */
+ int blocked_ssl_write_len;
#ifndef HAVE_KEYLOG_CALLBACK
/* Set to true once a valid keylog entry has been created to avoid dupes.
This is a bool and not a bitfield because it is passed by address. */
# download with looong urls
@pytest.mark.parametrize("proto", ['http/1.1', 'h2', 'h3'])
- @pytest.mark.parametrize("url_junk", [1024, 16*1024, 32*1024, 64*1024])
+ @pytest.mark.parametrize("url_junk", [1024, 16*1024, 32*1024, 64*1024, 80*1024, 96*1024])
def test_02_36_looong_urls(self, env: Env, httpd, nghttpx, proto, url_junk):
if proto == 'h3' and not env.have_h3():
pytest.skip("h3 not supported")
# h2 is unable to send such large headers (frame limits)
r.check_exit_code(55)
elif proto == 'h3':
- r.check_exit_code(0)
- # nghttpx reports 431 Request Header Field too Large
- r.check_response(http_status=431)
+ if url_junk <= 64*1024:
+ r.check_exit_code(0)
+ # nghttpx reports 431 Request Header Field too Large
+ r.check_response(http_status=431)
+ else:
+ # nghttpx destroys the connection with internal error
+ # ERR_QPACK_HEADER_TOO_LARGE
+ r.check_exit_code(56)