mirror of
https://github.com/curl/curl.git
synced 2026-08-24 20:13:35 +03:00
Merge eb3999bba7 into 33a967318c
This commit is contained in:
commit
64cf6bfe5e
3 changed files with 74 additions and 1 deletions
25
lib/imap.c
25
lib/imap.c
|
|
@ -1215,6 +1215,30 @@ static bool is_custom_fetch_listing(struct IMAP *imap)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* imap_stream_resp()
|
||||||
|
*
|
||||||
|
* Tells the pingpong layer that a very long untagged response line should be
|
||||||
|
* streamed to the download body rather than buffered whole. This applies to
|
||||||
|
* the SEARCH response, which lists every matching message number on a single
|
||||||
|
* line and can be arbitrarily long on a large mailbox. Custom requests (e.g.
|
||||||
|
* "-X SEARCH ..." or "-X UID SEARCH ...") run in the LIST state and have their
|
||||||
|
* untagged response written to the body the same way, so those are streamed
|
||||||
|
* too - except custom FETCH listings, whose data is delivered differently.
|
||||||
|
*/
|
||||||
|
static bool imap_stream_resp(struct Curl_easy *data, struct connectdata *conn)
|
||||||
|
{
|
||||||
|
struct imap_conn *imapc = Curl_conn_meta_get(conn, CURL_META_IMAP_CONN);
|
||||||
|
struct IMAP *imap = Curl_meta_get(data, CURL_META_IMAP_EASY);
|
||||||
|
if(!imapc)
|
||||||
|
return FALSE;
|
||||||
|
if(imapc->state == IMAP_SEARCH)
|
||||||
|
return TRUE;
|
||||||
|
if(imapc->state == IMAP_LIST && imap && !is_custom_fetch_listing(imap))
|
||||||
|
return TRUE;
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
/* For LIST and SEARCH responses */
|
/* For LIST and SEARCH responses */
|
||||||
static CURLcode imap_state_listsearch_resp(struct Curl_easy *data,
|
static CURLcode imap_state_listsearch_resp(struct Curl_easy *data,
|
||||||
struct imap_conn *imapc,
|
struct imap_conn *imapc,
|
||||||
|
|
@ -2284,6 +2308,7 @@ static CURLcode imap_setup_connection(struct Curl_easy *data,
|
||||||
|
|
||||||
pp = &imapc->pp;
|
pp = &imapc->pp;
|
||||||
PINGPONG_SETUP(pp, imap_pp_statemachine, imap_endofresp);
|
PINGPONG_SETUP(pp, imap_pp_statemachine, imap_endofresp);
|
||||||
|
pp->stream_resp = imap_stream_resp;
|
||||||
|
|
||||||
/* Set the default preferred authentication type and mechanism */
|
/* Set the default preferred authentication type and mechanism */
|
||||||
imapc->preftype = IMAP_TYPE_ANY;
|
imapc->preftype = IMAP_TYPE_ANY;
|
||||||
|
|
|
||||||
|
|
@ -286,7 +286,40 @@ CURLcode Curl_pp_readresp(struct Curl_easy *data,
|
||||||
|
|
||||||
do {
|
do {
|
||||||
const char *line = curlx_dyn_ptr(&pp->recvbuf);
|
const char *line = curlx_dyn_ptr(&pp->recvbuf);
|
||||||
const char *nl = memchr(line, '\n', curlx_dyn_len(&pp->recvbuf));
|
size_t buflen = curlx_dyn_len(&pp->recvbuf);
|
||||||
|
const char *nl = memchr(line, '\n', buflen);
|
||||||
|
|
||||||
|
/* Stream a very long response line to the download body instead of
|
||||||
|
buffering it whole, when the protocol opts in (e.g. IMAP SEARCH on a
|
||||||
|
large mailbox). This keeps memory bounded no matter how long the line
|
||||||
|
is. Once started (pp->streaming) keep going until the terminating
|
||||||
|
newline arrives, at which point the line is complete. */
|
||||||
|
if(pp->streaming ||
|
||||||
|
(!nl && buflen >= PP_STREAM_FLUSH &&
|
||||||
|
pp->stream_resp && pp->stream_resp(data, conn))) {
|
||||||
|
size_t chunk = nl ? (size_t)(nl - line + 1) : buflen;
|
||||||
|
if(chunk) {
|
||||||
|
result = Curl_client_write(data, CLIENTWRITE_BODY, line, chunk);
|
||||||
|
if(result)
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
if(nl) {
|
||||||
|
/* the streamed line is now complete */
|
||||||
|
pp->streaming = FALSE;
|
||||||
|
if(buflen > chunk)
|
||||||
|
/* keep whatever follows (e.g. the tagged response line) */
|
||||||
|
curlx_dyn_tail(&pp->recvbuf, buflen - chunk);
|
||||||
|
else
|
||||||
|
curlx_dyn_reset(&pp->recvbuf);
|
||||||
|
continue; /* re-scan any remaining buffered data */
|
||||||
|
}
|
||||||
|
/* still no newline: everything so far is streamed, wait for more */
|
||||||
|
pp->streaming = TRUE;
|
||||||
|
pp->overflow = 0;
|
||||||
|
curlx_dyn_reset(&pp->recvbuf);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if(nl) {
|
if(nl) {
|
||||||
/* a newline is CRLF in pp-talk, so the CR is ignored as
|
/* a newline is CRLF in pp-talk, so the CR is ignored as
|
||||||
the line is not really terminated until the LF comes */
|
the line is not really terminated until the LF comes */
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,14 @@ struct pingpong {
|
||||||
CURLcode (*statemachine)(struct Curl_easy *data, struct connectdata *conn);
|
CURLcode (*statemachine)(struct Curl_easy *data, struct connectdata *conn);
|
||||||
bool (*endofresp)(struct Curl_easy *data, struct connectdata *conn,
|
bool (*endofresp)(struct Curl_easy *data, struct connectdata *conn,
|
||||||
const char *ptr, size_t len, int *code);
|
const char *ptr, size_t len, int *code);
|
||||||
|
/* Optional. When a single response line grows past PP_STREAM_FLUSH bytes
|
||||||
|
without a terminating newline, this is called to ask the protocol whether
|
||||||
|
the partial line content should be streamed to the download body (as for a
|
||||||
|
large IMAP "* SEARCH ..." reply that lists all matching message numbers on
|
||||||
|
one line). Returns TRUE to stream, keeping the receive buffer bounded. */
|
||||||
|
bool (*stream_resp)(struct Curl_easy *data, struct connectdata *conn);
|
||||||
BIT(initialized);
|
BIT(initialized);
|
||||||
|
BIT(streaming); /* streaming a long response line to the body */
|
||||||
BIT(pending_resp); /* set TRUE when a server response is pending or in
|
BIT(pending_resp); /* set TRUE when a server response is pending or in
|
||||||
progress, and is cleared once the last response is
|
progress, and is cleared once the last response is
|
||||||
read */
|
read */
|
||||||
|
|
@ -74,6 +81,14 @@ struct pingpong {
|
||||||
* has CURLOPT_SERVER_RESPONSE_TIMEOUT(_MS) set. */
|
* has CURLOPT_SERVER_RESPONSE_TIMEOUT(_MS) set. */
|
||||||
#define PINGPONG_TIMEOUT_MS (60 * 1000)
|
#define PINGPONG_TIMEOUT_MS (60 * 1000)
|
||||||
|
|
||||||
|
/* When a response line exceeds this many bytes without a terminating newline
|
||||||
|
and the protocol opts in via pp->stream_resp(), its content is streamed to
|
||||||
|
the download body instead of being buffered as one whole line. This keeps
|
||||||
|
memory bounded for very large single-line responses (e.g. IMAP SEARCH on a
|
||||||
|
big mailbox) that would otherwise hit the receive buffer limit and fail with
|
||||||
|
CURLE_TOO_LARGE. */
|
||||||
|
#define PP_STREAM_FLUSH (32 * 1024)
|
||||||
|
|
||||||
#define PINGPONG_SETUP(pp, s, e) \
|
#define PINGPONG_SETUP(pp, s, e) \
|
||||||
do { \
|
do { \
|
||||||
(pp)->statemachine = s; \
|
(pp)->statemachine = s; \
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue