mirror of
https://github.com/curl/curl.git
synced 2026-08-04 23:46:12 +03:00
lib: Curl_read/Curl_write clarifications
- 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 (to be made
into a sperate PR, also)
Added as documented [in
CLIENT-READER.md](5b1f31dfba/docs/CLIENT-READERS.md).
- old `Curl_buffer_send()` completely replaced by new `Curl_req_send()`
- old `Curl_fillreadbuffer()` replaced with `Curl_client_read()`
- HTTP chunked uploads are now formatted in a client reader added when
needed.
- FTP line-end conversions are done in a client reader added when
needed.
- when sending requests headers, remaining buffer space is filled with
body data for sending in "one go". This is independent of the request
body size. Resolves #12938 as now small and large requests have the
same code path.
Changes done to test cases:
- test513: now fails before sending request headers as this initial
"client read" triggers the setup fault. Behaves now the same as in
hyper build
- test547, test555, test1620: fix the length check in the lib code to
only fail for reads *smaller* than expected. This was a bug in the
test code that never triggered in the old implementation.
Closes #12969
This commit is contained in:
parent
8d67c61c47
commit
9369c30cd8
26 changed files with 1191 additions and 1021 deletions
21
lib/bufq.c
21
lib/bufq.c
|
|
@ -417,6 +417,17 @@ ssize_t Curl_bufq_write(struct bufq *q,
|
|||
return nwritten;
|
||||
}
|
||||
|
||||
CURLcode Curl_bufq_cwrite(struct bufq *q,
|
||||
const char *buf, size_t len,
|
||||
size_t *pnwritten)
|
||||
{
|
||||
ssize_t n;
|
||||
CURLcode result;
|
||||
n = Curl_bufq_write(q, (const unsigned char *)buf, len, &result);
|
||||
*pnwritten = (n < 0)? 0 : (size_t)n;
|
||||
return result;
|
||||
}
|
||||
|
||||
ssize_t Curl_bufq_read(struct bufq *q, unsigned char *buf, size_t len,
|
||||
CURLcode *err)
|
||||
{
|
||||
|
|
@ -440,6 +451,16 @@ ssize_t Curl_bufq_read(struct bufq *q, unsigned char *buf, size_t len,
|
|||
return nread;
|
||||
}
|
||||
|
||||
CURLcode Curl_bufq_cread(struct bufq *q, char *buf, size_t len,
|
||||
size_t *pnread)
|
||||
{
|
||||
ssize_t n;
|
||||
CURLcode result;
|
||||
n = Curl_bufq_read(q, (unsigned char *)buf, len, &result);
|
||||
*pnread = (n < 0)? 0 : (size_t)n;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Curl_bufq_peek(struct bufq *q,
|
||||
const unsigned char **pbuf, size_t *plen)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -178,6 +178,10 @@ ssize_t Curl_bufq_write(struct bufq *q,
|
|||
const unsigned char *buf, size_t len,
|
||||
CURLcode *err);
|
||||
|
||||
CURLcode Curl_bufq_cwrite(struct bufq *q,
|
||||
const char *buf, size_t len,
|
||||
size_t *pnwritten);
|
||||
|
||||
/**
|
||||
* Read buf from the start of the buffer queue. The buf is copied
|
||||
* and the amount of copied bytes is returned.
|
||||
|
|
@ -187,6 +191,9 @@ ssize_t Curl_bufq_write(struct bufq *q,
|
|||
ssize_t Curl_bufq_read(struct bufq *q, unsigned char *buf, size_t len,
|
||||
CURLcode *err);
|
||||
|
||||
CURLcode Curl_bufq_cread(struct bufq *q, char *buf, size_t len,
|
||||
size_t *pnread);
|
||||
|
||||
/**
|
||||
* Peek at the head chunk in the buffer queue. Returns a pointer to
|
||||
* the chunk buf (at the current offset) and its length. Does not
|
||||
|
|
|
|||
102
lib/c-hyper.c
102
lib/c-hyper.c
|
|
@ -117,8 +117,6 @@ size_t Curl_hyper_send(void *userp, hyper_context *ctx,
|
|||
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 */
|
||||
|
|
@ -659,48 +657,13 @@ static CURLcode request_target(struct Curl_easy *data,
|
|||
return result;
|
||||
}
|
||||
|
||||
static int uploadpostfields(void *userdata, hyper_context *ctx,
|
||||
hyper_buf **chunk)
|
||||
{
|
||||
struct Curl_easy *data = (struct Curl_easy *)userdata;
|
||||
(void)ctx;
|
||||
if(data->req.exp100 > EXP100_SEND_DATA) {
|
||||
if(data->req.exp100 == EXP100_FAILED)
|
||||
return HYPER_POLL_ERROR;
|
||||
|
||||
/* still waiting confirmation */
|
||||
if(data->hyp.exp100_waker)
|
||||
hyper_waker_free(data->hyp.exp100_waker);
|
||||
data->hyp.exp100_waker = hyper_context_waker(ctx);
|
||||
return HYPER_POLL_PENDING;
|
||||
}
|
||||
if(data->req.upload_done)
|
||||
*chunk = NULL; /* nothing more to deliver */
|
||||
else {
|
||||
/* send everything off in a single go */
|
||||
hyper_buf *copy = hyper_buf_copy(data->set.postfields,
|
||||
(size_t)data->req.p.http->postsize);
|
||||
if(copy)
|
||||
*chunk = copy;
|
||||
else {
|
||||
data->state.hresult = CURLE_OUT_OF_MEMORY;
|
||||
return HYPER_POLL_ERROR;
|
||||
}
|
||||
/* increasing the writebytecount here is a little premature but we
|
||||
don't know exactly when the body is sent */
|
||||
data->req.writebytecount += (size_t)data->req.p.http->postsize;
|
||||
Curl_pgrsSetUploadCounter(data, data->req.writebytecount);
|
||||
data->req.upload_done = TRUE;
|
||||
}
|
||||
return HYPER_POLL_READY;
|
||||
}
|
||||
|
||||
static int uploadstreamed(void *userdata, hyper_context *ctx,
|
||||
hyper_buf **chunk)
|
||||
{
|
||||
size_t fillcount;
|
||||
struct Curl_easy *data = (struct Curl_easy *)userdata;
|
||||
CURLcode result;
|
||||
bool eos;
|
||||
(void)ctx;
|
||||
|
||||
if(data->req.exp100 > EXP100_SEND_DATA) {
|
||||
|
|
@ -714,32 +677,15 @@ static int uploadstreamed(void *userdata, hyper_context *ctx,
|
|||
return HYPER_POLL_PENDING;
|
||||
}
|
||||
|
||||
if(data->req.upload_chunky && data->req.authneg) {
|
||||
fillcount = 0;
|
||||
data->req.upload_chunky = FALSE;
|
||||
result = CURLE_OK;
|
||||
}
|
||||
else {
|
||||
result = Curl_fillreadbuffer(data, data->set.upload_buffer_size,
|
||||
&fillcount);
|
||||
}
|
||||
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;
|
||||
}
|
||||
if(!fillcount) {
|
||||
if((data->req.keepon & KEEP_SEND_PAUSE) != KEEP_SEND_PAUSE)
|
||||
/* done! */
|
||||
*chunk = NULL;
|
||||
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;
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
if(fillcount) {
|
||||
hyper_buf *copy = hyper_buf_copy((uint8_t *)data->state.ulbuf, fillcount);
|
||||
if(copy)
|
||||
*chunk = copy;
|
||||
|
|
@ -751,8 +697,19 @@ static int uploadstreamed(void *userdata, hyper_context *ctx,
|
|||
don't know exactly when the body is sent */
|
||||
data->req.writebytecount += fillcount;
|
||||
Curl_pgrsSetUploadCounter(data, data->req.writebytecount);
|
||||
return HYPER_POLL_READY;
|
||||
}
|
||||
else if(eos) {
|
||||
*chunk = NULL;
|
||||
return 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;
|
||||
}
|
||||
return HYPER_POLL_READY;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -773,7 +730,7 @@ static CURLcode bodysend(struct Curl_easy *data,
|
|||
else {
|
||||
hyper_body *body;
|
||||
Curl_dyn_init(&req, DYN_HTTP_REQUEST);
|
||||
result = Curl_http_req_send(data, &req, httpreq);
|
||||
result = Curl_http_req_complete(data, &req, httpreq);
|
||||
|
||||
if(!result)
|
||||
result = Curl_hyper_header(data, headers, Curl_dyn_ptr(&req));
|
||||
|
|
@ -782,18 +739,15 @@ static CURLcode bodysend(struct Curl_easy *data,
|
|||
|
||||
body = hyper_body_new();
|
||||
hyper_body_set_userdata(body, data);
|
||||
if(data->set.postfields)
|
||||
hyper_body_set_data_func(body, uploadpostfields);
|
||||
else {
|
||||
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);
|
||||
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)) {
|
||||
/* fail */
|
||||
result = CURLE_OUT_OF_MEMORY;
|
||||
|
|
@ -888,7 +842,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
|
|||
may be parts of the request that is not yet sent, since we can deal with
|
||||
the rest of the request in the PERFORM phase. */
|
||||
*done = TRUE;
|
||||
Curl_cw_reset(data);
|
||||
Curl_client_reset(data);
|
||||
|
||||
/* Add collecting of headers written to client. For a new connection,
|
||||
* we might have done that already, but reuse
|
||||
|
|
|
|||
|
|
@ -1013,7 +1013,7 @@ out:
|
|||
data->req.header = TRUE; /* assume header */
|
||||
data->req.bytecount = 0;
|
||||
data->req.ignorebody = FALSE;
|
||||
Curl_cw_reset(data);
|
||||
Curl_client_reset(data);
|
||||
Curl_pgrsSetUploadCounter(data, 0);
|
||||
Curl_pgrsSetDownloadCounter(data, 0);
|
||||
|
||||
|
|
|
|||
12
lib/file.c
12
lib/file.c
|
|
@ -293,10 +293,10 @@ static CURLcode file_upload(struct Curl_easy *data)
|
|||
CURLcode result = CURLE_OK;
|
||||
char *xfer_buf;
|
||||
size_t xfer_blen;
|
||||
char *uphere_save;
|
||||
curl_off_t bytecount = 0;
|
||||
struct_stat file_stat;
|
||||
const char *sendbuf;
|
||||
bool eos = FALSE;
|
||||
|
||||
/*
|
||||
* Since FILE: doesn't do the full init, we need to provide some extra
|
||||
|
|
@ -340,21 +340,16 @@ static CURLcode file_upload(struct Curl_easy *data)
|
|||
data->state.resume_from = (curl_off_t)file_stat.st_size;
|
||||
}
|
||||
|
||||
/* Yikes! Curl_fillreadbuffer uses data->req.upload_fromhere to READ
|
||||
* client data to! Please, someone fix... */
|
||||
uphere_save = data->req.upload_fromhere;
|
||||
|
||||
result = Curl_multi_xfer_buf_borrow(data, &xfer_buf, &xfer_blen);
|
||||
if(result)
|
||||
goto out;
|
||||
|
||||
while(!result) {
|
||||
while(!result && !eos) {
|
||||
size_t nread;
|
||||
ssize_t nwrite;
|
||||
size_t readcount;
|
||||
|
||||
data->req.upload_fromhere = xfer_buf;
|
||||
result = Curl_fillreadbuffer(data, xfer_blen, &readcount);
|
||||
result = Curl_client_read(data, xfer_buf, xfer_blen, &readcount, &eos);
|
||||
if(result)
|
||||
break;
|
||||
|
||||
|
|
@ -401,7 +396,6 @@ static CURLcode file_upload(struct Curl_easy *data)
|
|||
out:
|
||||
close(fd);
|
||||
Curl_multi_xfer_buf_release(data, xfer_buf);
|
||||
data->req.upload_fromhere = uphere_save;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
|
|||
560
lib/http.c
560
lib/http.c
|
|
@ -543,7 +543,7 @@ static CURLcode http_perhapsrewind(struct Curl_easy *data,
|
|||
closure so we can safely do the rewind right now */
|
||||
}
|
||||
|
||||
if(bytessent) {
|
||||
if(Curl_client_read_needs_rewind(data)) {
|
||||
/* mark for rewind since if we already sent something */
|
||||
data->state.rewindbeforesend = TRUE;
|
||||
infof(data, "Please rewind output before next send");
|
||||
|
|
@ -1174,277 +1174,6 @@ 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
|
||||
* sent in the first send(). This function will then be called from the
|
||||
* transfer.c loop when more data is to be sent to the peer.
|
||||
*
|
||||
* Returns the amount of bytes it filled the buffer with.
|
||||
*/
|
||||
static size_t readmoredata(char *buffer,
|
||||
size_t size,
|
||||
size_t nitems,
|
||||
void *userp)
|
||||
{
|
||||
struct HTTP *http = (struct HTTP *)userp;
|
||||
struct Curl_easy *data = http->backup.data;
|
||||
size_t fullsize = size * nitems;
|
||||
|
||||
if(!http->postsize)
|
||||
/* nothing to return */
|
||||
return 0;
|
||||
|
||||
/* make sure that an HTTP request is never sent away chunked! */
|
||||
data->req.forbidchunk = (http->sending == HTTPSEND_REQUEST)?TRUE:FALSE;
|
||||
|
||||
if(data->set.max_send_speed &&
|
||||
(data->set.max_send_speed < (curl_off_t)fullsize) &&
|
||||
(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);
|
||||
fullsize = (size_t)http->postsize;
|
||||
|
||||
if(http->backup.postsize) {
|
||||
/* move backup data into focus and continue on that */
|
||||
http->postdata = http->backup.postdata;
|
||||
http->postsize = http->backup.postsize;
|
||||
data->state.fread_func = http->backup.fread_func;
|
||||
data->state.in = http->backup.fread_in;
|
||||
|
||||
http->sending++; /* move one step up */
|
||||
|
||||
http->backup.postsize = 0;
|
||||
}
|
||||
else
|
||||
http->postsize = 0;
|
||||
|
||||
return fullsize;
|
||||
}
|
||||
|
||||
memcpy(buffer, http->postdata, fullsize);
|
||||
http->postdata += fullsize;
|
||||
http->postsize -= fullsize;
|
||||
|
||||
return fullsize;
|
||||
}
|
||||
|
||||
/*
|
||||
* Curl_buffer_send() sends a header buffer and frees all associated
|
||||
* memory. Body data may be appended to the header data if desired.
|
||||
*
|
||||
* Returns CURLcode
|
||||
*/
|
||||
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)
|
||||
{
|
||||
size_t amount;
|
||||
CURLcode result;
|
||||
char *ptr;
|
||||
size_t size;
|
||||
struct connectdata *conn = data->conn;
|
||||
size_t sendsize;
|
||||
size_t headersize;
|
||||
|
||||
/* 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 */
|
||||
|
||||
ptr = Curl_dyn_ptr(in);
|
||||
size = Curl_dyn_len(in);
|
||||
|
||||
headersize = size - (size_t)included_body_bytes; /* the initial part that
|
||||
isn't body is header */
|
||||
|
||||
DEBUGASSERT(size > (size_t)included_body_bytes);
|
||||
|
||||
if((conn->handler->flags & PROTOPT_SSL
|
||||
#ifndef CURL_DISABLE_PROXY
|
||||
|| IS_HTTPS_PROXY(conn->http_proxy.proxytype)
|
||||
#endif
|
||||
)
|
||||
&& 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;
|
||||
|
||||
/* 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
|
||||
is not enough, we must use the exact same address. For this reason, we
|
||||
must copy the data to the uploadbuffer first, since that is the buffer
|
||||
we will be using if this send is retried later.
|
||||
*/
|
||||
result = Curl_get_upload_buffer(data);
|
||||
if(result) {
|
||||
/* malloc failed, free memory and return to the caller */
|
||||
Curl_dyn_free(in);
|
||||
return result;
|
||||
}
|
||||
/* We never send more than upload_buffer_size bytes in one single chunk
|
||||
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
|
||||
buffer is using this size.
|
||||
*/
|
||||
if(sendsize > (size_t)data->set.upload_buffer_size)
|
||||
sendsize = (size_t)data->set.upload_buffer_size;
|
||||
|
||||
memcpy(data->state.ulbuf, ptr, sendsize);
|
||||
ptr = data->state.ulbuf;
|
||||
}
|
||||
else {
|
||||
#ifdef CURLDEBUG
|
||||
/* Allow debug builds to override this logic to force short initial
|
||||
sends
|
||||
*/
|
||||
char *p = getenv("CURL_SMALLREQSEND");
|
||||
if(p) {
|
||||
size_t altsize = (size_t)strtoul(p, NULL, 10);
|
||||
if(altsize)
|
||||
sendsize = CURLMIN(size, altsize);
|
||||
else
|
||||
sendsize = size;
|
||||
}
|
||||
else
|
||||
#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;
|
||||
}
|
||||
|
||||
/* We currently cannot send more that this for http here:
|
||||
* - if sending blocks, it return 0 as amount
|
||||
* - we then whisk aside the `in` into the `http` struct
|
||||
* and install our own `data->state.fread_func` that
|
||||
* on subsequent calls reads `in` empty.
|
||||
* - when the whisked away `in` is empty, the `fread_func`
|
||||
* is restored to its original state.
|
||||
* The problem is that `fread_func` can only return
|
||||
* `upload_buffer_size` lengths. If the send we do here
|
||||
* is larger and blocks, we do re-sending with smaller
|
||||
* amounts of data and connection filters do not like
|
||||
* that.
|
||||
*/
|
||||
if(http && (sendsize > (size_t)data->set.upload_buffer_size))
|
||||
sendsize = (size_t)data->set.upload_buffer_size;
|
||||
}
|
||||
|
||||
result = Curl_xfer_send(data, ptr, sendsize, &amount);
|
||||
|
||||
if(!result) {
|
||||
/*
|
||||
* Note that we may not send the entire chunk at once, and we have a set
|
||||
* number of data bytes at the end of the big buffer (out of which we may
|
||||
* only send away a part).
|
||||
*/
|
||||
/* how much of the header that was sent */
|
||||
size_t headlen = (size_t)amount>headersize ? headersize : (size_t)amount;
|
||||
size_t bodylen = amount - headlen;
|
||||
|
||||
/* this data _may_ contain binary stuff */
|
||||
Curl_debug(data, CURLINFO_HEADER_OUT, ptr, headlen);
|
||||
if(bodylen)
|
||||
/* there was body data sent beyond the initial header part, pass that on
|
||||
to the debug callback too */
|
||||
Curl_debug(data, CURLINFO_DATA_OUT, ptr + headlen, bodylen);
|
||||
|
||||
/* 'amount' can never be a very large value here so typecasting it so a
|
||||
signed 31 bit value should not cause problems even if ssize_t is
|
||||
64bit */
|
||||
*bytes_written += (long)amount;
|
||||
|
||||
if(http) {
|
||||
/* if we sent a piece of the body here, up the byte counter for it
|
||||
accordingly */
|
||||
data->req.writebytecount += bodylen;
|
||||
Curl_pgrsSetUploadCounter(data, data->req.writebytecount);
|
||||
|
||||
if((size_t)amount != size) {
|
||||
/* The whole request could not be sent in one system call. We must
|
||||
queue it up and send it later when we get the chance. We must not
|
||||
loop here and wait until it might work again. */
|
||||
|
||||
size -= amount;
|
||||
|
||||
ptr = Curl_dyn_ptr(in) + amount;
|
||||
|
||||
/* backup the currently set pointers */
|
||||
http->backup.fread_func = data->state.fread_func;
|
||||
http->backup.fread_in = data->state.in;
|
||||
http->backup.postdata = http->postdata;
|
||||
http->backup.postsize = http->postsize;
|
||||
http->backup.data = data;
|
||||
|
||||
/* set the new pointers for the request-sending */
|
||||
data->state.fread_func = (curl_read_callback)readmoredata;
|
||||
data->state.in = (void *)http;
|
||||
http->postdata = ptr;
|
||||
http->postsize = (curl_off_t)size;
|
||||
|
||||
/* this much data is remaining header: */
|
||||
data->req.pendingheader = headersize - headlen;
|
||||
|
||||
http->send_buffer = *in; /* copy the whole struct */
|
||||
http->sending = HTTPSEND_REQUEST;
|
||||
return CURLE_OK;
|
||||
}
|
||||
http->sending = HTTPSEND_BODY;
|
||||
/* the full buffer was sent, clean up and return */
|
||||
}
|
||||
else {
|
||||
if((size_t)amount != size)
|
||||
/* We have no continue-send mechanism now, fail. This can only happen
|
||||
when this function is used from the CONNECT sending function. We
|
||||
currently (stupidly) assume that the whole request is always sent
|
||||
away in the first single chunk.
|
||||
|
||||
This needs FIXing.
|
||||
*/
|
||||
return CURLE_SEND_ERROR;
|
||||
}
|
||||
}
|
||||
Curl_dyn_free(in);
|
||||
|
||||
/* no remaining header data */
|
||||
data->req.pendingheader = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
/* 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) */
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Curl_compareheader()
|
||||
*
|
||||
|
|
@ -1648,51 +1377,6 @@ enum proxy_use {
|
|||
HEADER_CONNECT /* sending CONNECT to a proxy */
|
||||
};
|
||||
|
||||
/* used to compile the provided trailers into one buffer
|
||||
will return an error code if one of the headers is
|
||||
not formatted correctly */
|
||||
CURLcode Curl_http_compile_trailers(struct curl_slist *trailers,
|
||||
struct dynbuf *b,
|
||||
struct Curl_easy *handle)
|
||||
{
|
||||
char *ptr = NULL;
|
||||
CURLcode result = CURLE_OK;
|
||||
const char *endofline_native = NULL;
|
||||
const char *endofline_network = NULL;
|
||||
|
||||
if(
|
||||
#ifdef CURL_DO_LINEEND_CONV
|
||||
(handle->state.prefer_ascii) ||
|
||||
#endif
|
||||
(handle->set.crlf)) {
|
||||
/* \n will become \r\n later on */
|
||||
endofline_native = "\n";
|
||||
endofline_network = "\x0a";
|
||||
}
|
||||
else {
|
||||
endofline_native = "\r\n";
|
||||
endofline_network = "\x0d\x0a";
|
||||
}
|
||||
|
||||
while(trailers) {
|
||||
/* only add correctly formatted trailers */
|
||||
ptr = strchr(trailers->data, ':');
|
||||
if(ptr && *(ptr + 1) == ' ') {
|
||||
result = Curl_dyn_add(b, trailers->data);
|
||||
if(result)
|
||||
return result;
|
||||
result = Curl_dyn_add(b, endofline_native);
|
||||
if(result)
|
||||
return result;
|
||||
}
|
||||
else
|
||||
infof(handle, "Malformatted trailing header, skipping trailer");
|
||||
trailers = trailers->next;
|
||||
}
|
||||
result = Curl_dyn_add(b, endofline_network);
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool hd_name_eq(const char *n1, size_t n1len,
|
||||
const char *n2, size_t n2len)
|
||||
{
|
||||
|
|
@ -2461,13 +2145,9 @@ static CURLcode addexpect(struct Curl_easy *data, struct dynbuf *r)
|
|||
return CURLE_OK;
|
||||
}
|
||||
|
||||
CURLcode Curl_http_req_send(struct Curl_easy *data,
|
||||
struct dynbuf *r, Curl_HttpReq httpreq)
|
||||
CURLcode Curl_http_req_complete(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;
|
||||
#endif
|
||||
CURLcode result = CURLE_OK;
|
||||
struct HTTP *http = data->req.p.http;
|
||||
|
||||
|
|
@ -2487,32 +2167,24 @@ CURLcode Curl_http_req_send(struct Curl_easy *data,
|
|||
result = Curl_dyn_addf(r, "Content-Length: %" CURL_FORMAT_CURL_OFF_T
|
||||
"\r\n", http->postsize);
|
||||
if(result)
|
||||
return result;
|
||||
goto out;
|
||||
}
|
||||
|
||||
result = addexpect(data, r);
|
||||
if(result)
|
||||
return result;
|
||||
goto out;
|
||||
|
||||
/* end of headers */
|
||||
result = Curl_dyn_addn(r, STRCONST("\r\n"));
|
||||
if(result)
|
||||
return result;
|
||||
goto out;
|
||||
|
||||
/* set the upload size to the progress meter */
|
||||
Curl_pgrsSetUploadSize(data, http->postsize);
|
||||
|
||||
/* this sends the buffer and frees all the buffer resources */
|
||||
result = buffer_send(r, data, data->req.p.http,
|
||||
&data->info.request_size, 0);
|
||||
if(result)
|
||||
failf(data, "Failed sending PUT request");
|
||||
if(!http->postsize)
|
||||
result = Client_reader_set_null(data);
|
||||
else
|
||||
/* prepare for transfer */
|
||||
Curl_xfer_setup(data, FIRSTSOCKET, -1, TRUE,
|
||||
http->postsize?FIRSTSOCKET:-1);
|
||||
if(result)
|
||||
return result;
|
||||
result = Client_reader_set_fread(data, data->state.infilesize);
|
||||
break;
|
||||
|
||||
#if !defined(CURL_DISABLE_MIME) || !defined(CURL_DISABLE_FORM_API)
|
||||
|
|
@ -2522,16 +2194,12 @@ CURLcode Curl_http_req_send(struct Curl_easy *data,
|
|||
if(data->req.authneg) {
|
||||
/* nothing to post! */
|
||||
result = Curl_dyn_addn(r, STRCONST("Content-Length: 0\r\n\r\n"));
|
||||
if(!result)
|
||||
result = Client_reader_set_null(data);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
result = buffer_send(r, data, data->req.p.http,
|
||||
&data->info.request_size, 0);
|
||||
if(result)
|
||||
failf(data, "Failed sending POST request");
|
||||
else
|
||||
/* setup variables for the upcoming transfer */
|
||||
Curl_xfer_setup(data, FIRSTSOCKET, -1, TRUE, -1);
|
||||
/* setup variables for the upcoming transfer */
|
||||
Curl_xfer_setup(data, FIRSTSOCKET, -1, TRUE, -1);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -2548,7 +2216,7 @@ CURLcode Curl_http_req_send(struct Curl_easy *data,
|
|||
"Content-Length: %" CURL_FORMAT_CURL_OFF_T
|
||||
"\r\n", http->postsize);
|
||||
if(result)
|
||||
return result;
|
||||
goto out;
|
||||
}
|
||||
|
||||
#ifndef CURL_DISABLE_MIME
|
||||
|
|
@ -2559,40 +2227,32 @@ CURLcode Curl_http_req_send(struct Curl_easy *data,
|
|||
for(hdr = data->state.mimepost->curlheaders; hdr; hdr = hdr->next) {
|
||||
result = Curl_dyn_addf(r, "%s\r\n", hdr->data);
|
||||
if(result)
|
||||
return result;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
result = addexpect(data, r);
|
||||
if(result)
|
||||
return result;
|
||||
goto out;
|
||||
|
||||
/* make the request end in a true CRLF */
|
||||
result = Curl_dyn_addn(r, STRCONST("\r\n"));
|
||||
if(result)
|
||||
return result;
|
||||
goto out;
|
||||
|
||||
/* set the upload size to the progress meter */
|
||||
Curl_pgrsSetUploadSize(data, http->postsize);
|
||||
|
||||
/* Read from mime structure. */
|
||||
data->state.fread_func = (curl_read_callback) Curl_mime_read;
|
||||
data->state.in = (void *) data->state.mimepost;
|
||||
if(!http->postsize)
|
||||
result = Client_reader_set_null(data);
|
||||
else {
|
||||
/* Read from mime structure. We could do a special client reader
|
||||
* for this, but replacing the callback seems to work fine. */
|
||||
data->state.fread_func = (curl_read_callback) Curl_mime_read;
|
||||
data->state.in = (void *) data->state.mimepost;
|
||||
result = Client_reader_set_fread(data, data->state.infilesize);
|
||||
}
|
||||
http->sending = HTTPSEND_BODY;
|
||||
|
||||
/* this sends the buffer and frees all the buffer resources */
|
||||
result = buffer_send(r, data, data->req.p.http,
|
||||
&data->info.request_size, 0);
|
||||
if(result)
|
||||
failf(data, "Failed sending POST request");
|
||||
else
|
||||
/* prepare for transfer */
|
||||
Curl_xfer_setup(data, FIRSTSOCKET, -1, TRUE,
|
||||
http->postsize?FIRSTSOCKET:-1);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
break;
|
||||
#endif
|
||||
case HTTPREQ_POST:
|
||||
|
|
@ -2615,147 +2275,53 @@ CURLcode Curl_http_req_send(struct Curl_easy *data,
|
|||
result = Curl_dyn_addf(r, "Content-Length: %" CURL_FORMAT_CURL_OFF_T
|
||||
"\r\n", http->postsize);
|
||||
if(result)
|
||||
return result;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if(!Curl_checkheaders(data, STRCONST("Content-Type"))) {
|
||||
result = Curl_dyn_addn(r, STRCONST("Content-Type: application/"
|
||||
"x-www-form-urlencoded\r\n"));
|
||||
if(result)
|
||||
return result;
|
||||
goto out;
|
||||
}
|
||||
|
||||
result = addexpect(data, r);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
#ifndef USE_HYPER
|
||||
/* With Hyper the body is always passed on separately */
|
||||
if(data->set.postfields) {
|
||||
if(!data->state.expect100header &&
|
||||
(http->postsize < MAX_INITIAL_POST_SIZE)) {
|
||||
/* if we don't use expect: 100 AND
|
||||
postsize is less than MAX_INITIAL_POST_SIZE
|
||||
|
||||
then append the post data to the HTTP request header. This limit
|
||||
is no magic limit but only set to prevent really huge POSTs to
|
||||
get the data duplicated with malloc() and family. */
|
||||
|
||||
/* end of headers! */
|
||||
result = Curl_dyn_addn(r, STRCONST("\r\n"));
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
if(!data->req.upload_chunky) {
|
||||
/* We're not sending it 'chunked', append it to the request
|
||||
already now to reduce the number of send() calls */
|
||||
result = Curl_dyn_addn(r, data->set.postfields,
|
||||
(size_t)http->postsize);
|
||||
included_body = http->postsize;
|
||||
}
|
||||
else {
|
||||
if(http->postsize) {
|
||||
char chunk[16];
|
||||
/* Append the POST data chunky-style */
|
||||
msnprintf(chunk, sizeof(chunk), "%x\r\n", (int)http->postsize);
|
||||
result = Curl_dyn_add(r, chunk);
|
||||
if(!result) {
|
||||
included_body = http->postsize + strlen(chunk);
|
||||
result = Curl_dyn_addn(r, data->set.postfields,
|
||||
(size_t)http->postsize);
|
||||
if(!result)
|
||||
result = Curl_dyn_addn(r, STRCONST("\r\n"));
|
||||
included_body += 2;
|
||||
}
|
||||
}
|
||||
if(!result) {
|
||||
result = Curl_dyn_addn(r, STRCONST("\x30\x0d\x0a\x0d\x0a"));
|
||||
/* 0 CR LF CR LF */
|
||||
included_body += 5;
|
||||
}
|
||||
}
|
||||
if(result)
|
||||
return result;
|
||||
/* Make sure the progress information is accurate */
|
||||
Curl_pgrsSetUploadSize(data, http->postsize);
|
||||
}
|
||||
else {
|
||||
/* A huge POST coming up, do data separate from the request */
|
||||
http->postdata = data->set.postfields;
|
||||
http->sending = HTTPSEND_BODY;
|
||||
http->backup.data = data;
|
||||
data->state.fread_func = (curl_read_callback)readmoredata;
|
||||
data->state.in = (void *)http;
|
||||
|
||||
/* set the upload size to the progress meter */
|
||||
Curl_pgrsSetUploadSize(data, http->postsize);
|
||||
|
||||
/* end of headers! */
|
||||
result = Curl_dyn_addn(r, STRCONST("\r\n"));
|
||||
if(result)
|
||||
return result;
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
/* end of headers! */
|
||||
result = Curl_dyn_addn(r, STRCONST("\r\n"));
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
if(data->req.upload_chunky && data->req.authneg) {
|
||||
/* Chunky upload is selected and we're negotiating auth still, send
|
||||
end-of-data only */
|
||||
result = Curl_dyn_addn(r, (char *)STRCONST("\x30\x0d\x0a\x0d\x0a"));
|
||||
/* 0 CR LF CR LF */
|
||||
if(result)
|
||||
return result;
|
||||
}
|
||||
|
||||
else if(data->state.infilesize) {
|
||||
/* set the upload size to the progress meter */
|
||||
Curl_pgrsSetUploadSize(data, http->postsize?http->postsize:-1);
|
||||
|
||||
/* set the pointer to mark that we will send the post body using the
|
||||
read callback, but only if we're not in authenticate negotiation */
|
||||
if(!data->req.authneg)
|
||||
http->postdata = (char *)&http->postdata;
|
||||
}
|
||||
}
|
||||
/* issue the request */
|
||||
result = buffer_send(r, data, data->req.p.http,
|
||||
&data->info.request_size, included_body);
|
||||
goto out;
|
||||
|
||||
result = Curl_dyn_addn(r, STRCONST("\r\n"));
|
||||
if(result)
|
||||
failf(data, "Failed sending HTTP POST request");
|
||||
else
|
||||
Curl_xfer_setup(data, FIRSTSOCKET, -1, TRUE,
|
||||
http->postdata?FIRSTSOCKET:-1);
|
||||
goto out;
|
||||
|
||||
if(!http->postsize) {
|
||||
Curl_pgrsSetUploadSize(data, -1);
|
||||
result = Client_reader_set_null(data);
|
||||
}
|
||||
else if(data->set.postfields) { /* we have the bytes */
|
||||
Curl_pgrsSetUploadSize(data, http->postsize);
|
||||
result = Client_reader_set_buf(data, data->set.postfields,
|
||||
(size_t)http->postsize);
|
||||
}
|
||||
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:
|
||||
/* HTTP GET/HEAD download, has no body, needs no Content-Length */
|
||||
result = Curl_dyn_addn(r, STRCONST("\r\n"));
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
/* issue the request */
|
||||
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((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 */
|
||||
Curl_xfer_setup(data, FIRSTSOCKET, -1, TRUE, FIRSTSOCKET);
|
||||
#endif
|
||||
else
|
||||
/* HTTP GET/HEAD download: */
|
||||
Curl_xfer_setup(data, FIRSTSOCKET, -1, TRUE, -1);
|
||||
if(!result)
|
||||
result = Client_reader_set_null(data);
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
if(!result) {
|
||||
/* setup variables for the upcoming transfer */
|
||||
Curl_xfer_setup(data, FIRSTSOCKET, -1, TRUE, FIRSTSOCKET);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
@ -3320,17 +2886,15 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
|
|||
Curl_pgrsSetUploadSize(data, 0); /* nothing */
|
||||
|
||||
/* req_send takes ownership of the 'req' memory on success */
|
||||
result = Curl_http_req_send(data, &req, httpreq);
|
||||
result = Curl_http_req_complete(data, &req, httpreq);
|
||||
if(!result && data->req.upload_chunky)
|
||||
result = Curl_httpchunk_add_reader(data);
|
||||
if(!result)
|
||||
result = Curl_req_send(data, &req);
|
||||
}
|
||||
if(result) {
|
||||
Curl_dyn_free(&req);
|
||||
Curl_dyn_free(&req);
|
||||
if(result)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if((http->postsize > -1) &&
|
||||
(http->postsize <= data->req.writebytecount) &&
|
||||
(http->sending != HTTPSEND_REQUEST))
|
||||
data->req.upload_done = TRUE;
|
||||
|
||||
if(data->req.writebytecount) {
|
||||
/* if a request-body has been sent off, we make sure this progress is noted
|
||||
|
|
|
|||
|
|
@ -94,10 +94,6 @@ CURLcode Curl_dynhds_add_custom(struct Curl_easy *data,
|
|||
bool is_connect,
|
||||
struct dynhds *hds);
|
||||
|
||||
CURLcode Curl_http_compile_trailers(struct curl_slist *trailers,
|
||||
struct dynbuf *buf,
|
||||
struct Curl_easy *handle);
|
||||
|
||||
void Curl_http_method(struct Curl_easy *data, struct connectdata *conn,
|
||||
const char **method, Curl_HttpReq *);
|
||||
CURLcode Curl_http_useragent(struct Curl_easy *data);
|
||||
|
|
@ -112,8 +108,8 @@ CURLcode Curl_transferencode(struct Curl_easy *data);
|
|||
CURLcode Curl_http_body(struct Curl_easy *data, struct connectdata *conn,
|
||||
Curl_HttpReq httpreq,
|
||||
const char **teep);
|
||||
CURLcode Curl_http_req_send(struct Curl_easy *data,
|
||||
struct dynbuf *r, Curl_HttpReq httpreq);
|
||||
CURLcode Curl_http_req_complete(struct Curl_easy *data,
|
||||
struct dynbuf *r, Curl_HttpReq httpreq);
|
||||
bool Curl_use_http_1_1plus(const struct Curl_easy *data,
|
||||
const struct connectdata *conn);
|
||||
#ifndef CURL_DISABLE_COOKIES
|
||||
|
|
|
|||
|
|
@ -27,10 +27,12 @@
|
|||
#ifndef CURL_DISABLE_HTTP
|
||||
|
||||
#include "urldata.h" /* it includes http_chunks.h */
|
||||
#include "curl_printf.h"
|
||||
#include "sendf.h" /* for the client write stuff */
|
||||
#include "dynbuf.h"
|
||||
#include "content_encoding.h"
|
||||
#include "http.h"
|
||||
#include "multiif.h"
|
||||
#include "strtoofft.h"
|
||||
#include "warnless.h"
|
||||
|
||||
|
|
@ -458,4 +460,193 @@ const struct Curl_cwtype Curl_httpchunk_unencoder = {
|
|||
sizeof(struct chunked_writer)
|
||||
};
|
||||
|
||||
/* max length of a HTTP chunk that we want to generate */
|
||||
#define CURL_CHUNKED_MINLEN (1024)
|
||||
#define CURL_CHUNKED_MAXLEN (64 * 1024)
|
||||
|
||||
struct chunked_reader {
|
||||
struct Curl_creader super;
|
||||
struct bufq chunkbuf;
|
||||
BIT(read_eos); /* we read an EOS from the next reader */
|
||||
BIT(eos); /* we have returned an EOS */
|
||||
};
|
||||
|
||||
static CURLcode cr_chunked_init(struct Curl_easy *data,
|
||||
struct Curl_creader *reader)
|
||||
{
|
||||
struct chunked_reader *ctx = (struct chunked_reader *)reader;
|
||||
(void)data;
|
||||
Curl_bufq_init2(&ctx->chunkbuf, CURL_CHUNKED_MAXLEN, 2, BUFQ_OPT_SOFT_LIMIT);
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
static void cr_chunked_close(struct Curl_easy *data,
|
||||
struct Curl_creader *reader)
|
||||
{
|
||||
struct chunked_reader *ctx = (struct chunked_reader *)reader;
|
||||
(void)data;
|
||||
Curl_bufq_free(&ctx->chunkbuf);
|
||||
}
|
||||
|
||||
static CURLcode add_last_chunk(struct Curl_easy *data,
|
||||
struct Curl_creader *reader)
|
||||
{
|
||||
struct chunked_reader *ctx = (struct chunked_reader *)reader;
|
||||
struct curl_slist *trailers = NULL, *tr;
|
||||
CURLcode result;
|
||||
size_t n;
|
||||
int rc;
|
||||
|
||||
if(!data->set.trailer_callback) {
|
||||
return Curl_bufq_cwrite(&ctx->chunkbuf, STRCONST("0\r\n\r\n"), &n);
|
||||
}
|
||||
|
||||
result = Curl_bufq_cwrite(&ctx->chunkbuf, STRCONST("0\r\n"), &n);
|
||||
if(result)
|
||||
goto out;
|
||||
|
||||
Curl_set_in_callback(data, true);
|
||||
rc = data->set.trailer_callback(&trailers, data->set.trailer_data);
|
||||
Curl_set_in_callback(data, false);
|
||||
|
||||
if(rc != CURL_TRAILERFUNC_OK) {
|
||||
failf(data, "operation aborted by trailing headers callback");
|
||||
result = CURLE_ABORTED_BY_CALLBACK;
|
||||
goto out;
|
||||
}
|
||||
|
||||
for(tr = trailers; tr; tr = tr->next) {
|
||||
/* only add correctly formatted trailers */
|
||||
char *ptr = strchr(tr->data, ':');
|
||||
if(!ptr || *(ptr + 1) != ' ') {
|
||||
infof(data, "Malformatted trailing header, skipping trailer");
|
||||
continue;
|
||||
}
|
||||
|
||||
result = Curl_bufq_cwrite(&ctx->chunkbuf, tr->data,
|
||||
strlen(tr->data), &n);
|
||||
if(!result)
|
||||
result = Curl_bufq_cwrite(&ctx->chunkbuf, STRCONST("\r\n"), &n);
|
||||
if(result)
|
||||
goto out;
|
||||
}
|
||||
|
||||
result = Curl_bufq_cwrite(&ctx->chunkbuf, STRCONST("\r\n"), &n);
|
||||
|
||||
out:
|
||||
curl_slist_free_all(trailers);
|
||||
return result;
|
||||
}
|
||||
|
||||
static CURLcode add_chunk(struct Curl_easy *data,
|
||||
struct Curl_creader *reader,
|
||||
char *buf, size_t blen)
|
||||
{
|
||||
struct chunked_reader *ctx = (struct chunked_reader *)reader;
|
||||
CURLcode result;
|
||||
char tmp[CURL_CHUNKED_MINLEN];
|
||||
size_t nread;
|
||||
bool eos;
|
||||
|
||||
DEBUGASSERT(!ctx->read_eos);
|
||||
blen = CURLMIN(blen, CURL_CHUNKED_MAXLEN); /* respect our buffer pref */
|
||||
if(blen < sizeof(tmp)) {
|
||||
/* small read, make a chunk of decent size */
|
||||
buf = tmp;
|
||||
blen = sizeof(tmp);
|
||||
}
|
||||
else {
|
||||
/* larger read, make a chunk that will fit when read back */
|
||||
blen -= (8 + 2 + 2); /* deduct max overhead, 8 hex + 2*crlf */
|
||||
}
|
||||
|
||||
result = Curl_creader_read(data, reader->next, buf, blen, &nread, &eos);
|
||||
if(result)
|
||||
return result;
|
||||
if(eos)
|
||||
ctx->read_eos = TRUE;
|
||||
|
||||
if(nread) {
|
||||
/* actually got bytes, wrap them into the chunkbuf */
|
||||
char hd[11] = "";
|
||||
int hdlen;
|
||||
size_t n;
|
||||
|
||||
hdlen = msnprintf(hd, sizeof(hd), "%zx\r\n", nread);
|
||||
if(hdlen <= 0)
|
||||
return CURLE_READ_ERROR;
|
||||
/* On a soft-limited bufq, we do not need to check that all was written */
|
||||
result = Curl_bufq_cwrite(&ctx->chunkbuf, hd, hdlen, &n);
|
||||
if(!result)
|
||||
result = Curl_bufq_cwrite(&ctx->chunkbuf, buf, nread, &n);
|
||||
if(!result)
|
||||
result = Curl_bufq_cwrite(&ctx->chunkbuf, "\r\n", 2, &n);
|
||||
if(result)
|
||||
return result;
|
||||
}
|
||||
|
||||
if(ctx->read_eos)
|
||||
return add_last_chunk(data, reader);
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
static CURLcode cr_chunked_read(struct Curl_easy *data,
|
||||
struct Curl_creader *reader,
|
||||
char *buf, size_t blen,
|
||||
size_t *pnread, bool *peos)
|
||||
{
|
||||
struct chunked_reader *ctx = (struct chunked_reader *)reader;
|
||||
CURLcode result = CURLE_READ_ERROR;
|
||||
|
||||
*pnread = 0;
|
||||
*peos = ctx->eos;
|
||||
|
||||
if(!ctx->eos) {
|
||||
if(!ctx->read_eos && Curl_bufq_is_empty(&ctx->chunkbuf)) {
|
||||
/* Still getting data form the next reader, buffer is empty */
|
||||
result = add_chunk(data, reader, buf, blen);
|
||||
if(result)
|
||||
return result;
|
||||
}
|
||||
|
||||
if(!Curl_bufq_is_empty(&ctx->chunkbuf)) {
|
||||
result = Curl_bufq_cread(&ctx->chunkbuf, buf, blen, pnread);
|
||||
if(!result && ctx->read_eos && Curl_bufq_is_empty(&ctx->chunkbuf)) {
|
||||
/* no more data, read all, done. */
|
||||
ctx->eos = TRUE;
|
||||
*peos = TRUE;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
/* We may get here, because we are done or because callbacks paused */
|
||||
DEBUGASSERT(ctx->eos || !ctx->read_eos);
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
/* HTTP chunked Transfer-Encoding encoder */
|
||||
const struct Curl_crtype Curl_httpchunk_encoder = {
|
||||
"chunked",
|
||||
cr_chunked_init,
|
||||
cr_chunked_read,
|
||||
cr_chunked_close,
|
||||
Curl_creader_def_needs_rewind,
|
||||
sizeof(struct chunked_reader)
|
||||
};
|
||||
|
||||
CURLcode Curl_httpchunk_add_reader(struct Curl_easy *data)
|
||||
{
|
||||
struct Curl_creader *reader = NULL;
|
||||
CURLcode result;
|
||||
|
||||
result = Curl_creader_create(&reader, data, &Curl_httpchunk_encoder,
|
||||
CURL_CR_TRANSFER_ENCODE);
|
||||
if(!result)
|
||||
result = Curl_creader_add(data, reader);
|
||||
|
||||
if(result && reader)
|
||||
Curl_creader_free(data, reader);
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif /* CURL_DISABLE_HTTP */
|
||||
|
|
|
|||
|
|
@ -133,6 +133,13 @@ bool Curl_httpchunk_is_done(struct Curl_easy *data, struct Curl_chunker *ch);
|
|||
|
||||
extern const struct Curl_cwtype Curl_httpchunk_unencoder;
|
||||
|
||||
extern const struct Curl_crtype Curl_httpchunk_encoder;
|
||||
|
||||
/**
|
||||
* Add a transfer-encoding "chunked" reader to the transfers reader stack
|
||||
*/
|
||||
CURLcode Curl_httpchunk_add_reader(struct Curl_easy *data);
|
||||
|
||||
#endif /* !CURL_DISABLE_HTTP */
|
||||
|
||||
#endif /* HEADER_CURL_HTTP_CHUNKS_H */
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
#include "urldata.h"
|
||||
#include "dynbuf.h"
|
||||
#include "doh.h"
|
||||
#include "multiif.h"
|
||||
#include "progress.h"
|
||||
#include "request.h"
|
||||
#include "sendf.h"
|
||||
|
|
@ -48,7 +49,7 @@ CURLcode Curl_req_start(struct SingleRequest *req,
|
|||
struct Curl_easy *data)
|
||||
{
|
||||
req->start = Curl_now();
|
||||
Curl_cw_reset(data);
|
||||
Curl_client_reset(data);
|
||||
if(!req->sendbuf_init) {
|
||||
Curl_bufq_init2(&req->sendbuf, data->set.upload_buffer_size, 1,
|
||||
BUFQ_OPT_SOFT_LIMIT);
|
||||
|
|
@ -72,7 +73,7 @@ CURLcode Curl_req_done(struct SingleRequest *req,
|
|||
(void)req;
|
||||
if(!aborted)
|
||||
(void)Curl_req_flush(data);
|
||||
Curl_cw_reset(data);
|
||||
Curl_client_reset(data);
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
|
|
@ -85,7 +86,7 @@ void Curl_req_reset(struct SingleRequest *req, struct Curl_easy *data)
|
|||
* free this safely without leaks. */
|
||||
Curl_safefree(req->p.http);
|
||||
Curl_safefree(req->newurl);
|
||||
Curl_cw_reset(data);
|
||||
Curl_client_reset(data);
|
||||
|
||||
#ifndef CURL_DISABLE_DOH
|
||||
if(req->doh) {
|
||||
|
|
@ -114,7 +115,7 @@ void Curl_req_free(struct SingleRequest *req, struct Curl_easy *data)
|
|||
Curl_safefree(req->newurl);
|
||||
if(req->sendbuf_init)
|
||||
Curl_bufq_free(&req->sendbuf);
|
||||
Curl_cw_reset(data);
|
||||
Curl_client_reset(data);
|
||||
|
||||
#ifndef CURL_DISABLE_DOH
|
||||
if(req->doh) {
|
||||
|
|
@ -171,22 +172,6 @@ static CURLcode req_send(struct Curl_easy *data,
|
|||
return result;
|
||||
}
|
||||
|
||||
static CURLcode req_send_buffer_add(struct Curl_easy *data,
|
||||
const char *buf, size_t blen,
|
||||
size_t hds_len)
|
||||
{
|
||||
CURLcode result = CURLE_OK;
|
||||
ssize_t n;
|
||||
n = Curl_bufq_write(&data->req.sendbuf,
|
||||
(const unsigned char *)buf, blen, &result);
|
||||
if(n < 0)
|
||||
return result;
|
||||
/* We rely on a SOFTLIMIT on sendbuf, so it can take all data in */
|
||||
DEBUGASSERT((size_t)n == blen);
|
||||
data->req.sendbuf_hds_len += hds_len;
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
static CURLcode req_send_buffer_flush(struct Curl_easy *data)
|
||||
{
|
||||
CURLcode result = CURLE_OK;
|
||||
|
|
@ -200,8 +185,19 @@ static CURLcode req_send_buffer_flush(struct Curl_easy *data)
|
|||
break;
|
||||
|
||||
Curl_bufq_skip(&data->req.sendbuf, nwritten);
|
||||
if(hds_len)
|
||||
if(hds_len) {
|
||||
data->req.sendbuf_hds_len -= CURLMIN(hds_len, nwritten);
|
||||
if(!data->req.sendbuf_hds_len) {
|
||||
/* all request headers sent */
|
||||
if(data->req.exp100 == EXP100_SENDING_REQUEST) {
|
||||
/* We are now waiting for a reply from the server or
|
||||
* a timeout on our side */
|
||||
data->req.exp100 = EXP100_AWAITING_CONTINUE;
|
||||
data->req.start100 = Curl_now();
|
||||
Curl_expire(data, data->set.expect_100_timeout, EXPIRE_100_TIMEOUT);
|
||||
}
|
||||
}
|
||||
}
|
||||
/* leave if we could not send all. Maybe network blocking or
|
||||
* speed limits on transfer */
|
||||
if(nwritten < blen)
|
||||
|
|
@ -228,9 +224,41 @@ CURLcode Curl_req_flush(struct Curl_easy *data)
|
|||
return CURLE_OK;
|
||||
}
|
||||
|
||||
CURLcode Curl_req_send(struct Curl_easy *data,
|
||||
const char *buf, size_t blen,
|
||||
size_t hds_len)
|
||||
#ifndef USE_HYPER
|
||||
|
||||
static CURLcode req_send_buffer_add(struct Curl_easy *data,
|
||||
const char *buf, size_t blen,
|
||||
size_t hds_len)
|
||||
{
|
||||
CURLcode result = CURLE_OK;
|
||||
ssize_t n;
|
||||
n = Curl_bufq_write(&data->req.sendbuf,
|
||||
(const unsigned char *)buf, blen, &result);
|
||||
if(n < 0)
|
||||
return result;
|
||||
/* We rely on a SOFTLIMIT on sendbuf, so it can take all data in */
|
||||
DEBUGASSERT((size_t)n == blen);
|
||||
data->req.sendbuf_hds_len += hds_len;
|
||||
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;
|
||||
|
||||
|
|
@ -242,14 +270,25 @@ CURLcode Curl_req_send(struct Curl_easy *data,
|
|||
* important for TLS libraries that expect this.
|
||||
* We *could* optimized for non-TLS transfers, but that would mean
|
||||
* separate code paths and seems not worth it. */
|
||||
result = req_send_buffer_add(data, buf, blen, hds_len);
|
||||
result = req_send_buffer_add(data, Curl_dyn_ptr(buf), Curl_dyn_len(buf),
|
||||
Curl_dyn_len(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;
|
||||
}
|
||||
#endif /* !USE_HYPER */
|
||||
|
||||
bool Curl_req_want_send(struct Curl_easy *data)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -64,8 +64,6 @@ struct SingleRequest {
|
|||
curl_off_t bytecount; /* total number of bytes read */
|
||||
curl_off_t writebytecount; /* number of bytes written */
|
||||
|
||||
size_t pendingheader; /* this many bytes left to send is actually
|
||||
header and not body */
|
||||
struct curltime start; /* transfer started at this time */
|
||||
unsigned int headerbytecount; /* received server headers (not CONNECT
|
||||
headers) */
|
||||
|
|
@ -87,9 +85,12 @@ struct SingleRequest {
|
|||
enum expect100 exp100; /* expect 100 continue state */
|
||||
enum upgrade101 upgr101; /* 101 upgrade state */
|
||||
|
||||
/* Client Writer stack, handles trasnfer- and content-encodings, protocol
|
||||
/* Client Writer stack, handles transfer- and content-encodings, protocol
|
||||
* checks, pausing by client callbacks. */
|
||||
struct Curl_cwriter *writer_stack;
|
||||
/* Client Reader stack, handles transfer- and content-encodings, protocol
|
||||
* checks, pausing by client callbacks. */
|
||||
struct Curl_creader *reader_stack;
|
||||
struct bufq sendbuf; /* data which needs to be send to the server */
|
||||
size_t sendbuf_hds_len; /* amount of header bytes in sendbuf */
|
||||
time_t timeofdoc;
|
||||
|
|
@ -137,6 +138,7 @@ struct SingleRequest {
|
|||
BIT(content_range); /* set TRUE if Content-Range: was found */
|
||||
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(ignorebody); /* we read a response-body but we ignore it! */
|
||||
|
|
@ -190,24 +192,18 @@ void Curl_req_free(struct SingleRequest *req, struct Curl_easy *data);
|
|||
*/
|
||||
void Curl_req_reset(struct SingleRequest *req, struct Curl_easy *data);
|
||||
|
||||
|
||||
#ifndef USE_HYPER
|
||||
/**
|
||||
* Send request bytes for transfer. If not all could be sent
|
||||
* Send request headers. If not all could be sent
|
||||
* they will be buffered. Use `Curl_req_flush()` to make sure
|
||||
* bytes are really send.
|
||||
* @param data the transfer making the request
|
||||
* @param buf the bytes to send
|
||||
* @param blen the number of bytes to send
|
||||
* @param hds_len the number of bytes from the start that are headers
|
||||
* @param buf the complete header bytes, no body
|
||||
* @return CURLE_OK (on blocking with *pnwritten == 0) or error.
|
||||
*/
|
||||
CURLcode Curl_req_send(struct Curl_easy *data,
|
||||
const char *buf, size_t blen,
|
||||
size_t hds_len);
|
||||
CURLcode Curl_req_send(struct Curl_easy *data, struct dynbuf *buf);
|
||||
|
||||
/* Convenience for sending only header bytes */
|
||||
#define Curl_req_send_hds(data, buf, blen) \
|
||||
Curl_req_send((data), (buf), (blen), (blen))
|
||||
#endif /* !USE_HYPER */
|
||||
|
||||
/**
|
||||
* Flush all buffered request bytes.
|
||||
|
|
|
|||
83
lib/rtsp.c
83
lib/rtsp.c
|
|
@ -241,6 +241,8 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done)
|
|||
const char *p_userpwd = NULL;
|
||||
|
||||
*done = TRUE;
|
||||
/* Initialize a dynamic send buffer */
|
||||
Curl_dyn_init(&req_buffer, DYN_RTSP_REQ_HEADER);
|
||||
|
||||
rtsp->CSeq_sent = data->state.rtsp_next_client_CSeq;
|
||||
rtsp->CSeq_recv = 0;
|
||||
|
|
@ -311,8 +313,7 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done)
|
|||
|
||||
if(rtspreq == RTSPREQ_RECEIVE) {
|
||||
Curl_xfer_setup(data, FIRSTSOCKET, -1, TRUE, -1);
|
||||
|
||||
return result;
|
||||
goto out;
|
||||
}
|
||||
|
||||
p_session_id = data->set.str[STRING_RTSP_SESSION_ID];
|
||||
|
|
@ -320,7 +321,8 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done)
|
|||
(rtspreq & ~(RTSPREQ_OPTIONS | RTSPREQ_DESCRIBE | RTSPREQ_SETUP))) {
|
||||
failf(data, "Refusing to issue an RTSP request [%s] without a session ID.",
|
||||
p_request);
|
||||
return CURLE_BAD_FUNCTION_ARGUMENT;
|
||||
result = CURLE_BAD_FUNCTION_ARGUMENT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Stream URI. Default to server '*' if not specified */
|
||||
|
|
@ -347,7 +349,8 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done)
|
|||
else {
|
||||
failf(data,
|
||||
"Refusing to issue an RTSP SETUP without a Transport: header.");
|
||||
return CURLE_BAD_FUNCTION_ARGUMENT;
|
||||
result = CURLE_BAD_FUNCTION_ARGUMENT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
p_transport = data->state.aptr.rtsp_transport;
|
||||
|
|
@ -366,9 +369,10 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done)
|
|||
data->state.aptr.accept_encoding =
|
||||
aprintf("Accept-Encoding: %s\r\n", data->set.str[STRING_ENCODING]);
|
||||
|
||||
if(!data->state.aptr.accept_encoding)
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
|
||||
if(!data->state.aptr.accept_encoding) {
|
||||
result = CURLE_OUT_OF_MEMORY;
|
||||
goto out;
|
||||
}
|
||||
p_accept_encoding = data->state.aptr.accept_encoding;
|
||||
}
|
||||
}
|
||||
|
|
@ -390,7 +394,7 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done)
|
|||
result = Curl_http_output_auth(data, conn, p_request, HTTPREQ_GET,
|
||||
p_stream_uri, FALSE);
|
||||
if(result)
|
||||
return result;
|
||||
goto out;
|
||||
|
||||
p_proxyuserpwd = data->state.aptr.proxyuserpwd;
|
||||
p_userpwd = data->state.aptr.userpwd;
|
||||
|
|
@ -424,23 +428,22 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done)
|
|||
*/
|
||||
if(Curl_checkheaders(data, STRCONST("CSeq"))) {
|
||||
failf(data, "CSeq cannot be set as a custom header.");
|
||||
return CURLE_RTSP_CSEQ_ERROR;
|
||||
result = CURLE_RTSP_CSEQ_ERROR;
|
||||
goto out;
|
||||
}
|
||||
if(Curl_checkheaders(data, STRCONST("Session"))) {
|
||||
failf(data, "Session ID cannot be set as a custom header.");
|
||||
return CURLE_BAD_FUNCTION_ARGUMENT;
|
||||
result = CURLE_BAD_FUNCTION_ARGUMENT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Initialize a dynamic send buffer */
|
||||
Curl_dyn_init(&req_buffer, DYN_RTSP_REQ_HEADER);
|
||||
|
||||
result =
|
||||
Curl_dyn_addf(&req_buffer,
|
||||
"%s %s RTSP/1.0\r\n" /* Request Stream-URI RTSP/1.0 */
|
||||
"CSeq: %ld\r\n", /* CSeq */
|
||||
p_request, p_stream_uri, rtsp->CSeq_sent);
|
||||
if(result)
|
||||
return result;
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* Rather than do a normal alloc line, keep the session_id unformatted
|
||||
|
|
@ -449,7 +452,7 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done)
|
|||
if(p_session_id) {
|
||||
result = Curl_dyn_addf(&req_buffer, "Session: %s\r\n", p_session_id);
|
||||
if(result)
|
||||
return result;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -481,17 +484,17 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done)
|
|||
Curl_safefree(data->state.aptr.userpwd);
|
||||
|
||||
if(result)
|
||||
return result;
|
||||
goto out;
|
||||
|
||||
if((rtspreq == RTSPREQ_SETUP) || (rtspreq == RTSPREQ_DESCRIBE)) {
|
||||
result = Curl_add_timecondition(data, &req_buffer);
|
||||
if(result)
|
||||
return result;
|
||||
goto out;
|
||||
}
|
||||
|
||||
result = Curl_add_custom_headers(data, FALSE, &req_buffer);
|
||||
if(result)
|
||||
return result;
|
||||
goto out;
|
||||
|
||||
if(rtspreq == RTSPREQ_ANNOUNCE ||
|
||||
rtspreq == RTSPREQ_SET_PARAMETER ||
|
||||
|
|
@ -500,13 +503,24 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done)
|
|||
if(data->state.upload) {
|
||||
putsize = data->state.infilesize;
|
||||
data->state.httpreq = HTTPREQ_PUT;
|
||||
|
||||
result = Client_reader_set_fread(data, putsize);
|
||||
if(result)
|
||||
goto out;
|
||||
}
|
||||
else {
|
||||
postsize = (data->state.infilesize != -1)?
|
||||
data->state.infilesize:
|
||||
(data->set.postfields? (curl_off_t)strlen(data->set.postfields):0);
|
||||
data->state.httpreq = HTTPREQ_POST;
|
||||
if(postsize > 0 && data->set.postfields)
|
||||
result = Client_reader_set_buf(data, data->set.postfields,
|
||||
(size_t)postsize);
|
||||
else if(!postsize)
|
||||
result = Client_reader_set_null(data);
|
||||
else
|
||||
result = Client_reader_set_fread(data, postsize);
|
||||
if(result)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if(putsize > 0 || postsize > 0) {
|
||||
|
|
@ -518,7 +532,7 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done)
|
|||
"Content-Length: %" CURL_FORMAT_CURL_OFF_T"\r\n",
|
||||
(data->state.upload ? putsize : postsize));
|
||||
if(result)
|
||||
return result;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if(rtspreq == RTSPREQ_SET_PARAMETER ||
|
||||
|
|
@ -528,7 +542,7 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done)
|
|||
STRCONST("Content-Type: "
|
||||
"text/parameters\r\n"));
|
||||
if(result)
|
||||
return result;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -538,7 +552,7 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done)
|
|||
STRCONST("Content-Type: "
|
||||
"application/sdp\r\n"));
|
||||
if(result)
|
||||
return result;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -550,31 +564,27 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done)
|
|||
data->req.no_body = TRUE;
|
||||
}
|
||||
}
|
||||
else {
|
||||
result = Client_reader_set_null(data);
|
||||
if(result)
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* RTSP never allows chunked transfer */
|
||||
data->req.forbidchunk = TRUE;
|
||||
/* Finish the request buffer */
|
||||
result = Curl_dyn_addn(&req_buffer, STRCONST("\r\n"));
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
if(postsize > 0) {
|
||||
result = Curl_dyn_addn(&req_buffer, data->set.postfields,
|
||||
(size_t)postsize);
|
||||
if(result)
|
||||
return result;
|
||||
}
|
||||
goto out;
|
||||
|
||||
/* issue the request */
|
||||
result = Curl_req_send_hds(data, Curl_dyn_ptr(&req_buffer),
|
||||
Curl_dyn_len(&req_buffer));
|
||||
Curl_dyn_free(&req_buffer);
|
||||
result = Curl_req_send(data, &req_buffer);
|
||||
if(result) {
|
||||
failf(data, "Failed sending RTSP request");
|
||||
return result;
|
||||
goto out;
|
||||
}
|
||||
|
||||
Curl_xfer_setup(data, FIRSTSOCKET, -1, TRUE, putsize?FIRSTSOCKET:-1);
|
||||
Curl_xfer_setup(data, FIRSTSOCKET, -1, TRUE, FIRSTSOCKET);
|
||||
|
||||
/* Increment the CSeq on success */
|
||||
data->state.rtsp_next_client_CSeq++;
|
||||
|
|
@ -586,7 +596,8 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done)
|
|||
if(Curl_pgrsUpdate(data))
|
||||
result = CURLE_ABORTED_BY_CALLBACK;
|
||||
}
|
||||
|
||||
out:
|
||||
Curl_dyn_free(&req_buffer);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
|||
528
lib/sendf.c
528
lib/sendf.c
|
|
@ -59,7 +59,7 @@
|
|||
#include "memdebug.h"
|
||||
|
||||
|
||||
static CURLcode do_init_stack(struct Curl_easy *data);
|
||||
static CURLcode do_init_writer_stack(struct Curl_easy *data);
|
||||
|
||||
/* Curl_client_write() sends data to the write callback(s)
|
||||
|
||||
|
|
@ -81,7 +81,7 @@ CURLcode Curl_client_write(struct Curl_easy *data,
|
|||
((type & ~(CLIENTWRITE_INFO|CLIENTWRITE_EOS)) == 0));
|
||||
|
||||
if(!data->req.writer_stack) {
|
||||
result = do_init_stack(data);
|
||||
result = do_init_writer_stack(data);
|
||||
if(result)
|
||||
return result;
|
||||
DEBUGASSERT(data->req.writer_stack);
|
||||
|
|
@ -90,16 +90,33 @@ CURLcode Curl_client_write(struct Curl_easy *data,
|
|||
return Curl_cwriter_write(data, data->req.writer_stack, type, buf, blen);
|
||||
}
|
||||
|
||||
void Curl_cw_reset(struct Curl_easy *data)
|
||||
static void cl_reset_writer(struct Curl_easy *data)
|
||||
{
|
||||
struct Curl_cwriter *writer = data->req.writer_stack;
|
||||
|
||||
while(writer) {
|
||||
data->req.writer_stack = writer->next;
|
||||
writer->cwt->do_close(data, writer);
|
||||
free(writer);
|
||||
writer = data->req.writer_stack;
|
||||
}
|
||||
}
|
||||
|
||||
static void cl_reset_reader(struct Curl_easy *data)
|
||||
{
|
||||
struct Curl_creader *reader = data->req.reader_stack;
|
||||
while(reader) {
|
||||
data->req.reader_stack = reader->next;
|
||||
reader->crt->do_close(data, reader);
|
||||
free(reader);
|
||||
reader = data->req.reader_stack;
|
||||
}
|
||||
}
|
||||
|
||||
void Curl_client_reset(struct Curl_easy *data)
|
||||
{
|
||||
DEBUGF(infof(data, "Curl_client_reset()"));
|
||||
cl_reset_reader(data);
|
||||
cl_reset_writer(data);
|
||||
|
||||
data->req.bytecount = 0;
|
||||
data->req.headerline = 0;
|
||||
|
|
@ -338,7 +355,7 @@ size_t Curl_cwriter_count(struct Curl_easy *data, Curl_cwriter_phase phase)
|
|||
return n;
|
||||
}
|
||||
|
||||
static CURLcode do_init_stack(struct Curl_easy *data)
|
||||
static CURLcode do_init_writer_stack(struct Curl_easy *data)
|
||||
{
|
||||
struct Curl_cwriter *writer;
|
||||
CURLcode result;
|
||||
|
|
@ -374,7 +391,7 @@ CURLcode Curl_cwriter_add(struct Curl_easy *data,
|
|||
struct Curl_cwriter **anchor = &data->req.writer_stack;
|
||||
|
||||
if(!*anchor) {
|
||||
result = do_init_stack(data);
|
||||
result = do_init_writer_stack(data);
|
||||
if(result)
|
||||
return result;
|
||||
}
|
||||
|
|
@ -426,3 +443,502 @@ void Curl_cwriter_remove_by_name(struct Curl_easy *data,
|
|||
}
|
||||
}
|
||||
|
||||
CURLcode Curl_creader_read(struct Curl_easy *data,
|
||||
struct Curl_creader *reader,
|
||||
char *buf, size_t blen, size_t *nread, bool *eos)
|
||||
{
|
||||
if(!reader)
|
||||
return CURLE_READ_ERROR;
|
||||
return reader->crt->do_read(data, reader, buf, blen, nread, eos);
|
||||
}
|
||||
|
||||
CURLcode Curl_creader_def_init(struct Curl_easy *data,
|
||||
struct Curl_creader *reader)
|
||||
{
|
||||
(void)data;
|
||||
(void)reader;
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
void Curl_creader_def_close(struct Curl_easy *data,
|
||||
struct Curl_creader *reader)
|
||||
{
|
||||
(void)data;
|
||||
(void)reader;
|
||||
}
|
||||
|
||||
bool Curl_creader_def_needs_rewind(struct Curl_easy *data,
|
||||
struct Curl_creader *reader)
|
||||
{
|
||||
(void)data;
|
||||
(void)reader;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
struct cr_in_ctx {
|
||||
struct Curl_creader super;
|
||||
curl_off_t total_len;
|
||||
curl_off_t read_len;
|
||||
CURLcode error_result;
|
||||
BIT(seen_eos);
|
||||
BIT(errored);
|
||||
BIT(has_used_cb);
|
||||
};
|
||||
|
||||
static CURLcode cr_in_init(struct Curl_easy *data, struct Curl_creader *reader)
|
||||
{
|
||||
struct cr_in_ctx *ctx = (struct cr_in_ctx *)reader;
|
||||
(void)data;
|
||||
ctx->total_len = -1;
|
||||
ctx->read_len = 0;
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
/* Real client reader to installed client callbacks. */
|
||||
static CURLcode cr_in_read(struct Curl_easy *data,
|
||||
struct Curl_creader *reader,
|
||||
char *buf, size_t blen,
|
||||
size_t *pnread, bool *peos)
|
||||
{
|
||||
struct cr_in_ctx *ctx = (struct cr_in_ctx *)reader;
|
||||
size_t nread;
|
||||
|
||||
/* Once we have errored, we will return the same error forever */
|
||||
if(ctx->errored) {
|
||||
*pnread = 0;
|
||||
*peos = FALSE;
|
||||
return ctx->error_result;
|
||||
}
|
||||
if(ctx->seen_eos) {
|
||||
*pnread = 0;
|
||||
*peos = TRUE;
|
||||
return CURLE_OK;
|
||||
}
|
||||
/* respect length limitations */
|
||||
if(ctx->total_len >= 0) {
|
||||
curl_off_t remain = ctx->total_len - ctx->read_len;
|
||||
if(remain <= 0)
|
||||
blen = 0;
|
||||
else if(remain < (curl_off_t)blen)
|
||||
blen = (size_t)remain;
|
||||
}
|
||||
nread = 0;
|
||||
if(data->state.fread_func && blen) {
|
||||
Curl_set_in_callback(data, true);
|
||||
nread = data->state.fread_func(buf, 1, blen, data->state.in);
|
||||
Curl_set_in_callback(data, false);
|
||||
ctx->has_used_cb = TRUE;
|
||||
}
|
||||
|
||||
switch(nread) {
|
||||
case 0:
|
||||
if((ctx->total_len >= 0) && (ctx->read_len < ctx->total_len)) {
|
||||
failf(data, "client read function EOF fail, only "
|
||||
"only %"CURL_FORMAT_CURL_OFF_T"/%"CURL_FORMAT_CURL_OFF_T
|
||||
" of needed bytes read", ctx->read_len, ctx->total_len);
|
||||
return CURLE_READ_ERROR;
|
||||
}
|
||||
*pnread = 0;
|
||||
*peos = TRUE;
|
||||
ctx->seen_eos = TRUE;
|
||||
break;
|
||||
|
||||
case CURL_READFUNC_ABORT:
|
||||
failf(data, "operation aborted by callback");
|
||||
*pnread = 0;
|
||||
*peos = FALSE;
|
||||
ctx->errored = TRUE;
|
||||
ctx->error_result = CURLE_ABORTED_BY_CALLBACK;
|
||||
return CURLE_ABORTED_BY_CALLBACK;
|
||||
|
||||
case CURL_READFUNC_PAUSE:
|
||||
if(data->conn->handler->flags & PROTOPT_NONETWORK) {
|
||||
/* protocols that work without network cannot be paused. This is
|
||||
actually only FILE:// just now, and it can't pause since the transfer
|
||||
isn't done using the "normal" procedure. */
|
||||
failf(data, "Read callback asked for PAUSE when not supported");
|
||||
return CURLE_READ_ERROR;
|
||||
}
|
||||
/* CURL_READFUNC_PAUSE pauses read callbacks that feed socket writes */
|
||||
data->req.keepon |= KEEP_SEND_PAUSE; /* mark socket send as paused */
|
||||
*pnread = 0;
|
||||
*peos = FALSE;
|
||||
break; /* nothing was read */
|
||||
|
||||
default:
|
||||
if(nread > blen) {
|
||||
/* the read function returned a too large value */
|
||||
failf(data, "read function returned funny value");
|
||||
*pnread = 0;
|
||||
*peos = FALSE;
|
||||
ctx->errored = TRUE;
|
||||
ctx->error_result = CURLE_READ_ERROR;
|
||||
return CURLE_READ_ERROR;
|
||||
}
|
||||
ctx->read_len += nread;
|
||||
*pnread = nread;
|
||||
*peos = FALSE;
|
||||
break;
|
||||
}
|
||||
DEBUGF(infof(data, "cr_in_read(len=%zu, total=%"CURL_FORMAT_CURL_OFF_T
|
||||
", read=%"CURL_FORMAT_CURL_OFF_T") -> %d, %zu, %d",
|
||||
blen, ctx->total_len, ctx->read_len, CURLE_OK, *pnread, *peos));
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
static bool cr_in_needs_rewind(struct Curl_easy *data,
|
||||
struct Curl_creader *reader)
|
||||
{
|
||||
struct cr_in_ctx *ctx = (struct cr_in_ctx *)reader;
|
||||
(void)data;
|
||||
return ctx->has_used_cb;
|
||||
}
|
||||
|
||||
static const struct Curl_crtype cr_in = {
|
||||
"cr-in",
|
||||
cr_in_init,
|
||||
cr_in_read,
|
||||
Curl_creader_def_close,
|
||||
cr_in_needs_rewind,
|
||||
sizeof(struct cr_in_ctx)
|
||||
};
|
||||
|
||||
CURLcode Curl_creader_create(struct Curl_creader **preader,
|
||||
struct Curl_easy *data,
|
||||
const struct Curl_crtype *crt,
|
||||
Curl_creader_phase phase)
|
||||
{
|
||||
struct Curl_creader *reader;
|
||||
CURLcode result = CURLE_OUT_OF_MEMORY;
|
||||
|
||||
DEBUGASSERT(crt->creader_size >= sizeof(struct Curl_creader));
|
||||
reader = (struct Curl_creader *) calloc(1, crt->creader_size);
|
||||
if(!reader)
|
||||
goto out;
|
||||
|
||||
reader->crt = crt;
|
||||
reader->phase = phase;
|
||||
result = crt->do_init(data, reader);
|
||||
|
||||
out:
|
||||
*preader = result? NULL : reader;
|
||||
if(result)
|
||||
free(reader);
|
||||
return result;
|
||||
}
|
||||
|
||||
void Curl_creader_free(struct Curl_easy *data, struct Curl_creader *reader)
|
||||
{
|
||||
if(reader) {
|
||||
reader->crt->do_close(data, reader);
|
||||
free(reader);
|
||||
}
|
||||
}
|
||||
|
||||
struct cr_lc_ctx {
|
||||
struct Curl_creader super;
|
||||
struct bufq buf;
|
||||
BIT(read_eos); /* we read an EOS from the next reader */
|
||||
BIT(eos); /* we have returned an EOS */
|
||||
};
|
||||
|
||||
static CURLcode cr_lc_init(struct Curl_easy *data, struct Curl_creader *reader)
|
||||
{
|
||||
struct cr_lc_ctx *ctx = (struct cr_lc_ctx *)reader;
|
||||
(void)data;
|
||||
Curl_bufq_init2(&ctx->buf, (16 * 1024), 1, BUFQ_OPT_SOFT_LIMIT);
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
static void cr_lc_close(struct Curl_easy *data, struct Curl_creader *reader)
|
||||
{
|
||||
struct cr_lc_ctx *ctx = (struct cr_lc_ctx *)reader;
|
||||
(void)data;
|
||||
Curl_bufq_free(&ctx->buf);
|
||||
}
|
||||
|
||||
/* client reader doing line end conversions. */
|
||||
static CURLcode cr_lc_read(struct Curl_easy *data,
|
||||
struct Curl_creader *reader,
|
||||
char *buf, size_t blen,
|
||||
size_t *pnread, bool *peos)
|
||||
{
|
||||
struct cr_lc_ctx *ctx = (struct cr_lc_ctx *)reader;
|
||||
CURLcode result;
|
||||
size_t nread, i, start, n;
|
||||
bool eos;
|
||||
|
||||
if(ctx->eos) {
|
||||
*pnread = 0;
|
||||
*peos = TRUE;
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
if(Curl_bufq_is_empty(&ctx->buf)) {
|
||||
if(ctx->read_eos) {
|
||||
ctx->eos = TRUE;
|
||||
*pnread = 0;
|
||||
*peos = TRUE;
|
||||
return CURLE_OK;
|
||||
}
|
||||
/* Still getting data form the next reader, ctx->buf is empty */
|
||||
result = Curl_creader_read(data, reader->next, buf, blen, &nread, &eos);
|
||||
if(result)
|
||||
return result;
|
||||
ctx->read_eos = eos;
|
||||
|
||||
if(!nread || !memchr(buf, '\n', nread)) {
|
||||
/* nothing to convert, return this right away */
|
||||
if(ctx->read_eos)
|
||||
ctx->eos = TRUE;
|
||||
*pnread = nread;
|
||||
*peos = ctx->eos;
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
/* at least one \n needs conversion to '\r\n', place into ctx->buf */
|
||||
for(i = start = 0; i < nread; ++i) {
|
||||
if(buf[i] != '\n')
|
||||
continue;
|
||||
/* on a soft limit bufq, we do not need to check length */
|
||||
result = Curl_bufq_cwrite(&ctx->buf, buf + start, i - start, &n);
|
||||
if(!result)
|
||||
result = Curl_bufq_cwrite(&ctx->buf, STRCONST("\r\n"), &n);
|
||||
if(result)
|
||||
return result;
|
||||
start = i + 1;
|
||||
if(!data->set.crlf && (data->state.infilesize != -1)) {
|
||||
/* we're here only because FTP is in ASCII mode...
|
||||
bump infilesize for the LF we just added */
|
||||
data->state.infilesize++;
|
||||
/* comment: this might work for FTP, but in HTTP we could not change
|
||||
* the content length after having started the request... */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DEBUGASSERT(!Curl_bufq_is_empty(&ctx->buf));
|
||||
*peos = FALSE;
|
||||
result = Curl_bufq_cread(&ctx->buf, buf, blen, pnread);
|
||||
if(!result && ctx->read_eos && Curl_bufq_is_empty(&ctx->buf)) {
|
||||
/* no more data, read all, done. */
|
||||
ctx->eos = TRUE;
|
||||
*peos = TRUE;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static const struct Curl_crtype cr_lc = {
|
||||
"cr-lineconv",
|
||||
cr_lc_init,
|
||||
cr_lc_read,
|
||||
cr_lc_close,
|
||||
Curl_creader_def_needs_rewind,
|
||||
sizeof(struct cr_lc_ctx)
|
||||
};
|
||||
|
||||
static CURLcode cr_lc_add(struct Curl_easy *data)
|
||||
{
|
||||
struct Curl_creader *reader = NULL;
|
||||
CURLcode result;
|
||||
|
||||
result = Curl_creader_create(&reader, data, &cr_lc,
|
||||
CURL_CR_TRANSFER_ENCODE);
|
||||
if(!result)
|
||||
result = Curl_creader_add(data, reader);
|
||||
|
||||
if(result && reader)
|
||||
Curl_creader_free(data, reader);
|
||||
return result;
|
||||
}
|
||||
|
||||
static CURLcode do_init_reader_stack(struct Curl_easy *data,
|
||||
const struct Curl_crtype *crt,
|
||||
struct Curl_creader **preader)
|
||||
{
|
||||
CURLcode result;
|
||||
|
||||
DEBUGASSERT(!data->req.reader_stack);
|
||||
result = Curl_creader_create(preader, data, crt, CURL_CR_CLIENT);
|
||||
if(result)
|
||||
return result;
|
||||
data->req.reader_stack = *preader;
|
||||
|
||||
if(data->set.crlf
|
||||
#ifdef CURL_DO_LINEEND_CONV
|
||||
|| data->state.prefer_ascii
|
||||
#endif
|
||||
) {
|
||||
result = cr_lc_add(data);
|
||||
if(result)
|
||||
return result;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
CURLcode Client_reader_set_fread(struct Curl_easy *data, curl_off_t len)
|
||||
{
|
||||
CURLcode result;
|
||||
struct Curl_creader *r;
|
||||
|
||||
cl_reset_reader(data);
|
||||
result = do_init_reader_stack(data, &cr_in, &r);
|
||||
if(!result && r) {
|
||||
struct cr_in_ctx *ctx = (struct cr_in_ctx *)r;
|
||||
DEBUGASSERT(r->crt == &cr_in);
|
||||
ctx->total_len = len;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
CURLcode Curl_creader_add(struct Curl_easy *data,
|
||||
struct Curl_creader *reader)
|
||||
{
|
||||
CURLcode result;
|
||||
struct Curl_creader **anchor = &data->req.reader_stack;
|
||||
|
||||
if(!*anchor) {
|
||||
result = Client_reader_set_fread(data, data->state.infilesize);
|
||||
if(result)
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Insert the writer as first in its phase.
|
||||
* Skip existing readers of lower phases. */
|
||||
while(*anchor && (*anchor)->phase < reader->phase)
|
||||
anchor = &((*anchor)->next);
|
||||
reader->next = *anchor;
|
||||
*anchor = reader;
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
CURLcode Curl_client_read(struct Curl_easy *data, char *buf, size_t blen,
|
||||
size_t *nread, bool *eos)
|
||||
{
|
||||
CURLcode result;
|
||||
|
||||
DEBUGASSERT(buf);
|
||||
DEBUGASSERT(blen);
|
||||
DEBUGASSERT(nread);
|
||||
DEBUGASSERT(eos);
|
||||
|
||||
if(!data->req.reader_stack) {
|
||||
result = Client_reader_set_fread(data, data->state.infilesize);
|
||||
if(result)
|
||||
return result;
|
||||
DEBUGASSERT(data->req.reader_stack);
|
||||
}
|
||||
|
||||
result = Curl_creader_read(data, data->req.reader_stack, buf, blen,
|
||||
nread, eos);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Curl_client_read_needs_rewind(struct Curl_easy *data)
|
||||
{
|
||||
struct Curl_creader *reader = data->req.reader_stack;
|
||||
while(reader) {
|
||||
if(reader->crt->needs_rewind(data, reader))
|
||||
return TRUE;
|
||||
reader = reader->next;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static CURLcode cr_null_read(struct Curl_easy *data,
|
||||
struct Curl_creader *reader,
|
||||
char *buf, size_t blen,
|
||||
size_t *pnread, bool *peos)
|
||||
{
|
||||
(void)data;
|
||||
(void)reader;
|
||||
(void)buf;
|
||||
(void)blen;
|
||||
*pnread = 0;
|
||||
*peos = TRUE;
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
static const struct Curl_crtype cr_null = {
|
||||
"cr-null",
|
||||
Curl_creader_def_init,
|
||||
cr_null_read,
|
||||
Curl_creader_def_close,
|
||||
Curl_creader_def_needs_rewind,
|
||||
sizeof(struct Curl_creader)
|
||||
};
|
||||
|
||||
CURLcode Client_reader_set_null(struct Curl_easy *data)
|
||||
{
|
||||
struct Curl_creader *r;
|
||||
|
||||
cl_reset_reader(data);
|
||||
return do_init_reader_stack(data, &cr_null, &r);
|
||||
}
|
||||
|
||||
struct cr_buf_ctx {
|
||||
struct Curl_creader super;
|
||||
const char *buf;
|
||||
size_t blen;
|
||||
size_t index;
|
||||
};
|
||||
|
||||
static CURLcode cr_buf_read(struct Curl_easy *data,
|
||||
struct Curl_creader *reader,
|
||||
char *buf, size_t blen,
|
||||
size_t *pnread, bool *peos)
|
||||
{
|
||||
struct cr_buf_ctx *ctx = (struct cr_buf_ctx *)reader;
|
||||
size_t nread = ctx->blen - ctx->index;
|
||||
|
||||
(void)data;
|
||||
if(!nread || !ctx->buf) {
|
||||
*pnread = 0;
|
||||
*peos = TRUE;
|
||||
}
|
||||
else {
|
||||
if(nread > blen)
|
||||
nread = blen;
|
||||
memcpy(buf, ctx->buf + ctx->index, nread);
|
||||
*pnread = nread;
|
||||
ctx->index += nread;
|
||||
*peos = (ctx->index == ctx->blen);
|
||||
}
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
static bool cr_buf_needs_rewind(struct Curl_easy *data,
|
||||
struct Curl_creader *reader)
|
||||
{
|
||||
struct cr_buf_ctx *ctx = (struct cr_buf_ctx *)reader;
|
||||
(void)data;
|
||||
return ctx->index > 0;
|
||||
}
|
||||
|
||||
static const struct Curl_crtype cr_buf = {
|
||||
"cr-buf",
|
||||
Curl_creader_def_init,
|
||||
cr_buf_read,
|
||||
Curl_creader_def_close,
|
||||
cr_buf_needs_rewind,
|
||||
sizeof(struct cr_buf_ctx)
|
||||
};
|
||||
|
||||
CURLcode Client_reader_set_buf(struct Curl_easy *data,
|
||||
const char *buf, size_t blen)
|
||||
{
|
||||
CURLcode result;
|
||||
struct Curl_creader *r;
|
||||
|
||||
cl_reset_reader(data);
|
||||
result = do_init_reader_stack(data, &cr_buf, &r);
|
||||
if(!result && r) {
|
||||
struct cr_buf_ctx *ctx = (struct cr_buf_ctx *)r;
|
||||
DEBUGASSERT(r->crt == &cr_buf);
|
||||
ctx->buf = buf;
|
||||
ctx->blen = blen;
|
||||
ctx->index = 0;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
|
|||
105
lib/sendf.h
105
lib/sendf.h
|
|
@ -61,7 +61,7 @@ CURLcode Curl_client_write(struct Curl_easy *data, int type, const char *ptr,
|
|||
/**
|
||||
* Free all resources related to client writing.
|
||||
*/
|
||||
void Curl_cw_reset(struct Curl_easy *data);
|
||||
void Curl_client_reset(struct Curl_easy *data);
|
||||
|
||||
/**
|
||||
* Client Writers - a chain passing transfer BODY data to the client.
|
||||
|
|
@ -175,4 +175,107 @@ void Curl_cwriter_def_close(struct Curl_easy *data,
|
|||
struct Curl_cwriter *writer);
|
||||
|
||||
|
||||
/* Client Reader Type, provides the implementation */
|
||||
struct Curl_crtype {
|
||||
const char *name; /* writer name. */
|
||||
CURLcode (*do_init)(struct Curl_easy *data, struct Curl_creader *writer);
|
||||
CURLcode (*do_read)(struct Curl_easy *data, struct Curl_creader *reader,
|
||||
char *buf, size_t blen, size_t *nread, bool *eos);
|
||||
void (*do_close)(struct Curl_easy *data, struct Curl_creader *reader);
|
||||
bool (*needs_rewind)(struct Curl_easy *data, struct Curl_creader *reader);
|
||||
size_t creader_size; /* sizeof() allocated struct Curl_creader */
|
||||
};
|
||||
|
||||
/* Phase a reader operates at. */
|
||||
typedef enum {
|
||||
CURL_CR_NET, /* data send to the network (connection filters) */
|
||||
CURL_CR_TRANSFER_ENCODE, /* add transfer-encodings */
|
||||
CURL_CR_PROTOCOL, /* before transfer, but after content decoding */
|
||||
CURL_CR_CONTENT_ENCODE, /* add content-encodings */
|
||||
CURL_CR_CLIENT /* data read from client */
|
||||
} Curl_creader_phase;
|
||||
|
||||
/* Client reader instance */
|
||||
struct Curl_creader {
|
||||
const struct Curl_crtype *crt; /* type implementation */
|
||||
struct Curl_creader *next; /* Downstream reader. */
|
||||
Curl_creader_phase phase; /* phase at which it operates */
|
||||
};
|
||||
|
||||
/**
|
||||
* Default implementations for do_init, do_write, do_close that
|
||||
* do nothing and pass the data through.
|
||||
*/
|
||||
CURLcode Curl_creader_def_init(struct Curl_easy *data,
|
||||
struct Curl_creader *reader);
|
||||
void Curl_creader_def_close(struct Curl_easy *data,
|
||||
struct Curl_creader *reader);
|
||||
bool Curl_creader_def_needs_rewind(struct Curl_easy *data,
|
||||
struct Curl_creader *reader);
|
||||
|
||||
/**
|
||||
* Convenience method for calling `reader->do_read()` that
|
||||
* checks for NULL reader.
|
||||
*/
|
||||
CURLcode Curl_creader_read(struct Curl_easy *data,
|
||||
struct Curl_creader *reader,
|
||||
char *buf, size_t blen, size_t *nread, bool *eos);
|
||||
|
||||
/**
|
||||
* Create a new creader instance with given type and phase. Is not
|
||||
* inserted into the writer chain by this call.
|
||||
* Invokes `reader->do_init()`.
|
||||
*/
|
||||
CURLcode Curl_creader_create(struct Curl_creader **preader,
|
||||
struct Curl_easy *data,
|
||||
const struct Curl_crtype *cr_handler,
|
||||
Curl_creader_phase phase);
|
||||
|
||||
/**
|
||||
* Free a creader instance.
|
||||
* Invokes `reader->do_close()`.
|
||||
*/
|
||||
void Curl_creader_free(struct Curl_easy *data, struct Curl_creader *reader);
|
||||
|
||||
/**
|
||||
* Adds a reader to the transfer's reader chain.
|
||||
* The readers `phase` determines where in the chain it is inserted.
|
||||
*/
|
||||
CURLcode Curl_creader_add(struct Curl_easy *data,
|
||||
struct Curl_creader *reader);
|
||||
|
||||
/**
|
||||
* Read at most `blen` bytes at `buf` from the client.
|
||||
* @param date the transfer to read client bytes for
|
||||
* @param buf the memory location to read to
|
||||
* @param blen the amount of memory at `buf`
|
||||
* @param nread on return the number of bytes read into `buf`
|
||||
* @param eos TRUE iff bytes are the end of data from client
|
||||
* @return CURLE_OK on successful read (even 0 length) or error
|
||||
*/
|
||||
CURLcode Curl_client_read(struct Curl_easy *data, char *buf, size_t blen,
|
||||
size_t *nread, bool *eos) WARN_UNUSED_RESULT;
|
||||
|
||||
/**
|
||||
* TRUE iff client reader needs rewing before it can be used for
|
||||
* a retry request.
|
||||
*/
|
||||
bool Curl_client_read_needs_rewind(struct Curl_easy *data);
|
||||
|
||||
/**
|
||||
* Set the client reader to provide 0 bytes, immediate EOS.
|
||||
*/
|
||||
CURLcode Client_reader_set_null(struct Curl_easy *data);
|
||||
|
||||
/**
|
||||
* Set the client reader the reads from fread callback.
|
||||
*/
|
||||
CURLcode Client_reader_set_fread(struct Curl_easy *data, curl_off_t len);
|
||||
|
||||
/**
|
||||
* Set the client reader the reads from the supplied buf (NOT COPIED).
|
||||
*/
|
||||
CURLcode Client_reader_set_buf(struct Curl_easy *data,
|
||||
const char *buf, size_t blen);
|
||||
|
||||
#endif /* HEADER_CURL_SENDF_H */
|
||||
|
|
|
|||
|
|
@ -811,8 +811,9 @@ static CURLcode smb_send_and_recv(struct Curl_easy *data, void **msg)
|
|||
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;
|
||||
data->req.upload_fromhere = data->state.ulbuf;
|
||||
result = Curl_fillreadbuffer(data, nread, &nread);
|
||||
bool eos;
|
||||
|
||||
result = Curl_client_read(data, data->state.ulbuf, nread, &nread, &eos);
|
||||
if(result && result != CURLE_AGAIN)
|
||||
return result;
|
||||
if(!nread)
|
||||
|
|
|
|||
|
|
@ -708,6 +708,8 @@ static CURLcode tftp_tx(struct tftp_state_data *state, tftp_event_t event)
|
|||
struct SingleRequest *k = &data->req;
|
||||
size_t cb; /* Bytes currently read */
|
||||
char buffer[STRERROR_LEN];
|
||||
char *bufptr;
|
||||
bool eos;
|
||||
|
||||
switch(event) {
|
||||
|
||||
|
|
@ -771,13 +773,14 @@ static CURLcode tftp_tx(struct tftp_state_data *state, tftp_event_t event)
|
|||
* data block.
|
||||
* */
|
||||
state->sbytes = 0;
|
||||
state->data->req.upload_fromhere = (char *)state->spacket.data + 4;
|
||||
bufptr = (char *)state->spacket.data + 4;
|
||||
do {
|
||||
result = Curl_fillreadbuffer(data, state->blksize - state->sbytes, &cb);
|
||||
result = Curl_client_read(data, bufptr, state->blksize - state->sbytes,
|
||||
&cb, &eos);
|
||||
if(result)
|
||||
return result;
|
||||
state->sbytes += (int)cb;
|
||||
state->data->req.upload_fromhere += cb;
|
||||
bufptr += cb;
|
||||
} while(state->sbytes < state->blksize && cb);
|
||||
|
||||
sbytes = sendto(state->sockfd, (void *) state->spacket.data,
|
||||
|
|
|
|||
343
lib/transfer.c
343
lib/transfer.c
|
|
@ -125,250 +125,6 @@ CURLcode Curl_get_upload_buffer(struct Curl_easy *data)
|
|||
return CURLE_OK;
|
||||
}
|
||||
|
||||
#ifndef CURL_DISABLE_HTTP
|
||||
/*
|
||||
* This function will be called to loop through the trailers buffer
|
||||
* until no more data is available for sending.
|
||||
*/
|
||||
static size_t trailers_read(char *buffer, size_t size, size_t nitems,
|
||||
void *raw)
|
||||
{
|
||||
struct Curl_easy *data = (struct Curl_easy *)raw;
|
||||
struct dynbuf *trailers_buf = &data->state.trailers_buf;
|
||||
size_t bytes_left = Curl_dyn_len(trailers_buf) -
|
||||
data->state.trailers_bytes_sent;
|
||||
size_t to_copy = (size*nitems < bytes_left) ? size*nitems : bytes_left;
|
||||
if(to_copy) {
|
||||
memcpy(buffer,
|
||||
Curl_dyn_ptr(trailers_buf) + data->state.trailers_bytes_sent,
|
||||
to_copy);
|
||||
data->state.trailers_bytes_sent += to_copy;
|
||||
}
|
||||
return to_copy;
|
||||
}
|
||||
|
||||
static size_t trailers_left(void *raw)
|
||||
{
|
||||
struct Curl_easy *data = (struct Curl_easy *)raw;
|
||||
struct dynbuf *trailers_buf = &data->state.trailers_buf;
|
||||
return Curl_dyn_len(trailers_buf) - data->state.trailers_bytes_sent;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This function will call the read callback to fill our buffer with data
|
||||
* to upload.
|
||||
*/
|
||||
CURLcode Curl_fillreadbuffer(struct Curl_easy *data, size_t bytes,
|
||||
size_t *nreadp)
|
||||
{
|
||||
size_t buffersize = bytes;
|
||||
size_t nread;
|
||||
curl_read_callback readfunc = NULL;
|
||||
void *extra_data = NULL;
|
||||
int eof_index = 0;
|
||||
|
||||
#ifndef CURL_DISABLE_HTTP
|
||||
if(data->state.trailers_state == TRAILERS_INITIALIZED) {
|
||||
struct curl_slist *trailers = NULL;
|
||||
CURLcode result;
|
||||
int trailers_ret_code;
|
||||
|
||||
/* at this point we already verified that the callback exists
|
||||
so we compile and store the trailers buffer, then proceed */
|
||||
infof(data,
|
||||
"Moving trailers state machine from initialized to sending.");
|
||||
data->state.trailers_state = TRAILERS_SENDING;
|
||||
Curl_dyn_init(&data->state.trailers_buf, DYN_TRAILERS);
|
||||
|
||||
data->state.trailers_bytes_sent = 0;
|
||||
Curl_set_in_callback(data, true);
|
||||
trailers_ret_code = data->set.trailer_callback(&trailers,
|
||||
data->set.trailer_data);
|
||||
Curl_set_in_callback(data, false);
|
||||
if(trailers_ret_code == CURL_TRAILERFUNC_OK) {
|
||||
result = Curl_http_compile_trailers(trailers, &data->state.trailers_buf,
|
||||
data);
|
||||
}
|
||||
else {
|
||||
failf(data, "operation aborted by trailing headers callback");
|
||||
*nreadp = 0;
|
||||
result = CURLE_ABORTED_BY_CALLBACK;
|
||||
}
|
||||
if(result) {
|
||||
Curl_dyn_free(&data->state.trailers_buf);
|
||||
curl_slist_free_all(trailers);
|
||||
return result;
|
||||
}
|
||||
infof(data, "Successfully compiled trailers.");
|
||||
curl_slist_free_all(trailers);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef CURL_DISABLE_HTTP
|
||||
/* if we are transmitting trailing data, we don't need to write
|
||||
a chunk size so we skip this */
|
||||
if(data->req.upload_chunky &&
|
||||
data->state.trailers_state == TRAILERS_NONE) {
|
||||
/* if chunked Transfer-Encoding */
|
||||
buffersize -= (8 + 2 + 2); /* 32bit hex + CRLF + CRLF */
|
||||
data->req.upload_fromhere += (8 + 2); /* 32bit hex + CRLF */
|
||||
}
|
||||
|
||||
if(data->state.trailers_state == TRAILERS_SENDING) {
|
||||
/* if we're here then that means that we already sent the last empty chunk
|
||||
but we didn't send a final CR LF, so we sent 0 CR LF. We then start
|
||||
pulling trailing data until we have no more at which point we
|
||||
simply return to the previous point in the state machine as if
|
||||
nothing happened.
|
||||
*/
|
||||
readfunc = trailers_read;
|
||||
extra_data = (void *)data;
|
||||
eof_index = 1;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
readfunc = data->state.fread_func;
|
||||
extra_data = data->state.in;
|
||||
}
|
||||
|
||||
if(!data->req.fread_eof[eof_index]) {
|
||||
Curl_set_in_callback(data, true);
|
||||
nread = readfunc(data->req.upload_fromhere, 1, buffersize, extra_data);
|
||||
Curl_set_in_callback(data, false);
|
||||
/* make sure the callback is not called again after EOF */
|
||||
data->req.fread_eof[eof_index] = !nread;
|
||||
}
|
||||
else
|
||||
nread = 0;
|
||||
|
||||
if(nread == CURL_READFUNC_ABORT) {
|
||||
failf(data, "operation aborted by callback");
|
||||
*nreadp = 0;
|
||||
return CURLE_ABORTED_BY_CALLBACK;
|
||||
}
|
||||
if(nread == CURL_READFUNC_PAUSE) {
|
||||
struct SingleRequest *k = &data->req;
|
||||
|
||||
if(data->conn->handler->flags & PROTOPT_NONETWORK) {
|
||||
/* protocols that work without network cannot be paused. This is
|
||||
actually only FILE:// just now, and it can't pause since the transfer
|
||||
isn't done using the "normal" procedure. */
|
||||
failf(data, "Read callback asked for PAUSE when not supported");
|
||||
return CURLE_READ_ERROR;
|
||||
}
|
||||
|
||||
/* CURL_READFUNC_PAUSE pauses read callbacks that feed socket writes */
|
||||
k->keepon |= KEEP_SEND_PAUSE; /* mark socket send as paused */
|
||||
if(data->req.upload_chunky) {
|
||||
/* Back out the preallocation done above */
|
||||
data->req.upload_fromhere -= (8 + 2);
|
||||
}
|
||||
*nreadp = 0;
|
||||
|
||||
return CURLE_OK; /* nothing was read */
|
||||
}
|
||||
else if(nread > buffersize) {
|
||||
/* the read function returned a too large value */
|
||||
*nreadp = 0;
|
||||
failf(data, "read function returned funny value");
|
||||
return CURLE_READ_ERROR;
|
||||
}
|
||||
|
||||
#ifndef CURL_DISABLE_HTTP
|
||||
if(!data->req.forbidchunk && data->req.upload_chunky) {
|
||||
/* if chunked Transfer-Encoding
|
||||
* build chunk:
|
||||
*
|
||||
* <HEX SIZE> CRLF
|
||||
* <DATA> CRLF
|
||||
*/
|
||||
/* On non-ASCII platforms the <DATA> may or may not be
|
||||
translated based on state.prefer_ascii while the protocol
|
||||
portion must always be translated to the network encoding.
|
||||
To further complicate matters, line end conversion might be
|
||||
done later on, so we need to prevent CRLFs from becoming
|
||||
CRCRLFs if that's the case. To do this we use bare LFs
|
||||
here, knowing they'll become CRLFs later on.
|
||||
*/
|
||||
|
||||
bool added_crlf = FALSE;
|
||||
int hexlen = 0;
|
||||
const char *endofline_native;
|
||||
const char *endofline_network;
|
||||
|
||||
if(
|
||||
#ifdef CURL_DO_LINEEND_CONV
|
||||
(data->state.prefer_ascii) ||
|
||||
#endif
|
||||
(data->set.crlf)) {
|
||||
/* \n will become \r\n later on */
|
||||
endofline_native = "\n";
|
||||
endofline_network = "\x0a";
|
||||
}
|
||||
else {
|
||||
endofline_native = "\r\n";
|
||||
endofline_network = "\x0d\x0a";
|
||||
}
|
||||
|
||||
/* if we're not handling trailing data, proceed as usual */
|
||||
if(data->state.trailers_state != TRAILERS_SENDING) {
|
||||
char hexbuffer[11] = "";
|
||||
hexlen = msnprintf(hexbuffer, sizeof(hexbuffer),
|
||||
"%zx%s", nread, endofline_native);
|
||||
|
||||
/* move buffer pointer */
|
||||
data->req.upload_fromhere -= hexlen;
|
||||
nread += hexlen;
|
||||
|
||||
/* copy the prefix to the buffer, leaving out the NUL */
|
||||
memcpy(data->req.upload_fromhere, hexbuffer, hexlen);
|
||||
|
||||
/* always append ASCII CRLF to the data unless
|
||||
we have a valid trailer callback */
|
||||
if((nread-hexlen) == 0 &&
|
||||
data->set.trailer_callback != NULL &&
|
||||
data->state.trailers_state == TRAILERS_NONE) {
|
||||
data->state.trailers_state = TRAILERS_INITIALIZED;
|
||||
}
|
||||
else {
|
||||
memcpy(data->req.upload_fromhere + nread,
|
||||
endofline_network,
|
||||
strlen(endofline_network));
|
||||
added_crlf = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if(data->state.trailers_state == TRAILERS_SENDING &&
|
||||
!trailers_left(data)) {
|
||||
Curl_dyn_free(&data->state.trailers_buf);
|
||||
data->state.trailers_state = TRAILERS_DONE;
|
||||
data->set.trailer_data = NULL;
|
||||
data->set.trailer_callback = NULL;
|
||||
/* mark the transfer as done */
|
||||
data->req.upload_done = TRUE;
|
||||
infof(data, "Signaling end of chunked upload after trailers.");
|
||||
}
|
||||
else
|
||||
if((nread - hexlen) == 0 &&
|
||||
data->state.trailers_state != TRAILERS_INITIALIZED) {
|
||||
/* mark this as done once this chunk is transferred */
|
||||
data->req.upload_done = TRUE;
|
||||
infof(data,
|
||||
"Signaling end of chunked upload via terminating chunk.");
|
||||
}
|
||||
|
||||
if(added_crlf)
|
||||
nread += strlen(endofline_network); /* for the added end of line */
|
||||
}
|
||||
#endif
|
||||
|
||||
*nreadp = nread;
|
||||
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
static int data_pending(struct Curl_easy *data)
|
||||
{
|
||||
struct connectdata *conn = data->conn;
|
||||
|
|
@ -616,13 +372,12 @@ static CURLcode readwrite_upload(struct Curl_easy *data,
|
|||
struct connectdata *conn,
|
||||
int *didwhat)
|
||||
{
|
||||
ssize_t i, si;
|
||||
size_t bytes_written;
|
||||
CURLcode result;
|
||||
ssize_t nread; /* number of bytes read */
|
||||
bool sending_http_headers = FALSE;
|
||||
struct SingleRequest *k = &data->req;
|
||||
|
||||
(void)conn;
|
||||
*didwhat |= KEEP_SEND;
|
||||
|
||||
if(!(k->keepon & KEEP_SEND_PAUSE)) {
|
||||
|
|
@ -636,13 +391,14 @@ static CURLcode readwrite_upload(struct Curl_easy *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 -
|
||||
(curl_off_t)k->pendingheader == data->state.infilesize)) {
|
||||
!(k->writebytecount + (curl_off_t)k->upload_present ==
|
||||
data->state.infilesize)) {
|
||||
offset = k->upload_present;
|
||||
}
|
||||
|
||||
|
|
@ -661,10 +417,8 @@ static CURLcode readwrite_upload(struct Curl_easy *data,
|
|||
/* HTTP pollution, this should be written nicer to become more
|
||||
protocol agnostic. */
|
||||
size_t fillcount;
|
||||
struct HTTP *http = k->p.http;
|
||||
|
||||
if((k->exp100 == EXP100_SENDING_REQUEST) &&
|
||||
(http->sending == HTTPSEND_BODY)) {
|
||||
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 */
|
||||
|
|
@ -677,18 +431,10 @@ static CURLcode readwrite_upload(struct Curl_easy *data,
|
|||
break;
|
||||
}
|
||||
|
||||
if(conn->handler->protocol&(PROTO_FAMILY_HTTP|CURLPROTO_RTSP)) {
|
||||
if(http->sending == HTTPSEND_REQUEST)
|
||||
/* We're sending the HTTP request headers, not the data.
|
||||
Remember that so we don't change the line endings. */
|
||||
sending_http_headers = TRUE;
|
||||
else
|
||||
sending_http_headers = FALSE;
|
||||
}
|
||||
|
||||
k->upload_fromhere += offset;
|
||||
result = Curl_fillreadbuffer(data, data->set.upload_buffer_size-offset,
|
||||
&fillcount);
|
||||
result = Curl_client_read(data, k->upload_fromhere,
|
||||
data->set.upload_buffer_size-offset,
|
||||
&fillcount, &eos);
|
||||
k->upload_fromhere -= offset;
|
||||
if(result)
|
||||
return result;
|
||||
|
|
@ -712,59 +458,6 @@ static CURLcode readwrite_upload(struct Curl_easy *data,
|
|||
/* store number of bytes available for upload */
|
||||
k->upload_present = nread;
|
||||
|
||||
/* convert LF to CRLF if so asked */
|
||||
if((!sending_http_headers) && (
|
||||
#ifdef CURL_DO_LINEEND_CONV
|
||||
/* always convert if we're FTPing in ASCII mode */
|
||||
(data->state.prefer_ascii) ||
|
||||
#endif
|
||||
(data->set.crlf))) {
|
||||
/* Do we need to allocate a scratch buffer? */
|
||||
if(!data->state.scratch) {
|
||||
data->state.scratch = malloc(2 * data->set.upload_buffer_size);
|
||||
if(!data->state.scratch) {
|
||||
failf(data, "Failed to alloc scratch buffer");
|
||||
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* ASCII/EBCDIC Note: This is presumably a text (not binary)
|
||||
* transfer so the data should already be in ASCII.
|
||||
* That means the hex values for ASCII CR (0x0d) & LF (0x0a)
|
||||
* must be used instead of the escape sequences \r & \n.
|
||||
*/
|
||||
if(offset)
|
||||
memcpy(data->state.scratch, k->upload_fromhere, offset);
|
||||
for(i = offset, si = offset; i < nread; i++, si++) {
|
||||
if(k->upload_fromhere[i] == 0x0a) {
|
||||
data->state.scratch[si++] = 0x0d;
|
||||
data->state.scratch[si] = 0x0a;
|
||||
if(!data->set.crlf) {
|
||||
/* we're here only because FTP is in ASCII mode...
|
||||
bump infilesize for the LF we just added */
|
||||
if(data->state.infilesize != -1)
|
||||
data->state.infilesize++;
|
||||
}
|
||||
}
|
||||
else
|
||||
data->state.scratch[si] = k->upload_fromhere[i];
|
||||
}
|
||||
|
||||
if(si != nread) {
|
||||
/* only perform the special operation if we really did replace
|
||||
anything */
|
||||
nread = si;
|
||||
|
||||
/* upload from the new (replaced) buffer instead */
|
||||
k->upload_fromhere = data->state.scratch;
|
||||
|
||||
/* set the new amount too */
|
||||
k->upload_present = nread;
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef CURL_DISABLE_SMTP
|
||||
if(conn->handler->protocol & PROTO_FAMILY_SMTP) {
|
||||
result = Curl_smtp_escape_eob(data, nread, offset);
|
||||
|
|
@ -796,17 +489,7 @@ static CURLcode readwrite_upload(struct Curl_easy *data,
|
|||
}
|
||||
#endif
|
||||
|
||||
if(k->pendingheader) {
|
||||
/* parts of what was sent was header */
|
||||
size_t n = CURLMIN(k->pendingheader, bytes_written);
|
||||
/* show the data before we change the pointer upload_fromhere */
|
||||
Curl_debug(data, CURLINFO_HEADER_OUT, k->upload_fromhere, n);
|
||||
k->pendingheader -= n;
|
||||
nbody = bytes_written - n; /* size of the written body part */
|
||||
}
|
||||
else
|
||||
nbody = bytes_written;
|
||||
|
||||
nbody = bytes_written;
|
||||
if(nbody) {
|
||||
/* show the data before we change the pointer upload_fromhere */
|
||||
Curl_debug(data, CURLINFO_DATA_OUT,
|
||||
|
|
@ -1014,7 +697,6 @@ CURLcode Curl_readwrite(struct Curl_easy *data,
|
|||
* The transfer has been performed. Just make some general checks before
|
||||
* returning.
|
||||
*/
|
||||
|
||||
if(!(data->req.no_body) && (k->size != -1) &&
|
||||
(k->bytecount != k->size) &&
|
||||
#ifdef CURL_DO_LINEEND_CONV
|
||||
|
|
@ -1587,8 +1269,7 @@ CURLcode Curl_retry_request(struct Curl_easy *data, char **url)
|
|||
transferred! */
|
||||
|
||||
|
||||
if((conn->handler->protocol&PROTO_FAMILY_HTTP) &&
|
||||
data->req.writebytecount) {
|
||||
if(Curl_client_read_needs_rewind(data)) {
|
||||
data->state.rewindbeforesend = TRUE;
|
||||
infof(data, "state.rewindbeforesend = TRUE");
|
||||
}
|
||||
|
|
@ -1611,7 +1292,6 @@ void Curl_xfer_setup(
|
|||
{
|
||||
struct SingleRequest *k = &data->req;
|
||||
struct connectdata *conn = data->conn;
|
||||
struct HTTP *http = data->req.p.http;
|
||||
bool want_send = Curl_req_want_send(data);
|
||||
|
||||
DEBUGASSERT(conn != NULL);
|
||||
|
|
@ -1664,8 +1344,7 @@ void Curl_xfer_setup(
|
|||
state info where we wait for the 100-return code
|
||||
*/
|
||||
if((data->state.expect100header) &&
|
||||
(conn->handler->protocol&PROTO_FAMILY_HTTP) &&
|
||||
(http->sending == HTTPSEND_BODY)) {
|
||||
(conn->handler->protocol&PROTO_FAMILY_HTTP)) {
|
||||
/* wait with write until we either got 100-continue or a timeout */
|
||||
k->exp100 = EXP100_AWAITING_CONTINUE;
|
||||
k->start100 = Curl_now();
|
||||
|
|
|
|||
|
|
@ -48,8 +48,6 @@ CURLcode Curl_follow(struct Curl_easy *data, char *newurl,
|
|||
CURLcode Curl_readwrite(struct Curl_easy *data, bool *done);
|
||||
int Curl_single_getsock(struct Curl_easy *data,
|
||||
struct connectdata *conn, curl_socket_t *socks);
|
||||
CURLcode Curl_fillreadbuffer(struct Curl_easy *data, size_t bytes,
|
||||
size_t *nreadp);
|
||||
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);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue