Merge aa3ede6d27 into b4834a7d6d
This commit is contained in:
commit
a182327b35
@ -123,6 +123,7 @@ LIB_CFILES = \
|
|||||||
cf-socket.c \
|
cf-socket.c \
|
||||||
cfilters.c \
|
cfilters.c \
|
||||||
conncache.c \
|
conncache.c \
|
||||||
|
cshutdn.c \
|
||||||
connect.c \
|
connect.c \
|
||||||
content_encoding.c \
|
content_encoding.c \
|
||||||
cookie.c \
|
cookie.c \
|
||||||
@ -259,6 +260,7 @@ LIB_HFILES = \
|
|||||||
cf-socket.h \
|
cf-socket.h \
|
||||||
cfilters.h \
|
cfilters.h \
|
||||||
conncache.h \
|
conncache.h \
|
||||||
|
cshutdn.h \
|
||||||
connect.h \
|
connect.h \
|
||||||
content_encoding.h \
|
content_encoding.h \
|
||||||
cookie.h \
|
cookie.h \
|
||||||
|
|||||||
@ -202,7 +202,7 @@ CURLcode Curl_conn_shutdown(struct Curl_easy *data, int sockindex, bool *done)
|
|||||||
if(!Curl_shutdown_started(data, sockindex)) {
|
if(!Curl_shutdown_started(data, sockindex)) {
|
||||||
CURL_TRC_M(data, "shutdown start on%s connection",
|
CURL_TRC_M(data, "shutdown start on%s connection",
|
||||||
sockindex ? " secondary" : "");
|
sockindex ? " secondary" : "");
|
||||||
Curl_shutdown_start(data, sockindex, &now);
|
Curl_shutdown_start(data, sockindex, 0, &now);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
timeout_ms = Curl_shutdown_timeleft(data->conn, sockindex, &now);
|
timeout_ms = Curl_shutdown_timeleft(data->conn, sockindex, &now);
|
||||||
|
|||||||
1184
lib/conncache.c
1184
lib/conncache.c
File diff suppressed because it is too large
Load Diff
@ -35,6 +35,19 @@ struct Curl_waitfds;
|
|||||||
struct Curl_multi;
|
struct Curl_multi;
|
||||||
struct Curl_share;
|
struct Curl_share;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Terminate the connection, e.g. close and destroy.
|
||||||
|
* If the connection is in a cpool, remove it.
|
||||||
|
* If a `cshutdn` is available (e.g. data has a multi handle),
|
||||||
|
* pass the connection to that for controlled shutdown.
|
||||||
|
* Otherwise terminate it right away.
|
||||||
|
* Takes ownership of `conn`.
|
||||||
|
* `data` should not be attached to a connection.
|
||||||
|
*/
|
||||||
|
void Curl_conn_terminate(struct Curl_easy *data,
|
||||||
|
struct connectdata *conn,
|
||||||
|
bool aborted);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Callback invoked when disconnecting connections.
|
* Callback invoked when disconnecting connections.
|
||||||
* @param data transfer last handling the connection, not attached
|
* @param data transfer last handling the connection, not attached
|
||||||
@ -54,12 +67,11 @@ struct cpool {
|
|||||||
curl_off_t next_connection_id;
|
curl_off_t next_connection_id;
|
||||||
curl_off_t next_easy_id;
|
curl_off_t next_easy_id;
|
||||||
struct curltime last_cleanup;
|
struct curltime last_cleanup;
|
||||||
struct Curl_llist shutdowns; /* The connections being shut down */
|
struct Curl_easy *idata; /* internal handle for maintenance */
|
||||||
struct Curl_easy *idata; /* internal handle used for discard */
|
|
||||||
struct Curl_multi *multi; /* != NULL iff pool belongs to multi */
|
|
||||||
struct Curl_share *share; /* != NULL iff pool belongs to share */
|
struct Curl_share *share; /* != NULL iff pool belongs to share */
|
||||||
Curl_cpool_disconnect_cb *disconnect_cb;
|
Curl_cpool_disconnect_cb *disconnect_cb;
|
||||||
BIT(locked);
|
BIT(locked);
|
||||||
|
BIT(initialised);
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Init the pool, pass multi only if pool is owned by it.
|
/* Init the pool, pass multi only if pool is owned by it.
|
||||||
@ -67,7 +79,7 @@ struct cpool {
|
|||||||
*/
|
*/
|
||||||
int Curl_cpool_init(struct cpool *cpool,
|
int Curl_cpool_init(struct cpool *cpool,
|
||||||
Curl_cpool_disconnect_cb *disconnect_cb,
|
Curl_cpool_disconnect_cb *disconnect_cb,
|
||||||
struct Curl_multi *multi,
|
struct Curl_easy *idata,
|
||||||
struct Curl_share *share,
|
struct Curl_share *share,
|
||||||
size_t size);
|
size_t size);
|
||||||
|
|
||||||
@ -78,14 +90,13 @@ void Curl_cpool_destroy(struct cpool *connc);
|
|||||||
* Assigns `data->id`. */
|
* Assigns `data->id`. */
|
||||||
void Curl_cpool_xfer_init(struct Curl_easy *data);
|
void Curl_cpool_xfer_init(struct Curl_easy *data);
|
||||||
|
|
||||||
/**
|
/* Get the connection with the given id from `data`'s conn pool. */
|
||||||
* Get the connection with the given id from the transfer's pool.
|
|
||||||
*/
|
|
||||||
struct connectdata *Curl_cpool_get_conn(struct Curl_easy *data,
|
struct connectdata *Curl_cpool_get_conn(struct Curl_easy *data,
|
||||||
curl_off_t conn_id);
|
curl_off_t conn_id);
|
||||||
|
|
||||||
CURLcode Curl_cpool_add_conn(struct Curl_easy *data,
|
/* Add the connection to the pool. */
|
||||||
struct connectdata *conn) WARN_UNUSED_RESULT;
|
CURLcode Curl_cpool_add(struct Curl_easy *data,
|
||||||
|
struct connectdata *conn) WARN_UNUSED_RESULT;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return if the pool has reached its configured limits for adding
|
* Return if the pool has reached its configured limits for adding
|
||||||
@ -131,17 +142,6 @@ bool Curl_cpool_find(struct Curl_easy *data,
|
|||||||
bool Curl_cpool_conn_now_idle(struct Curl_easy *data,
|
bool Curl_cpool_conn_now_idle(struct Curl_easy *data,
|
||||||
struct connectdata *conn);
|
struct connectdata *conn);
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove the connection from the pool and tear it down.
|
|
||||||
* If `aborted` is FALSE, the connection will be shut down first
|
|
||||||
* before closing and destroying it.
|
|
||||||
* If the shutdown is not immediately complete, the connection
|
|
||||||
* will be placed into the pool's shutdown queue.
|
|
||||||
*/
|
|
||||||
void Curl_cpool_disconnect(struct Curl_easy *data,
|
|
||||||
struct connectdata *conn,
|
|
||||||
bool aborted);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function scans the data's connection pool for half-open/dead
|
* This function scans the data's connection pool for half-open/dead
|
||||||
* connections, closes and removes them.
|
* connections, closes and removes them.
|
||||||
@ -178,22 +178,4 @@ void Curl_cpool_do_locked(struct Curl_easy *data,
|
|||||||
struct connectdata *conn,
|
struct connectdata *conn,
|
||||||
Curl_cpool_conn_do_cb *cb, void *cbdata);
|
Curl_cpool_conn_do_cb *cb, void *cbdata);
|
||||||
|
|
||||||
/**
|
|
||||||
* Add sockets and POLLIN/OUT flags for connections handled by the pool.
|
|
||||||
*/
|
|
||||||
CURLcode Curl_cpool_add_pollfds(struct cpool *connc,
|
|
||||||
struct curl_pollfds *cpfds);
|
|
||||||
unsigned int Curl_cpool_add_waitfds(struct cpool *connc,
|
|
||||||
struct Curl_waitfds *cwfds);
|
|
||||||
|
|
||||||
void Curl_cpool_setfds(struct cpool *cpool,
|
|
||||||
fd_set *read_fd_set, fd_set *write_fd_set,
|
|
||||||
int *maxfd);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Run connections on socket. If socket is CURL_SOCKET_TIMEOUT, run
|
|
||||||
* maintenance on all connections.
|
|
||||||
*/
|
|
||||||
void Curl_cpool_multi_perform(struct Curl_multi *multi, curl_socket_t s);
|
|
||||||
|
|
||||||
#endif /* HEADER_CURL_CONNCACHE_H */
|
#endif /* HEADER_CURL_CONNCACHE_H */
|
||||||
|
|||||||
@ -161,7 +161,7 @@ timediff_t Curl_timeleft(struct Curl_easy *data,
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Curl_shutdown_start(struct Curl_easy *data, int sockindex,
|
void Curl_shutdown_start(struct Curl_easy *data, int sockindex,
|
||||||
struct curltime *nowp)
|
int timeout_ms, struct curltime *nowp)
|
||||||
{
|
{
|
||||||
struct curltime now;
|
struct curltime now;
|
||||||
|
|
||||||
@ -171,8 +171,13 @@ void Curl_shutdown_start(struct Curl_easy *data, int sockindex,
|
|||||||
nowp = &now;
|
nowp = &now;
|
||||||
}
|
}
|
||||||
data->conn->shutdown.start[sockindex] = *nowp;
|
data->conn->shutdown.start[sockindex] = *nowp;
|
||||||
data->conn->shutdown.timeout_ms = (data->set.shutdowntimeout > 0) ?
|
data->conn->shutdown.timeout_ms = (timeout_ms >= 0) ?
|
||||||
data->set.shutdowntimeout : DEFAULT_SHUTDOWN_TIMEOUT_MS;
|
(unsigned int)timeout_ms :
|
||||||
|
((data->set.shutdowntimeout > 0) ?
|
||||||
|
data->set.shutdowntimeout : DEFAULT_SHUTDOWN_TIMEOUT_MS);
|
||||||
|
if(data->conn->shutdown.timeout_ms)
|
||||||
|
Curl_expire_ex(data, nowp, data->conn->shutdown.timeout_ms,
|
||||||
|
EXPIRE_SHUTDOWN);
|
||||||
}
|
}
|
||||||
|
|
||||||
timediff_t Curl_shutdown_timeleft(struct connectdata *conn, int sockindex,
|
timediff_t Curl_shutdown_timeleft(struct connectdata *conn, int sockindex,
|
||||||
|
|||||||
@ -45,7 +45,7 @@ timediff_t Curl_timeleft(struct Curl_easy *data,
|
|||||||
#define DEFAULT_SHUTDOWN_TIMEOUT_MS (2 * 1000)
|
#define DEFAULT_SHUTDOWN_TIMEOUT_MS (2 * 1000)
|
||||||
|
|
||||||
void Curl_shutdown_start(struct Curl_easy *data, int sockindex,
|
void Curl_shutdown_start(struct Curl_easy *data, int sockindex,
|
||||||
struct curltime *nowp);
|
int timeout_ms, struct curltime *nowp);
|
||||||
|
|
||||||
/* return how much time there is left to shutdown the connection at
|
/* return how much time there is left to shutdown the connection at
|
||||||
* sockindex. Returns 0 if there is no limit or shutdown has not started. */
|
* sockindex. Returns 0 if there is no limit or shutdown has not started. */
|
||||||
|
|||||||
566
lib/cshutdn.c
Normal file
566
lib/cshutdn.c
Normal file
@ -0,0 +1,566 @@
|
|||||||
|
/***************************************************************************
|
||||||
|
* _ _ ____ _
|
||||||
|
* Project ___| | | | _ \| |
|
||||||
|
* / __| | | | |_) | |
|
||||||
|
* | (__| |_| | _ <| |___
|
||||||
|
* \___|\___/|_| \_\_____|
|
||||||
|
*
|
||||||
|
* Copyright (C) Linus Nielsen Feltzing, <linus@haxx.se>
|
||||||
|
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||||
|
*
|
||||||
|
* This software is licensed as described in the file COPYING, which
|
||||||
|
* you should have received as part of this distribution. The terms
|
||||||
|
* are also available at https://curl.se/docs/copyright.html.
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
* furnished to do so, under the terms of the COPYING file.
|
||||||
|
*
|
||||||
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||||
|
* KIND, either express or implied.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: curl
|
||||||
|
*
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#include "curl_setup.h"
|
||||||
|
|
||||||
|
#include <curl/curl.h>
|
||||||
|
|
||||||
|
#include "urldata.h"
|
||||||
|
#include "url.h"
|
||||||
|
#include "cfilters.h"
|
||||||
|
#include "progress.h"
|
||||||
|
#include "multiif.h"
|
||||||
|
#include "multi_ev.h"
|
||||||
|
#include "sendf.h"
|
||||||
|
#include "cshutdn.h"
|
||||||
|
#include "http_negotiate.h"
|
||||||
|
#include "http_ntlm.h"
|
||||||
|
#include "sigpipe.h"
|
||||||
|
#include "connect.h"
|
||||||
|
#include "select.h"
|
||||||
|
#include "strcase.h"
|
||||||
|
#include "strparse.h"
|
||||||
|
|
||||||
|
/* The last 3 #include files should be in this order */
|
||||||
|
#include "curl_printf.h"
|
||||||
|
#include "curl_memory.h"
|
||||||
|
#include "memdebug.h"
|
||||||
|
|
||||||
|
|
||||||
|
static void cshutdn_run_conn_handler(struct Curl_easy *data,
|
||||||
|
struct connectdata *conn)
|
||||||
|
{
|
||||||
|
if(!conn->bits.shutdown_handler) {
|
||||||
|
if(conn->dns_entry)
|
||||||
|
Curl_resolv_unlink(data, &conn->dns_entry);
|
||||||
|
|
||||||
|
/* Cleanup NTLM connection-related data */
|
||||||
|
Curl_http_auth_cleanup_ntlm(conn);
|
||||||
|
|
||||||
|
/* Cleanup NEGOTIATE connection-related data */
|
||||||
|
Curl_http_auth_cleanup_negotiate(conn);
|
||||||
|
|
||||||
|
if(conn->handler && conn->handler->disconnect) {
|
||||||
|
/* Some disconnect handlers do a blocking wait on server responses.
|
||||||
|
* FTP/IMAP/SMTP and SFTP are among them. When using the internal
|
||||||
|
* handle, set an overall short timeout so we do not hang for the
|
||||||
|
* default 120 seconds. */
|
||||||
|
if(data->state.internal) {
|
||||||
|
data->set.timeout = DEFAULT_SHUTDOWN_TIMEOUT_MS;
|
||||||
|
(void)Curl_pgrsTime(data, TIMER_STARTOP);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This is set if protocol-specific cleanups should be made */
|
||||||
|
DEBUGF(infof(data, "connection #%" FMT_OFF_T
|
||||||
|
", shutdown protocol handler (aborted=%d)",
|
||||||
|
conn->connection_id, conn->bits.aborted));
|
||||||
|
/* There are protocol handlers that block on retrieving
|
||||||
|
* server responses here (FTP). Set a short timeout. */
|
||||||
|
conn->handler->disconnect(data, conn, conn->bits.aborted);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* possible left-overs from the async name resolvers */
|
||||||
|
Curl_resolver_cancel(data);
|
||||||
|
|
||||||
|
conn->bits.shutdown_handler = TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cshutdn_run_once(struct Curl_easy *data,
|
||||||
|
struct connectdata *conn,
|
||||||
|
bool *done)
|
||||||
|
{
|
||||||
|
CURLcode r1, r2;
|
||||||
|
bool done1, done2;
|
||||||
|
|
||||||
|
/* We expect to be attached when called */
|
||||||
|
DEBUGASSERT(data->conn == conn);
|
||||||
|
|
||||||
|
cshutdn_run_conn_handler(data, conn);
|
||||||
|
|
||||||
|
if(conn->bits.shutdown_filters) {
|
||||||
|
*done = TRUE;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!conn->connect_only && Curl_conn_is_connected(conn, FIRSTSOCKET))
|
||||||
|
r1 = Curl_conn_shutdown(data, FIRSTSOCKET, &done1);
|
||||||
|
else {
|
||||||
|
r1 = CURLE_OK;
|
||||||
|
done1 = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!conn->connect_only && Curl_conn_is_connected(conn, SECONDARYSOCKET))
|
||||||
|
r2 = Curl_conn_shutdown(data, SECONDARYSOCKET, &done2);
|
||||||
|
else {
|
||||||
|
r2 = CURLE_OK;
|
||||||
|
done2 = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* we are done when any failed or both report success */
|
||||||
|
*done = (r1 || r2 || (done1 && done2));
|
||||||
|
if(*done)
|
||||||
|
conn->bits.shutdown_filters = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Curl_cshutdn_run_once(struct Curl_easy *data,
|
||||||
|
struct connectdata *conn,
|
||||||
|
bool *done)
|
||||||
|
{
|
||||||
|
DEBUGASSERT(!data->conn);
|
||||||
|
Curl_attach_connection(data, conn);
|
||||||
|
cshutdn_run_once(data, conn, done);
|
||||||
|
CURL_TRC_M(data, "[SHUTDOWN] shutdown, done=%d", *done);
|
||||||
|
Curl_detach_connection(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Curl_cshutdn_terminate(struct Curl_easy *data,
|
||||||
|
struct connectdata *conn,
|
||||||
|
bool do_shutdown)
|
||||||
|
{
|
||||||
|
struct Curl_easy *admin = data;
|
||||||
|
bool done;
|
||||||
|
|
||||||
|
/* there must be a connection to close */
|
||||||
|
DEBUGASSERT(conn);
|
||||||
|
/* it must be removed from the connection pool */
|
||||||
|
DEBUGASSERT(!conn->bits.in_cpool);
|
||||||
|
/* the transfer must be detached from the connection */
|
||||||
|
DEBUGASSERT(data && !data->conn);
|
||||||
|
|
||||||
|
/* If we can obtain an internal admin handle, use that to attach
|
||||||
|
* and terminate the connection. Some protocol will try to mess with
|
||||||
|
* `data` during shutdown and we do not want that with a `data` from
|
||||||
|
* the application. */
|
||||||
|
if(data->multi && data->multi->admin)
|
||||||
|
admin = data->multi->admin;
|
||||||
|
|
||||||
|
Curl_attach_connection(admin, conn);
|
||||||
|
|
||||||
|
cshutdn_run_conn_handler(admin, conn);
|
||||||
|
if(do_shutdown) {
|
||||||
|
/* Make a last attempt to shutdown handlers and filters, if
|
||||||
|
* not done so already. */
|
||||||
|
cshutdn_run_once(admin, conn, &done);
|
||||||
|
}
|
||||||
|
CURL_TRC_M(admin, "[SHUTDOWN] closing connection");
|
||||||
|
Curl_conn_close(admin, SECONDARYSOCKET);
|
||||||
|
Curl_conn_close(admin, FIRSTSOCKET);
|
||||||
|
Curl_detach_connection(admin);
|
||||||
|
|
||||||
|
if(data->multi)
|
||||||
|
Curl_multi_ev_conn_done(data->multi, data, conn);
|
||||||
|
Curl_conn_free(admin, conn);
|
||||||
|
|
||||||
|
if(data->multi) {
|
||||||
|
CURL_TRC_M(data, "[SHUTDOWN] trigger multi connchanged");
|
||||||
|
Curl_multi_connchanged(data->multi);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cshutdn_destroy_oldest(struct cshutdn *cshutdn,
|
||||||
|
struct Curl_easy *data)
|
||||||
|
{
|
||||||
|
struct Curl_llist_node *e;
|
||||||
|
struct connectdata *conn;
|
||||||
|
|
||||||
|
e = Curl_llist_head(&cshutdn->list);
|
||||||
|
if(e) {
|
||||||
|
SIGPIPE_VARIABLE(pipe_st);
|
||||||
|
conn = Curl_node_elem(e);
|
||||||
|
Curl_node_remove(e);
|
||||||
|
sigpipe_init(&pipe_st);
|
||||||
|
sigpipe_apply(data, &pipe_st);
|
||||||
|
Curl_cshutdn_terminate(data, conn, FALSE);
|
||||||
|
sigpipe_restore(&pipe_st);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define NUM_POLLS_ON_STACK 10
|
||||||
|
|
||||||
|
static CURLcode cshutdn_wait(struct cshutdn *cshutdn,
|
||||||
|
struct Curl_easy *data,
|
||||||
|
int timeout_ms)
|
||||||
|
{
|
||||||
|
struct pollfd a_few_on_stack[NUM_POLLS_ON_STACK];
|
||||||
|
struct curl_pollfds cpfds;
|
||||||
|
CURLcode result;
|
||||||
|
|
||||||
|
Curl_pollfds_init(&cpfds, a_few_on_stack, NUM_POLLS_ON_STACK);
|
||||||
|
|
||||||
|
result = Curl_cshutdn_add_pollfds(cshutdn, data, &cpfds);
|
||||||
|
if(result)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
Curl_poll(cpfds.pfds, cpfds.n, CURLMIN(timeout_ms, 1000));
|
||||||
|
|
||||||
|
out:
|
||||||
|
Curl_pollfds_cleanup(&cpfds);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void cshutdn_perform(struct cshutdn *cshutdn,
|
||||||
|
struct Curl_easy *data)
|
||||||
|
{
|
||||||
|
struct Curl_llist_node *e = Curl_llist_head(&cshutdn->list);
|
||||||
|
struct Curl_llist_node *enext;
|
||||||
|
struct connectdata *conn;
|
||||||
|
struct curltime *nowp = NULL;
|
||||||
|
struct curltime now;
|
||||||
|
timediff_t next_expire_ms = 0, ms;
|
||||||
|
bool done;
|
||||||
|
|
||||||
|
if(!e)
|
||||||
|
return;
|
||||||
|
|
||||||
|
CURL_TRC_M(data, "[SHUTDOWN] perform on %zu connections",
|
||||||
|
Curl_llist_count(&cshutdn->list));
|
||||||
|
while(e) {
|
||||||
|
enext = Curl_node_next(e);
|
||||||
|
conn = Curl_node_elem(e);
|
||||||
|
Curl_cshutdn_run_once(data, conn, &done);
|
||||||
|
if(done) {
|
||||||
|
Curl_node_remove(e);
|
||||||
|
Curl_cshutdn_terminate(data, conn, FALSE);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* idata has one timer list, but maybe more than one connection.
|
||||||
|
* Set EXPIRE_SHUTDOWN to the smallest time left for all. */
|
||||||
|
if(!nowp) {
|
||||||
|
now = Curl_now();
|
||||||
|
nowp = &now;
|
||||||
|
}
|
||||||
|
ms = Curl_conn_shutdown_timeleft(conn, nowp);
|
||||||
|
if(ms && ms < next_expire_ms)
|
||||||
|
next_expire_ms = ms;
|
||||||
|
}
|
||||||
|
e = enext;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(next_expire_ms)
|
||||||
|
Curl_expire_ex(data, nowp, next_expire_ms, EXPIRE_SHUTDOWN);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void cshutdn_terminate_all(struct cshutdn *cshutdn,
|
||||||
|
struct Curl_easy *data,
|
||||||
|
int timeout_ms)
|
||||||
|
{
|
||||||
|
struct curltime started = Curl_now();
|
||||||
|
struct Curl_llist_node *e;
|
||||||
|
SIGPIPE_VARIABLE(pipe_st);
|
||||||
|
|
||||||
|
DEBUGASSERT(cshutdn);
|
||||||
|
DEBUGASSERT(data);
|
||||||
|
|
||||||
|
CURL_TRC_M(data, "[SHUTDOWN] shutdown all");
|
||||||
|
sigpipe_init(&pipe_st);
|
||||||
|
sigpipe_apply(data, &pipe_st);
|
||||||
|
|
||||||
|
while(Curl_llist_head(&cshutdn->list)) {
|
||||||
|
timediff_t timespent;
|
||||||
|
int remain_ms;
|
||||||
|
|
||||||
|
cshutdn_perform(cshutdn, data);
|
||||||
|
|
||||||
|
if(!Curl_llist_head(&cshutdn->list)) {
|
||||||
|
CURL_TRC_M(data, "[SHUTDOWN] shutdown finished cleanly");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* wait for activity, timeout or "nothing" */
|
||||||
|
timespent = Curl_timediff(Curl_now(), started);
|
||||||
|
if(timespent >= (timediff_t)timeout_ms) {
|
||||||
|
CURL_TRC_M(data, "[SHUTDOWN] shutdown finished, %s",
|
||||||
|
(timeout_ms > 0) ? "timeout" : "best effort done");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
remain_ms = timeout_ms - (int)timespent;
|
||||||
|
if(cshutdn_wait(cshutdn, data, remain_ms)) {
|
||||||
|
CURL_TRC_M(data, "[SHUTDOWN] shutdown finished, aborted");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Terminate any remaining. */
|
||||||
|
e = Curl_llist_head(&cshutdn->list);
|
||||||
|
while(e) {
|
||||||
|
struct connectdata *conn = Curl_node_elem(e);
|
||||||
|
Curl_node_remove(e);
|
||||||
|
Curl_cshutdn_terminate(data, conn, FALSE);
|
||||||
|
e = Curl_llist_head(&cshutdn->list);
|
||||||
|
}
|
||||||
|
DEBUGASSERT(!Curl_llist_count(&cshutdn->list));
|
||||||
|
|
||||||
|
Curl_hostcache_clean(data, data->dns.hostcache);
|
||||||
|
|
||||||
|
sigpipe_restore(&pipe_st);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int Curl_cshutdn_init(struct cshutdn *cshutdn,
|
||||||
|
struct Curl_multi *multi)
|
||||||
|
{
|
||||||
|
DEBUGASSERT(multi);
|
||||||
|
cshutdn->multi = multi;
|
||||||
|
Curl_llist_init(&cshutdn->list, NULL);
|
||||||
|
cshutdn->initialised = TRUE;
|
||||||
|
return 0; /* good */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Curl_cshutdn_destroy(struct cshutdn *cshutdn,
|
||||||
|
struct Curl_easy *data)
|
||||||
|
{
|
||||||
|
if(cshutdn->initialised && data) {
|
||||||
|
int timeout_ms = 0;
|
||||||
|
/* Just for testing, run graceful shutdown */
|
||||||
|
#ifdef DEBUGBUILD
|
||||||
|
{
|
||||||
|
const char *p = getenv("CURL_GRACEFUL_SHUTDOWN");
|
||||||
|
if(p) {
|
||||||
|
curl_off_t l;
|
||||||
|
if(!Curl_str_number(&p, &l, INT_MAX))
|
||||||
|
timeout_ms = (int)l;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
CURL_TRC_M(data, "[SHUTDOWN] destroy, %zu connections, timeout=%dms",
|
||||||
|
Curl_llist_count(&cshutdn->list), timeout_ms);
|
||||||
|
cshutdn_terminate_all(cshutdn, data, timeout_ms);
|
||||||
|
}
|
||||||
|
cshutdn->multi = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Curl_cshutdn_count(struct Curl_easy *data)
|
||||||
|
{
|
||||||
|
if(data && data->multi) {
|
||||||
|
struct cshutdn *csd = &data->multi->cshutdn;
|
||||||
|
return Curl_llist_count(&csd->list);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Curl_cshutdn_dest_count(struct Curl_easy *data,
|
||||||
|
const char *destination)
|
||||||
|
{
|
||||||
|
if(data && data->multi) {
|
||||||
|
struct cshutdn *csd = &data->multi->cshutdn;
|
||||||
|
size_t n = 0;
|
||||||
|
struct Curl_llist_node *e = Curl_llist_head(&csd->list);
|
||||||
|
while(e) {
|
||||||
|
struct connectdata *conn = Curl_node_elem(e);
|
||||||
|
if(!strcmp(destination, conn->destination))
|
||||||
|
++n;
|
||||||
|
e = Curl_node_next(e);
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static CURLMcode cshutdn_update_ev(struct cshutdn *cshutdn,
|
||||||
|
struct Curl_easy *data,
|
||||||
|
struct connectdata *conn)
|
||||||
|
{
|
||||||
|
CURLMcode mresult;
|
||||||
|
|
||||||
|
DEBUGASSERT(cshutdn);
|
||||||
|
DEBUGASSERT(cshutdn->multi->socket_cb);
|
||||||
|
|
||||||
|
Curl_attach_connection(data, conn);
|
||||||
|
mresult = Curl_multi_ev_assess_conn(cshutdn->multi, data, conn);
|
||||||
|
Curl_detach_connection(data);
|
||||||
|
return mresult;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Curl_cshutdn_add(struct cshutdn *cshutdn,
|
||||||
|
struct connectdata *conn,
|
||||||
|
size_t conns_in_pool)
|
||||||
|
{
|
||||||
|
struct Curl_easy *data = cshutdn->multi->admin;
|
||||||
|
size_t max_total = (cshutdn->multi->max_total_connections > 0) ?
|
||||||
|
(size_t)cshutdn->multi->max_total_connections : 0;
|
||||||
|
|
||||||
|
/* Add the connection to our shutdown list for non-blocking shutdown
|
||||||
|
* during multi processing. */
|
||||||
|
if(max_total > 0 && (max_total <=
|
||||||
|
(conns_in_pool + Curl_llist_count(&cshutdn->list)))) {
|
||||||
|
CURL_TRC_M(data, "[SHUTDOWN] discarding oldest shutdown connection "
|
||||||
|
"due to connection limit of %zu", max_total);
|
||||||
|
cshutdn_destroy_oldest(cshutdn, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(cshutdn->multi->socket_cb) {
|
||||||
|
if(cshutdn_update_ev(cshutdn, data, conn)) {
|
||||||
|
CURL_TRC_M(data, "[SHUTDOWN] update events failed, discarding #%"
|
||||||
|
FMT_OFF_T, conn->connection_id);
|
||||||
|
Curl_cshutdn_terminate(data, conn, FALSE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Curl_llist_append(&cshutdn->list, conn, &conn->cshutdn_node);
|
||||||
|
CURL_TRC_M(data, "[SHUTDOWN] added #%" FMT_OFF_T
|
||||||
|
" to shutdowns, now %zu conns in shutdown",
|
||||||
|
conn->connection_id, Curl_llist_count(&cshutdn->list));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void cshutdn_multi_socket(struct cshutdn *cshutdn,
|
||||||
|
struct Curl_easy *data,
|
||||||
|
curl_socket_t s)
|
||||||
|
{
|
||||||
|
struct Curl_llist_node *e;
|
||||||
|
struct connectdata *conn;
|
||||||
|
bool done;
|
||||||
|
|
||||||
|
DEBUGASSERT(cshutdn->multi->socket_cb);
|
||||||
|
e = Curl_llist_head(&cshutdn->list);
|
||||||
|
while(e) {
|
||||||
|
conn = Curl_node_elem(e);
|
||||||
|
if(s == conn->sock[FIRSTSOCKET] || s == conn->sock[SECONDARYSOCKET]) {
|
||||||
|
Curl_cshutdn_run_once(data, conn, &done);
|
||||||
|
if(done || cshutdn_update_ev(cshutdn, data, conn)) {
|
||||||
|
Curl_node_remove(e);
|
||||||
|
Curl_cshutdn_terminate(data, conn, FALSE);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
e = Curl_node_next(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Curl_cshutdn_perform(struct cshutdn *cshutdn,
|
||||||
|
struct Curl_easy *data,
|
||||||
|
curl_socket_t s)
|
||||||
|
{
|
||||||
|
if((s == CURL_SOCKET_TIMEOUT) || (!cshutdn->multi->socket_cb))
|
||||||
|
cshutdn_perform(cshutdn, data);
|
||||||
|
else
|
||||||
|
cshutdn_multi_socket(cshutdn, data, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* return fd_set info about the shutdown connections */
|
||||||
|
void Curl_cshutdn_setfds(struct cshutdn *cshutdn,
|
||||||
|
struct Curl_easy *data,
|
||||||
|
fd_set *read_fd_set, fd_set *write_fd_set,
|
||||||
|
int *maxfd)
|
||||||
|
{
|
||||||
|
if(Curl_llist_head(&cshutdn->list)) {
|
||||||
|
struct Curl_llist_node *e;
|
||||||
|
|
||||||
|
for(e = Curl_llist_head(&cshutdn->list); e;
|
||||||
|
e = Curl_node_next(e)) {
|
||||||
|
struct easy_pollset ps;
|
||||||
|
unsigned int i;
|
||||||
|
struct connectdata *conn = Curl_node_elem(e);
|
||||||
|
memset(&ps, 0, sizeof(ps));
|
||||||
|
Curl_attach_connection(data, conn);
|
||||||
|
Curl_conn_adjust_pollset(data, conn, &ps);
|
||||||
|
Curl_detach_connection(data);
|
||||||
|
|
||||||
|
for(i = 0; i < ps.num; i++) {
|
||||||
|
#if defined(__DJGPP__)
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Warith-conversion"
|
||||||
|
#endif
|
||||||
|
if(ps.actions[i] & CURL_POLL_IN)
|
||||||
|
FD_SET(ps.sockets[i], read_fd_set);
|
||||||
|
if(ps.actions[i] & CURL_POLL_OUT)
|
||||||
|
FD_SET(ps.sockets[i], write_fd_set);
|
||||||
|
#if defined(__DJGPP__)
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
#endif
|
||||||
|
if((ps.actions[i] & (CURL_POLL_OUT | CURL_POLL_IN)) &&
|
||||||
|
((int)ps.sockets[i] > *maxfd))
|
||||||
|
*maxfd = (int)ps.sockets[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* return information about the shutdown connections */
|
||||||
|
unsigned int Curl_cshutdn_add_waitfds(struct cshutdn *cshutdn,
|
||||||
|
struct Curl_easy *data,
|
||||||
|
struct Curl_waitfds *cwfds)
|
||||||
|
{
|
||||||
|
unsigned int need = 0;
|
||||||
|
|
||||||
|
if(Curl_llist_head(&cshutdn->list)) {
|
||||||
|
struct Curl_llist_node *e;
|
||||||
|
struct easy_pollset ps;
|
||||||
|
struct connectdata *conn;
|
||||||
|
|
||||||
|
for(e = Curl_llist_head(&cshutdn->list); e;
|
||||||
|
e = Curl_node_next(e)) {
|
||||||
|
conn = Curl_node_elem(e);
|
||||||
|
memset(&ps, 0, sizeof(ps));
|
||||||
|
Curl_attach_connection(data, conn);
|
||||||
|
Curl_conn_adjust_pollset(data, conn, &ps);
|
||||||
|
Curl_detach_connection(data);
|
||||||
|
|
||||||
|
need += Curl_waitfds_add_ps(cwfds, &ps);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return need;
|
||||||
|
}
|
||||||
|
|
||||||
|
CURLcode Curl_cshutdn_add_pollfds(struct cshutdn *cshutdn,
|
||||||
|
struct Curl_easy *data,
|
||||||
|
struct curl_pollfds *cpfds)
|
||||||
|
{
|
||||||
|
CURLcode result = CURLE_OK;
|
||||||
|
|
||||||
|
if(Curl_llist_head(&cshutdn->list)) {
|
||||||
|
struct Curl_llist_node *e;
|
||||||
|
struct easy_pollset ps;
|
||||||
|
struct connectdata *conn;
|
||||||
|
|
||||||
|
for(e = Curl_llist_head(&cshutdn->list); e;
|
||||||
|
e = Curl_node_next(e)) {
|
||||||
|
conn = Curl_node_elem(e);
|
||||||
|
memset(&ps, 0, sizeof(ps));
|
||||||
|
Curl_attach_connection(data, conn);
|
||||||
|
Curl_conn_adjust_pollset(data, conn, &ps);
|
||||||
|
Curl_detach_connection(data);
|
||||||
|
|
||||||
|
result = Curl_pollfds_add_ps(cpfds, &ps);
|
||||||
|
if(result) {
|
||||||
|
Curl_pollfds_cleanup(cpfds);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out:
|
||||||
|
return result;
|
||||||
|
}
|
||||||
104
lib/cshutdn.h
Normal file
104
lib/cshutdn.h
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
#ifndef HEADER_CURL_CSHUTDN_H
|
||||||
|
#define HEADER_CURL_CSHUTDN_H
|
||||||
|
/***************************************************************************
|
||||||
|
* _ _ ____ _
|
||||||
|
* Project ___| | | | _ \| |
|
||||||
|
* / __| | | | |_) | |
|
||||||
|
* | (__| |_| | _ <| |___
|
||||||
|
* \___|\___/|_| \_\_____|
|
||||||
|
*
|
||||||
|
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||||
|
* Copyright (C) Linus Nielsen Feltzing, <linus@haxx.se>
|
||||||
|
*
|
||||||
|
* This software is licensed as described in the file COPYING, which
|
||||||
|
* you should have received as part of this distribution. The terms
|
||||||
|
* are also available at https://curl.se/docs/copyright.html.
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
* furnished to do so, under the terms of the COPYING file.
|
||||||
|
*
|
||||||
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||||
|
* KIND, either express or implied.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: curl
|
||||||
|
*
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#include <curl/curl.h>
|
||||||
|
#include "timeval.h"
|
||||||
|
|
||||||
|
struct connectdata;
|
||||||
|
struct Curl_easy;
|
||||||
|
struct curl_pollfds;
|
||||||
|
struct Curl_waitfds;
|
||||||
|
struct Curl_multi;
|
||||||
|
struct Curl_share;
|
||||||
|
|
||||||
|
/* Run the shutdown of the connection once.
|
||||||
|
* Will shortly attach/detach `data` to `conn` while doing so.
|
||||||
|
* `done` will be set TRUE if any error was encountered or if
|
||||||
|
* the connection was shut down completely. */
|
||||||
|
void Curl_cshutdn_run_once(struct Curl_easy *data,
|
||||||
|
struct connectdata *conn,
|
||||||
|
bool *done);
|
||||||
|
|
||||||
|
/* Terminates the connection, e.g. closes and destroys it.
|
||||||
|
* If `run_shutdown` is TRUE, the shutdown will be run once before
|
||||||
|
* terminating it.
|
||||||
|
* Takes ownership of `conn`. */
|
||||||
|
void Curl_cshutdn_terminate(struct Curl_easy *data,
|
||||||
|
struct connectdata *conn,
|
||||||
|
bool run_shutdown);
|
||||||
|
|
||||||
|
/* A `cshutdown` is always owned by a multi handle to maintain
|
||||||
|
* the connections to be shut down. It registers timers and
|
||||||
|
* sockets to monitor via the multi handle. */
|
||||||
|
struct cshutdn {
|
||||||
|
struct Curl_llist list; /* connections being shut down */
|
||||||
|
struct Curl_multi *multi; /* the multi owning this */
|
||||||
|
BIT(initialised);
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Init as part of the given multi handle. */
|
||||||
|
int Curl_cshutdn_init(struct cshutdn *cshutdn,
|
||||||
|
struct Curl_multi *multi);
|
||||||
|
|
||||||
|
/* Terminate all remaining connections and free resources. */
|
||||||
|
void Curl_cshutdn_destroy(struct cshutdn *cshutdn,
|
||||||
|
struct Curl_easy *data);
|
||||||
|
|
||||||
|
/* Number of connections being shut down. */
|
||||||
|
size_t Curl_cshutdn_count(struct Curl_easy *data);
|
||||||
|
|
||||||
|
/* Number of connections to the destination being shut down. */
|
||||||
|
size_t Curl_cshutdn_dest_count(struct Curl_easy *data,
|
||||||
|
const char *destination);
|
||||||
|
|
||||||
|
/* Add a connection to have it shut down. Will terminate the oldest
|
||||||
|
* connection when total connection limit of multi is being reached. */
|
||||||
|
void Curl_cshutdn_add(struct cshutdn *cshutdn,
|
||||||
|
struct connectdata *conn,
|
||||||
|
size_t conns_in_pool);
|
||||||
|
|
||||||
|
/* Add sockets and POLLIN/OUT flags for connections being shut down. */
|
||||||
|
CURLcode Curl_cshutdn_add_pollfds(struct cshutdn *cshutdn,
|
||||||
|
struct Curl_easy *data,
|
||||||
|
struct curl_pollfds *cpfds);
|
||||||
|
|
||||||
|
unsigned int Curl_cshutdn_add_waitfds(struct cshutdn *cshutdn,
|
||||||
|
struct Curl_easy *data,
|
||||||
|
struct Curl_waitfds *cwfds);
|
||||||
|
|
||||||
|
void Curl_cshutdn_setfds(struct cshutdn *cshutdn,
|
||||||
|
struct Curl_easy *data,
|
||||||
|
fd_set *read_fd_set, fd_set *write_fd_set,
|
||||||
|
int *maxfd);
|
||||||
|
|
||||||
|
/* Run shut down connections using socket. If socket is CURL_SOCKET_TIMEOUT,
|
||||||
|
* run maintenance on all connections. */
|
||||||
|
void Curl_cshutdn_perform(struct cshutdn *cshutdn,
|
||||||
|
struct Curl_easy *data,
|
||||||
|
curl_socket_t s);
|
||||||
|
|
||||||
|
#endif /* HEADER_CURL_CSHUTDN_H */
|
||||||
@ -770,7 +770,7 @@ static CURLcode easy_perform(struct Curl_easy *data, bool events)
|
|||||||
Curl_detach_connection(data);
|
Curl_detach_connection(data);
|
||||||
s = Curl_getconnectinfo(data, &c);
|
s = Curl_getconnectinfo(data, &c);
|
||||||
if((s != CURL_SOCKET_BAD) && c) {
|
if((s != CURL_SOCKET_BAD) && c) {
|
||||||
Curl_cpool_disconnect(data, c, TRUE);
|
Curl_conn_terminate(data, c, TRUE);
|
||||||
}
|
}
|
||||||
DEBUGASSERT(!data->conn);
|
DEBUGASSERT(!data->conn);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -587,8 +587,9 @@ static CURLcode ftp_readresp(struct Curl_easy *data,
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* store the latest code for later retrieval */
|
/* store the latest code for later retrieval, except during shutdown */
|
||||||
data->info.httpcode = code;
|
if(!data->conn->proto.ftpc.shutdown)
|
||||||
|
data->info.httpcode = code;
|
||||||
|
|
||||||
if(ftpcode)
|
if(ftpcode)
|
||||||
*ftpcode = code;
|
*ftpcode = code;
|
||||||
@ -3131,6 +3132,8 @@ static CURLcode ftp_block_statemach(struct Curl_easy *data,
|
|||||||
CURLcode result = CURLE_OK;
|
CURLcode result = CURLE_OK;
|
||||||
|
|
||||||
while(ftpc->state != FTP_STOP) {
|
while(ftpc->state != FTP_STOP) {
|
||||||
|
if(ftpc->shutdown)
|
||||||
|
CURL_TRC_FTP(data, "in shutdown, waiting for server response");
|
||||||
result = Curl_pp_statemach(data, pp, TRUE, TRUE /* disconnecting */);
|
result = Curl_pp_statemach(data, pp, TRUE, TRUE /* disconnecting */);
|
||||||
if(result)
|
if(result)
|
||||||
break;
|
break;
|
||||||
@ -4042,6 +4045,7 @@ static CURLcode ftp_quit(struct Curl_easy *data, struct connectdata *conn)
|
|||||||
CURLcode result = CURLE_OK;
|
CURLcode result = CURLE_OK;
|
||||||
|
|
||||||
if(conn->proto.ftpc.ctl_valid) {
|
if(conn->proto.ftpc.ctl_valid) {
|
||||||
|
CURL_TRC_FTP(data, "sending QUIT to close session");
|
||||||
result = Curl_pp_sendf(data, &conn->proto.ftpc.pp, "%s", "QUIT");
|
result = Curl_pp_sendf(data, &conn->proto.ftpc.pp, "%s", "QUIT");
|
||||||
if(result) {
|
if(result) {
|
||||||
failf(data, "Failure sending QUIT command: %s",
|
failf(data, "Failure sending QUIT command: %s",
|
||||||
@ -4081,6 +4085,7 @@ static CURLcode ftp_disconnect(struct Curl_easy *data,
|
|||||||
ftp_quit() will check the state of ftp->ctl_valid. If it is ok it
|
ftp_quit() will check the state of ftp->ctl_valid. If it is ok it
|
||||||
will try to send the QUIT command, otherwise it will just return.
|
will try to send the QUIT command, otherwise it will just return.
|
||||||
*/
|
*/
|
||||||
|
ftpc->shutdown = TRUE;
|
||||||
if(dead_connection)
|
if(dead_connection)
|
||||||
ftpc->ctl_valid = FALSE;
|
ftpc->ctl_valid = FALSE;
|
||||||
|
|
||||||
|
|||||||
@ -160,6 +160,7 @@ struct ftp_conn {
|
|||||||
BIT(cwdfail); /* set TRUE if a CWD command fails, as then we must prevent
|
BIT(cwdfail); /* set TRUE if a CWD command fails, as then we must prevent
|
||||||
caching the current directory */
|
caching the current directory */
|
||||||
BIT(wait_data_conn); /* this is set TRUE if data connection is waited */
|
BIT(wait_data_conn); /* this is set TRUE if data connection is waited */
|
||||||
|
BIT(shutdown); /* connection is being shutdown, e.g. QUIT */
|
||||||
};
|
};
|
||||||
|
|
||||||
#define DEFAULT_ACCEPT_TIMEOUT 60000 /* milliseconds == one minute */
|
#define DEFAULT_ACCEPT_TIMEOUT 60000 /* milliseconds == one minute */
|
||||||
|
|||||||
@ -1450,7 +1450,7 @@ CURLcode Curl_once_resolved(struct Curl_easy *data, bool *protocol_done)
|
|||||||
|
|
||||||
if(result) {
|
if(result) {
|
||||||
Curl_detach_connection(data);
|
Curl_detach_connection(data);
|
||||||
Curl_cpool_disconnect(data, conn, TRUE);
|
Curl_conn_terminate(data, conn, TRUE);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
62
lib/multi.c
62
lib/multi.c
@ -227,8 +227,23 @@ struct Curl_multi *Curl_multi_handle(size_t ev_hashsize, /* event hash */
|
|||||||
Curl_hash_init(&multi->proto_hash, 23,
|
Curl_hash_init(&multi->proto_hash, 23,
|
||||||
Curl_hash_str, Curl_str_key_compare, ph_freeentry);
|
Curl_hash_str, Curl_str_key_compare, ph_freeentry);
|
||||||
|
|
||||||
|
multi->admin = curl_easy_init();
|
||||||
|
if(!multi->admin)
|
||||||
|
goto error;
|
||||||
|
/* Initialize admin handle to operate inside this multi */
|
||||||
|
multi->admin->multi = multi;
|
||||||
|
multi->admin->state.internal = TRUE;
|
||||||
|
Curl_llist_init(&multi->admin->state.timeoutlist, NULL);
|
||||||
|
#ifdef DEBUGBUILD
|
||||||
|
if(getenv("CURL_DEBUG"))
|
||||||
|
multi->admin->set.verbose = TRUE;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if(Curl_cshutdn_init(&multi->cshutdn, multi))
|
||||||
|
goto error;
|
||||||
|
|
||||||
if(Curl_cpool_init(&multi->cpool, Curl_on_disconnect,
|
if(Curl_cpool_init(&multi->cpool, Curl_on_disconnect,
|
||||||
multi, NULL, chashsize))
|
multi->admin, NULL, chashsize))
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
if(Curl_ssl_scache_create(sesssize, 2, &multi->ssl_scache))
|
if(Curl_ssl_scache_create(sesssize, 2, &multi->ssl_scache))
|
||||||
@ -264,7 +279,13 @@ error:
|
|||||||
Curl_hash_destroy(&multi->proto_hash);
|
Curl_hash_destroy(&multi->proto_hash);
|
||||||
Curl_hash_destroy(&multi->hostcache);
|
Curl_hash_destroy(&multi->hostcache);
|
||||||
Curl_cpool_destroy(&multi->cpool);
|
Curl_cpool_destroy(&multi->cpool);
|
||||||
|
Curl_cshutdn_destroy(&multi->cshutdn, multi->admin);
|
||||||
Curl_ssl_scache_destroy(multi->ssl_scache);
|
Curl_ssl_scache_destroy(multi->ssl_scache);
|
||||||
|
if(multi->admin) {
|
||||||
|
multi->admin->multi = NULL;
|
||||||
|
Curl_close(&multi->admin);
|
||||||
|
}
|
||||||
|
|
||||||
free(multi);
|
free(multi);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@ -396,6 +417,15 @@ CURLMcode curl_multi_add_handle(CURLM *m, CURL *d)
|
|||||||
Curl_cpool_xfer_init(data);
|
Curl_cpool_xfer_init(data);
|
||||||
multi_warn_debug(multi, data);
|
multi_warn_debug(multi, data);
|
||||||
|
|
||||||
|
/* The admin handle only ever has default timeouts set. To improve the
|
||||||
|
state somewhat we clone the timeouts from each added handle so that the
|
||||||
|
admin handle always has the same timeouts as the most recently added
|
||||||
|
easy handle. */
|
||||||
|
multi->admin->set.timeout = data->set.timeout;
|
||||||
|
multi->admin->set.server_response_timeout =
|
||||||
|
data->set.server_response_timeout;
|
||||||
|
multi->admin->set.no_signal = data->set.no_signal;
|
||||||
|
|
||||||
CURL_TRC_M(data, "added, transfers=%u", multi->num_easy);
|
CURL_TRC_M(data, "added, transfers=%u", multi->num_easy);
|
||||||
return CURLM_OK;
|
return CURLM_OK;
|
||||||
}
|
}
|
||||||
@ -475,7 +505,7 @@ static void multi_done_locked(struct connectdata *conn,
|
|||||||
conn->bits.close, mdctx->premature,
|
conn->bits.close, mdctx->premature,
|
||||||
Curl_conn_is_multiplex(conn, FIRSTSOCKET));
|
Curl_conn_is_multiplex(conn, FIRSTSOCKET));
|
||||||
connclose(conn, "disconnecting");
|
connclose(conn, "disconnecting");
|
||||||
Curl_cpool_disconnect(data, conn, mdctx->premature);
|
Curl_conn_terminate(data, conn, mdctx->premature);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
/* the connection is no longer in use by any transfer */
|
/* the connection is no longer in use by any transfer */
|
||||||
@ -684,7 +714,7 @@ CURLMcode curl_multi_remove_handle(CURLM *m, CURL *d)
|
|||||||
curl_socket_t s;
|
curl_socket_t s;
|
||||||
s = Curl_getconnectinfo(data, &c);
|
s = Curl_getconnectinfo(data, &c);
|
||||||
if((s != CURL_SOCKET_BAD) && c) {
|
if((s != CURL_SOCKET_BAD) && c) {
|
||||||
Curl_cpool_disconnect(data, c, TRUE);
|
Curl_conn_terminate(data, c, TRUE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1032,7 +1062,8 @@ CURLMcode curl_multi_fdset(CURLM *m,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Curl_cpool_setfds(&multi->cpool, read_fd_set, write_fd_set, &this_max_fd);
|
Curl_cshutdn_setfds(&multi->cshutdn, multi->admin,
|
||||||
|
read_fd_set, write_fd_set, &this_max_fd);
|
||||||
|
|
||||||
*max_fd = this_max_fd;
|
*max_fd = this_max_fd;
|
||||||
|
|
||||||
@ -1068,7 +1099,7 @@ CURLMcode curl_multi_waitfds(CURLM *m,
|
|||||||
need += Curl_waitfds_add_ps(&cwfds, &ps);
|
need += Curl_waitfds_add_ps(&cwfds, &ps);
|
||||||
}
|
}
|
||||||
|
|
||||||
need += Curl_cpool_add_waitfds(&multi->cpool, &cwfds);
|
need += Curl_cshutdn_add_waitfds(&multi->cshutdn, multi->admin, &cwfds);
|
||||||
|
|
||||||
if(need != cwfds.n && ufds) {
|
if(need != cwfds.n && ufds) {
|
||||||
result = CURLM_OUT_OF_MEMORY;
|
result = CURLM_OUT_OF_MEMORY;
|
||||||
@ -1146,7 +1177,7 @@ static CURLMcode multi_wait(struct Curl_multi *multi,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(Curl_cpool_add_pollfds(&multi->cpool, &cpfds)) {
|
if(Curl_cshutdn_add_pollfds(&multi->cshutdn, multi->admin, &cpfds)) {
|
||||||
result = CURLM_OUT_OF_MEMORY;
|
result = CURLM_OUT_OF_MEMORY;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
@ -2492,7 +2523,7 @@ statemachine_end:
|
|||||||
We do not have to do this in every case block above where a
|
We do not have to do this in every case block above where a
|
||||||
failure is detected */
|
failure is detected */
|
||||||
Curl_detach_connection(data);
|
Curl_detach_connection(data);
|
||||||
Curl_cpool_disconnect(data, conn, dead_connection);
|
Curl_conn_terminate(data, conn, dead_connection);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(data->mstate == MSTATE_CONNECT) {
|
else if(data->mstate == MSTATE_CONNECT) {
|
||||||
@ -2581,7 +2612,7 @@ CURLMcode curl_multi_perform(CURLM *m, int *running_handles)
|
|||||||
pointer now */
|
pointer now */
|
||||||
n = Curl_node_next(e);
|
n = Curl_node_next(e);
|
||||||
|
|
||||||
if(data && data != multi->cpool.idata) {
|
if(data && data != multi->admin) {
|
||||||
/* connection pool handle is processed below */
|
/* connection pool handle is processed below */
|
||||||
sigpipe_apply(data, &pipe_st);
|
sigpipe_apply(data, &pipe_st);
|
||||||
result = multi_runsingle(multi, &now, data);
|
result = multi_runsingle(multi, &now, data);
|
||||||
@ -2590,8 +2621,8 @@ CURLMcode curl_multi_perform(CURLM *m, int *running_handles)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sigpipe_apply(multi->cpool.idata, &pipe_st);
|
sigpipe_apply(multi->admin, &pipe_st);
|
||||||
Curl_cpool_multi_perform(multi, CURL_SOCKET_TIMEOUT);
|
Curl_cshutdn_perform(&multi->cshutdn, multi->admin, CURL_SOCKET_TIMEOUT);
|
||||||
sigpipe_restore(&pipe_st);
|
sigpipe_restore(&pipe_st);
|
||||||
|
|
||||||
if(multi_ischanged(m, TRUE))
|
if(multi_ischanged(m, TRUE))
|
||||||
@ -2690,6 +2721,11 @@ CURLMcode curl_multi_cleanup(CURLM *m)
|
|||||||
}
|
}
|
||||||
|
|
||||||
Curl_cpool_destroy(&multi->cpool);
|
Curl_cpool_destroy(&multi->cpool);
|
||||||
|
Curl_cshutdn_destroy(&multi->cshutdn, multi->admin);
|
||||||
|
if(multi->admin) {
|
||||||
|
multi->admin->multi = NULL;
|
||||||
|
Curl_close(&multi->admin);
|
||||||
|
}
|
||||||
|
|
||||||
multi->magic = 0; /* not good anymore */
|
multi->magic = 0; /* not good anymore */
|
||||||
|
|
||||||
@ -2855,7 +2891,7 @@ static CURLMcode multi_run_expired(struct multi_run_ctx *mrc)
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
(void)add_next_timeout(mrc->now, multi, data);
|
(void)add_next_timeout(mrc->now, multi, data);
|
||||||
if(data == multi->cpool.idata) {
|
if(data == multi->admin) {
|
||||||
mrc->run_cpool = TRUE;
|
mrc->run_cpool = TRUE;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -2931,8 +2967,8 @@ static CURLMcode multi_socket(struct Curl_multi *multi,
|
|||||||
|
|
||||||
out:
|
out:
|
||||||
if(mrc.run_cpool) {
|
if(mrc.run_cpool) {
|
||||||
sigpipe_apply(multi->cpool.idata, &mrc.pipe_st);
|
sigpipe_apply(multi->admin, &mrc.pipe_st);
|
||||||
Curl_cpool_multi_perform(multi, s);
|
Curl_cshutdn_perform(&multi->cshutdn, multi->admin, s);
|
||||||
}
|
}
|
||||||
sigpipe_restore(&mrc.pipe_st);
|
sigpipe_restore(&mrc.pipe_st);
|
||||||
|
|
||||||
|
|||||||
@ -27,11 +27,13 @@
|
|||||||
#include "llist.h"
|
#include "llist.h"
|
||||||
#include "hash.h"
|
#include "hash.h"
|
||||||
#include "conncache.h"
|
#include "conncache.h"
|
||||||
|
#include "cshutdn.h"
|
||||||
#include "multi_ev.h"
|
#include "multi_ev.h"
|
||||||
#include "psl.h"
|
#include "psl.h"
|
||||||
#include "socketpair.h"
|
#include "socketpair.h"
|
||||||
|
|
||||||
struct connectdata;
|
struct connectdata;
|
||||||
|
struct Curl_easy;
|
||||||
|
|
||||||
struct Curl_message {
|
struct Curl_message {
|
||||||
struct Curl_llist_node list;
|
struct Curl_llist_node list;
|
||||||
@ -99,6 +101,8 @@ struct Curl_multi {
|
|||||||
struct Curl_llist msgsent; /* in MSGSENT */
|
struct Curl_llist msgsent; /* in MSGSENT */
|
||||||
curl_off_t next_easy_mid; /* next multi-id for easy handle added */
|
curl_off_t next_easy_mid; /* next multi-id for easy handle added */
|
||||||
|
|
||||||
|
struct Curl_easy *admin; /* internal easy handle for admin operations */
|
||||||
|
|
||||||
/* callback function and user data pointer for the *socket() API */
|
/* callback function and user data pointer for the *socket() API */
|
||||||
curl_socket_callback socket_cb;
|
curl_socket_callback socket_cb;
|
||||||
void *socket_userp;
|
void *socket_userp;
|
||||||
@ -140,8 +144,8 @@ struct Curl_multi {
|
|||||||
* the multi handle is cleaned up (see Curl_hash_add2()).*/
|
* the multi handle is cleaned up (see Curl_hash_add2()).*/
|
||||||
struct Curl_hash proto_hash;
|
struct Curl_hash proto_hash;
|
||||||
|
|
||||||
/* Shared connection cache (bundles)*/
|
struct cshutdn cshutdn; /* connection shutdown handling */
|
||||||
struct cpool cpool;
|
struct cpool cpool; /* connection pool (bundles) */
|
||||||
|
|
||||||
long max_host_connections; /* if >0, a fixed limit of the maximum number
|
long max_host_connections; /* if >0, a fixed limit of the maximum number
|
||||||
of connections per host */
|
of connections per host */
|
||||||
|
|||||||
@ -29,6 +29,7 @@
|
|||||||
|
|
||||||
#include "urldata.h"
|
#include "urldata.h"
|
||||||
#include "cfilters.h"
|
#include "cfilters.h"
|
||||||
|
#include "connect.h"
|
||||||
#include "sendf.h"
|
#include "sendf.h"
|
||||||
#include "select.h"
|
#include "select.h"
|
||||||
#include "progress.h"
|
#include "progress.h"
|
||||||
@ -74,6 +75,11 @@ timediff_t Curl_pp_state_timeout(struct Curl_easy *data,
|
|||||||
timeout_ms = CURLMIN(timeout_ms, timeout2_ms);
|
timeout_ms = CURLMIN(timeout_ms, timeout2_ms);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(disconnecting) {
|
||||||
|
timediff_t total_left_ms = Curl_timeleft(data, NULL, FALSE);
|
||||||
|
timeout_ms = CURLMIN(timeout_ms, CURLMAX(total_left_ms, 0));
|
||||||
|
}
|
||||||
|
|
||||||
return timeout_ms;
|
return timeout_ms;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,6 +102,7 @@ CURLcode Curl_pp_statemach(struct Curl_easy *data,
|
|||||||
return CURLE_OPERATION_TIMEDOUT; /* already too little time */
|
return CURLE_OPERATION_TIMEDOUT; /* already too little time */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DEBUGF(infof(data, "pp_statematch, timeout=%" FMT_TIMEDIFF_T, timeout_ms));
|
||||||
if(block) {
|
if(block) {
|
||||||
interval_ms = 1000; /* use 1 second timeout intervals */
|
interval_ms = 1000; /* use 1 second timeout intervals */
|
||||||
if(timeout_ms < interval_ms)
|
if(timeout_ms < interval_ms)
|
||||||
@ -135,6 +142,8 @@ CURLcode Curl_pp_statemach(struct Curl_easy *data,
|
|||||||
}
|
}
|
||||||
else if(rc)
|
else if(rc)
|
||||||
result = pp->statemachine(data, data->conn);
|
result = pp->statemachine(data, data->conn);
|
||||||
|
else if(disconnecting)
|
||||||
|
return CURLE_OPERATION_TIMEDOUT;
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
15
lib/share.c
15
lib/share.c
@ -47,6 +47,16 @@ curl_share_init(void)
|
|||||||
share->magic = CURL_GOOD_SHARE;
|
share->magic = CURL_GOOD_SHARE;
|
||||||
share->specifier |= (1 << CURL_LOCK_DATA_SHARE);
|
share->specifier |= (1 << CURL_LOCK_DATA_SHARE);
|
||||||
Curl_init_dnscache(&share->hostcache, 23);
|
Curl_init_dnscache(&share->hostcache, 23);
|
||||||
|
share->admin = curl_easy_init();
|
||||||
|
if(!share->admin) {
|
||||||
|
free(share);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
share->admin->state.internal = TRUE;
|
||||||
|
#ifdef DEBUGBUILD
|
||||||
|
if(getenv("CURL_DEBUG"))
|
||||||
|
share->admin->set.verbose = TRUE;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
return share;
|
return share;
|
||||||
@ -125,9 +135,9 @@ curl_share_setopt(CURLSH *sh, CURLSHoption option, ...)
|
|||||||
|
|
||||||
case CURL_LOCK_DATA_CONNECT:
|
case CURL_LOCK_DATA_CONNECT:
|
||||||
/* It is safe to set this option several times on a share. */
|
/* It is safe to set this option several times on a share. */
|
||||||
if(!share->cpool.idata) {
|
if(!share->cpool.initialised) {
|
||||||
if(Curl_cpool_init(&share->cpool, Curl_on_disconnect,
|
if(Curl_cpool_init(&share->cpool, Curl_on_disconnect,
|
||||||
NULL, share, 103))
|
share->admin, share, 103))
|
||||||
res = CURLSHE_NOMEM;
|
res = CURLSHE_NOMEM;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -257,6 +267,7 @@ curl_share_cleanup(CURLSH *sh)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
Curl_psl_destroy(&share->psl);
|
Curl_psl_destroy(&share->psl);
|
||||||
|
Curl_close(&share->admin);
|
||||||
|
|
||||||
if(share->unlockfunc)
|
if(share->unlockfunc)
|
||||||
share->unlockfunc(NULL, CURL_LOCK_DATA_SHARE, share->clientdata);
|
share->unlockfunc(NULL, CURL_LOCK_DATA_SHARE, share->clientdata);
|
||||||
|
|||||||
@ -31,6 +31,7 @@
|
|||||||
#include "urldata.h"
|
#include "urldata.h"
|
||||||
#include "conncache.h"
|
#include "conncache.h"
|
||||||
|
|
||||||
|
struct Curl_easy;
|
||||||
struct Curl_ssl_scache;
|
struct Curl_ssl_scache;
|
||||||
|
|
||||||
#define CURL_GOOD_SHARE 0x7e117a1e
|
#define CURL_GOOD_SHARE 0x7e117a1e
|
||||||
@ -48,6 +49,7 @@ struct Curl_share {
|
|||||||
curl_lock_function lockfunc;
|
curl_lock_function lockfunc;
|
||||||
curl_unlock_function unlockfunc;
|
curl_unlock_function unlockfunc;
|
||||||
void *clientdata;
|
void *clientdata;
|
||||||
|
struct Curl_easy *admin;
|
||||||
struct cpool cpool;
|
struct cpool cpool;
|
||||||
struct Curl_hash hostcache;
|
struct Curl_hash hostcache;
|
||||||
#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)
|
#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)
|
||||||
|
|||||||
18
lib/url.c
18
lib/url.c
@ -1209,7 +1209,7 @@ static bool url_match_conn(struct connectdata *conn, void *userdata)
|
|||||||
}
|
}
|
||||||
else if(Curl_conn_seems_dead(conn, data, NULL)) {
|
else if(Curl_conn_seems_dead(conn, data, NULL)) {
|
||||||
/* removed and disconnect. Do not treat as aborted. */
|
/* removed and disconnect. Do not treat as aborted. */
|
||||||
Curl_cpool_disconnect(data, conn, FALSE);
|
Curl_conn_terminate(data, conn, FALSE);
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3540,14 +3540,12 @@ static CURLcode create_conn(struct Curl_easy *data,
|
|||||||
/* Setup a "faked" transfer that will do nothing */
|
/* Setup a "faked" transfer that will do nothing */
|
||||||
if(!result) {
|
if(!result) {
|
||||||
Curl_attach_connection(data, conn);
|
Curl_attach_connection(data, conn);
|
||||||
result = Curl_cpool_add_conn(data, conn);
|
result = Curl_cpool_add(data, conn);
|
||||||
if(result)
|
if(!result) {
|
||||||
goto out;
|
/* Setup whatever necessary for a resumed transfer */
|
||||||
|
result = setup_range(data);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Setup whatever necessary for a resumed transfer
|
|
||||||
*/
|
|
||||||
result = setup_range(data);
|
|
||||||
if(result) {
|
if(result) {
|
||||||
DEBUGASSERT(conn->handler->done);
|
DEBUGASSERT(conn->handler->done);
|
||||||
/* we ignore the return code for the protocol-specific DONE */
|
/* we ignore the return code for the protocol-specific DONE */
|
||||||
@ -3684,7 +3682,7 @@ static CURLcode create_conn(struct Curl_easy *data,
|
|||||||
}
|
}
|
||||||
|
|
||||||
Curl_attach_connection(data, conn);
|
Curl_attach_connection(data, conn);
|
||||||
result = Curl_cpool_add_conn(data, conn);
|
result = Curl_cpool_add(data, conn);
|
||||||
if(result)
|
if(result)
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
@ -3823,7 +3821,7 @@ CURLcode Curl_connect(struct Curl_easy *data,
|
|||||||
/* We are not allowed to return failure with memory left allocated in the
|
/* We are not allowed to return failure with memory left allocated in the
|
||||||
connectdata struct, free those here */
|
connectdata struct, free those here */
|
||||||
Curl_detach_connection(data);
|
Curl_detach_connection(data);
|
||||||
Curl_cpool_disconnect(data, conn, TRUE);
|
Curl_conn_terminate(data, conn, TRUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
@ -753,6 +753,7 @@ struct ldapconninfo;
|
|||||||
*/
|
*/
|
||||||
struct connectdata {
|
struct connectdata {
|
||||||
struct Curl_llist_node cpool_node; /* conncache lists */
|
struct Curl_llist_node cpool_node; /* conncache lists */
|
||||||
|
struct Curl_llist_node cshutdn_node; /* cshutdn list */
|
||||||
|
|
||||||
curl_closesocket_callback fclosesocket; /* function closing the socket(s) */
|
curl_closesocket_callback fclosesocket; /* function closing the socket(s) */
|
||||||
void *closesocket_client;
|
void *closesocket_client;
|
||||||
@ -1133,6 +1134,7 @@ typedef enum {
|
|||||||
EXPIRE_QUIC,
|
EXPIRE_QUIC,
|
||||||
EXPIRE_FTP_ACCEPT,
|
EXPIRE_FTP_ACCEPT,
|
||||||
EXPIRE_ALPN_EYEBALLS,
|
EXPIRE_ALPN_EYEBALLS,
|
||||||
|
EXPIRE_SHUTDOWN,
|
||||||
EXPIRE_LAST /* not an actual timer, used as a marker only */
|
EXPIRE_LAST /* not an actual timer, used as a marker only */
|
||||||
} expire_id;
|
} expire_id;
|
||||||
|
|
||||||
|
|||||||
@ -1853,7 +1853,7 @@ CURLcode Curl_ssl_cfilter_remove(struct Curl_easy *data,
|
|||||||
if(cf->cft == &Curl_cft_ssl) {
|
if(cf->cft == &Curl_cft_ssl) {
|
||||||
bool done;
|
bool done;
|
||||||
CURL_TRC_CF(data, cf, "shutdown and remove SSL, start");
|
CURL_TRC_CF(data, cf, "shutdown and remove SSL, start");
|
||||||
Curl_shutdown_start(data, sockindex, NULL);
|
Curl_shutdown_start(data, sockindex, 0, NULL);
|
||||||
result = vtls_shutdown_blocking(cf, data, send_shutdown, &done);
|
result = vtls_shutdown_blocking(cf, data, send_shutdown, &done);
|
||||||
Curl_shutdown_clear(data, sockindex);
|
Curl_shutdown_clear(data, sockindex);
|
||||||
if(!result && !done) /* blocking failed? */
|
if(!result && !done) /* blocking failed? */
|
||||||
|
|||||||
@ -2589,6 +2589,10 @@ static int cb_socket(CURL *easy, curl_socket_t s, int action,
|
|||||||
int events = 0;
|
int events = 0;
|
||||||
(void)easy;
|
(void)easy;
|
||||||
|
|
||||||
|
#if DEBUG_UV
|
||||||
|
fprintf(tool_stderr, "parallel_event: cb_socket, fd=%d, action=%x, p=%p\n",
|
||||||
|
(int)s, action, socketp);
|
||||||
|
#endif
|
||||||
switch(action) {
|
switch(action) {
|
||||||
case CURL_POLL_IN:
|
case CURL_POLL_IN:
|
||||||
case CURL_POLL_OUT:
|
case CURL_POLL_OUT:
|
||||||
@ -2678,12 +2682,26 @@ static CURLcode parallel_event(struct parastate *s)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
result = s->result;
|
||||||
|
|
||||||
|
/* Make sure to return some kind of error if there was a multi problem */
|
||||||
|
if(s->mcode) {
|
||||||
|
result = (s->mcode == CURLM_OUT_OF_MEMORY) ? CURLE_OUT_OF_MEMORY :
|
||||||
|
/* The other multi errors should never happen, so return
|
||||||
|
something suitably generic */
|
||||||
|
CURLE_BAD_FUNCTION_ARGUMENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We need to cleanup the multi here, since the uv context lives on the
|
||||||
|
* stack and will be gone. multi_cleanup can triggere events! */
|
||||||
|
curl_multi_cleanup(s->multi);
|
||||||
|
|
||||||
#if DEBUG_UV
|
#if DEBUG_UV
|
||||||
fprintf(tool_stderr, "DONE parallel_event -> %d, mcode=%d, %d running, "
|
fprintf(tool_stderr, "DONE parallel_event -> %d, mcode=%d, %d running, "
|
||||||
"%d more\n",
|
"%d more\n",
|
||||||
s->result, s->mcode, uv.s->still_running, s->more_transfers);
|
result, s->mcode, uv.s->still_running, s->more_transfers);
|
||||||
#endif
|
#endif
|
||||||
return s->result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@ -2787,7 +2805,7 @@ static CURLcode parallel_transfers(struct GlobalConfig *global,
|
|||||||
#ifdef DEBUGBUILD
|
#ifdef DEBUGBUILD
|
||||||
if(global->test_event_based)
|
if(global->test_event_based)
|
||||||
#ifdef USE_LIBUV
|
#ifdef USE_LIBUV
|
||||||
result = parallel_event(s);
|
return parallel_event(s);
|
||||||
#else
|
#else
|
||||||
errorf(global, "Testing --parallel event-based requires libuv");
|
errorf(global, "Testing --parallel event-based requires libuv");
|
||||||
#endif
|
#endif
|
||||||
@ -3228,7 +3246,9 @@ CURLcode operate(struct GlobalConfig *global, int argc, argv_item_t argv[])
|
|||||||
curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_DNS);
|
curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_DNS);
|
||||||
curl_share_setopt(share, CURLSHOPT_SHARE,
|
curl_share_setopt(share, CURLSHOPT_SHARE,
|
||||||
CURL_LOCK_DATA_SSL_SESSION);
|
CURL_LOCK_DATA_SSL_SESSION);
|
||||||
curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_CONNECT);
|
/* Running parallel, use the multi connection cache */
|
||||||
|
if(!global->parallel)
|
||||||
|
curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_CONNECT);
|
||||||
curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_PSL);
|
curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_PSL);
|
||||||
curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_HSTS);
|
curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_HSTS);
|
||||||
|
|
||||||
|
|||||||
@ -67,8 +67,6 @@ run 1: foobar and so on fun!
|
|||||||
-> Mutex lock SHARE
|
-> Mutex lock SHARE
|
||||||
<- Mutex unlock SHARE
|
<- Mutex unlock SHARE
|
||||||
-> Mutex lock SHARE
|
-> Mutex lock SHARE
|
||||||
-> Mutex lock CONNECT
|
|
||||||
<- Mutex unlock CONNECT
|
|
||||||
<- Mutex unlock SHARE
|
<- Mutex unlock SHARE
|
||||||
</datacheck>
|
</datacheck>
|
||||||
</reply>
|
</reply>
|
||||||
|
|||||||
@ -105,7 +105,7 @@ class TestShutdown:
|
|||||||
r = curl.http_download(urls=[url], alpn_proto=proto)
|
r = curl.http_download(urls=[url], alpn_proto=proto)
|
||||||
r.check_response(http_status=200, count=count)
|
r.check_response(http_status=200, count=count)
|
||||||
shutdowns = [line for line in r.trace_lines
|
shutdowns = [line for line in r.trace_lines
|
||||||
if re.match(r'.*\[CPOOL\] shutdown, done=1', line)]
|
if re.match(r'.*\[SHUTDOWN\] shutdown, done=1', line)]
|
||||||
assert len(shutdowns) == count, f'{shutdowns}'
|
assert len(shutdowns) == count, f'{shutdowns}'
|
||||||
|
|
||||||
# run downloads with CURLOPT_FORBID_REUSE set, meaning *we* close
|
# run downloads with CURLOPT_FORBID_REUSE set, meaning *we* close
|
||||||
@ -128,7 +128,7 @@ class TestShutdown:
|
|||||||
])
|
])
|
||||||
r.check_exit_code(0)
|
r.check_exit_code(0)
|
||||||
shutdowns = [line for line in r.trace_lines
|
shutdowns = [line for line in r.trace_lines
|
||||||
if re.match(r'.*CPOOL\] shutdown, done=1', line)]
|
if re.match(r'.*SHUTDOWN\] shutdown, done=1', line)]
|
||||||
assert len(shutdowns) == count, f'{shutdowns}'
|
assert len(shutdowns) == count, f'{shutdowns}'
|
||||||
|
|
||||||
# run event-based downloads with CURLOPT_FORBID_REUSE set, meaning *we* close
|
# run event-based downloads with CURLOPT_FORBID_REUSE set, meaning *we* close
|
||||||
@ -153,7 +153,7 @@ class TestShutdown:
|
|||||||
r.check_response(http_status=200, count=count)
|
r.check_response(http_status=200, count=count)
|
||||||
# check that we closed all connections
|
# check that we closed all connections
|
||||||
closings = [line for line in r.trace_lines
|
closings = [line for line in r.trace_lines
|
||||||
if re.match(r'.*CPOOL\] closing', line)]
|
if re.match(r'.*SHUTDOWN\] closing', line)]
|
||||||
assert len(closings) == count, f'{closings}'
|
assert len(closings) == count, f'{closings}'
|
||||||
# check that all connection sockets were removed from event
|
# check that all connection sockets were removed from event
|
||||||
removes = [line for line in r.trace_lines
|
removes = [line for line in r.trace_lines
|
||||||
@ -178,5 +178,5 @@ class TestShutdown:
|
|||||||
r.check_response(http_status=200, count=2)
|
r.check_response(http_status=200, count=2)
|
||||||
# check connection cache closings
|
# check connection cache closings
|
||||||
shutdowns = [line for line in r.trace_lines
|
shutdowns = [line for line in r.trace_lines
|
||||||
if re.match(r'.*CPOOL\] shutdown, done=1', line)]
|
if re.match(r'.*SHUTDOWN\] shutdown, done=1', line)]
|
||||||
assert len(shutdowns) == 1, f'{shutdowns}'
|
assert len(shutdowns) == 1, f'{shutdowns}'
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user