lib: further send/upload handling polish

- Move all the "upload_done" handling to request.c

  - add possibility to abort sending of a request
  - add `Curl_req_done_sending()` for checks
  - transfer.c: readwrite_upload() now clean

- removing data->state.ulbuf and data->req.upload_fromhere

  - as well as data->req.upload_present
  - set data->req.upload_done on having read all from
    the client and completely flushed the send buffer

- tftp, remove setting of data->req.upload_fromhere

  - serves no purpose as `upload_present` is not set
    and the data itself is directly `sendto()` anyway

- smtp, make upload EOB conversion a client reader
- xfer_ulbuf addition

  - add xfer_ulbuf for borrowing, similar to xfer_buf
  - use in file upload
  - use in c-hyper body sending

- h1-proxy, remove init of data->state.uilbuf that is never used
- smb, add own send_buf instead of using data->state.ulbuf

Closes #13010
This commit is contained in:
Stefan Eissing 2024-02-28 14:51:53 +01:00 committed by Daniel Stenberg
parent 46aea3d990
commit e3905de819
No known key found for this signature in database
GPG Key ID: 5CC908FDB71E12C2
22 changed files with 485 additions and 535 deletions

View File

@ -469,7 +469,7 @@ CURLcode Curl_hyper_stream(struct Curl_easy *data,
infof(data, "Got 417 while waiting for a 100");
data->state.disableexpect = TRUE;
data->req.newurl = strdup(data->state.url);
Curl_done_sending(data, k);
Curl_req_abort_sending(data);
}
result = status_line(data, conn,
@ -663,7 +663,10 @@ static int uploadstreamed(void *userdata, hyper_context *ctx,
size_t fillcount;
struct Curl_easy *data = (struct Curl_easy *)userdata;
CURLcode result;
char *xfer_ulbuf;
size_t xfer_ulblen;
bool eos;
int rc = HYPER_POLL_ERROR;
(void)ctx;
if(data->req.exp100 > EXP100_SEND_DATA) {
@ -677,39 +680,44 @@ static int uploadstreamed(void *userdata, hyper_context *ctx,
return HYPER_POLL_PENDING;
}
result = Curl_client_read(data, data->state.ulbuf,
data->set.upload_buffer_size,
&fillcount, &eos);
if(result) {
data->state.hresult = result;
return HYPER_POLL_ERROR;
}
result = Curl_multi_xfer_ulbuf_borrow(data, &xfer_ulbuf, &xfer_ulblen);
if(result)
goto out;
result = Curl_client_read(data, xfer_ulbuf, xfer_ulblen, &fillcount, &eos);
if(result)
goto out;
if(fillcount) {
hyper_buf *copy = hyper_buf_copy((uint8_t *)data->state.ulbuf, fillcount);
hyper_buf *copy = hyper_buf_copy((uint8_t *)xfer_ulbuf, fillcount);
if(copy)
*chunk = copy;
else {
data->state.hresult = CURLE_OUT_OF_MEMORY;
return HYPER_POLL_ERROR;
result = CURLE_OUT_OF_MEMORY;
goto out;
}
/* increasing the writebytecount here is a little premature but we
don't know exactly when the body is sent */
data->req.writebytecount += fillcount;
Curl_pgrsSetUploadCounter(data, data->req.writebytecount);
return HYPER_POLL_READY;
rc = HYPER_POLL_READY;
}
else if(eos) {
*chunk = NULL;
return HYPER_POLL_READY;
rc = HYPER_POLL_READY;
}
else {
/* paused, save a waker */
if(data->hyp.send_body_waker)
hyper_waker_free(data->hyp.send_body_waker);
data->hyp.send_body_waker = hyper_context_waker(ctx);
return HYPER_POLL_PENDING;
rc = HYPER_POLL_PENDING;
}
out:
Curl_multi_xfer_ulbuf_release(data, xfer_ulbuf);
data->state.hresult = result;
return rc;
}
/*
@ -722,7 +730,6 @@ static CURLcode bodysend(struct Curl_easy *data,
hyper_request *hyperreq,
Curl_HttpReq httpreq)
{
struct HTTP *http = data->req.p.http;
CURLcode result = CURLE_OK;
struct dynbuf req;
if((httpreq == HTTPREQ_GET) || (httpreq == HTTPREQ_HEAD))
@ -731,21 +738,21 @@ static CURLcode bodysend(struct Curl_easy *data,
hyper_body *body;
Curl_dyn_init(&req, DYN_HTTP_REQUEST);
result = Curl_http_req_complete(data, &req, httpreq);
if(result)
return result;
if(!result)
/* if the "complete" above did produce more than the closing line,
parse the added headers */
if(Curl_dyn_len(&req) != 2 || strcmp(Curl_dyn_ptr(&req), "\r\n")) {
result = Curl_hyper_header(data, headers, Curl_dyn_ptr(&req));
if(result)
return result;
}
Curl_dyn_free(&req);
body = hyper_body_new();
hyper_body_set_userdata(body, data);
result = Curl_get_upload_buffer(data);
if(result) {
hyper_body_free(body);
return result;
}
/* init the "upload from here" pointer */
data->req.upload_fromhere = data->state.ulbuf;
hyper_body_set_data_func(body, uploadstreamed);
if(HYPERE_OK != hyper_request_set_body(hyperreq, body)) {
@ -753,7 +760,6 @@ static CURLcode bodysend(struct Curl_easy *data,
result = CURLE_OUT_OF_MEMORY;
}
}
http->sending = HTTPSEND_BODY;
return result;
}

View File

@ -114,18 +114,12 @@ static CURLcode tunnel_init(struct Curl_cfilter *cf,
struct h1_tunnel_state **pts)
{
struct h1_tunnel_state *ts;
CURLcode result;
if(cf->conn->handler->flags & PROTOPT_NOTCPPROXY) {
failf(data, "%s cannot be done over CONNECT", cf->conn->handler->scheme);
return CURLE_UNSUPPORTED_PROTOCOL;
}
/* we might need the upload buffer for streaming a partial request */
result = Curl_get_upload_buffer(data);
if(result)
return result;
ts = calloc(1, sizeof(*ts));
if(!ts)
return CURLE_OUT_OF_MEMORY;

View File

@ -291,8 +291,8 @@ static CURLcode file_upload(struct Curl_easy *data)
int fd;
int mode;
CURLcode result = CURLE_OK;
char *xfer_buf;
size_t xfer_blen;
char *xfer_ulbuf;
size_t xfer_ulblen;
curl_off_t bytecount = 0;
struct_stat file_stat;
const char *sendbuf;
@ -340,7 +340,7 @@ static CURLcode file_upload(struct Curl_easy *data)
data->state.resume_from = (curl_off_t)file_stat.st_size;
}
result = Curl_multi_xfer_buf_borrow(data, &xfer_buf, &xfer_blen);
result = Curl_multi_xfer_ulbuf_borrow(data, &xfer_ulbuf, &xfer_ulblen);
if(result)
goto out;
@ -349,7 +349,7 @@ static CURLcode file_upload(struct Curl_easy *data)
ssize_t nwrite;
size_t readcount;
result = Curl_client_read(data, xfer_buf, xfer_blen, &readcount, &eos);
result = Curl_client_read(data, xfer_ulbuf, xfer_ulblen, &readcount, &eos);
if(result)
break;
@ -363,16 +363,16 @@ static CURLcode file_upload(struct Curl_easy *data)
if((curl_off_t)nread <= data->state.resume_from) {
data->state.resume_from -= nread;
nread = 0;
sendbuf = xfer_buf;
sendbuf = xfer_ulbuf;
}
else {
sendbuf = xfer_buf + data->state.resume_from;
sendbuf = xfer_ulbuf + data->state.resume_from;
nread -= (size_t)data->state.resume_from;
data->state.resume_from = 0;
}
}
else
sendbuf = xfer_buf;
sendbuf = xfer_ulbuf;
/* write the data to the target */
nwrite = write(fd, sendbuf, nread);
@ -395,7 +395,7 @@ static CURLcode file_upload(struct Curl_easy *data)
out:
close(fd);
Curl_multi_xfer_buf_release(data, xfer_buf);
Curl_multi_xfer_ulbuf_release(data, xfer_ulbuf);
return result;
}

View File

@ -1283,7 +1283,6 @@ CURLcode Curl_http_done(struct Curl_easy *data,
if(!http)
return CURLE_OK;
Curl_dyn_free(&http->send_buffer);
Curl_dyn_reset(&data->state.headerb);
Curl_hyper_done(data);
Curl_ws_done(data);
@ -2151,6 +2150,12 @@ CURLcode Curl_http_req_complete(struct Curl_easy *data,
CURLcode result = CURLE_OK;
struct HTTP *http = data->req.p.http;
if(data->req.upload_chunky) {
result = Curl_httpchunk_add_reader(data);
if(result)
return result;
}
DEBUGASSERT(data->conn);
switch(httpreq) {
case HTTPREQ_PUT: /* Let's PUT the data to the server! */
@ -2252,7 +2257,6 @@ CURLcode Curl_http_req_complete(struct Curl_easy *data,
data->state.in = (void *) data->state.mimepost;
result = Client_reader_set_fread(data, data->state.infilesize);
}
http->sending = HTTPSEND_BODY;
break;
#endif
case HTTPREQ_POST:
@ -2294,19 +2298,21 @@ CURLcode Curl_http_req_complete(struct Curl_easy *data,
goto out;
if(!http->postsize) {
Curl_pgrsSetUploadSize(data, -1);
Curl_pgrsSetUploadSize(data, 0);
result = Client_reader_set_null(data);
}
else if(data->set.postfields) { /* we have the bytes */
else if(data->set.postfields) {
Curl_pgrsSetUploadSize(data, http->postsize);
result = Client_reader_set_buf(data, data->set.postfields,
(size_t)http->postsize);
if(http->postsize > 0)
result = Client_reader_set_buf(data, data->set.postfields,
(size_t)http->postsize);
else
result = Client_reader_set_null(data);
}
else { /* we read the bytes from the callback */
Curl_pgrsSetUploadSize(data, http->postsize);
result = Client_reader_set_fread(data, http->postsize);
}
http->sending = HTTPSEND_BODY;
break;
default:
@ -2647,7 +2653,6 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
{
struct connectdata *conn = data->conn;
CURLcode result = CURLE_OK;
struct HTTP *http;
Curl_HttpReq httpreq;
const char *te = ""; /* transfer-encoding */
const char *request;
@ -2699,9 +2704,6 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
if(result)
goto fail;
http = data->req.p.http;
DEBUGASSERT(http);
result = Curl_http_host(data, conn);
if(result)
goto fail;
@ -2880,7 +2882,6 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
result = Curl_add_custom_headers(data, FALSE, &req);
if(!result) {
http->postdata = NULL; /* nothing to post at this point */
if((httpreq == HTTPREQ_GET) ||
(httpreq == HTTPREQ_HEAD))
Curl_pgrsSetUploadSize(data, 0); /* nothing */
@ -2896,29 +2897,6 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
if(result)
goto fail;
if(data->req.writebytecount) {
/* if a request-body has been sent off, we make sure this progress is noted
properly */
Curl_pgrsSetUploadCounter(data, data->req.writebytecount);
if(Curl_pgrsUpdate(data))
result = CURLE_ABORTED_BY_CALLBACK;
if(!http->postsize) {
/* already sent the entire request body, mark the "upload" as
complete */
infof(data, "upload completely sent off: %" CURL_FORMAT_CURL_OFF_T
" out of %" CURL_FORMAT_CURL_OFF_T " bytes",
data->req.writebytecount, http->postsize);
data->req.upload_done = TRUE;
data->req.keepon &= ~KEEP_SEND; /* we're done writing */
data->req.exp100 = EXP100_SEND_DATA; /* already sent */
Curl_expire_done(data, EXPIRE_100_TIMEOUT);
}
}
if(data->req.upload_done)
Curl_conn_ev_data_done_send(data);
if((conn->httpversion >= 20) && data->req.upload_chunky)
/* upload_chunky was set above to set up the request in a chunky fashion,
but is disabled here again to avoid that the chunked encoded version is
@ -3782,7 +3760,7 @@ static CURLcode http_rw_headers(struct Curl_easy *data,
* connection for closure after we've read the entire response.
*/
Curl_expire_done(data, EXPIRE_100_TIMEOUT);
if(!k->upload_done) {
if(!Curl_req_done_sending(data)) {
if((k->httpcode == 417) && data->state.expect100header) {
/* 417 Expectation Failed - try again without the Expect
header */
@ -3801,7 +3779,7 @@ static CURLcode http_rw_headers(struct Curl_easy *data,
data->state.disableexpect = TRUE;
DEBUGASSERT(!data->req.newurl);
data->req.newurl = strdup(data->state.url);
Curl_done_sending(data, k);
Curl_req_abort_sending(data);
}
else if(data->set.http_keep_sending_on_error) {
infof(data, "HTTP error before end of send, keep sending");
@ -3813,10 +3791,9 @@ static CURLcode http_rw_headers(struct Curl_easy *data,
else {
infof(data, "HTTP error before end of send, stop sending");
streamclose(conn, "Stop sending data before everything sent");
result = Curl_done_sending(data, k);
result = Curl_req_abort_sending(data);
if(result)
return result;
k->upload_done = TRUE;
if(data->state.expect100header)
k->exp100 = EXP100_FAILED;
}
@ -3828,8 +3805,7 @@ static CURLcode http_rw_headers(struct Curl_easy *data,
}
}
if(data->state.rewindbeforesend &&
(conn->writesockfd != CURL_SOCKET_BAD)) {
if(data->state.rewindbeforesend && !Curl_req_done_sending(data)) {
/* We rewind before next send, continue sending now */
infof(data, "Keep sending data to get tossed away");
k->keepon |= KEEP_SEND;

View File

@ -189,27 +189,10 @@ CURLcode Curl_http_auth_act(struct Curl_easy *data);
***************************************************************************/
struct HTTP {
curl_off_t postsize; /* off_t to handle large file sizes */
const char *postdata;
struct back {
curl_read_callback fread_func; /* backup storage for fread pointer */
void *fread_in; /* backup storage for fread_in pointer */
const char *postdata;
curl_off_t postsize;
struct Curl_easy *data;
} backup;
enum {
HTTPSEND_NADA, /* init */
HTTPSEND_REQUEST, /* sending a request */
HTTPSEND_BODY /* sending body */
} sending;
#ifndef CURL_DISABLE_HTTP
void *h2_ctx; /* HTTP/2 implementation context */
void *h3_ctx; /* HTTP/3 implementation context */
struct dynbuf send_buffer; /* used if the request couldn't be sent in one
chunk, points to an allocated send_buffer
struct */
#endif
};

View File

@ -94,7 +94,7 @@ static CURLMcode add_next_timeout(struct curltime now,
static CURLMcode multi_timeout(struct Curl_multi *multi,
long *timeout_ms);
static void process_pending_handles(struct Curl_multi *multi);
static void multi_xfer_buf_free(struct Curl_multi *multi);
static void multi_xfer_bufs_free(struct Curl_multi *multi);
#ifdef DEBUGBUILD
static const char * const multi_statename[]={
@ -192,7 +192,7 @@ static void mstate(struct Curl_easy *data, CURLMstate state
data->multi->num_alive--;
if(!data->multi->num_alive) {
/* free the transfer buffer when we have no more active transfers */
multi_xfer_buf_free(data->multi);
multi_xfer_bufs_free(data->multi);
}
}
@ -716,8 +716,6 @@ static CURLcode multi_done(struct Curl_easy *data,
if(!result)
result = Curl_req_done(&data->req, data, premature);
Curl_safefree(data->state.ulbuf);
CONNCACHE_LOCK(data);
Curl_detach_connection(data);
if(CONN_INUSE(conn)) {
@ -2900,7 +2898,7 @@ CURLMcode curl_multi_cleanup(struct Curl_multi *multi)
Curl_free_multi_ssl_backend_data(multi->ssl_backend_data);
#endif
multi_xfer_buf_free(multi);
multi_xfer_bufs_free(multi);
free(multi);
return CURLM_OK;
@ -3891,10 +3889,66 @@ void Curl_multi_xfer_buf_release(struct Curl_easy *data, char *buf)
data->multi->xfer_buf_borrowed = FALSE;
}
static void multi_xfer_buf_free(struct Curl_multi *multi)
CURLcode Curl_multi_xfer_ulbuf_borrow(struct Curl_easy *data,
char **pbuf, size_t *pbuflen)
{
DEBUGASSERT(data);
DEBUGASSERT(data->multi);
*pbuf = NULL;
*pbuflen = 0;
if(!data->multi) {
failf(data, "transfer has no multi handle");
return CURLE_FAILED_INIT;
}
if(!data->set.upload_buffer_size) {
failf(data, "transfer upload buffer size is 0");
return CURLE_FAILED_INIT;
}
if(data->multi->xfer_ulbuf_borrowed) {
failf(data, "attempt to borrow xfer_ulbuf when already borrowed");
return CURLE_AGAIN;
}
if(data->multi->xfer_ulbuf &&
data->set.upload_buffer_size > data->multi->xfer_ulbuf_len) {
/* not large enough, get a new one */
free(data->multi->xfer_ulbuf);
data->multi->xfer_ulbuf = NULL;
data->multi->xfer_ulbuf_len = 0;
}
if(!data->multi->xfer_ulbuf) {
data->multi->xfer_ulbuf = malloc((size_t)data->set.upload_buffer_size);
if(!data->multi->xfer_ulbuf) {
failf(data, "could not allocate xfer_ulbuf of %zu bytes",
(size_t)data->set.upload_buffer_size);
return CURLE_OUT_OF_MEMORY;
}
data->multi->xfer_ulbuf_len = data->set.upload_buffer_size;
}
data->multi->xfer_ulbuf_borrowed = TRUE;
*pbuf = data->multi->xfer_ulbuf;
*pbuflen = data->multi->xfer_ulbuf_len;
return CURLE_OK;
}
void Curl_multi_xfer_ulbuf_release(struct Curl_easy *data, char *buf)
{
(void)buf;
DEBUGASSERT(data);
DEBUGASSERT(data->multi);
DEBUGASSERT(!buf || data->multi->xfer_ulbuf == buf);
data->multi->xfer_ulbuf_borrowed = FALSE;
}
static void multi_xfer_bufs_free(struct Curl_multi *multi)
{
DEBUGASSERT(multi);
Curl_safefree(multi->xfer_buf);
multi->xfer_buf_len = 0;
multi->xfer_buf_borrowed = FALSE;
Curl_safefree(multi->xfer_ulbuf);
multi->xfer_ulbuf_len = 0;
multi->xfer_ulbuf_borrowed = FALSE;
}

View File

@ -127,6 +127,9 @@ struct Curl_multi {
/* buffer used for transfer data, lazy initialized */
char *xfer_buf; /* the actual buffer */
size_t xfer_buf_len; /* the allocated length */
/* buffer used for upload data, lazy initialized */
char *xfer_ulbuf; /* the actual buffer */
size_t xfer_ulbuf_len; /* the allocated length */
#if defined(USE_SSL)
struct multi_ssl_backend_data *ssl_backend_data;
@ -176,6 +179,7 @@ struct Curl_multi {
BIT(dead); /* a callback returned error, everything needs to crash and
burn */
BIT(xfer_buf_borrowed); /* xfer_buf is currently being borrowed */
BIT(xfer_ulbuf_borrowed); /* xfer_buf is currently being borrowed */
#ifdef DEBUGBUILD
BIT(warned); /* true after user warned of DEBUGBUILD */
#endif

View File

@ -118,4 +118,29 @@ CURLcode Curl_multi_xfer_buf_borrow(struct Curl_easy *data,
*/
void Curl_multi_xfer_buf_release(struct Curl_easy *data, char *buf);
/**
* Borrow the upload buffer from the multi, suitable
* for the given transfer `data`. The buffer may only be used in one
* multi processing of the easy handle. It MUST be returned to the
* multi before it can be borrowed again.
* Pointers into the buffer remain only valid as long as it is borrowed.
*
* @param data the easy handle
* @param pbuf on return, the buffer to use or NULL on error
* @param pbuflen on return, the size of *pbuf or 0 on error
* @return CURLE_OK when buffer is available and is returned.
* CURLE_OUT_OF_MEMORy on failure to allocate the buffer,
* CURLE_FAILED_INIT if the easy handle is without multi.
* CURLE_AGAIN if the buffer is borrowed already.
*/
CURLcode Curl_multi_xfer_ulbuf_borrow(struct Curl_easy *data,
char **pbuf, size_t *pbuflen);
/**
* Release the borrowed upload buffer. All references into the buffer become
* invalid after this.
* @param buf the upload buffer pointer borrowed for coding error checks.
*/
void Curl_multi_xfer_ulbuf_release(struct Curl_easy *data, char *buf);
#endif /* HEADER_CURL_MULTIIF_H */

View File

@ -25,6 +25,7 @@
#include "curl_setup.h"
#include "urldata.h"
#include "cfilters.h"
#include "dynbuf.h"
#include "doh.h"
#include "multiif.h"
@ -67,12 +68,14 @@ CURLcode Curl_req_start(struct SingleRequest *req,
return CURLE_OK;
}
static CURLcode req_flush(struct Curl_easy *data);
CURLcode Curl_req_done(struct SingleRequest *req,
struct Curl_easy *data, bool aborted)
{
(void)req;
if(!aborted)
(void)Curl_req_flush(data);
(void)req_flush(data);
Curl_client_reset(data);
return CURLE_OK;
}
@ -129,9 +132,9 @@ void Curl_req_free(struct SingleRequest *req, struct Curl_easy *data)
#endif
}
static CURLcode req_send(struct Curl_easy *data,
const char *buf, size_t blen,
size_t hds_len, size_t *pnwritten)
static CURLcode xfer_send(struct Curl_easy *data,
const char *buf, size_t blen,
size_t hds_len, size_t *pnwritten)
{
CURLcode result = CURLE_OK;
@ -180,7 +183,7 @@ static CURLcode req_send_buffer_flush(struct Curl_easy *data)
while(Curl_bufq_peek(&data->req.sendbuf, &buf, &blen)) {
size_t nwritten, hds_len = CURLMIN(data->req.sendbuf_hds_len, blen);
result = req_send(data, (const char *)buf, blen, hds_len, &nwritten);
result = xfer_send(data, (const char *)buf, blen, hds_len, &nwritten);
if(result)
break;
@ -206,7 +209,33 @@ static CURLcode req_send_buffer_flush(struct Curl_easy *data)
return result;
}
CURLcode Curl_req_flush(struct Curl_easy *data)
static CURLcode req_set_upload_done(struct Curl_easy *data)
{
DEBUGASSERT(!data->req.upload_done);
data->req.upload_done = TRUE;
data->req.keepon &= ~KEEP_SEND; /* we're done sending */
/* FIXME: http specific stuff, need to go somewhere else */
data->req.exp100 = EXP100_SEND_DATA;
Curl_expire_done(data, EXPIRE_100_TIMEOUT);
if(data->req.upload_aborted) {
if(data->req.writebytecount)
infof(data, "abort upload after having sent %" CURL_FORMAT_CURL_OFF_T
" bytes", data->req.writebytecount);
else
infof(data, "abort upload");
}
else if(data->req.writebytecount)
infof(data, "upload completely sent off: %" CURL_FORMAT_CURL_OFF_T
" bytes", data->req.writebytecount);
else
infof(data, "We are completely uploaded and fine");
return Curl_xfer_send_close(data);
}
static CURLcode req_flush(struct Curl_easy *data)
{
CURLcode result;
@ -221,9 +250,30 @@ CURLcode Curl_req_flush(struct Curl_easy *data)
return CURLE_AGAIN;
}
}
if(!data->req.upload_done && data->req.eos_read &&
Curl_bufq_is_empty(&data->req.sendbuf)) {
return req_set_upload_done(data);
}
return CURLE_OK;
}
static ssize_t add_from_client(void *reader_ctx,
unsigned char *buf, size_t buflen,
CURLcode *err)
{
struct Curl_easy *data = reader_ctx;
size_t nread;
bool eos;
*err = Curl_client_read(data, (char *)buf, buflen, &nread, &eos);
if(*err)
return -1;
if(eos)
data->req.eos_read = TRUE;
return (ssize_t)nread;
}
#ifndef USE_HYPER
static CURLcode req_send_buffer_add(struct Curl_easy *data,
@ -242,22 +292,6 @@ static CURLcode req_send_buffer_add(struct Curl_easy *data,
return CURLE_OK;
}
static ssize_t add_from_client(void *reader_ctx,
unsigned char *buf, size_t buflen,
CURLcode *err)
{
struct Curl_easy *data = reader_ctx;
size_t nread;
bool eos;
*err = Curl_client_read(data, (char *)buf, buflen, &nread, &eos);
if(*err)
return -1;
if(eos)
data->req.eos_read = TRUE;
return (ssize_t)nread;
}
CURLcode Curl_req_send(struct Curl_easy *data, struct dynbuf *buf)
{
CURLcode result;
@ -275,18 +309,7 @@ CURLcode Curl_req_send(struct Curl_easy *data, struct dynbuf *buf)
if(result)
return result;
if((data->req.exp100 == EXP100_SEND_DATA) &&
!Curl_bufq_is_full(&data->req.sendbuf)) {
ssize_t nread = Curl_bufq_sipn(&data->req.sendbuf, 0,
add_from_client, data, &result);
if(nread < 0 && result != CURLE_AGAIN)
return result;
}
result = req_send_buffer_flush(data);
if(result == CURLE_AGAIN)
result = CURLE_OK;
return result;
return Curl_req_send_more(data);
}
#endif /* !USE_HYPER */
@ -294,3 +317,42 @@ bool Curl_req_want_send(struct Curl_easy *data)
{
return data->req.sendbuf_init && !Curl_bufq_is_empty(&data->req.sendbuf);
}
bool Curl_req_done_sending(struct Curl_easy *data)
{
if(data->req.upload_done) {
DEBUGASSERT(Curl_bufq_is_empty(&data->req.sendbuf));
return TRUE;
}
return FALSE;
}
CURLcode Curl_req_send_more(struct Curl_easy *data)
{
CURLcode result;
/* Fill our send buffer if more from client can be read and
* we are not in a "expect-100" situation. */
if(!data->req.eos_read && !Curl_bufq_is_full(&data->req.sendbuf) &&
(data->req.exp100 == EXP100_SEND_DATA)) {
ssize_t nread = Curl_bufq_sipn(&data->req.sendbuf, 0,
add_from_client, data, &result);
if(nread < 0 && result != CURLE_AGAIN)
return result;
}
result = req_flush(data);
if(result == CURLE_AGAIN)
result = CURLE_OK;
return result;
}
CURLcode Curl_req_abort_sending(struct Curl_easy *data)
{
if(!data->req.upload_done) {
Curl_bufq_reset(&data->req.sendbuf);
data->req.upload_aborted = TRUE;
return req_set_upload_done(data);
}
return CURLE_OK;
}

View File

@ -100,16 +100,6 @@ struct SingleRequest {
char *newurl; /* Set to the new URL to use when a redirect or a retry is
wanted */
/* 'upload_present' is used to keep a byte counter of how much data there is
still left in the buffer, aimed for upload. */
size_t upload_present;
/* 'upload_fromhere' is used as a read-pointer when we uploaded parts of a
buffer, so the next read should read from where this pointer points to,
and the 'upload_present' contains the number of bytes available at this
position */
char *upload_fromhere;
/* Allocated protocol-specific data. Each protocol handler makes sure this
points to data it needs. */
union {
@ -139,8 +129,9 @@ struct SingleRequest {
BIT(download_done); /* set to TRUE when download is complete */
BIT(eos_written); /* iff EOS has been written to client */
BIT(eos_read); /* iff EOS has been read from the client */
BIT(upload_done); /* set to TRUE when doing chunked transfer-encoding
upload and we're uploading the last chunk */
BIT(upload_done); /* set to TRUE when all request data has been sent */
BIT(upload_aborted); /* set to TRUE when upload was aborted. Will also
* show `upload_done` as TRUE. */
BIT(ignorebody); /* we read a response-body but we ignore it! */
BIT(http_bodyless); /* HTTP response status code is between 100 and 199,
204 or 304 */
@ -206,15 +197,26 @@ CURLcode Curl_req_send(struct Curl_easy *data, struct dynbuf *buf);
#endif /* !USE_HYPER */
/**
* Flush all buffered request bytes.
* @return CURLE_OK on success, CURLE_AGAIN if sending was blocked,
* or the error on the sending.
* TRUE iff the request has sent all request headers and data.
*/
CURLcode Curl_req_flush(struct Curl_easy *data);
bool Curl_req_done_sending(struct Curl_easy *data);
/*
* Read more from client and flush all buffered request bytes.
* @return CURLE_OK on success or the error on the sending.
* Never returns CURLE_AGAIN.
*/
CURLcode Curl_req_send_more(struct Curl_easy *data);
/**
* TRUE iff the request wants to send, e.g. has buffered bytes.
*/
bool Curl_req_want_send(struct Curl_easy *data);
/**
* Stop sending any more request data to the server.
* Will clear the send buffer and mark request sending as done.
*/
CURLcode Curl_req_abort_sending(struct Curl_easy *data);
#endif /* HEADER_CURL_REQUEST_H */

View File

@ -577,6 +577,8 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done)
if(result)
goto out;
Curl_xfer_setup(data, FIRSTSOCKET, -1, TRUE, FIRSTSOCKET);
/* issue the request */
result = Curl_req_send(data, &req_buffer);
if(result) {
@ -584,8 +586,6 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done)
goto out;
}
Curl_xfer_setup(data, FIRSTSOCKET, -1, TRUE, FIRSTSOCKET);
/* Increment the CSeq on success */
data->state.rtsp_next_client_CSeq++;

View File

@ -576,8 +576,10 @@ static CURLcode cr_in_read(struct Curl_easy *data,
return CURLE_READ_ERROR;
}
ctx->read_len += nread;
if(ctx->total_len >= 0)
ctx->seen_eos = (ctx->read_len >= ctx->total_len);
*pnread = nread;
*peos = FALSE;
*peos = ctx->seen_eos;
break;
}
DEBUGF(infof(data, "cr_in_read(len=%zu, total=%"CURL_FORMAT_CURL_OFF_T
@ -743,7 +745,7 @@ static CURLcode cr_lc_add(struct Curl_easy *data)
CURLcode result;
result = Curl_creader_create(&reader, data, &cr_lc,
CURL_CR_TRANSFER_ENCODE);
CURL_CR_CONTENT_ENCODE);
if(!result)
result = Curl_creader_add(data, reader);

View File

@ -2239,7 +2239,6 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
arg = UPLOADBUFFER_MIN;
data->set.upload_buffer_size = (unsigned int)arg;
Curl_safefree(data->state.ulbuf); /* force a realloc next opportunity */
break;
case CURLOPT_NOSIGNAL:

View File

@ -456,6 +456,9 @@ static CURLcode smb_connect(struct Curl_easy *data, bool *done)
smbc->recv_buf = malloc(MAX_MESSAGE_SIZE);
if(!smbc->recv_buf)
return CURLE_OUT_OF_MEMORY;
smbc->send_buf = malloc(MAX_MESSAGE_SIZE);
if(!smbc->send_buf)
return CURLE_OUT_OF_MEMORY;
/* Multiple requests are allowed with this connection */
connkeep(conn, "SMB default");
@ -567,7 +570,7 @@ static CURLcode smb_send(struct Curl_easy *data, size_t len,
size_t bytes_written;
CURLcode result;
result = Curl_xfer_send(data, data->state.ulbuf, len, &bytes_written);
result = Curl_xfer_send(data, smbc->send_buf, len, &bytes_written);
if(result)
return result;
@ -592,7 +595,7 @@ static CURLcode smb_flush(struct Curl_easy *data)
if(!smbc->send_size)
return CURLE_OK;
result = Curl_xfer_send(data, data->state.ulbuf + smbc->sent, len,
result = Curl_xfer_send(data, smbc->send_buf + smbc->sent, len,
&bytes_written);
if(result)
return result;
@ -608,13 +611,13 @@ static CURLcode smb_flush(struct Curl_easy *data)
static CURLcode smb_send_message(struct Curl_easy *data, unsigned char cmd,
const void *msg, size_t msg_len)
{
CURLcode result = Curl_get_upload_buffer(data);
if(result)
return result;
smb_format_message(data, (struct smb_header *)data->state.ulbuf,
struct connectdata *conn = data->conn;
struct smb_conn *smbc = &conn->proto.smbc;
smb_format_message(data, (struct smb_header *)smbc->send_buf,
cmd, msg_len);
memcpy(data->state.ulbuf + sizeof(struct smb_header),
msg, msg_len);
DEBUGASSERT((sizeof(struct smb_header) + msg_len) <= MAX_MESSAGE_SIZE);
memcpy(smbc->send_buf + sizeof(struct smb_header), msg, msg_len);
return smb_send(data, sizeof(struct smb_header) + msg_len, 0);
}
@ -772,15 +775,14 @@ static CURLcode smb_send_read(struct Curl_easy *data)
static CURLcode smb_send_write(struct Curl_easy *data)
{
struct connectdata *conn = data->conn;
struct smb_conn *smbc = &conn->proto.smbc;
struct smb_write *msg;
struct smb_request *req = data->req.p.smb;
curl_off_t offset = data->req.offset;
curl_off_t upload_size = data->req.size - data->req.bytecount;
CURLcode result = Curl_get_upload_buffer(data);
if(result)
return result;
msg = (struct smb_write *)data->state.ulbuf;
msg = (struct smb_write *)smbc->send_buf;
if(upload_size >= MAX_PAYLOAD_SIZE - 1) /* There is one byte of padding */
upload_size = MAX_PAYLOAD_SIZE - 1;
@ -809,11 +811,11 @@ static CURLcode smb_send_and_recv(struct Curl_easy *data, void **msg)
/* Check if there is data in the transfer buffer */
if(!smbc->send_size && smbc->upload_size) {
size_t nread = smbc->upload_size > (size_t)data->set.upload_buffer_size ?
(size_t)data->set.upload_buffer_size : smbc->upload_size;
size_t nread = smbc->upload_size > (size_t)MAX_MESSAGE_SIZE ?
(size_t)MAX_MESSAGE_SIZE : smbc->upload_size;
bool eos;
result = Curl_client_read(data, data->state.ulbuf, nread, &nread, &eos);
result = Curl_client_read(data, smbc->send_buf, nread, &nread, &eos);
if(result && result != CURLE_AGAIN)
return result;
if(!nread)
@ -1131,6 +1133,7 @@ static CURLcode smb_disconnect(struct Curl_easy *data,
Curl_safefree(smbc->share);
Curl_safefree(smbc->domain);
Curl_safefree(smbc->recv_buf);
Curl_safefree(smbc->send_buf);
return CURLE_OK;
}

View File

@ -42,6 +42,7 @@ struct smb_conn {
unsigned int session_key;
unsigned short uid;
char *recv_buf;
char *send_buf;
size_t upload_size;
size_t send_size;
size_t sent;

View File

@ -111,6 +111,7 @@ static CURLcode smtp_continue_auth(struct Curl_easy *data, const char *mech,
const struct bufref *resp);
static CURLcode smtp_cancel_auth(struct Curl_easy *data, const char *mech);
static CURLcode smtp_get_message(struct Curl_easy *data, struct bufref *out);
static CURLcode cr_eob_add(struct Curl_easy *data);
/*
* SMTP protocol handler.
@ -618,7 +619,7 @@ static CURLcode smtp_perform_mail(struct Curl_easy *data)
result = smtp_parse_address(data->set.str[STRING_MAIL_FROM],
&address, &host);
if(result)
return result;
goto out;
/* Establish whether we should report SMTPUTF8 to the server for this
mailbox as per RFC-6531 sect. 3.1 point 4 and sect. 3.4 */
@ -642,8 +643,10 @@ static CURLcode smtp_perform_mail(struct Curl_easy *data)
/* Null reverse-path, RFC-5321, sect. 3.6.3 */
from = strdup("<>");
if(!from)
return CURLE_OUT_OF_MEMORY;
if(!from) {
result = CURLE_OUT_OF_MEMORY;
goto out;
}
/* Calculate the optional AUTH parameter */
if(data->set.str[STRING_MAIL_AUTH] && conn->proto.smtpc.sasl.authused) {
@ -655,10 +658,8 @@ static CURLcode smtp_perform_mail(struct Curl_easy *data)
converting the host name to an IDN A-label if necessary */
result = smtp_parse_address(data->set.str[STRING_MAIL_AUTH],
&address, &host);
if(result) {
free(from);
return result;
}
if(result)
goto out;
/* Establish whether we should report SMTPUTF8 to the server for this
mailbox as per RFC-6531 sect. 3.1 point 4 and sect. 3.4 */
@ -676,17 +677,14 @@ static CURLcode smtp_perform_mail(struct Curl_easy *data)
/* An invalid mailbox was provided but we'll simply let the server
worry about it */
auth = aprintf("<%s>", address);
free(address);
}
else
/* Empty AUTH, RFC-2554, sect. 5 */
auth = strdup("<>");
if(!auth) {
free(from);
return CURLE_OUT_OF_MEMORY;
result = CURLE_OUT_OF_MEMORY;
goto out;
}
}
@ -710,12 +708,8 @@ static CURLcode smtp_perform_mail(struct Curl_easy *data)
if(!result)
result = Curl_mime_rewind(&data->set.mimepost);
if(result) {
free(from);
free(auth);
return result;
}
if(result)
goto out;
data->state.infilesize = Curl_mime_size(&data->set.mimepost);
@ -730,10 +724,8 @@ static CURLcode smtp_perform_mail(struct Curl_easy *data)
size = aprintf("%" CURL_FORMAT_CURL_OFF_T, data->state.infilesize);
if(!size) {
free(from);
free(auth);
return CURLE_OUT_OF_MEMORY;
result = CURLE_OUT_OF_MEMORY;
goto out;
}
}
@ -754,6 +746,15 @@ static CURLcode smtp_perform_mail(struct Curl_easy *data)
}
}
/* Setup client reader for size and EOB conversion */
result = Client_reader_set_fread(data, data->state.infilesize);
if(result)
goto out;
/* Add the client reader doing STMP EOB escaping */
result = cr_eob_add(data);
if(result)
goto out;
/* Send the MAIL command */
result = Curl_pp_sendf(data, &conn->proto.smtpc.pp,
"MAIL FROM:%s%s%s%s%s%s",
@ -765,6 +766,7 @@ static CURLcode smtp_perform_mail(struct Curl_easy *data)
utf8 ? " SMTPUTF8" /* Internationalised mailbox */
: ""); /* included in our envelope */
out:
free(from);
free(auth);
free(size);
@ -1393,9 +1395,6 @@ static CURLcode smtp_done(struct Curl_easy *data, CURLcode status,
CURLcode result = CURLE_OK;
struct connectdata *conn = data->conn;
struct SMTP *smtp = data->req.p.smtp;
struct pingpong *pp = &conn->proto.smtpc.pp;
char *eob;
size_t len, bytes_written;
(void)premature;
@ -1411,46 +1410,6 @@ static CURLcode smtp_done(struct Curl_easy *data, CURLcode status,
}
else if(!data->set.connect_only && data->set.mail_rcpt &&
(data->state.upload || IS_MIME_POST(data))) {
/* Calculate the EOB taking into account any terminating CRLF from the
previous line of the email or the CRLF of the DATA command when there
is "no mail data". RFC-5321, sect. 4.1.1.4.
Note: As some SSL backends, such as OpenSSL, will cause Curl_write() to
fail when using a different pointer following a previous write, that
returned CURLE_AGAIN, we duplicate the EOB now rather than when the
bytes written doesn't equal len. */
if(smtp->trailing_crlf || !data->state.infilesize) {
eob = strdup(&SMTP_EOB[2]);
len = SMTP_EOB_LEN - 2;
}
else {
eob = strdup(SMTP_EOB);
len = SMTP_EOB_LEN;
}
if(!eob)
return CURLE_OUT_OF_MEMORY;
/* Send the end of block data */
result = Curl_xfer_send(data, eob, len, &bytes_written);
if(result) {
free(eob);
return result;
}
if(bytes_written != len) {
/* The whole chunk was not sent so keep it around and adjust the
pingpong structure accordingly */
pp->sendthis = eob;
pp->sendsize = len;
pp->sendleft = len - bytes_written;
}
else {
/* Successfully sent so adjust the response timeout relative to now */
pp->response = Curl_now();
free(eob);
}
smtp_state(data, SMTP_POSTDATA);
@ -1818,108 +1777,159 @@ static CURLcode smtp_parse_address(const char *fqma, char **address,
return result;
}
CURLcode Curl_smtp_escape_eob(struct Curl_easy *data,
const ssize_t nread,
const ssize_t offset)
struct cr_eob_ctx {
struct Curl_creader super;
struct bufq buf;
size_t n_eob; /* how many EOB bytes we matched so far */
size_t eob; /* Number of bytes of the EOB (End Of Body) that
have been received so far */
BIT(read_eos); /* we read an EOS from the next reader */
BIT(eos); /* we have returned an EOS */
};
static CURLcode cr_eob_init(struct Curl_easy *data,
struct Curl_creader *reader)
{
/* When sending a SMTP payload we must detect CRLF. sequences making sure
they are sent as CRLF.. instead, as a . on the beginning of a line will
be deleted by the server when not part of an EOB terminator and a
genuine CRLF.CRLF which isn't escaped will wrongly be detected as end of
data by the server
*/
ssize_t i;
ssize_t si;
struct SMTP *smtp = data->req.p.smtp;
char *scratch = data->state.scratch;
char *newscratch = NULL;
char *oldscratch = NULL;
size_t eob_sent;
/* Do we need to allocate a scratch buffer? */
if(!scratch || data->set.crlf) {
oldscratch = scratch;
scratch = newscratch = malloc(2 * data->set.upload_buffer_size);
if(!newscratch) {
failf(data, "Failed to alloc scratch buffer");
return CURLE_OUT_OF_MEMORY;
}
}
DEBUGASSERT((size_t)data->set.upload_buffer_size >= (size_t)nread);
/* Have we already sent part of the EOB? */
eob_sent = smtp->eob;
/* This loop can be improved by some kind of Boyer-Moore style of
approach but that is saved for later... */
if(offset)
memcpy(scratch, data->req.upload_fromhere, offset);
for(i = offset, si = offset; i < nread; i++) {
if(SMTP_EOB[smtp->eob] == data->req.upload_fromhere[i]) {
smtp->eob++;
/* Is the EOB potentially the terminating CRLF? */
if(2 == smtp->eob || SMTP_EOB_LEN == smtp->eob)
smtp->trailing_crlf = TRUE;
else
smtp->trailing_crlf = FALSE;
}
else if(smtp->eob) {
/* A previous substring matched so output that first */
memcpy(&scratch[si], &SMTP_EOB[eob_sent], smtp->eob - eob_sent);
si += smtp->eob - eob_sent;
/* Then compare the first byte */
if(SMTP_EOB[0] == data->req.upload_fromhere[i])
smtp->eob = 1;
else
smtp->eob = 0;
eob_sent = 0;
/* Reset the trailing CRLF flag as there was more data */
smtp->trailing_crlf = FALSE;
}
/* Do we have a match for CRLF. as per RFC-5321, sect. 4.5.2 */
if(SMTP_EOB_FIND_LEN == smtp->eob) {
/* Copy the replacement data to the target buffer */
memcpy(&scratch[si], &SMTP_EOB_REPL[eob_sent],
SMTP_EOB_REPL_LEN - eob_sent);
si += SMTP_EOB_REPL_LEN - eob_sent;
smtp->eob = 0;
eob_sent = 0;
}
else if(!smtp->eob)
scratch[si++] = data->req.upload_fromhere[i];
}
if(smtp->eob - eob_sent) {
/* A substring matched before processing ended so output that now */
memcpy(&scratch[si], &SMTP_EOB[eob_sent], smtp->eob - eob_sent);
si += smtp->eob - eob_sent;
}
/* Only use the new buffer if we replaced something */
if(si != nread) {
/* Upload from the new (replaced) buffer instead */
data->req.upload_fromhere = scratch;
/* Save the buffer so it can be freed later */
data->state.scratch = scratch;
/* Free the old scratch buffer */
free(oldscratch);
/* Set the new amount too */
data->req.upload_present = si;
}
else
free(newscratch);
struct cr_eob_ctx *ctx = (struct cr_eob_ctx *)reader;
(void)data;
/* The first char we read is the first on a line, as if we had
* read CRLF just before */
ctx->n_eob = 2;
Curl_bufq_init2(&ctx->buf, (16 * 1024), 1, BUFQ_OPT_SOFT_LIMIT);
return CURLE_OK;
}
static void cr_eob_close(struct Curl_easy *data, struct Curl_creader *reader)
{
struct cr_eob_ctx *ctx = (struct cr_eob_ctx *)reader;
(void)data;
Curl_bufq_free(&ctx->buf);
}
/* this is the 5-bytes End-Of-Body marker for SMTP */
#define SMTP_EOB "\r\n.\r\n"
#define SMTP_EOB_FIND_LEN 3
/* client reader doing SMTP End-Of-Body escaping. */
static CURLcode cr_eob_read(struct Curl_easy *data,
struct Curl_creader *reader,
char *buf, size_t blen,
size_t *pnread, bool *peos)
{
struct cr_eob_ctx *ctx = (struct cr_eob_ctx *)reader;
CURLcode result = CURLE_OK;
size_t nread, i, start, n;
bool eos;
if(!ctx->read_eos && Curl_bufq_is_empty(&ctx->buf)) {
/* Get more and convert it when needed */
result = Curl_creader_read(data, reader->next, buf, blen, &nread, &eos);
if(result)
return result;
ctx->read_eos = eos;
if(nread) {
if(!ctx->n_eob && !memchr(buf, SMTP_EOB[0], nread)) {
/* not in the middle of a match, no EOB start found, just pass */
*pnread = nread;
*peos = FALSE;
return CURLE_OK;
}
/* scan for EOB (continuation) and convert */
for(i = start = 0; i < nread; ++i) {
if(ctx->n_eob >= SMTP_EOB_FIND_LEN) {
/* matched the EOB prefix and seeing additional char, add '.' */
result = Curl_bufq_cwrite(&ctx->buf, buf + start, i - start, &n);
if(result)
return result;
result = Curl_bufq_cwrite(&ctx->buf, ".", 1, &n);
if(result)
return result;
ctx->n_eob = 0;
start = i;
if(data->state.infilesize > 0)
data->state.infilesize++;
}
if(buf[i] != SMTP_EOB[ctx->n_eob])
ctx->n_eob = 0;
if(buf[i] == SMTP_EOB[ctx->n_eob]) {
/* matching another char of the EOB */
++ctx->n_eob;
}
}
/* add any remainder to buf */
if(start < nread) {
result = Curl_bufq_cwrite(&ctx->buf, buf + start, nread - start, &n);
if(result)
return result;
}
}
if(ctx->read_eos) {
/* if we last matched a CRLF or if the data was empty, add ".\r\n"
* to end the body. If we sent something and it did not end with "\r\n",
* add "\r\n.\r\n" to end the body */
const char *eob = SMTP_EOB;
switch(ctx->n_eob) {
case 2:
/* seen a CRLF at the end, just add the remainder */
eob = &SMTP_EOB[2];
break;
case 3:
/* ended with '\r\n.', we should escpe the last '.' */
eob = "." SMTP_EOB;
break;
default:
break;
}
result = Curl_bufq_cwrite(&ctx->buf, eob, strlen(eob), &n);
if(result)
return result;
}
}
*peos = FALSE;
if(!Curl_bufq_is_empty(&ctx->buf)) {
result = Curl_bufq_cread(&ctx->buf, buf, blen, pnread);
}
else
*pnread = 0;
if(ctx->read_eos && Curl_bufq_is_empty(&ctx->buf)) {
/* no more data, read all, done. */
ctx->eos = TRUE;
}
*peos = ctx->eos;
DEBUGF(infof(data, "cr_eob_read(%zu) -> %d, %zd, %d",
blen, result, *pnread, *peos));
return CURLE_OK;
}
static const struct Curl_crtype cr_eob = {
"cr-smtp-eob",
cr_eob_init,
cr_eob_read,
cr_eob_close,
Curl_creader_def_needs_rewind,
sizeof(struct cr_eob_ctx)
};
static CURLcode cr_eob_add(struct Curl_easy *data)
{
struct Curl_creader *reader = NULL;
CURLcode result;
result = Curl_creader_create(&reader, data, &cr_eob,
CURL_CR_CONTENT_ENCODE);
if(!result)
result = Curl_creader_add(data, reader);
if(result && reader)
Curl_creader_free(data, reader);
return result;
}
#endif /* CURL_DISABLE_SMTP */

View File

@ -84,17 +84,4 @@ struct smtp_conn {
extern const struct Curl_handler Curl_handler_smtp;
extern const struct Curl_handler Curl_handler_smtps;
/* this is the 5-bytes End-Of-Body marker for SMTP */
#define SMTP_EOB "\x0d\x0a\x2e\x0d\x0a"
#define SMTP_EOB_LEN 5
#define SMTP_EOB_FIND_LEN 3
/* if found in data, replace it with this string instead */
#define SMTP_EOB_REPL "\x0d\x0a\x2e\x2e"
#define SMTP_EOB_REPL_LEN 4
CURLcode Curl_smtp_escape_eob(struct Curl_easy *data,
const ssize_t nread,
const ssize_t offset);
#endif /* HEADER_CURL_SMTP_H */

View File

@ -452,8 +452,6 @@ static CURLcode tftp_send_first(struct tftp_state_data *state,
if(data->state.upload) {
/* If we are uploading, send an WRQ */
setpacketevent(&state->spacket, TFTP_EVENT_WRQ);
state->data->req.upload_fromhere =
(char *)state->spacket.data + 4;
if(data->state.infilesize != -1)
Curl_pgrsSetUploadSize(data, data->state.infilesize);
}

View File

@ -115,16 +115,6 @@ char *Curl_checkheaders(const struct Curl_easy *data,
}
#endif
CURLcode Curl_get_upload_buffer(struct Curl_easy *data)
{
if(!data->state.ulbuf) {
data->state.ulbuf = malloc(data->set.upload_buffer_size);
if(!data->state.ulbuf)
return CURLE_OUT_OF_MEMORY;
}
return CURLE_OK;
}
static int data_pending(struct Curl_easy *data)
{
struct connectdata *conn = data->conn;
@ -330,17 +320,6 @@ out:
return result;
}
CURLcode Curl_done_sending(struct Curl_easy *data,
struct SingleRequest *k)
{
k->keepon &= ~KEEP_SEND; /* we're done writing */
/* These functions should be moved into the handler struct! */
Curl_conn_ev_data_done_send(data);
return CURLE_OK;
}
#if defined(_WIN32) && defined(USE_WINSOCK)
#ifndef SIO_IDEAL_SEND_BACKLOG_QUERY
#define SIO_IDEAL_SEND_BACKLOG_QUERY 0x4004747B
@ -368,174 +347,37 @@ static void win_update_buffer_size(curl_socket_t sockfd)
/*
* Send data to upload to the server, when the socket is writable.
*/
static CURLcode readwrite_upload(struct Curl_easy *data,
struct connectdata *conn,
int *didwhat)
static CURLcode readwrite_upload(struct Curl_easy *data, int *didwhat)
{
size_t bytes_written;
CURLcode result;
ssize_t nread; /* number of bytes read */
struct SingleRequest *k = &data->req;
CURLcode result = CURLE_OK;
(void)conn;
*didwhat |= KEEP_SEND;
if((data->req.keepon & KEEP_SEND_PAUSE))
return CURLE_OK;
if(!(k->keepon & KEEP_SEND_PAUSE)) {
result = Curl_req_flush(data);
if(result == CURLE_AGAIN) /* unable to send all we have */
return CURLE_OK;
else if(result)
return result;
}
/* We should not get here when the sending is already done. It
* probably means that someone set `data-req.keepon |= KEEP_SEND`
* when it should not. */
DEBUGASSERT(!Curl_req_done_sending(data));
do {
curl_off_t nbody;
ssize_t offset = 0;
bool eos;
if(0 != k->upload_present &&
k->upload_present < curl_upload_refill_watermark(data) &&
!k->upload_chunky &&/*(variable sized chunked header; append not safe)*/
!k->upload_done && /*!(k->upload_done once k->upload_present sent)*/
!(k->writebytecount + (curl_off_t)k->upload_present ==
data->state.infilesize)) {
offset = k->upload_present;
}
/* only read more data if there's no upload data already
present in the upload buffer, or if appending to upload buffer */
if(0 == k->upload_present || offset) {
result = Curl_get_upload_buffer(data);
if(result)
return result;
if(offset && k->upload_fromhere != data->state.ulbuf)
memmove(data->state.ulbuf, k->upload_fromhere, offset);
/* init the "upload from here" pointer */
k->upload_fromhere = data->state.ulbuf;
if(!k->upload_done) {
/* HTTP pollution, this should be written nicer to become more
protocol agnostic. */
size_t fillcount;
if(k->exp100 == EXP100_SENDING_REQUEST) {
/* If this call is to send body data, we must take some action:
We have sent off the full HTTP 1.1 request, and we shall now
go into the Expect: 100 state and await such a header */
k->exp100 = EXP100_AWAITING_CONTINUE; /* wait for the header */
k->keepon &= ~KEEP_SEND; /* disable writing */
k->start100 = Curl_now(); /* timeout count starts now */
*didwhat &= ~KEEP_SEND; /* we didn't write anything actually */
/* set a timeout for the multi interface */
Curl_expire(data, data->set.expect_100_timeout, EXPIRE_100_TIMEOUT);
break;
}
k->upload_fromhere += offset;
result = Curl_client_read(data, k->upload_fromhere,
data->set.upload_buffer_size-offset,
&fillcount, &eos);
k->upload_fromhere -= offset;
if(result)
return result;
nread = offset + fillcount;
}
else
nread = 0; /* we're done uploading/reading */
if(!nread && (k->keepon & KEEP_SEND_PAUSE)) {
/* this is a paused transfer */
break;
}
if(nread <= 0) {
result = Curl_done_sending(data, k);
if(result)
return result;
break;
}
/* store number of bytes available for upload */
k->upload_present = nread;
#ifndef CURL_DISABLE_SMTP
if(conn->handler->protocol & PROTO_FAMILY_SMTP) {
result = Curl_smtp_escape_eob(data, nread, offset);
if(result)
return result;
}
#endif /* CURL_DISABLE_SMTP */
} /* if 0 == k->upload_present or appended to upload buffer */
else {
/* We have a partial buffer left from a previous "round". Use
that instead of reading more data */
}
/* write to socket (send away data) */
result = Curl_xfer_send(data,
k->upload_fromhere, /* buffer pointer */
k->upload_present, /* buffer size */
&bytes_written); /* actually sent */
if(!Curl_req_done_sending(data)) {
*didwhat |= KEEP_SEND;
result = Curl_req_send_more(data);
if(result)
return result;
#if defined(_WIN32) && defined(USE_WINSOCK)
/* FIXME: this looks like it would fit better into cf-socket.c
* but then I do not know enough Windows to say... */
{
struct curltime n = Curl_now();
if(Curl_timediff(n, conn->last_sndbuf_update) > 1000) {
win_update_buffer_size(conn->writesockfd);
conn->last_sndbuf_update = n;
if(Curl_timediff(n, data->conn->last_sndbuf_update) > 1000) {
win_update_buffer_size(data->conn->writesockfd);
data->conn->last_sndbuf_update = n;
}
}
#endif
nbody = bytes_written;
if(nbody) {
/* show the data before we change the pointer upload_fromhere */
Curl_debug(data, CURLINFO_DATA_OUT,
&k->upload_fromhere[bytes_written - nbody],
(size_t)nbody);
k->writebytecount += nbody;
Curl_pgrsSetUploadCounter(data, k->writebytecount);
}
if((!k->upload_chunky || k->forbidchunk) &&
(k->writebytecount == data->state.infilesize)) {
/* we have sent all data we were supposed to */
k->upload_done = TRUE;
infof(data, "We are completely uploaded and fine");
}
if(k->upload_present != bytes_written) {
/* we only wrote a part of the buffer (if anything), deal with it! */
/* store the amount of bytes left in the buffer to write */
k->upload_present -= bytes_written;
/* advance the pointer where to find the buffer when the next send
is to happen */
k->upload_fromhere += bytes_written;
}
else {
/* we've uploaded that buffer now */
result = Curl_get_upload_buffer(data);
if(result)
return result;
k->upload_fromhere = data->state.ulbuf;
k->upload_present = 0; /* no more bytes left */
if(k->upload_done) {
result = Curl_done_sending(data, k);
if(result)
return result;
}
}
} while(0); /* just to break out from! */
return CURLE_OK;
}
return result;
}
static int select_bits_paused(struct Curl_easy *data, int select_bits)
@ -626,7 +468,7 @@ CURLcode Curl_readwrite(struct Curl_easy *data,
if((k->keepon & KEEP_SEND) && (select_bits & CURL_CSELECT_OUT)) {
/* write */
result = readwrite_upload(data, conn, &didwhat);
result = readwrite_upload(data, &didwhat);
if(result)
goto out;
}
@ -1458,3 +1300,9 @@ CURLcode Curl_xfer_recv(struct Curl_easy *data,
blen = (size_t)data->set.buffer_size;
return Curl_conn_recv(data, sockindex, buf, blen, pnrcvd);
}
CURLcode Curl_xfer_send_close(struct Curl_easy *data)
{
Curl_conn_ev_data_done_send(data);
return CURLE_OK;
}

View File

@ -50,10 +50,6 @@ int Curl_single_getsock(struct Curl_easy *data,
struct connectdata *conn, curl_socket_t *socks);
CURLcode Curl_retry_request(struct Curl_easy *data, char **url);
bool Curl_meets_timecondition(struct Curl_easy *data, time_t timeofdoc);
CURLcode Curl_get_upload_buffer(struct Curl_easy *data);
CURLcode Curl_done_sending(struct Curl_easy *data,
struct SingleRequest *k);
/**
* Write the transfer raw response bytes, as received from the connection.
@ -106,4 +102,6 @@ CURLcode Curl_xfer_recv(struct Curl_easy *data,
char *buf, size_t blen,
ssize_t *pnrcvd);
CURLcode Curl_xfer_send_close(struct Curl_easy *data);
#endif /* HEADER_CURL_TRANSFER_H */

View File

@ -277,7 +277,6 @@ CURLcode Curl_close(struct Curl_easy **datap)
up_free(data);
Curl_dyn_free(&data->state.headerb);
Curl_safefree(data->state.ulbuf);
Curl_flush_cookies(data, TRUE);
Curl_altsvc_save(data, data->asi, data->set.str[STRING_ALTSVC]);
Curl_altsvc_cleanup(&data->asi);

View File

@ -1215,7 +1215,6 @@ struct UrlState {
struct dynbuf headerb; /* buffer to store headers in */
struct curl_slist *hstslist; /* list of HSTS files set by
curl_easy_setopt(HSTS) calls */
char *ulbuf; /* allocated upload buffer or NULL */
curl_off_t current_speed; /* the ProgressShow() function sets this,
bytes / second */