2002-09-03 19:52:59 +08:00
|
|
|
/***************************************************************************
|
2004-06-03 19:41:05 +08:00
|
|
|
* _ _ ____ _
|
|
|
|
* Project ___| | | | _ \| |
|
|
|
|
* / __| | | | |_) | |
|
|
|
|
* | (__| |_| | _ <| |___
|
1999-12-29 22:20:26 +08:00
|
|
|
* \___|\___/|_| \_\_____|
|
|
|
|
*
|
2023-01-02 20:51:48 +08:00
|
|
|
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
|
1999-12-29 22:20:26 +08:00
|
|
|
*
|
2002-09-03 19:52:59 +08:00
|
|
|
* This software is licensed as described in the file COPYING, which
|
|
|
|
* you should have received as part of this distribution. The terms
|
2020-11-04 21:02:01 +08:00
|
|
|
* are also available at https://curl.se/docs/copyright.html.
|
2004-06-03 19:41:05 +08:00
|
|
|
*
|
2001-01-03 17:29:33 +08:00
|
|
|
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
|
|
|
* copies of the Software, and permit persons to whom the Software is
|
2002-09-03 19:52:59 +08:00
|
|
|
* furnished to do so, under the terms of the COPYING file.
|
1999-12-29 22:20:26 +08:00
|
|
|
*
|
2001-01-03 17:29:33 +08:00
|
|
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
|
|
|
* KIND, either express or implied.
|
1999-12-29 22:20:26 +08:00
|
|
|
*
|
2006-11-12 05:34:43 +08:00
|
|
|
* SPDX-License-Identifier: curl
|
2022-05-17 17:16:50 +08:00
|
|
|
*
|
2002-09-03 19:52:59 +08:00
|
|
|
***************************************************************************/
|
1999-12-29 22:20:26 +08:00
|
|
|
|
2013-01-07 02:06:49 +08:00
|
|
|
#include "curl_setup.h"
|
2000-08-24 22:26:33 +08:00
|
|
|
|
2017-12-06 16:11:05 +08:00
|
|
|
#ifdef HAVE_NETINET_IN_H
|
|
|
|
#include <netinet/in.h>
|
|
|
|
#endif
|
|
|
|
|
2017-11-06 07:59:55 +08:00
|
|
|
#ifdef HAVE_LINUX_TCP_H
|
|
|
|
#include <linux/tcp.h>
|
2020-11-29 01:17:11 +08:00
|
|
|
#elif defined(HAVE_NETINET_TCP_H)
|
|
|
|
#include <netinet/tcp.h>
|
2017-11-06 07:59:55 +08:00
|
|
|
#endif
|
|
|
|
|
1999-12-29 22:20:26 +08:00
|
|
|
#include <curl/curl.h>
|
2012-12-15 00:38:18 +08:00
|
|
|
|
2013-01-04 09:50:28 +08:00
|
|
|
#include "urldata.h"
|
|
|
|
#include "sendf.h"
|
2022-11-11 18:45:34 +08:00
|
|
|
#include "cfilters.h"
|
2013-01-04 09:50:28 +08:00
|
|
|
#include "connect.h"
|
2023-09-20 17:59:16 +08:00
|
|
|
#include "content_encoding.h"
|
2024-02-07 19:05:05 +08:00
|
|
|
#include "cw-out.h"
|
2013-12-18 06:32:47 +08:00
|
|
|
#include "vtls/vtls.h"
|
2019-11-18 04:04:37 +08:00
|
|
|
#include "vssh/ssh.h"
|
2018-02-10 22:13:15 +08:00
|
|
|
#include "easyif.h"
|
2013-01-04 09:50:28 +08:00
|
|
|
#include "multiif.h"
|
|
|
|
#include "strerror.h"
|
2016-02-20 03:38:20 +08:00
|
|
|
#include "select.h"
|
2017-03-27 18:14:57 +08:00
|
|
|
#include "strdup.h"
|
2020-02-27 16:42:11 +08:00
|
|
|
#include "http2.h"
|
2023-10-23 16:33:07 +08:00
|
|
|
#include "progress.h"
|
2024-02-29 17:12:39 +08:00
|
|
|
#include "warnless.h"
|
2022-09-09 21:11:14 +08:00
|
|
|
#include "ws.h"
|
2011-04-20 06:48:20 +08:00
|
|
|
|
2016-04-29 21:46:40 +08:00
|
|
|
/* The last 3 #include files should be in this order */
|
|
|
|
#include "curl_printf.h"
|
2015-03-25 06:12:03 +08:00
|
|
|
#include "curl_memory.h"
|
2013-01-04 09:50:28 +08:00
|
|
|
#include "memdebug.h"
|
2000-09-21 16:49:16 +08:00
|
|
|
|
2023-10-23 16:33:07 +08:00
|
|
|
|
2024-02-15 23:22:53 +08:00
|
|
|
static CURLcode do_init_writer_stack(struct Curl_easy *data);
|
2023-10-23 16:33:07 +08:00
|
|
|
|
2014-12-09 22:43:51 +08:00
|
|
|
/* Curl_client_write() sends data to the write callback(s)
|
|
|
|
|
|
|
|
The bit pattern defines to what "streams" to write to. Body and/or header.
|
2021-10-25 19:54:57 +08:00
|
|
|
The defines are in sendf.h of course.
|
2014-12-09 22:43:51 +08:00
|
|
|
*/
|
2021-01-09 00:58:15 +08:00
|
|
|
CURLcode Curl_client_write(struct Curl_easy *data,
|
2024-02-06 19:10:19 +08:00
|
|
|
int type, const char *buf, size_t blen)
|
2014-12-09 22:43:51 +08:00
|
|
|
{
|
2023-10-23 16:33:07 +08:00
|
|
|
CURLcode result;
|
|
|
|
|
2023-09-19 18:31:31 +08:00
|
|
|
/* it is one of those, at least */
|
|
|
|
DEBUGASSERT(type & (CLIENTWRITE_BODY|CLIENTWRITE_HEADER|CLIENTWRITE_INFO));
|
lib: replace readwrite with write_resp
This clarifies the handling of server responses by folding the code for
the complicated protocols into their protocol handlers. This concerns
mainly HTTP and its bastard sibling RTSP.
The terms "read" and "write" are often used without clear context if
they refer to the connect or the client/application side of a
transfer. This PR uses "read/write" for operations on the client side
and "send/receive" for the connection, e.g. server side. If this is
considered useful, we can revisit renaming of further methods in another
PR.
Curl's protocol handler `readwrite()` method been changed:
```diff
- CURLcode (*readwrite)(struct Curl_easy *data, struct connectdata *conn,
- const char *buf, size_t blen,
- size_t *pconsumed, bool *readmore);
+ CURLcode (*write_resp)(struct Curl_easy *data, const char *buf, size_t blen,
+ bool is_eos, bool *done);
```
The name was changed to clarify that this writes reponse data to the
client side. The parameter changes are:
* `conn` removed as it always operates on `data->conn`
* `pconsumed` removed as the method needs to handle all data on success
* `readmore` removed as no longer necessary
* `is_eos` as indicator that this is the last call for the transfer
response (end-of-stream).
* `done` TRUE on return iff the transfer response is to be treated as
finished
This change affects many files only because of updated comments in
handlers that provide no implementation. The real change is that the
HTTP protocol handlers now provide an implementation.
The HTTP protocol handlers `write_resp()` implementation will get passed
**all** raw data of a server response for the transfer. The HTTP/1.x
formatted status and headers, as well as the undecoded response
body. `Curl_http_write_resp_hds()` is used internally to parse the
response headers and pass them on. This method is public as the RTSP
protocol handler also uses it.
HTTP/1.1 "chunked" transport encoding is now part of the general
*content encoding* writer stack, just like other encodings. A new flag
`CLIENTWRITE_EOS` was added for the last client write. This allows
writers to verify that they are in a valid end state. The chunked
decoder will check if it indeed has seen the last chunk.
The general response handling in `transfer.c:466` happens in function
`readwrite_data()`. This mainly operates now like:
```
static CURLcode readwrite_data(data, ...)
{
do {
Curl_xfer_recv_resp(data, buf)
...
Curl_xfer_write_resp(data, buf)
...
} while(interested);
...
}
```
All the response data handling is implemented in
`Curl_xfer_write_resp()`. It calls the protocol handler's `write_resp()`
implementation if available, or does the default behaviour.
All raw response data needs to pass through this function. Which also
means that anyone in possession of such data may call
`Curl_xfer_write_resp()`.
Closes #12480
2023-12-01 20:50:32 +08:00
|
|
|
/* BODY is only BODY (with optional EOS) */
|
|
|
|
DEBUGASSERT(!(type & CLIENTWRITE_BODY) ||
|
|
|
|
((type & ~(CLIENTWRITE_BODY|CLIENTWRITE_EOS)) == 0));
|
|
|
|
/* INFO is only INFO (with optional EOS) */
|
|
|
|
DEBUGASSERT(!(type & CLIENTWRITE_INFO) ||
|
|
|
|
((type & ~(CLIENTWRITE_INFO|CLIENTWRITE_EOS)) == 0));
|
2023-09-20 17:59:16 +08:00
|
|
|
|
2023-10-23 16:33:07 +08:00
|
|
|
if(!data->req.writer_stack) {
|
2024-02-15 23:22:53 +08:00
|
|
|
result = do_init_writer_stack(data);
|
2023-10-23 16:33:07 +08:00
|
|
|
if(result)
|
|
|
|
return result;
|
|
|
|
DEBUGASSERT(data->req.writer_stack);
|
2023-09-20 17:59:16 +08:00
|
|
|
}
|
2023-10-23 16:33:07 +08:00
|
|
|
|
2024-03-28 21:12:54 +08:00
|
|
|
result = Curl_cwriter_write(data, data->req.writer_stack, type, buf, blen);
|
|
|
|
CURL_TRC_WRITE(data, "client_write(type=%x, len=%zu) -> %d",
|
|
|
|
type, blen, result);
|
|
|
|
return result;
|
2023-09-19 18:31:31 +08:00
|
|
|
}
|
|
|
|
|
2024-02-15 23:22:53 +08:00
|
|
|
static void cl_reset_writer(struct Curl_easy *data)
|
2023-09-20 17:59:16 +08:00
|
|
|
{
|
2023-10-23 16:33:07 +08:00
|
|
|
struct Curl_cwriter *writer = data->req.writer_stack;
|
2023-09-20 17:59:16 +08:00
|
|
|
while(writer) {
|
2023-10-23 16:33:07 +08:00
|
|
|
data->req.writer_stack = writer->next;
|
|
|
|
writer->cwt->do_close(data, writer);
|
2023-09-20 17:59:16 +08:00
|
|
|
free(writer);
|
|
|
|
writer = data->req.writer_stack;
|
|
|
|
}
|
2024-02-15 23:22:53 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-02-29 17:12:39 +08:00
|
|
|
void Curl_client_cleanup(struct Curl_easy *data)
|
2024-02-15 23:22:53 +08:00
|
|
|
{
|
|
|
|
cl_reset_reader(data);
|
|
|
|
cl_reset_writer(data);
|
2023-09-20 17:59:16 +08:00
|
|
|
|
2023-11-21 18:24:18 +08:00
|
|
|
data->req.bytecount = 0;
|
|
|
|
data->req.headerline = 0;
|
2023-09-20 17:59:16 +08:00
|
|
|
}
|
|
|
|
|
2024-02-29 17:12:39 +08:00
|
|
|
void Curl_client_reset(struct Curl_easy *data)
|
|
|
|
{
|
|
|
|
if(data->req.rewind_read) {
|
|
|
|
/* already requested */
|
2024-03-28 21:12:54 +08:00
|
|
|
CURL_TRC_READ(data, "client_reset, will rewind reader");
|
2024-02-29 17:12:39 +08:00
|
|
|
}
|
|
|
|
else {
|
2024-03-28 21:12:54 +08:00
|
|
|
CURL_TRC_READ(data, "client_reset, clear readers");
|
2024-02-29 17:12:39 +08:00
|
|
|
cl_reset_reader(data);
|
|
|
|
}
|
|
|
|
cl_reset_writer(data);
|
|
|
|
|
|
|
|
data->req.bytecount = 0;
|
|
|
|
data->req.headerline = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
CURLcode Curl_client_start(struct Curl_easy *data)
|
|
|
|
{
|
|
|
|
if(data->req.rewind_read) {
|
|
|
|
struct Curl_creader *r = data->req.reader_stack;
|
|
|
|
CURLcode result = CURLE_OK;
|
|
|
|
|
2024-03-28 21:12:54 +08:00
|
|
|
CURL_TRC_READ(data, "client start, rewind readers");
|
2024-02-29 17:12:39 +08:00
|
|
|
while(r) {
|
|
|
|
result = r->crt->rewind(data, r);
|
|
|
|
if(result) {
|
|
|
|
failf(data, "rewind of client reader '%s' failed: %d",
|
|
|
|
r->crt->name, result);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
r = r->next;
|
|
|
|
}
|
|
|
|
data->req.rewind_read = FALSE;
|
|
|
|
cl_reset_reader(data);
|
|
|
|
}
|
|
|
|
return CURLE_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Curl_creader_will_rewind(struct Curl_easy *data)
|
|
|
|
{
|
|
|
|
return data->req.rewind_read;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Curl_creader_set_rewind(struct Curl_easy *data, bool enable)
|
|
|
|
{
|
|
|
|
data->req.rewind_read = !!enable;
|
|
|
|
}
|
|
|
|
|
2024-04-25 19:53:39 +08:00
|
|
|
/* Write data using an unencoding writer stack. */
|
2023-10-23 16:33:07 +08:00
|
|
|
CURLcode Curl_cwriter_write(struct Curl_easy *data,
|
|
|
|
struct Curl_cwriter *writer, int type,
|
|
|
|
const char *buf, size_t nbytes)
|
2023-09-20 17:59:16 +08:00
|
|
|
{
|
2023-10-23 16:33:07 +08:00
|
|
|
if(!writer)
|
|
|
|
return CURLE_WRITE_ERROR;
|
|
|
|
return writer->cwt->do_write(data, writer, type, buf, nbytes);
|
|
|
|
}
|
|
|
|
|
|
|
|
CURLcode Curl_cwriter_def_init(struct Curl_easy *data,
|
|
|
|
struct Curl_cwriter *writer)
|
|
|
|
{
|
|
|
|
(void)data;
|
2023-09-20 17:59:16 +08:00
|
|
|
(void)writer;
|
|
|
|
return CURLE_OK;
|
|
|
|
}
|
|
|
|
|
2023-10-23 16:33:07 +08:00
|
|
|
CURLcode Curl_cwriter_def_write(struct Curl_easy *data,
|
|
|
|
struct Curl_cwriter *writer, int type,
|
|
|
|
const char *buf, size_t nbytes)
|
2023-09-20 17:59:16 +08:00
|
|
|
{
|
2023-10-23 16:33:07 +08:00
|
|
|
return Curl_cwriter_write(data, writer->next, type, buf, nbytes);
|
2023-09-20 17:59:16 +08:00
|
|
|
}
|
|
|
|
|
2023-10-23 16:33:07 +08:00
|
|
|
void Curl_cwriter_def_close(struct Curl_easy *data,
|
|
|
|
struct Curl_cwriter *writer)
|
2023-09-20 17:59:16 +08:00
|
|
|
{
|
|
|
|
(void) data;
|
|
|
|
(void) writer;
|
|
|
|
}
|
|
|
|
|
2023-11-21 18:24:18 +08:00
|
|
|
static size_t get_max_body_write_len(struct Curl_easy *data, curl_off_t limit)
|
|
|
|
{
|
|
|
|
if(limit != -1) {
|
|
|
|
/* How much more are we allowed to write? */
|
|
|
|
curl_off_t remain_diff;
|
|
|
|
remain_diff = limit - data->req.bytecount;
|
|
|
|
if(remain_diff < 0) {
|
|
|
|
/* already written too much! */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#if SIZEOF_CURL_OFF_T > SIZEOF_SIZE_T
|
|
|
|
else if(remain_diff > SSIZE_T_MAX) {
|
|
|
|
return SIZE_T_MAX;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
else {
|
|
|
|
return (size_t)remain_diff;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return SIZE_T_MAX;
|
|
|
|
}
|
|
|
|
|
2024-03-05 18:48:16 +08:00
|
|
|
struct cw_download_ctx {
|
|
|
|
struct Curl_cwriter super;
|
|
|
|
BIT(started_response);
|
|
|
|
};
|
2023-10-23 16:33:07 +08:00
|
|
|
/* Download client writer in phase CURL_CW_PROTOCOL that
|
|
|
|
* sees the "real" download body data. */
|
|
|
|
static CURLcode cw_download_write(struct Curl_easy *data,
|
|
|
|
struct Curl_cwriter *writer, int type,
|
|
|
|
const char *buf, size_t nbytes)
|
|
|
|
{
|
2024-03-06 16:52:43 +08:00
|
|
|
struct cw_download_ctx *ctx = writer->ctx;
|
2023-10-23 16:33:07 +08:00
|
|
|
CURLcode result;
|
2023-11-21 18:24:18 +08:00
|
|
|
size_t nwrite, excess_len = 0;
|
2024-03-05 18:48:16 +08:00
|
|
|
bool is_connect = !!(type & CLIENTWRITE_CONNECT);
|
|
|
|
|
|
|
|
if(!is_connect && !ctx->started_response) {
|
|
|
|
Curl_pgrsTime(data, TIMER_STARTTRANSFER);
|
|
|
|
ctx->started_response = TRUE;
|
|
|
|
}
|
2023-10-23 16:33:07 +08:00
|
|
|
|
|
|
|
if(!(type & CLIENTWRITE_BODY)) {
|
2024-03-05 18:48:16 +08:00
|
|
|
if(is_connect && data->set.suppress_connect_headers)
|
2023-10-23 16:33:07 +08:00
|
|
|
return CURLE_OK;
|
2024-03-28 21:12:54 +08:00
|
|
|
result = Curl_cwriter_write(data, writer->next, type, buf, nbytes);
|
|
|
|
CURL_TRC_WRITE(data, "download_write header(type=%x, blen=%zu) -> %d",
|
|
|
|
type, nbytes, result);
|
|
|
|
return result;
|
2023-10-23 16:33:07 +08:00
|
|
|
}
|
|
|
|
|
lib: replace readwrite with write_resp
This clarifies the handling of server responses by folding the code for
the complicated protocols into their protocol handlers. This concerns
mainly HTTP and its bastard sibling RTSP.
The terms "read" and "write" are often used without clear context if
they refer to the connect or the client/application side of a
transfer. This PR uses "read/write" for operations on the client side
and "send/receive" for the connection, e.g. server side. If this is
considered useful, we can revisit renaming of further methods in another
PR.
Curl's protocol handler `readwrite()` method been changed:
```diff
- CURLcode (*readwrite)(struct Curl_easy *data, struct connectdata *conn,
- const char *buf, size_t blen,
- size_t *pconsumed, bool *readmore);
+ CURLcode (*write_resp)(struct Curl_easy *data, const char *buf, size_t blen,
+ bool is_eos, bool *done);
```
The name was changed to clarify that this writes reponse data to the
client side. The parameter changes are:
* `conn` removed as it always operates on `data->conn`
* `pconsumed` removed as the method needs to handle all data on success
* `readmore` removed as no longer necessary
* `is_eos` as indicator that this is the last call for the transfer
response (end-of-stream).
* `done` TRUE on return iff the transfer response is to be treated as
finished
This change affects many files only because of updated comments in
handlers that provide no implementation. The real change is that the
HTTP protocol handlers now provide an implementation.
The HTTP protocol handlers `write_resp()` implementation will get passed
**all** raw data of a server response for the transfer. The HTTP/1.x
formatted status and headers, as well as the undecoded response
body. `Curl_http_write_resp_hds()` is used internally to parse the
response headers and pass them on. This method is public as the RTSP
protocol handler also uses it.
HTTP/1.1 "chunked" transport encoding is now part of the general
*content encoding* writer stack, just like other encodings. A new flag
`CLIENTWRITE_EOS` was added for the last client write. This allows
writers to verify that they are in a valid end state. The chunked
decoder will check if it indeed has seen the last chunk.
The general response handling in `transfer.c:466` happens in function
`readwrite_data()`. This mainly operates now like:
```
static CURLcode readwrite_data(data, ...)
{
do {
Curl_xfer_recv_resp(data, buf)
...
Curl_xfer_write_resp(data, buf)
...
} while(interested);
...
}
```
All the response data handling is implemented in
`Curl_xfer_write_resp()`. It calls the protocol handler's `write_resp()`
implementation if available, or does the default behaviour.
All raw response data needs to pass through this function. Which also
means that anyone in possession of such data may call
`Curl_xfer_write_resp()`.
Closes #12480
2023-12-01 20:50:32 +08:00
|
|
|
/* Here, we deal with REAL BODY bytes. All filtering and transfer
|
|
|
|
* encodings have been applied and only the true content, e.g. BODY,
|
|
|
|
* bytes are passed here.
|
|
|
|
* This allows us to check sizes, update stats, etc. independent
|
|
|
|
* from the protocol in play. */
|
|
|
|
|
|
|
|
if(data->req.no_body && nbytes > 0) {
|
|
|
|
/* BODY arrives although we want none, bail out */
|
|
|
|
streamclose(data->conn, "ignoring body");
|
2024-03-28 21:12:54 +08:00
|
|
|
CURL_TRC_WRITE(data, "download_write body(type=%x, blen=%zu), "
|
|
|
|
"did not want a BODY", type, nbytes);
|
lib: replace readwrite with write_resp
This clarifies the handling of server responses by folding the code for
the complicated protocols into their protocol handlers. This concerns
mainly HTTP and its bastard sibling RTSP.
The terms "read" and "write" are often used without clear context if
they refer to the connect or the client/application side of a
transfer. This PR uses "read/write" for operations on the client side
and "send/receive" for the connection, e.g. server side. If this is
considered useful, we can revisit renaming of further methods in another
PR.
Curl's protocol handler `readwrite()` method been changed:
```diff
- CURLcode (*readwrite)(struct Curl_easy *data, struct connectdata *conn,
- const char *buf, size_t blen,
- size_t *pconsumed, bool *readmore);
+ CURLcode (*write_resp)(struct Curl_easy *data, const char *buf, size_t blen,
+ bool is_eos, bool *done);
```
The name was changed to clarify that this writes reponse data to the
client side. The parameter changes are:
* `conn` removed as it always operates on `data->conn`
* `pconsumed` removed as the method needs to handle all data on success
* `readmore` removed as no longer necessary
* `is_eos` as indicator that this is the last call for the transfer
response (end-of-stream).
* `done` TRUE on return iff the transfer response is to be treated as
finished
This change affects many files only because of updated comments in
handlers that provide no implementation. The real change is that the
HTTP protocol handlers now provide an implementation.
The HTTP protocol handlers `write_resp()` implementation will get passed
**all** raw data of a server response for the transfer. The HTTP/1.x
formatted status and headers, as well as the undecoded response
body. `Curl_http_write_resp_hds()` is used internally to parse the
response headers and pass them on. This method is public as the RTSP
protocol handler also uses it.
HTTP/1.1 "chunked" transport encoding is now part of the general
*content encoding* writer stack, just like other encodings. A new flag
`CLIENTWRITE_EOS` was added for the last client write. This allows
writers to verify that they are in a valid end state. The chunked
decoder will check if it indeed has seen the last chunk.
The general response handling in `transfer.c:466` happens in function
`readwrite_data()`. This mainly operates now like:
```
static CURLcode readwrite_data(data, ...)
{
do {
Curl_xfer_recv_resp(data, buf)
...
Curl_xfer_write_resp(data, buf)
...
} while(interested);
...
}
```
All the response data handling is implemented in
`Curl_xfer_write_resp()`. It calls the protocol handler's `write_resp()`
implementation if available, or does the default behaviour.
All raw response data needs to pass through this function. Which also
means that anyone in possession of such data may call
`Curl_xfer_write_resp()`.
Closes #12480
2023-12-01 20:50:32 +08:00
|
|
|
data->req.download_done = TRUE;
|
2024-02-01 20:23:12 +08:00
|
|
|
if(data->info.header_size)
|
|
|
|
/* if headers have been received, this is fine */
|
|
|
|
return CURLE_OK;
|
lib: replace readwrite with write_resp
This clarifies the handling of server responses by folding the code for
the complicated protocols into their protocol handlers. This concerns
mainly HTTP and its bastard sibling RTSP.
The terms "read" and "write" are often used without clear context if
they refer to the connect or the client/application side of a
transfer. This PR uses "read/write" for operations on the client side
and "send/receive" for the connection, e.g. server side. If this is
considered useful, we can revisit renaming of further methods in another
PR.
Curl's protocol handler `readwrite()` method been changed:
```diff
- CURLcode (*readwrite)(struct Curl_easy *data, struct connectdata *conn,
- const char *buf, size_t blen,
- size_t *pconsumed, bool *readmore);
+ CURLcode (*write_resp)(struct Curl_easy *data, const char *buf, size_t blen,
+ bool is_eos, bool *done);
```
The name was changed to clarify that this writes reponse data to the
client side. The parameter changes are:
* `conn` removed as it always operates on `data->conn`
* `pconsumed` removed as the method needs to handle all data on success
* `readmore` removed as no longer necessary
* `is_eos` as indicator that this is the last call for the transfer
response (end-of-stream).
* `done` TRUE on return iff the transfer response is to be treated as
finished
This change affects many files only because of updated comments in
handlers that provide no implementation. The real change is that the
HTTP protocol handlers now provide an implementation.
The HTTP protocol handlers `write_resp()` implementation will get passed
**all** raw data of a server response for the transfer. The HTTP/1.x
formatted status and headers, as well as the undecoded response
body. `Curl_http_write_resp_hds()` is used internally to parse the
response headers and pass them on. This method is public as the RTSP
protocol handler also uses it.
HTTP/1.1 "chunked" transport encoding is now part of the general
*content encoding* writer stack, just like other encodings. A new flag
`CLIENTWRITE_EOS` was added for the last client write. This allows
writers to verify that they are in a valid end state. The chunked
decoder will check if it indeed has seen the last chunk.
The general response handling in `transfer.c:466` happens in function
`readwrite_data()`. This mainly operates now like:
```
static CURLcode readwrite_data(data, ...)
{
do {
Curl_xfer_recv_resp(data, buf)
...
Curl_xfer_write_resp(data, buf)
...
} while(interested);
...
}
```
All the response data handling is implemented in
`Curl_xfer_write_resp()`. It calls the protocol handler's `write_resp()`
implementation if available, or does the default behaviour.
All raw response data needs to pass through this function. Which also
means that anyone in possession of such data may call
`Curl_xfer_write_resp()`.
Closes #12480
2023-12-01 20:50:32 +08:00
|
|
|
return CURLE_WEIRD_SERVER_REPLY;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Determine if we see any bytes in excess to what is allowed.
|
|
|
|
* We write the allowed bytes and handle excess further below.
|
|
|
|
* This gives deterministic BODY writes on varying buffer receive
|
|
|
|
* lengths. */
|
2023-10-23 16:33:07 +08:00
|
|
|
nwrite = nbytes;
|
2023-11-21 18:24:18 +08:00
|
|
|
if(-1 != data->req.maxdownload) {
|
|
|
|
size_t wmax = get_max_body_write_len(data, data->req.maxdownload);
|
|
|
|
if(nwrite > wmax) {
|
|
|
|
excess_len = nbytes - wmax;
|
|
|
|
nwrite = wmax;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(nwrite == wmax) {
|
|
|
|
data->req.download_done = TRUE;
|
|
|
|
}
|
2024-05-22 22:52:16 +08:00
|
|
|
|
|
|
|
if((type & CLIENTWRITE_EOS) && !data->req.no_body &&
|
|
|
|
(data->req.maxdownload > data->req.bytecount)) {
|
|
|
|
failf(data, "end of response with %" CURL_FORMAT_CURL_OFF_T
|
|
|
|
" bytes missing", data->req.maxdownload - data->req.bytecount);
|
|
|
|
return CURLE_PARTIAL_FILE;
|
|
|
|
}
|
2023-11-21 18:24:18 +08:00
|
|
|
}
|
|
|
|
|
lib: replace readwrite with write_resp
This clarifies the handling of server responses by folding the code for
the complicated protocols into their protocol handlers. This concerns
mainly HTTP and its bastard sibling RTSP.
The terms "read" and "write" are often used without clear context if
they refer to the connect or the client/application side of a
transfer. This PR uses "read/write" for operations on the client side
and "send/receive" for the connection, e.g. server side. If this is
considered useful, we can revisit renaming of further methods in another
PR.
Curl's protocol handler `readwrite()` method been changed:
```diff
- CURLcode (*readwrite)(struct Curl_easy *data, struct connectdata *conn,
- const char *buf, size_t blen,
- size_t *pconsumed, bool *readmore);
+ CURLcode (*write_resp)(struct Curl_easy *data, const char *buf, size_t blen,
+ bool is_eos, bool *done);
```
The name was changed to clarify that this writes reponse data to the
client side. The parameter changes are:
* `conn` removed as it always operates on `data->conn`
* `pconsumed` removed as the method needs to handle all data on success
* `readmore` removed as no longer necessary
* `is_eos` as indicator that this is the last call for the transfer
response (end-of-stream).
* `done` TRUE on return iff the transfer response is to be treated as
finished
This change affects many files only because of updated comments in
handlers that provide no implementation. The real change is that the
HTTP protocol handlers now provide an implementation.
The HTTP protocol handlers `write_resp()` implementation will get passed
**all** raw data of a server response for the transfer. The HTTP/1.x
formatted status and headers, as well as the undecoded response
body. `Curl_http_write_resp_hds()` is used internally to parse the
response headers and pass them on. This method is public as the RTSP
protocol handler also uses it.
HTTP/1.1 "chunked" transport encoding is now part of the general
*content encoding* writer stack, just like other encodings. A new flag
`CLIENTWRITE_EOS` was added for the last client write. This allows
writers to verify that they are in a valid end state. The chunked
decoder will check if it indeed has seen the last chunk.
The general response handling in `transfer.c:466` happens in function
`readwrite_data()`. This mainly operates now like:
```
static CURLcode readwrite_data(data, ...)
{
do {
Curl_xfer_recv_resp(data, buf)
...
Curl_xfer_write_resp(data, buf)
...
} while(interested);
...
}
```
All the response data handling is implemented in
`Curl_xfer_write_resp()`. It calls the protocol handler's `write_resp()`
implementation if available, or does the default behaviour.
All raw response data needs to pass through this function. Which also
means that anyone in possession of such data may call
`Curl_xfer_write_resp()`.
Closes #12480
2023-12-01 20:50:32 +08:00
|
|
|
/* Error on too large filesize is handled below, after writing
|
|
|
|
* the permitted bytes */
|
2023-11-21 18:24:18 +08:00
|
|
|
if(data->set.max_filesize) {
|
|
|
|
size_t wmax = get_max_body_write_len(data, data->set.max_filesize);
|
|
|
|
if(nwrite > wmax) {
|
|
|
|
nwrite = wmax;
|
|
|
|
}
|
2023-10-23 16:33:07 +08:00
|
|
|
}
|
|
|
|
|
2024-02-07 19:05:05 +08:00
|
|
|
if(!data->req.ignorebody && (nwrite || (type & CLIENTWRITE_EOS))) {
|
2023-10-23 16:33:07 +08:00
|
|
|
result = Curl_cwriter_write(data, writer->next, type, buf, nwrite);
|
2024-03-28 21:12:54 +08:00
|
|
|
CURL_TRC_WRITE(data, "download_write body(type=%x, blen=%zu) -> %d",
|
|
|
|
type, nbytes, result);
|
2023-10-23 16:33:07 +08:00
|
|
|
if(result)
|
|
|
|
return result;
|
|
|
|
}
|
2024-02-07 19:05:05 +08:00
|
|
|
/* Update stats, write and report progress */
|
|
|
|
data->req.bytecount += nwrite;
|
2024-06-12 17:20:00 +08:00
|
|
|
#ifdef USE_HYPER
|
|
|
|
data->req.bodywritten = TRUE;
|
|
|
|
#endif
|
2023-10-23 16:33:07 +08:00
|
|
|
result = Curl_pgrsSetDownloadCounter(data, data->req.bytecount);
|
|
|
|
if(result)
|
|
|
|
return result;
|
|
|
|
|
2023-11-21 18:24:18 +08:00
|
|
|
if(excess_len) {
|
lib: replace readwrite with write_resp
This clarifies the handling of server responses by folding the code for
the complicated protocols into their protocol handlers. This concerns
mainly HTTP and its bastard sibling RTSP.
The terms "read" and "write" are often used without clear context if
they refer to the connect or the client/application side of a
transfer. This PR uses "read/write" for operations on the client side
and "send/receive" for the connection, e.g. server side. If this is
considered useful, we can revisit renaming of further methods in another
PR.
Curl's protocol handler `readwrite()` method been changed:
```diff
- CURLcode (*readwrite)(struct Curl_easy *data, struct connectdata *conn,
- const char *buf, size_t blen,
- size_t *pconsumed, bool *readmore);
+ CURLcode (*write_resp)(struct Curl_easy *data, const char *buf, size_t blen,
+ bool is_eos, bool *done);
```
The name was changed to clarify that this writes reponse data to the
client side. The parameter changes are:
* `conn` removed as it always operates on `data->conn`
* `pconsumed` removed as the method needs to handle all data on success
* `readmore` removed as no longer necessary
* `is_eos` as indicator that this is the last call for the transfer
response (end-of-stream).
* `done` TRUE on return iff the transfer response is to be treated as
finished
This change affects many files only because of updated comments in
handlers that provide no implementation. The real change is that the
HTTP protocol handlers now provide an implementation.
The HTTP protocol handlers `write_resp()` implementation will get passed
**all** raw data of a server response for the transfer. The HTTP/1.x
formatted status and headers, as well as the undecoded response
body. `Curl_http_write_resp_hds()` is used internally to parse the
response headers and pass them on. This method is public as the RTSP
protocol handler also uses it.
HTTP/1.1 "chunked" transport encoding is now part of the general
*content encoding* writer stack, just like other encodings. A new flag
`CLIENTWRITE_EOS` was added for the last client write. This allows
writers to verify that they are in a valid end state. The chunked
decoder will check if it indeed has seen the last chunk.
The general response handling in `transfer.c:466` happens in function
`readwrite_data()`. This mainly operates now like:
```
static CURLcode readwrite_data(data, ...)
{
do {
Curl_xfer_recv_resp(data, buf)
...
Curl_xfer_write_resp(data, buf)
...
} while(interested);
...
}
```
All the response data handling is implemented in
`Curl_xfer_write_resp()`. It calls the protocol handler's `write_resp()`
implementation if available, or does the default behaviour.
All raw response data needs to pass through this function. Which also
means that anyone in possession of such data may call
`Curl_xfer_write_resp()`.
Closes #12480
2023-12-01 20:50:32 +08:00
|
|
|
if(!data->req.ignorebody) {
|
2023-11-21 18:24:18 +08:00
|
|
|
infof(data,
|
|
|
|
"Excess found writing body:"
|
|
|
|
" excess = %zu"
|
|
|
|
", size = %" CURL_FORMAT_CURL_OFF_T
|
|
|
|
", maxdownload = %" CURL_FORMAT_CURL_OFF_T
|
|
|
|
", bytecount = %" CURL_FORMAT_CURL_OFF_T,
|
|
|
|
excess_len, data->req.size, data->req.maxdownload,
|
|
|
|
data->req.bytecount);
|
|
|
|
connclose(data->conn, "excess found in a read");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if(nwrite < nbytes) {
|
|
|
|
failf(data, "Exceeded the maximum allowed file size "
|
|
|
|
"(%" CURL_FORMAT_CURL_OFF_T ") with %"
|
|
|
|
CURL_FORMAT_CURL_OFF_T " bytes",
|
|
|
|
data->set.max_filesize, data->req.bytecount);
|
|
|
|
return CURLE_FILESIZE_EXCEEDED;
|
|
|
|
}
|
|
|
|
|
|
|
|
return CURLE_OK;
|
2023-10-23 16:33:07 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static const struct Curl_cwtype cw_download = {
|
2024-03-28 21:12:54 +08:00
|
|
|
"protocol",
|
2023-09-20 17:59:16 +08:00
|
|
|
NULL,
|
2023-10-23 16:33:07 +08:00
|
|
|
Curl_cwriter_def_init,
|
|
|
|
cw_download_write,
|
|
|
|
Curl_cwriter_def_close,
|
2024-03-05 18:48:16 +08:00
|
|
|
sizeof(struct cw_download_ctx)
|
2023-10-23 16:33:07 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
/* RAW client writer in phase CURL_CW_RAW that
|
|
|
|
* enabled tracing of raw data. */
|
|
|
|
static CURLcode cw_raw_write(struct Curl_easy *data,
|
|
|
|
struct Curl_cwriter *writer, int type,
|
|
|
|
const char *buf, size_t nbytes)
|
|
|
|
{
|
|
|
|
if(type & CLIENTWRITE_BODY && data->set.verbose && !data->req.ignorebody) {
|
|
|
|
Curl_debug(data, CURLINFO_DATA_IN, (char *)buf, nbytes);
|
|
|
|
}
|
|
|
|
return Curl_cwriter_write(data, writer->next, type, buf, nbytes);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct Curl_cwtype cw_raw = {
|
|
|
|
"raw",
|
2023-09-20 17:59:16 +08:00
|
|
|
NULL,
|
2023-10-23 16:33:07 +08:00
|
|
|
Curl_cwriter_def_init,
|
|
|
|
cw_raw_write,
|
|
|
|
Curl_cwriter_def_close,
|
|
|
|
sizeof(struct Curl_cwriter)
|
2023-09-20 17:59:16 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
/* Create an unencoding writer stage using the given handler. */
|
2023-10-23 16:33:07 +08:00
|
|
|
CURLcode Curl_cwriter_create(struct Curl_cwriter **pwriter,
|
2023-09-20 17:59:16 +08:00
|
|
|
struct Curl_easy *data,
|
2023-10-23 16:33:07 +08:00
|
|
|
const struct Curl_cwtype *cwt,
|
|
|
|
Curl_cwriter_phase phase)
|
2023-09-20 17:59:16 +08:00
|
|
|
{
|
2024-03-06 16:52:43 +08:00
|
|
|
struct Curl_cwriter *writer = NULL;
|
2023-09-20 17:59:16 +08:00
|
|
|
CURLcode result = CURLE_OUT_OF_MEMORY;
|
2024-03-06 16:52:43 +08:00
|
|
|
void *p;
|
2023-09-20 17:59:16 +08:00
|
|
|
|
2023-10-23 16:33:07 +08:00
|
|
|
DEBUGASSERT(cwt->cwriter_size >= sizeof(struct Curl_cwriter));
|
2024-03-06 16:52:43 +08:00
|
|
|
p = calloc(1, cwt->cwriter_size);
|
|
|
|
if(!p)
|
2023-09-20 17:59:16 +08:00
|
|
|
goto out;
|
|
|
|
|
2024-03-06 16:52:43 +08:00
|
|
|
writer = (struct Curl_cwriter *)p;
|
2023-10-23 16:33:07 +08:00
|
|
|
writer->cwt = cwt;
|
2024-03-06 16:52:43 +08:00
|
|
|
writer->ctx = p;
|
2023-10-23 16:33:07 +08:00
|
|
|
writer->phase = phase;
|
|
|
|
result = cwt->do_init(data, writer);
|
2023-09-20 17:59:16 +08:00
|
|
|
|
|
|
|
out:
|
|
|
|
*pwriter = result? NULL : writer;
|
|
|
|
if(result)
|
|
|
|
free(writer);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2023-10-23 16:33:07 +08:00
|
|
|
void Curl_cwriter_free(struct Curl_easy *data,
|
|
|
|
struct Curl_cwriter *writer)
|
2023-09-20 17:59:16 +08:00
|
|
|
{
|
|
|
|
if(writer) {
|
2023-10-23 16:33:07 +08:00
|
|
|
writer->cwt->do_close(data, writer);
|
2023-09-20 17:59:16 +08:00
|
|
|
free(writer);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-23 16:33:07 +08:00
|
|
|
size_t Curl_cwriter_count(struct Curl_easy *data, Curl_cwriter_phase phase)
|
|
|
|
{
|
|
|
|
struct Curl_cwriter *w;
|
|
|
|
size_t n = 0;
|
2023-09-20 17:59:16 +08:00
|
|
|
|
2023-10-23 16:33:07 +08:00
|
|
|
for(w = data->req.writer_stack; w; w = w->next) {
|
|
|
|
if(w->phase == phase)
|
|
|
|
++n;
|
|
|
|
}
|
|
|
|
return n;
|
|
|
|
}
|
2023-09-20 17:59:16 +08:00
|
|
|
|
2024-02-15 23:22:53 +08:00
|
|
|
static CURLcode do_init_writer_stack(struct Curl_easy *data)
|
2023-09-20 17:59:16 +08:00
|
|
|
{
|
2023-10-23 16:33:07 +08:00
|
|
|
struct Curl_cwriter *writer;
|
|
|
|
CURLcode result;
|
|
|
|
|
2023-09-20 17:59:16 +08:00
|
|
|
DEBUGASSERT(!data->req.writer_stack);
|
2023-10-23 16:33:07 +08:00
|
|
|
result = Curl_cwriter_create(&data->req.writer_stack,
|
2024-02-07 19:05:05 +08:00
|
|
|
data, &Curl_cwt_out, CURL_CW_CLIENT);
|
2023-10-23 16:33:07 +08:00
|
|
|
if(result)
|
|
|
|
return result;
|
|
|
|
|
|
|
|
result = Curl_cwriter_create(&writer, data, &cw_download, CURL_CW_PROTOCOL);
|
|
|
|
if(result)
|
|
|
|
return result;
|
|
|
|
result = Curl_cwriter_add(data, writer);
|
|
|
|
if(result) {
|
|
|
|
Curl_cwriter_free(data, writer);
|
|
|
|
}
|
|
|
|
|
|
|
|
result = Curl_cwriter_create(&writer, data, &cw_raw, CURL_CW_RAW);
|
|
|
|
if(result)
|
|
|
|
return result;
|
|
|
|
result = Curl_cwriter_add(data, writer);
|
|
|
|
if(result) {
|
|
|
|
Curl_cwriter_free(data, writer);
|
|
|
|
}
|
|
|
|
return result;
|
2023-09-20 17:59:16 +08:00
|
|
|
}
|
|
|
|
|
2023-10-23 16:33:07 +08:00
|
|
|
CURLcode Curl_cwriter_add(struct Curl_easy *data,
|
|
|
|
struct Curl_cwriter *writer)
|
2023-09-20 17:59:16 +08:00
|
|
|
{
|
|
|
|
CURLcode result;
|
2023-10-23 16:33:07 +08:00
|
|
|
struct Curl_cwriter **anchor = &data->req.writer_stack;
|
2023-09-20 17:59:16 +08:00
|
|
|
|
2023-10-23 16:33:07 +08:00
|
|
|
if(!*anchor) {
|
2024-02-15 23:22:53 +08:00
|
|
|
result = do_init_writer_stack(data);
|
2023-09-20 17:59:16 +08:00
|
|
|
if(result)
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2023-10-23 16:33:07 +08:00
|
|
|
/* Insert the writer as first in its phase.
|
|
|
|
* Skip existing writers of lower phases. */
|
|
|
|
while(*anchor && (*anchor)->phase < writer->phase)
|
|
|
|
anchor = &((*anchor)->next);
|
|
|
|
writer->next = *anchor;
|
|
|
|
*anchor = writer;
|
2023-09-20 17:59:16 +08:00
|
|
|
return CURLE_OK;
|
|
|
|
}
|
|
|
|
|
2024-02-06 20:55:07 +08:00
|
|
|
struct Curl_cwriter *Curl_cwriter_get_by_name(struct Curl_easy *data,
|
|
|
|
const char *name)
|
|
|
|
{
|
|
|
|
struct Curl_cwriter *writer;
|
|
|
|
for(writer = data->req.writer_stack; writer; writer = writer->next) {
|
|
|
|
if(!strcmp(name, writer->cwt->name))
|
|
|
|
return writer;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2024-02-07 19:05:05 +08:00
|
|
|
struct Curl_cwriter *Curl_cwriter_get_by_type(struct Curl_easy *data,
|
|
|
|
const struct Curl_cwtype *cwt)
|
|
|
|
{
|
|
|
|
struct Curl_cwriter *writer;
|
|
|
|
for(writer = data->req.writer_stack; writer; writer = writer->next) {
|
|
|
|
if(writer->cwt == cwt)
|
|
|
|
return writer;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
lib: replace readwrite with write_resp
This clarifies the handling of server responses by folding the code for
the complicated protocols into their protocol handlers. This concerns
mainly HTTP and its bastard sibling RTSP.
The terms "read" and "write" are often used without clear context if
they refer to the connect or the client/application side of a
transfer. This PR uses "read/write" for operations on the client side
and "send/receive" for the connection, e.g. server side. If this is
considered useful, we can revisit renaming of further methods in another
PR.
Curl's protocol handler `readwrite()` method been changed:
```diff
- CURLcode (*readwrite)(struct Curl_easy *data, struct connectdata *conn,
- const char *buf, size_t blen,
- size_t *pconsumed, bool *readmore);
+ CURLcode (*write_resp)(struct Curl_easy *data, const char *buf, size_t blen,
+ bool is_eos, bool *done);
```
The name was changed to clarify that this writes reponse data to the
client side. The parameter changes are:
* `conn` removed as it always operates on `data->conn`
* `pconsumed` removed as the method needs to handle all data on success
* `readmore` removed as no longer necessary
* `is_eos` as indicator that this is the last call for the transfer
response (end-of-stream).
* `done` TRUE on return iff the transfer response is to be treated as
finished
This change affects many files only because of updated comments in
handlers that provide no implementation. The real change is that the
HTTP protocol handlers now provide an implementation.
The HTTP protocol handlers `write_resp()` implementation will get passed
**all** raw data of a server response for the transfer. The HTTP/1.x
formatted status and headers, as well as the undecoded response
body. `Curl_http_write_resp_hds()` is used internally to parse the
response headers and pass them on. This method is public as the RTSP
protocol handler also uses it.
HTTP/1.1 "chunked" transport encoding is now part of the general
*content encoding* writer stack, just like other encodings. A new flag
`CLIENTWRITE_EOS` was added for the last client write. This allows
writers to verify that they are in a valid end state. The chunked
decoder will check if it indeed has seen the last chunk.
The general response handling in `transfer.c:466` happens in function
`readwrite_data()`. This mainly operates now like:
```
static CURLcode readwrite_data(data, ...)
{
do {
Curl_xfer_recv_resp(data, buf)
...
Curl_xfer_write_resp(data, buf)
...
} while(interested);
...
}
```
All the response data handling is implemented in
`Curl_xfer_write_resp()`. It calls the protocol handler's `write_resp()`
implementation if available, or does the default behaviour.
All raw response data needs to pass through this function. Which also
means that anyone in possession of such data may call
`Curl_xfer_write_resp()`.
Closes #12480
2023-12-01 20:50:32 +08:00
|
|
|
void Curl_cwriter_remove_by_name(struct Curl_easy *data,
|
|
|
|
const char *name)
|
|
|
|
{
|
|
|
|
struct Curl_cwriter **anchor = &data->req.writer_stack;
|
|
|
|
|
|
|
|
while(*anchor) {
|
|
|
|
if(!strcmp(name, (*anchor)->cwt->name)) {
|
|
|
|
struct Curl_cwriter *w = (*anchor);
|
|
|
|
*anchor = w->next;
|
|
|
|
Curl_cwriter_free(data, w);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
anchor = &((*anchor)->next);
|
|
|
|
}
|
|
|
|
}
|
2023-09-20 17:59:16 +08:00
|
|
|
|
2024-04-10 20:52:34 +08:00
|
|
|
bool Curl_cwriter_is_paused(struct Curl_easy *data)
|
|
|
|
{
|
|
|
|
return Curl_cw_out_is_paused(data);
|
|
|
|
}
|
|
|
|
|
|
|
|
CURLcode Curl_cwriter_unpause(struct Curl_easy *data)
|
|
|
|
{
|
|
|
|
return Curl_cw_out_unpause(data);
|
|
|
|
}
|
|
|
|
|
2024-02-15 23:22:53 +08:00
|
|
|
CURLcode Curl_creader_read(struct Curl_easy *data,
|
|
|
|
struct Curl_creader *reader,
|
|
|
|
char *buf, size_t blen, size_t *nread, bool *eos)
|
|
|
|
{
|
2024-04-19 15:42:25 +08:00
|
|
|
*nread = 0;
|
|
|
|
*eos = FALSE;
|
2024-02-15 23:22:53 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2024-03-07 17:08:35 +08:00
|
|
|
CURLcode Curl_creader_def_read(struct Curl_easy *data,
|
|
|
|
struct Curl_creader *reader,
|
|
|
|
char *buf, size_t blen,
|
|
|
|
size_t *nread, bool *eos)
|
|
|
|
{
|
|
|
|
if(reader->next)
|
|
|
|
return reader->next->crt->do_read(data, reader->next, buf, blen,
|
|
|
|
nread, eos);
|
|
|
|
else {
|
|
|
|
*nread = 0;
|
|
|
|
*eos = FALSE;
|
|
|
|
return CURLE_READ_ERROR;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-02-15 23:22:53 +08:00
|
|
|
bool Curl_creader_def_needs_rewind(struct Curl_easy *data,
|
|
|
|
struct Curl_creader *reader)
|
|
|
|
{
|
|
|
|
(void)data;
|
|
|
|
(void)reader;
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2024-02-29 17:12:39 +08:00
|
|
|
curl_off_t Curl_creader_def_total_length(struct Curl_easy *data,
|
|
|
|
struct Curl_creader *reader)
|
|
|
|
{
|
|
|
|
return reader->next?
|
|
|
|
reader->next->crt->total_length(data, reader->next) : -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
CURLcode Curl_creader_def_resume_from(struct Curl_easy *data,
|
|
|
|
struct Curl_creader *reader,
|
|
|
|
curl_off_t offset)
|
|
|
|
{
|
|
|
|
(void)data;
|
|
|
|
(void)reader;
|
|
|
|
(void)offset;
|
|
|
|
return CURLE_READ_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
CURLcode Curl_creader_def_rewind(struct Curl_easy *data,
|
|
|
|
struct Curl_creader *reader)
|
|
|
|
{
|
|
|
|
(void)data;
|
|
|
|
(void)reader;
|
|
|
|
return CURLE_OK;
|
|
|
|
}
|
|
|
|
|
2024-02-29 17:12:39 +08:00
|
|
|
CURLcode Curl_creader_def_unpause(struct Curl_easy *data,
|
|
|
|
struct Curl_creader *reader)
|
|
|
|
{
|
|
|
|
(void)data;
|
|
|
|
(void)reader;
|
|
|
|
return CURLE_OK;
|
|
|
|
}
|
|
|
|
|
2024-06-07 20:38:51 +08:00
|
|
|
bool Curl_creader_def_is_paused(struct Curl_easy *data,
|
|
|
|
struct Curl_creader *reader)
|
|
|
|
{
|
|
|
|
(void)data;
|
|
|
|
(void)reader;
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2024-03-12 00:23:15 +08:00
|
|
|
void Curl_creader_def_done(struct Curl_easy *data,
|
|
|
|
struct Curl_creader *reader, int premature)
|
|
|
|
{
|
|
|
|
(void)data;
|
|
|
|
(void)reader;
|
|
|
|
(void)premature;
|
|
|
|
}
|
|
|
|
|
2024-02-15 23:22:53 +08:00
|
|
|
struct cr_in_ctx {
|
|
|
|
struct Curl_creader super;
|
2024-02-29 17:12:39 +08:00
|
|
|
curl_read_callback read_cb;
|
|
|
|
void *cb_user_data;
|
2024-02-15 23:22:53 +08:00
|
|
|
curl_off_t total_len;
|
|
|
|
curl_off_t read_len;
|
|
|
|
CURLcode error_result;
|
|
|
|
BIT(seen_eos);
|
|
|
|
BIT(errored);
|
|
|
|
BIT(has_used_cb);
|
2024-06-07 20:38:51 +08:00
|
|
|
BIT(is_paused);
|
2024-02-15 23:22:53 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
static CURLcode cr_in_init(struct Curl_easy *data, struct Curl_creader *reader)
|
|
|
|
{
|
2024-03-06 16:52:43 +08:00
|
|
|
struct cr_in_ctx *ctx = reader->ctx;
|
2024-02-15 23:22:53 +08:00
|
|
|
(void)data;
|
2024-02-29 17:12:39 +08:00
|
|
|
ctx->read_cb = data->state.fread_func;
|
|
|
|
ctx->cb_user_data = data->state.in;
|
2024-02-15 23:22:53 +08:00
|
|
|
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)
|
|
|
|
{
|
2024-03-06 16:52:43 +08:00
|
|
|
struct cr_in_ctx *ctx = reader->ctx;
|
2024-02-15 23:22:53 +08:00
|
|
|
size_t nread;
|
|
|
|
|
2024-06-07 20:38:51 +08:00
|
|
|
ctx->is_paused = FALSE;
|
|
|
|
|
2024-02-15 23:22:53 +08:00
|
|
|
/* 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;
|
2024-02-29 17:12:39 +08:00
|
|
|
if(ctx->read_cb && blen) {
|
2024-02-15 23:22:53 +08:00
|
|
|
Curl_set_in_callback(data, true);
|
2024-02-29 17:12:39 +08:00
|
|
|
nread = ctx->read_cb(buf, 1, blen, ctx->cb_user_data);
|
2024-02-15 23:22:53 +08:00
|
|
|
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)) {
|
2024-05-07 22:25:37 +08:00
|
|
|
failf(data, "client read function EOF fail, "
|
2024-02-15 23:22:53 +08:00
|
|
|
"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
|
2024-07-01 22:47:21 +08:00
|
|
|
actually only FILE:// just now, and it cannot pause since the transfer
|
|
|
|
is not done using the "normal" procedure. */
|
2024-02-15 23:22:53 +08:00
|
|
|
failf(data, "Read callback asked for PAUSE when not supported");
|
|
|
|
return CURLE_READ_ERROR;
|
|
|
|
}
|
|
|
|
/* CURL_READFUNC_PAUSE pauses read callbacks that feed socket writes */
|
2024-05-22 22:52:16 +08:00
|
|
|
CURL_TRC_READ(data, "cr_in_read, callback returned CURL_READFUNC_PAUSE");
|
2024-06-07 20:38:51 +08:00
|
|
|
ctx->is_paused = TRUE;
|
2024-02-15 23:22:53 +08:00
|
|
|
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;
|
2024-02-28 21:51:53 +08:00
|
|
|
if(ctx->total_len >= 0)
|
|
|
|
ctx->seen_eos = (ctx->read_len >= ctx->total_len);
|
2024-02-15 23:22:53 +08:00
|
|
|
*pnread = nread;
|
2024-02-28 21:51:53 +08:00
|
|
|
*peos = ctx->seen_eos;
|
2024-02-15 23:22:53 +08:00
|
|
|
break;
|
|
|
|
}
|
2024-03-28 21:12:54 +08:00
|
|
|
CURL_TRC_READ(data, "cr_in_read(len=%zu, total=%"CURL_FORMAT_CURL_OFF_T
|
|
|
|
", read=%"CURL_FORMAT_CURL_OFF_T") -> %d, nread=%zu, eos=%d",
|
|
|
|
blen, ctx->total_len, ctx->read_len, CURLE_OK,
|
|
|
|
*pnread, *peos);
|
2024-02-15 23:22:53 +08:00
|
|
|
return CURLE_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool cr_in_needs_rewind(struct Curl_easy *data,
|
|
|
|
struct Curl_creader *reader)
|
|
|
|
{
|
2024-03-06 16:52:43 +08:00
|
|
|
struct cr_in_ctx *ctx = reader->ctx;
|
2024-02-15 23:22:53 +08:00
|
|
|
(void)data;
|
|
|
|
return ctx->has_used_cb;
|
|
|
|
}
|
|
|
|
|
2024-02-29 17:12:39 +08:00
|
|
|
static curl_off_t cr_in_total_length(struct Curl_easy *data,
|
|
|
|
struct Curl_creader *reader)
|
|
|
|
{
|
2024-03-06 16:52:43 +08:00
|
|
|
struct cr_in_ctx *ctx = reader->ctx;
|
2024-02-29 17:12:39 +08:00
|
|
|
(void)data;
|
|
|
|
return ctx->total_len;
|
|
|
|
}
|
|
|
|
|
|
|
|
static CURLcode cr_in_resume_from(struct Curl_easy *data,
|
|
|
|
struct Curl_creader *reader,
|
|
|
|
curl_off_t offset)
|
|
|
|
{
|
2024-03-06 16:52:43 +08:00
|
|
|
struct cr_in_ctx *ctx = reader->ctx;
|
2024-02-29 17:12:39 +08:00
|
|
|
int seekerr = CURL_SEEKFUNC_CANTSEEK;
|
|
|
|
|
|
|
|
DEBUGASSERT(data->conn);
|
|
|
|
/* already started reading? */
|
|
|
|
if(ctx->read_len)
|
|
|
|
return CURLE_READ_ERROR;
|
|
|
|
|
2024-03-07 18:05:53 +08:00
|
|
|
if(data->set.seek_func) {
|
2024-02-29 17:12:39 +08:00
|
|
|
Curl_set_in_callback(data, true);
|
2024-03-07 18:05:53 +08:00
|
|
|
seekerr = data->set.seek_func(data->set.seek_client, offset, SEEK_SET);
|
2024-02-29 17:12:39 +08:00
|
|
|
Curl_set_in_callback(data, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(seekerr != CURL_SEEKFUNC_OK) {
|
|
|
|
curl_off_t passed = 0;
|
|
|
|
|
|
|
|
if(seekerr != CURL_SEEKFUNC_CANTSEEK) {
|
|
|
|
failf(data, "Could not seek stream");
|
|
|
|
return CURLE_READ_ERROR;
|
|
|
|
}
|
2024-07-01 22:47:21 +08:00
|
|
|
/* when seekerr == CURL_SEEKFUNC_CANTSEEK (cannot seek to offset) */
|
2024-02-29 17:12:39 +08:00
|
|
|
do {
|
|
|
|
char scratch[4*1024];
|
|
|
|
size_t readthisamountnow =
|
|
|
|
(offset - passed > (curl_off_t)sizeof(scratch)) ?
|
|
|
|
sizeof(scratch) :
|
|
|
|
curlx_sotouz(offset - passed);
|
|
|
|
size_t actuallyread;
|
|
|
|
|
|
|
|
Curl_set_in_callback(data, true);
|
|
|
|
actuallyread = ctx->read_cb(scratch, 1, readthisamountnow,
|
|
|
|
ctx->cb_user_data);
|
|
|
|
Curl_set_in_callback(data, false);
|
|
|
|
|
|
|
|
passed += actuallyread;
|
|
|
|
if((actuallyread == 0) || (actuallyread > readthisamountnow)) {
|
|
|
|
/* this checks for greater-than only to make sure that the
|
|
|
|
CURL_READFUNC_ABORT return code still aborts */
|
|
|
|
failf(data, "Could only read %" CURL_FORMAT_CURL_OFF_T
|
|
|
|
" bytes from the input", passed);
|
|
|
|
return CURLE_READ_ERROR;
|
|
|
|
}
|
|
|
|
} while(passed < offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* now, decrease the size of the read */
|
|
|
|
if(ctx->total_len > 0) {
|
|
|
|
ctx->total_len -= offset;
|
|
|
|
|
|
|
|
if(ctx->total_len <= 0) {
|
|
|
|
failf(data, "File already completely uploaded");
|
|
|
|
return CURLE_PARTIAL_FILE;
|
|
|
|
}
|
|
|
|
}
|
2024-07-01 22:47:21 +08:00
|
|
|
/* we have passed, proceed as normal */
|
2024-02-29 17:12:39 +08:00
|
|
|
return CURLE_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static CURLcode cr_in_rewind(struct Curl_easy *data,
|
|
|
|
struct Curl_creader *reader)
|
|
|
|
{
|
2024-03-06 16:52:43 +08:00
|
|
|
struct cr_in_ctx *ctx = reader->ctx;
|
2024-02-29 17:12:39 +08:00
|
|
|
|
|
|
|
/* If we never invoked the callback, there is noting to rewind */
|
|
|
|
if(!ctx->has_used_cb)
|
|
|
|
return CURLE_OK;
|
|
|
|
|
|
|
|
if(data->set.seek_func) {
|
|
|
|
int err;
|
|
|
|
|
|
|
|
Curl_set_in_callback(data, true);
|
|
|
|
err = (data->set.seek_func)(data->set.seek_client, 0, SEEK_SET);
|
|
|
|
Curl_set_in_callback(data, false);
|
2024-03-28 21:12:54 +08:00
|
|
|
CURL_TRC_READ(data, "cr_in, rewind via set.seek_func -> %d", err);
|
2024-02-29 17:12:39 +08:00
|
|
|
if(err) {
|
|
|
|
failf(data, "seek callback returned error %d", (int)err);
|
|
|
|
return CURLE_SEND_FAIL_REWIND;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if(data->set.ioctl_func) {
|
|
|
|
curlioerr err;
|
|
|
|
|
|
|
|
Curl_set_in_callback(data, true);
|
|
|
|
err = (data->set.ioctl_func)(data, CURLIOCMD_RESTARTREAD,
|
|
|
|
data->set.ioctl_client);
|
|
|
|
Curl_set_in_callback(data, false);
|
2024-03-28 21:12:54 +08:00
|
|
|
CURL_TRC_READ(data, "cr_in, rewind via set.ioctl_func -> %d", (int)err);
|
2024-02-29 17:12:39 +08:00
|
|
|
if(err) {
|
|
|
|
failf(data, "ioctl callback returned error %d", (int)err);
|
|
|
|
return CURLE_SEND_FAIL_REWIND;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* If no CURLOPT_READFUNCTION is used, we know that we operate on a
|
|
|
|
given FILE * stream and we can actually attempt to rewind that
|
|
|
|
ourselves with fseek() */
|
|
|
|
if(data->state.fread_func == (curl_read_callback)fread) {
|
|
|
|
int err = fseek(data->state.in, 0, SEEK_SET);
|
2024-03-28 21:12:54 +08:00
|
|
|
CURL_TRC_READ(data, "cr_in, rewind via fseek -> %d(%d)",
|
|
|
|
(int)err, (int)errno);
|
2024-02-29 17:12:39 +08:00
|
|
|
if(-1 != err)
|
|
|
|
/* successful rewind */
|
|
|
|
return CURLE_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* no callback set or failure above, makes us fail at once */
|
2024-07-01 22:47:21 +08:00
|
|
|
failf(data, "necessary data rewind was not possible");
|
2024-02-29 17:12:39 +08:00
|
|
|
return CURLE_SEND_FAIL_REWIND;
|
|
|
|
}
|
|
|
|
return CURLE_OK;
|
|
|
|
}
|
|
|
|
|
2024-06-07 20:38:51 +08:00
|
|
|
static CURLcode cr_in_unpause(struct Curl_easy *data,
|
|
|
|
struct Curl_creader *reader)
|
|
|
|
{
|
|
|
|
struct cr_in_ctx *ctx = reader->ctx;
|
|
|
|
(void)data;
|
|
|
|
ctx->is_paused = FALSE;
|
|
|
|
return CURLE_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool cr_in_is_paused(struct Curl_easy *data,
|
|
|
|
struct Curl_creader *reader)
|
|
|
|
{
|
|
|
|
struct cr_in_ctx *ctx = reader->ctx;
|
|
|
|
(void)data;
|
|
|
|
return ctx->is_paused;
|
|
|
|
}
|
2024-02-29 17:12:39 +08:00
|
|
|
|
2024-02-15 23:22:53 +08:00
|
|
|
static const struct Curl_crtype cr_in = {
|
|
|
|
"cr-in",
|
|
|
|
cr_in_init,
|
|
|
|
cr_in_read,
|
|
|
|
Curl_creader_def_close,
|
|
|
|
cr_in_needs_rewind,
|
2024-02-29 17:12:39 +08:00
|
|
|
cr_in_total_length,
|
|
|
|
cr_in_resume_from,
|
|
|
|
cr_in_rewind,
|
2024-06-07 20:38:51 +08:00
|
|
|
cr_in_unpause,
|
|
|
|
cr_in_is_paused,
|
2024-03-12 00:23:15 +08:00
|
|
|
Curl_creader_def_done,
|
2024-02-15 23:22:53 +08:00
|
|
|
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)
|
|
|
|
{
|
2024-03-06 16:52:43 +08:00
|
|
|
struct Curl_creader *reader = NULL;
|
2024-02-15 23:22:53 +08:00
|
|
|
CURLcode result = CURLE_OUT_OF_MEMORY;
|
2024-03-06 16:52:43 +08:00
|
|
|
void *p;
|
2024-02-15 23:22:53 +08:00
|
|
|
|
|
|
|
DEBUGASSERT(crt->creader_size >= sizeof(struct Curl_creader));
|
2024-03-06 16:52:43 +08:00
|
|
|
p = calloc(1, crt->creader_size);
|
|
|
|
if(!p)
|
2024-02-15 23:22:53 +08:00
|
|
|
goto out;
|
|
|
|
|
2024-03-06 16:52:43 +08:00
|
|
|
reader = (struct Curl_creader *)p;
|
2024-02-15 23:22:53 +08:00
|
|
|
reader->crt = crt;
|
2024-03-06 16:52:43 +08:00
|
|
|
reader->ctx = p;
|
2024-02-15 23:22:53 +08:00
|
|
|
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)
|
|
|
|
{
|
2024-03-06 16:52:43 +08:00
|
|
|
struct cr_lc_ctx *ctx = reader->ctx;
|
2024-02-15 23:22:53 +08:00
|
|
|
(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)
|
|
|
|
{
|
2024-03-06 16:52:43 +08:00
|
|
|
struct cr_lc_ctx *ctx = reader->ctx;
|
2024-02-15 23:22:53 +08:00
|
|
|
(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)
|
|
|
|
{
|
2024-03-06 16:52:43 +08:00
|
|
|
struct cr_lc_ctx *ctx = reader->ctx;
|
2024-02-15 23:22:53 +08:00
|
|
|
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;
|
2024-03-28 21:12:54 +08:00
|
|
|
goto out;
|
2024-02-15 23:22:53 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* 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)) {
|
2024-07-01 22:47:21 +08:00
|
|
|
/* we are here only because FTP is in ASCII mode...
|
2024-02-15 23:22:53 +08:00
|
|
|
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... */
|
|
|
|
}
|
|
|
|
}
|
2024-07-12 20:07:29 +08:00
|
|
|
|
|
|
|
if(start < i) { /* leftover */
|
|
|
|
result = Curl_bufq_cwrite(&ctx->buf, buf + start, i - start, &n);
|
|
|
|
if(result)
|
|
|
|
return result;
|
|
|
|
}
|
2024-02-15 23:22:53 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
2024-03-28 21:12:54 +08:00
|
|
|
|
|
|
|
out:
|
|
|
|
CURL_TRC_READ(data, "cr_lc_read(len=%zu) -> %d, nread=%zu, eos=%d",
|
|
|
|
blen, result, *pnread, *peos);
|
2024-02-15 23:22:53 +08:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2024-02-29 17:12:39 +08:00
|
|
|
static curl_off_t cr_lc_total_length(struct Curl_easy *data,
|
|
|
|
struct Curl_creader *reader)
|
|
|
|
{
|
|
|
|
/* this reader changes length depending on input */
|
|
|
|
(void)data;
|
|
|
|
(void)reader;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2024-02-15 23:22:53 +08:00
|
|
|
static const struct Curl_crtype cr_lc = {
|
|
|
|
"cr-lineconv",
|
|
|
|
cr_lc_init,
|
|
|
|
cr_lc_read,
|
|
|
|
cr_lc_close,
|
|
|
|
Curl_creader_def_needs_rewind,
|
2024-02-29 17:12:39 +08:00
|
|
|
cr_lc_total_length,
|
|
|
|
Curl_creader_def_resume_from,
|
|
|
|
Curl_creader_def_rewind,
|
2024-02-29 17:12:39 +08:00
|
|
|
Curl_creader_def_unpause,
|
2024-06-07 20:38:51 +08:00
|
|
|
Curl_creader_def_is_paused,
|
2024-03-12 00:23:15 +08:00
|
|
|
Curl_creader_def_done,
|
2024-02-15 23:22:53 +08:00
|
|
|
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,
|
2024-02-28 21:51:53 +08:00
|
|
|
CURL_CR_CONTENT_ENCODE);
|
2024-02-15 23:22:53 +08:00
|
|
|
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,
|
2024-02-29 17:12:39 +08:00
|
|
|
struct Curl_creader *r)
|
2024-02-15 23:22:53 +08:00
|
|
|
{
|
2024-02-29 17:12:39 +08:00
|
|
|
CURLcode result = CURLE_OK;
|
|
|
|
curl_off_t clen;
|
2024-02-15 23:22:53 +08:00
|
|
|
|
2024-02-29 17:12:39 +08:00
|
|
|
DEBUGASSERT(r);
|
|
|
|
DEBUGASSERT(r->crt);
|
|
|
|
DEBUGASSERT(r->phase == CURL_CR_CLIENT);
|
2024-02-15 23:22:53 +08:00
|
|
|
DEBUGASSERT(!data->req.reader_stack);
|
|
|
|
|
2024-02-29 17:12:39 +08:00
|
|
|
data->req.reader_stack = r;
|
|
|
|
clen = r->crt->total_length(data, r);
|
2024-02-29 17:12:39 +08:00
|
|
|
/* if we do not have 0 length init, and crlf conversion is wanted,
|
|
|
|
* add the reader for it */
|
|
|
|
if(clen && (data->set.crlf
|
2024-02-15 23:22:53 +08:00
|
|
|
#ifdef CURL_DO_LINEEND_CONV
|
|
|
|
|| data->state.prefer_ascii
|
|
|
|
#endif
|
2024-02-29 17:12:39 +08:00
|
|
|
)) {
|
2024-02-15 23:22:53 +08:00
|
|
|
result = cr_lc_add(data);
|
|
|
|
if(result)
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2024-02-29 17:12:39 +08:00
|
|
|
CURLcode Curl_creader_set_fread(struct Curl_easy *data, curl_off_t len)
|
2024-02-15 23:22:53 +08:00
|
|
|
{
|
|
|
|
CURLcode result;
|
|
|
|
struct Curl_creader *r;
|
2024-02-29 17:12:39 +08:00
|
|
|
struct cr_in_ctx *ctx;
|
|
|
|
|
|
|
|
result = Curl_creader_create(&r, data, &cr_in, CURL_CR_CLIENT);
|
|
|
|
if(result)
|
2024-03-28 21:12:54 +08:00
|
|
|
goto out;
|
2024-03-06 16:52:43 +08:00
|
|
|
ctx = r->ctx;
|
2024-02-29 17:12:39 +08:00
|
|
|
ctx->total_len = len;
|
2024-02-15 23:22:53 +08:00
|
|
|
|
|
|
|
cl_reset_reader(data);
|
2024-03-28 21:12:54 +08:00
|
|
|
result = do_init_reader_stack(data, r);
|
|
|
|
out:
|
|
|
|
CURL_TRC_READ(data, "add fread reader, len=%"CURL_FORMAT_CURL_OFF_T
|
|
|
|
" -> %d", len, result);
|
|
|
|
return result;
|
2024-02-15 23:22:53 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
CURLcode Curl_creader_add(struct Curl_easy *data,
|
|
|
|
struct Curl_creader *reader)
|
|
|
|
{
|
|
|
|
CURLcode result;
|
|
|
|
struct Curl_creader **anchor = &data->req.reader_stack;
|
|
|
|
|
|
|
|
if(!*anchor) {
|
2024-02-29 17:12:39 +08:00
|
|
|
result = Curl_creader_set_fread(data, data->state.infilesize);
|
2024-02-15 23:22:53 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2024-02-29 17:12:39 +08:00
|
|
|
CURLcode Curl_creader_set(struct Curl_easy *data, struct Curl_creader *r)
|
|
|
|
{
|
|
|
|
CURLcode result;
|
|
|
|
|
|
|
|
DEBUGASSERT(r);
|
|
|
|
DEBUGASSERT(r->crt);
|
|
|
|
DEBUGASSERT(r->phase == CURL_CR_CLIENT);
|
|
|
|
|
|
|
|
cl_reset_reader(data);
|
|
|
|
result = do_init_reader_stack(data, r);
|
|
|
|
if(result)
|
|
|
|
Curl_creader_free(data, r);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2024-02-15 23:22:53 +08:00
|
|
|
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) {
|
2024-02-29 17:12:39 +08:00
|
|
|
result = Curl_creader_set_fread(data, data->state.infilesize);
|
2024-02-15 23:22:53 +08:00
|
|
|
if(result)
|
|
|
|
return result;
|
|
|
|
DEBUGASSERT(data->req.reader_stack);
|
|
|
|
}
|
|
|
|
|
|
|
|
result = Curl_creader_read(data, data->req.reader_stack, buf, blen,
|
|
|
|
nread, eos);
|
2024-03-28 21:12:54 +08:00
|
|
|
CURL_TRC_READ(data, "client_read(len=%zu) -> %d, nread=%zu, eos=%d",
|
|
|
|
blen, result, *nread, *eos);
|
2024-02-15 23:22:53 +08:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2024-02-29 17:12:39 +08:00
|
|
|
bool Curl_creader_needs_rewind(struct Curl_easy *data)
|
2024-02-15 23:22:53 +08:00
|
|
|
{
|
|
|
|
struct Curl_creader *reader = data->req.reader_stack;
|
|
|
|
while(reader) {
|
2024-03-28 21:12:54 +08:00
|
|
|
if(reader->crt->needs_rewind(data, reader)) {
|
|
|
|
CURL_TRC_READ(data, "client reader needs rewind before next request");
|
2024-02-15 23:22:53 +08:00
|
|
|
return TRUE;
|
2024-03-28 21:12:54 +08:00
|
|
|
}
|
2024-02-15 23:22:53 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2024-02-29 17:12:39 +08:00
|
|
|
static curl_off_t cr_null_total_length(struct Curl_easy *data,
|
|
|
|
struct Curl_creader *reader)
|
|
|
|
{
|
|
|
|
/* this reader changes length depending on input */
|
|
|
|
(void)data;
|
|
|
|
(void)reader;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2024-02-15 23:22:53 +08:00
|
|
|
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,
|
2024-02-29 17:12:39 +08:00
|
|
|
cr_null_total_length,
|
|
|
|
Curl_creader_def_resume_from,
|
|
|
|
Curl_creader_def_rewind,
|
2024-02-29 17:12:39 +08:00
|
|
|
Curl_creader_def_unpause,
|
2024-06-07 20:38:51 +08:00
|
|
|
Curl_creader_def_is_paused,
|
2024-03-12 00:23:15 +08:00
|
|
|
Curl_creader_def_done,
|
2024-02-15 23:22:53 +08:00
|
|
|
sizeof(struct Curl_creader)
|
|
|
|
};
|
|
|
|
|
2024-02-29 17:12:39 +08:00
|
|
|
CURLcode Curl_creader_set_null(struct Curl_easy *data)
|
2024-02-15 23:22:53 +08:00
|
|
|
{
|
|
|
|
struct Curl_creader *r;
|
2024-02-29 17:12:39 +08:00
|
|
|
CURLcode result;
|
|
|
|
|
|
|
|
result = Curl_creader_create(&r, data, &cr_null, CURL_CR_CLIENT);
|
|
|
|
if(result)
|
|
|
|
return result;
|
2024-02-15 23:22:53 +08:00
|
|
|
|
|
|
|
cl_reset_reader(data);
|
2024-02-29 17:12:39 +08:00
|
|
|
return do_init_reader_stack(data, r);
|
2024-02-15 23:22:53 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
2024-03-06 16:52:43 +08:00
|
|
|
struct cr_buf_ctx *ctx = reader->ctx;
|
2024-02-15 23:22:53 +08:00
|
|
|
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);
|
|
|
|
}
|
2024-03-28 21:12:54 +08:00
|
|
|
CURL_TRC_READ(data, "cr_buf_read(len=%zu) -> 0, nread=%zu, eos=%d",
|
|
|
|
blen, *pnread, *peos);
|
2024-02-15 23:22:53 +08:00
|
|
|
return CURLE_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool cr_buf_needs_rewind(struct Curl_easy *data,
|
|
|
|
struct Curl_creader *reader)
|
|
|
|
{
|
2024-03-06 16:52:43 +08:00
|
|
|
struct cr_buf_ctx *ctx = reader->ctx;
|
2024-02-15 23:22:53 +08:00
|
|
|
(void)data;
|
|
|
|
return ctx->index > 0;
|
|
|
|
}
|
|
|
|
|
2024-02-29 17:12:39 +08:00
|
|
|
static curl_off_t cr_buf_total_length(struct Curl_easy *data,
|
|
|
|
struct Curl_creader *reader)
|
|
|
|
{
|
2024-03-06 16:52:43 +08:00
|
|
|
struct cr_buf_ctx *ctx = reader->ctx;
|
2024-02-29 17:12:39 +08:00
|
|
|
(void)data;
|
|
|
|
return (curl_off_t)ctx->blen;
|
|
|
|
}
|
|
|
|
|
|
|
|
static CURLcode cr_buf_resume_from(struct Curl_easy *data,
|
|
|
|
struct Curl_creader *reader,
|
|
|
|
curl_off_t offset)
|
|
|
|
{
|
2024-03-06 16:52:43 +08:00
|
|
|
struct cr_buf_ctx *ctx = reader->ctx;
|
2024-02-29 17:12:39 +08:00
|
|
|
size_t boffset;
|
|
|
|
|
|
|
|
(void)data;
|
|
|
|
DEBUGASSERT(data->conn);
|
|
|
|
/* already started reading? */
|
|
|
|
if(ctx->index)
|
|
|
|
return CURLE_READ_ERROR;
|
|
|
|
if(offset <= 0)
|
|
|
|
return CURLE_OK;
|
|
|
|
boffset = (size_t)offset;
|
|
|
|
if(boffset > ctx->blen)
|
|
|
|
return CURLE_READ_ERROR;
|
|
|
|
|
|
|
|
ctx->buf += boffset;
|
|
|
|
ctx->blen -= boffset;
|
|
|
|
return CURLE_OK;
|
|
|
|
}
|
|
|
|
|
2024-02-15 23:22:53 +08:00
|
|
|
static const struct Curl_crtype cr_buf = {
|
|
|
|
"cr-buf",
|
|
|
|
Curl_creader_def_init,
|
|
|
|
cr_buf_read,
|
|
|
|
Curl_creader_def_close,
|
|
|
|
cr_buf_needs_rewind,
|
2024-02-29 17:12:39 +08:00
|
|
|
cr_buf_total_length,
|
|
|
|
cr_buf_resume_from,
|
|
|
|
Curl_creader_def_rewind,
|
2024-02-29 17:12:39 +08:00
|
|
|
Curl_creader_def_unpause,
|
2024-06-07 20:38:51 +08:00
|
|
|
Curl_creader_def_is_paused,
|
2024-03-12 00:23:15 +08:00
|
|
|
Curl_creader_def_done,
|
2024-02-15 23:22:53 +08:00
|
|
|
sizeof(struct cr_buf_ctx)
|
|
|
|
};
|
|
|
|
|
2024-02-29 17:12:39 +08:00
|
|
|
CURLcode Curl_creader_set_buf(struct Curl_easy *data,
|
2024-02-15 23:22:53 +08:00
|
|
|
const char *buf, size_t blen)
|
|
|
|
{
|
|
|
|
CURLcode result;
|
|
|
|
struct Curl_creader *r;
|
2024-02-29 17:12:39 +08:00
|
|
|
struct cr_buf_ctx *ctx;
|
|
|
|
|
|
|
|
result = Curl_creader_create(&r, data, &cr_buf, CURL_CR_CLIENT);
|
|
|
|
if(result)
|
2024-03-28 21:12:54 +08:00
|
|
|
goto out;
|
2024-03-06 16:52:43 +08:00
|
|
|
ctx = r->ctx;
|
2024-02-29 17:12:39 +08:00
|
|
|
ctx->buf = buf;
|
|
|
|
ctx->blen = blen;
|
|
|
|
ctx->index = 0;
|
2024-02-15 23:22:53 +08:00
|
|
|
|
|
|
|
cl_reset_reader(data);
|
2024-03-28 21:12:54 +08:00
|
|
|
result = do_init_reader_stack(data, r);
|
|
|
|
out:
|
|
|
|
CURL_TRC_READ(data, "add buf reader, len=%zu -> %d", blen, result);
|
|
|
|
return result;
|
2024-02-15 23:22:53 +08:00
|
|
|
}
|
2024-02-29 17:12:39 +08:00
|
|
|
|
|
|
|
curl_off_t Curl_creader_total_length(struct Curl_easy *data)
|
|
|
|
{
|
|
|
|
struct Curl_creader *r = data->req.reader_stack;
|
|
|
|
return r? r->crt->total_length(data, r) : -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
curl_off_t Curl_creader_client_length(struct Curl_easy *data)
|
|
|
|
{
|
|
|
|
struct Curl_creader *r = data->req.reader_stack;
|
|
|
|
while(r && r->phase != CURL_CR_CLIENT)
|
|
|
|
r = r->next;
|
|
|
|
return r? r->crt->total_length(data, r) : -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
CURLcode Curl_creader_resume_from(struct Curl_easy *data, curl_off_t offset)
|
|
|
|
{
|
|
|
|
struct Curl_creader *r = data->req.reader_stack;
|
|
|
|
while(r && r->phase != CURL_CR_CLIENT)
|
|
|
|
r = r->next;
|
|
|
|
return r? r->crt->resume_from(data, r, offset) : CURLE_READ_ERROR;
|
|
|
|
}
|
2024-02-29 17:12:39 +08:00
|
|
|
|
|
|
|
CURLcode Curl_creader_unpause(struct Curl_easy *data)
|
|
|
|
{
|
|
|
|
struct Curl_creader *reader = data->req.reader_stack;
|
|
|
|
CURLcode result = CURLE_OK;
|
|
|
|
|
|
|
|
while(reader) {
|
|
|
|
result = reader->crt->unpause(data, reader);
|
|
|
|
if(result)
|
|
|
|
break;
|
|
|
|
reader = reader->next;
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
2024-03-12 00:23:15 +08:00
|
|
|
|
2024-06-07 20:38:51 +08:00
|
|
|
bool Curl_creader_is_paused(struct Curl_easy *data)
|
|
|
|
{
|
|
|
|
struct Curl_creader *reader = data->req.reader_stack;
|
|
|
|
|
|
|
|
while(reader) {
|
|
|
|
if(reader->crt->is_paused(data, reader))
|
|
|
|
return TRUE;
|
|
|
|
reader = reader->next;
|
|
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2024-03-12 00:23:15 +08:00
|
|
|
void Curl_creader_done(struct Curl_easy *data, int premature)
|
|
|
|
{
|
|
|
|
struct Curl_creader *reader = data->req.reader_stack;
|
|
|
|
while(reader) {
|
|
|
|
reader->crt->done(data, reader, premature);
|
|
|
|
reader = reader->next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct Curl_creader *Curl_creader_get_by_type(struct Curl_easy *data,
|
|
|
|
const struct Curl_crtype *crt)
|
|
|
|
{
|
|
|
|
struct Curl_creader *r;
|
|
|
|
for(r = data->req.reader_stack; r; r = r->next) {
|
|
|
|
if(r->crt == crt)
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
}
|