Skip to content

Commit

Permalink
lib: Curl_read/Curl_write clarifications
Browse files Browse the repository at this point in the history
- replace `Curl_read()`, `Curl_write()` and `Curl_nwrite()` to
  clarify when and at what level they operate
- send/recv of transfer related data is now done via
  `Curl_xfer_send()/Curl_xfer_recv()` which no longer has
  socket/socketindex as parameter. It decides on the transfer
  setup of `conn->sockfd` and `conn->writesockfd` on which
  connection filter chain to operate.
- send/recv on a specific connection filter chain is done via
  `Curl_conn_send()/Curl_conn_recv()` which get the socket index
  as parameter.
- rename `Curl_setup_transfer()` to `Curl_xfer_setup()` for
  naming consistency
- clarify that the special CURLE_AGAIN hangling to return
  `CURLE_OK` with length 0 only applies to `Curl_xfer_send()`
  and CURLE_AGAIN is returned by all other send() variants.
- fix a bug in websocket `curl_ws_recv()` that mixed up data
  when it arrived in more than a single chunk

The method for sending not just raw bytes, but bytes that are either
"headers" or "body". The send abstraction stack, to to bottom, now is:

* `Curl_req_send()`: has parameter to indicate amount of header bytes,
  buffers all data.
* `Curl_xfer_send()`: knows on which socket index to send, returns
  amount of bytes sent.
* `Curl_conn_send()`: called with socket index, returns amount of bytes
  sent.

In addition there is `Curl_req_flush()` for writing out all buffered
bytes.

`Curl_req_send()` is active for requests without body,
`Curl_buffer_send()` still being used for others. This is because the
special quirks need to be addressed in future parts:

* `expect-100` handling
* `Curl_fillreadbuffer()` needs to add directly to the new
  `data->req.sendbuf`
* special body handlings, like `chunked` encodings and line end
  conversions will be moved into something like a Client Reader.

In functions of the pattern `CURLcode xxx_send(..., ssize_t *written)`,
replace the `ssize_t` with a `size_t`. It makes no sense to allow for negative
values as the returned `CURLcode` already specifies error conditions. This
allows easier handling of lengths without casting.

Closes curl#12964
  • Loading branch information
icing authored and bagder committed Feb 27, 2024
1 parent 757dfdf commit 3755153
Show file tree
Hide file tree
Showing 25 changed files with 314 additions and 142 deletions.
9 changes: 5 additions & 4 deletions lib/c-hyper.c
Original file line number Diff line number Diff line change
Expand Up @@ -112,11 +112,13 @@ size_t Curl_hyper_send(void *userp, hyper_context *ctx,
struct hyp_io_ctx *io_ctx = userp;
struct Curl_easy *data = io_ctx->data;
CURLcode result;
ssize_t nwrote;
size_t nwrote;

DEBUGF(infof(data, "Curl_hyper_send(%zu)", buflen));
result = Curl_conn_send(data, io_ctx->sockindex,
(void *)buf, buflen, &nwrote);
if(!result && !nwrote)
result = CURLE_AGAIN;
if(result == CURLE_AGAIN) {
DEBUGF(infof(data, "Curl_hyper_send(%zu) -> EAGAIN", buflen));
/* would block, register interest */
Expand Down Expand Up @@ -759,7 +761,6 @@ static int uploadstreamed(void *userdata, hyper_context *ctx,
*/

static CURLcode bodysend(struct Curl_easy *data,
struct connectdata *conn,
hyper_headers *headers,
hyper_request *hyperreq,
Curl_HttpReq httpreq)
Expand All @@ -772,7 +773,7 @@ static CURLcode bodysend(struct Curl_easy *data,
else {
hyper_body *body;
Curl_dyn_init(&req, DYN_HTTP_REQUEST);
result = Curl_http_bodysend(data, conn, &req, httpreq);
result = Curl_http_req_send(data, &req, httpreq);

if(!result)
result = Curl_hyper_header(data, headers, Curl_dyn_ptr(&req));
Expand Down Expand Up @@ -1171,7 +1172,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
if(result)
goto error;

result = bodysend(data, conn, headers, req, httpreq);
result = bodysend(data, headers, req, httpreq);
if(result)
goto error;

Expand Down
7 changes: 6 additions & 1 deletion lib/cf-h1-proxy.c
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,11 @@ static void tunnel_free(struct Curl_cfilter *cf,
}
}

static bool tunnel_want_send(struct h1_tunnel_state *ts)
{
return (ts->tunnel_state == H1_TUNNEL_CONNECT);
}

#ifndef USE_HYPER
static CURLcode start_CONNECT(struct Curl_cfilter *cf,
struct Curl_easy *data,
Expand Down Expand Up @@ -1032,7 +1037,7 @@ static void cf_h1_proxy_adjust_pollset(struct Curl_cfilter *cf,
wait for the socket to become readable to be able to get the
response headers or if we're still sending the request, wait
for write. */
if(ts->CONNECT.sending == HTTPSEND_REQUEST)
if(tunnel_want_send(ts))
Curl_pollset_set_out_only(data, ps, sock);
else
Curl_pollset_set_in_only(data, ps, sock);
Expand Down
5 changes: 2 additions & 3 deletions lib/cf-haproxy.c
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ static CURLcode cf_haproxy_connect(struct Curl_cfilter *cf,
case HAPROXY_SEND:
len = Curl_dyn_len(&ctx->data_out);
if(len > 0) {
ssize_t written;
size_t written;
result = Curl_conn_send(data, cf->sockindex,
Curl_dyn_ptr(&ctx->data_out),
len, &written);
Expand All @@ -139,8 +139,7 @@ static CURLcode cf_haproxy_connect(struct Curl_cfilter *cf,
}
else if(result)
goto out;
DEBUGASSERT(written >= 0);
Curl_dyn_tail(&ctx->data_out, len - (size_t)written);
Curl_dyn_tail(&ctx->data_out, len - written);
if(Curl_dyn_len(&ctx->data_out) > 0) {
result = CURLE_OK;
goto out;
Expand Down
4 changes: 2 additions & 2 deletions lib/cfilters.c
Original file line number Diff line number Diff line change
Expand Up @@ -694,7 +694,7 @@ CURLcode Curl_conn_recv(struct Curl_easy *data, int sockindex,

CURLcode Curl_conn_send(struct Curl_easy *data, int sockindex,
const void *buf, size_t blen,
ssize_t *pnwritten)
size_t *pnwritten)
{
ssize_t nwritten;
CURLcode result = CURLE_OK;
Expand All @@ -719,7 +719,7 @@ CURLcode Curl_conn_send(struct Curl_easy *data, int sockindex,
#endif
nwritten = conn->send[sockindex](data, sockindex, buf, blen, &result);
DEBUGASSERT((nwritten >= 0) || result);
*pnwritten = nwritten;
*pnwritten = (nwritten < 0)? 0 : (size_t)nwritten;
return result;
}

Expand Down
2 changes: 1 addition & 1 deletion lib/cfilters.h
Original file line number Diff line number Diff line change
Expand Up @@ -518,7 +518,7 @@ CURLcode Curl_conn_recv(struct Curl_easy *data, int sockindex,
*/
CURLcode Curl_conn_send(struct Curl_easy *data, int sockindex,
const void *buf, size_t blen,
ssize_t *pnwritten);
size_t *pnwritten);


void Curl_pollset_reset(struct Curl_easy *data,
Expand Down
2 changes: 1 addition & 1 deletion lib/dict.c
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ static CURLcode sendf(struct Curl_easy *data,

static CURLcode sendf(struct Curl_easy *data, const char *fmt, ...)
{
ssize_t bytes_written;
size_t bytes_written;
size_t write_len;
CURLcode result = CURLE_OK;
char *s;
Expand Down
6 changes: 3 additions & 3 deletions lib/easy.c
Original file line number Diff line number Diff line change
Expand Up @@ -1251,7 +1251,7 @@ CURLcode Curl_connect_only_attach(struct Curl_easy *data)
* This is the private internal version of curl_easy_send()
*/
CURLcode Curl_senddata(struct Curl_easy *data, const void *buffer,
size_t buflen, ssize_t *n)
size_t buflen, size_t *n)
{
CURLcode result;
struct connectdata *c = NULL;
Expand Down Expand Up @@ -1283,13 +1283,13 @@ CURLcode Curl_senddata(struct Curl_easy *data, const void *buffer,
CURLcode curl_easy_send(struct Curl_easy *data, const void *buffer,
size_t buflen, size_t *n)
{
ssize_t written = 0;
size_t written = 0;
CURLcode result;
if(Curl_is_in_callback(data))
return CURLE_RECURSIVE_API_CALL;

result = Curl_senddata(data, buffer, buflen, &written);
*n = (size_t)written;
*n = written;
return result;
}

Expand Down
2 changes: 1 addition & 1 deletion lib/easyif.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
* Prototypes for library-wide functions provided by easy.c
*/
CURLcode Curl_senddata(struct Curl_easy *data, const void *buffer,
size_t buflen, ssize_t *n);
size_t buflen, size_t *n);

#ifdef USE_WEBSOCKETS
CURLcode Curl_connect_only_attach(struct Curl_easy *data);
Expand Down
4 changes: 2 additions & 2 deletions lib/gopher.c
Original file line number Diff line number Diff line change
Expand Up @@ -139,8 +139,8 @@ static CURLcode gopher_do(struct Curl_easy *data, bool *done)
char *sel = NULL;
char *sel_org = NULL;
timediff_t timeout_ms;
ssize_t amount, k;
size_t len;
ssize_t k;
size_t amount, len;
int what;

*done = TRUE; /* unconditionally */
Expand Down
90 changes: 40 additions & 50 deletions lib/http.c
Original file line number Diff line number Diff line change
Expand Up @@ -1174,6 +1174,7 @@ static bool http_should_fail(struct Curl_easy *data)
return data->state.authproblem;
}

#ifndef USE_HYPER
/*
* readmoredata() is a "fread() emulation" to provide POST and/or request
* data. It is used when a huge POST is to be made and the entire chunk wasn't
Expand Down Expand Up @@ -1238,26 +1239,23 @@ static size_t readmoredata(char *buffer,
*
* Returns CURLcode
*/
CURLcode Curl_buffer_send(struct dynbuf *in,
struct Curl_easy *data,
struct HTTP *http,
/* add the number of sent bytes to this
counter */
curl_off_t *bytes_written,
/* how much of the buffer contains body data */
curl_off_t included_body_bytes,
int sockindex)
static CURLcode buffer_send(struct dynbuf *in,
struct Curl_easy *data,
struct HTTP *http,
/* add the number of sent bytes to this
counter */
curl_off_t *bytes_written,
/* how much of the buffer contains body data */
curl_off_t included_body_bytes)
{
ssize_t amount;
size_t amount;
CURLcode result;
char *ptr;
size_t size;
struct connectdata *conn = data->conn;
size_t sendsize;
size_t headersize;

DEBUGASSERT(sockindex <= SECONDARYSOCKET && sockindex >= 0);

/* The looping below is required since we use non-blocking sockets, but due
to the circumstances we will just loop and try again and again etc */

Expand Down Expand Up @@ -1356,11 +1354,7 @@ CURLcode Curl_buffer_send(struct dynbuf *in,
sendsize = (size_t)data->set.upload_buffer_size;
}

result = Curl_conn_send(data, sockindex, ptr, sendsize, &amount);
if(result == CURLE_AGAIN) {
result = CURLE_OK;
amount = 0;
}
result = Curl_xfer_send(data, ptr, sendsize, &amount);

if(!result) {
/*
Expand Down Expand Up @@ -1443,6 +1437,11 @@ CURLcode Curl_buffer_send(struct dynbuf *in,

/* end of the add_buffer functions */
/* ------------------------------------------------------------------------- */
#else /* !USE_HYPER */
/* In hyper, this is an ugly NOP */
#define buffer_send(a,b,c,d,e) CURLE_OK

#endif /* !USE_HYPER(else) */



Expand Down Expand Up @@ -1619,13 +1618,12 @@ static const char *get_http_string(const struct Curl_easy *data,
#endif

/* check and possibly add an Expect: header */
static CURLcode expect100(struct Curl_easy *data,
struct connectdata *conn,
struct dynbuf *req)
static CURLcode expect100(struct Curl_easy *data, struct dynbuf *req)
{
CURLcode result = CURLE_OK;
if(!data->state.disableexpect && Curl_use_http_1_1plus(data, conn) &&
(conn->httpversion < 20)) {
if(!data->state.disableexpect &&
Curl_use_http_1_1plus(data, data->conn) &&
(data->conn->httpversion < 20)) {
/* if not doing HTTP 1.0 or version 2, or disabled explicitly, we add an
Expect: 100-continue to the headers which actually speeds up post
operations (as there is one packet coming back from the web server) */
Expand Down Expand Up @@ -2441,8 +2439,7 @@ CURLcode Curl_http_body(struct Curl_easy *data, struct connectdata *conn,
return result;
}

static CURLcode addexpect(struct Curl_easy *data, struct connectdata *conn,
struct dynbuf *r)
static CURLcode addexpect(struct Curl_easy *data, struct dynbuf *r)
{
data->state.expect100header = FALSE;
/* Avoid Expect: 100-continue if Upgrade: is used */
Expand All @@ -2459,24 +2456,22 @@ static CURLcode addexpect(struct Curl_easy *data, struct connectdata *conn,
STRCONST("100-continue"));
}
else if(http->postsize > EXPECT_100_THRESHOLD || http->postsize < 0)
return expect100(data, conn, r);
return expect100(data, r);
}
return CURLE_OK;
}

CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn,
CURLcode Curl_http_req_send(struct Curl_easy *data,
struct dynbuf *r, Curl_HttpReq httpreq)
{
#ifndef USE_HYPER
/* Hyper always handles the body separately */
curl_off_t included_body = 0;
#else
/* from this point down, this function should not be used */
#define Curl_buffer_send(a,b,c,d,e,f) CURLE_OK
#endif
CURLcode result = CURLE_OK;
struct HTTP *http = data->req.p.http;

DEBUGASSERT(data->conn);
switch(httpreq) {
case HTTPREQ_PUT: /* Let's PUT the data to the server! */

Expand All @@ -2495,7 +2490,7 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn,
return result;
}

result = addexpect(data, conn, r);
result = addexpect(data, r);
if(result)
return result;

Expand All @@ -2508,9 +2503,8 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn,
Curl_pgrsSetUploadSize(data, http->postsize);

/* this sends the buffer and frees all the buffer resources */
result = Curl_buffer_send(r, data, data->req.p.http,
&data->info.request_size, 0,
FIRSTSOCKET);
result = buffer_send(r, data, data->req.p.http,
&data->info.request_size, 0);
if(result)
failf(data, "Failed sending PUT request");
else
Expand All @@ -2531,9 +2525,8 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn,
if(result)
return result;

result = Curl_buffer_send(r, data, data->req.p.http,
&data->info.request_size, 0,
FIRSTSOCKET);
result = buffer_send(r, data, data->req.p.http,
&data->info.request_size, 0);
if(result)
failf(data, "Failed sending POST request");
else
Expand Down Expand Up @@ -2571,7 +2564,7 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn,
}
#endif

result = addexpect(data, conn, r);
result = addexpect(data, r);
if(result)
return result;

Expand All @@ -2589,9 +2582,8 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn,
http->sending = HTTPSEND_BODY;

/* this sends the buffer and frees all the buffer resources */
result = Curl_buffer_send(r, data, data->req.p.http,
&data->info.request_size, 0,
FIRSTSOCKET);
result = buffer_send(r, data, data->req.p.http,
&data->info.request_size, 0);
if(result)
failf(data, "Failed sending POST request");
else
Expand Down Expand Up @@ -2633,7 +2625,7 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn,
return result;
}

result = addexpect(data, conn, r);
result = addexpect(data, r);
if(result)
return result;

Expand Down Expand Up @@ -2732,9 +2724,8 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn,
}
}
/* issue the request */
result = Curl_buffer_send(r, data, data->req.p.http,
&data->info.request_size, included_body,
FIRSTSOCKET);
result = buffer_send(r, data, data->req.p.http,
&data->info.request_size, included_body);

if(result)
failf(data, "Failed sending HTTP POST request");
Expand All @@ -2749,13 +2740,12 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn,
return result;

/* issue the request */
result = Curl_buffer_send(r, data, data->req.p.http,
&data->info.request_size, 0,
FIRSTSOCKET);
result = Curl_req_send_hds(data, Curl_dyn_ptr(r), Curl_dyn_len(r));
Curl_dyn_free(r);
if(result)
failf(data, "Failed sending HTTP request");
#ifdef USE_WEBSOCKETS
else if((conn->handler->protocol & (CURLPROTO_WS|CURLPROTO_WSS)) &&
else if((data->conn->handler->protocol & (CURLPROTO_WS|CURLPROTO_WSS)) &&
!(data->set.connect_only))
/* Set up the transfer for two-way since without CONNECT_ONLY set, this
request probably wants to send data too post upgrade */
Expand Down Expand Up @@ -3329,8 +3319,8 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
(httpreq == HTTPREQ_HEAD))
Curl_pgrsSetUploadSize(data, 0); /* nothing */

/* bodysend takes ownership of the 'req' memory on success */
result = Curl_http_bodysend(data, conn, &req, httpreq);
/* req_send takes ownership of the 'req' memory on success */
result = Curl_http_req_send(data, &req, httpreq);
}
if(result) {
Curl_dyn_free(&req);
Expand Down
Loading

0 comments on commit 3755153

Please sign in to comment.