lib: connect/h2/h3 refactor

Refactoring of connection setup and happy eyeballing. Move
nghttp2. ngtcp2, quiche and msh3 into connection filters.

 - eyeballing cfilter that uses sub-filters for performing parallel connects
 - socket cfilter for all transport types, including QUIC
 - QUIC implementations in cfilter, can now participate in eyeballing
 - connection setup is more dynamic in order to adapt to what filter did
   really connect.  Relevant to see if a SSL filter needs to be added or
   if SSL has already been provided
 - HTTP/3 test cases similar to HTTP/2
 - multiuse of parallel transfers for HTTP/3, tested for ngtcp2 and quiche

 - Fix for data attach/detach in VTLS filters that could lead to crashes
   during parallel transfers.
 - Eliminating setup() methods in cfilters, no longer needed.
 - Improving Curl_conn_is_alive() to replace Curl_connalive() and
   integrated ssl alive checks into cfilter.
 - Adding CF_CNTRL_CONN_INFO_UPDATE to tell filters to update
   connection into and persist it at the easy handle.

 - Several more cfilter related cleanups and moves:
   - stream_weigth and dependency info is now wrapped in struct
     Curl_data_priority
   - Curl_data_priority members depend is available in HTTP2|HTTP3
   - Curl_data_priority members depend on NGHTTP2 support
   - handling init/reset/cleanup of priority part of url.c
   - data->state.priority same struct, but shallow copy for compares only

 - PROTOPT_STREAM has been removed
   - Curl_conn_is_mulitplex() now available to check on capability

 - Adding query method to connection filters.
   - ngtcp2+quiche: implementing query for max concurrent transfers.

 - Adding is_alive and keep_alive cfilter methods. Adding DATA_SETUP event.
   - setting keepalive timestamp on connect
   - DATA_SETUP is called after the connection has been completely
     setup (but may not connected yet) to allow filters to initialize
     data members they use.

 - there is no socket to be had with msh3, it is unclear how select
   shall work

 - manual test via "curl --http3 https://curl.se" fail with "empty
   reply from server".

 - Various socket/conn related cleanups:
   - Curl_socket is now Curl_socket_open and in cf-socket.c
   - Curl_closesocket is now Curl_socket_close and in cf-socket.c
   - Curl_ssl_use has been replaced with Cur_conn_is_ssl
   - Curl_conn_tcp_accepted_set has been split into
     Curl_conn_tcp_listen_set and Curl_conn_tcp_accepted_set
     with a clearer purpose

Closes #10141
This commit is contained in:
Stefan Eissing 2022-12-30 09:14:55 +01:00 committed by Daniel Stenberg
parent 1c18f8da51
commit 71b7e01610
No known key found for this signature in database
GPG Key ID: 5CC908FDB71E12C2
48 changed files with 6908 additions and 4675 deletions

View File

@ -88,7 +88,8 @@ LIB_VQUIC_HFILES = \
vquic/msh3.h \
vquic/ngtcp2.h \
vquic/quiche.h \
vquic/vquic.h
vquic/vquic.h \
vquic/vquic_int.h
LIB_VSSH_CFILES = \
vssh/libssh.c \
@ -106,6 +107,7 @@ LIB_CFILES = \
base64.c \
bufref.c \
c-hyper.c \
cf-socket.c \
cfilters.c \
conncache.c \
connect.c \
@ -229,6 +231,7 @@ LIB_HFILES = \
asyn.h \
bufref.h \
c-hyper.h \
cf-socket.h \
cfilters.h \
conncache.h \
connect.h \
@ -312,7 +315,6 @@ LIB_HFILES = \
pop3.h \
progress.h \
psl.h \
quic.h \
rand.h \
rename.h \
rtsp.h \

1534
lib/cf-socket.c Normal file

File diff suppressed because it is too large Load Diff

169
lib/cf-socket.h Normal file
View File

@ -0,0 +1,169 @@
#ifndef HEADER_CURL_CF_SOCKET_H
#define HEADER_CURL_CF_SOCKET_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 1998 - 2022, 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 "nonblock.h" /* for curlx_nonblock(), formerly Curl_nonblock() */
#include "sockaddr.h"
struct Curl_addrinfo;
struct Curl_cfilter;
struct Curl_easy;
struct connectdata;
struct Curl_sockaddr_ex;
/*
* The Curl_sockaddr_ex structure is basically libcurl's external API
* curl_sockaddr structure with enough space available to directly hold any
* protocol-specific address structures. The variable declared here will be
* used to pass / receive data to/from the fopensocket callback if this has
* been set, before that, it is initialized from parameters.
*/
struct Curl_sockaddr_ex {
int family;
int socktype;
int protocol;
unsigned int addrlen;
union {
struct sockaddr addr;
struct Curl_sockaddr_storage buff;
} _sa_ex_u;
};
#define sa_addr _sa_ex_u.addr
/*
* Create a socket based on info from 'conn' and 'ai'.
*
* Fill in 'addr' and 'sockfd' accordingly if OK is returned. If the open
* socket callback is set, used that!
*
*/
CURLcode Curl_socket_open(struct Curl_easy *data,
const struct Curl_addrinfo *ai,
struct Curl_sockaddr_ex *addr,
int transport,
curl_socket_t *sockfd);
int Curl_socket_close(struct Curl_easy *data, struct connectdata *conn,
curl_socket_t sock);
/*
* This function should return TRUE if the socket is to be assumed to
* be dead. Most commonly this happens when the server has closed the
* connection due to inactivity.
*/
bool Curl_socket_is_dead(curl_socket_t sock);
/**
* Determine the curl code for a socket connect() == -1 with errno.
*/
CURLcode Curl_socket_connect_result(struct Curl_easy *data,
const char *ipaddress, int error);
#ifdef USE_WINSOCK
/* When you run a program that uses the Windows Sockets API, you may
experience slow performance when you copy data to a TCP server.
https://support.microsoft.com/kb/823764
Work-around: Make the Socket Send Buffer Size Larger Than the Program Send
Buffer Size
*/
void Curl_sndbufset(curl_socket_t sockfd);
#else
#define Curl_sndbufset(y) Curl_nop_stmt
#endif
/**
* Creates a cfilter that opens a TCP socket to the given address
* when calling its `connect` implementation.
* The filter will not touch any connection/data flags and can be
* used in happy eyeballing. Once selected for use, its `_active()`
* method needs to be called.
*/
CURLcode Curl_cf_tcp_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
struct connectdata *conn,
const struct Curl_addrinfo *ai);
/**
* Creates a cfilter that opens a UDP socket to the given address
* when calling its `connect` implementation.
* The filter will not touch any connection/data flags and can be
* used in happy eyeballing. Once selected for use, its `_active()`
* method needs to be called.
*/
CURLcode Curl_cf_udp_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
struct connectdata *conn,
const struct Curl_addrinfo *ai);
/**
* Creates a cfilter that opens a UNIX socket to the given address
* when calling its `connect` implementation.
* The filter will not touch any connection/data flags and can be
* used in happy eyeballing. Once selected for use, its `_active()`
* method needs to be called.
*/
CURLcode Curl_cf_unix_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
struct connectdata *conn,
const struct Curl_addrinfo *ai);
/**
* Creates a cfilter that keeps a listening socket.
*/
CURLcode Curl_conn_tcp_listen_set(struct Curl_easy *data,
struct connectdata *conn,
int sockindex,
curl_socket_t *s);
/**
* Replace the listen socket with the accept()ed one.
*/
CURLcode Curl_conn_tcp_accepted_set(struct Curl_easy *data,
struct connectdata *conn,
int sockindex,
curl_socket_t *s);
/**
* Return TRUE iff `cf` is a socket filter.
*/
bool Curl_cf_is_socket(struct Curl_cfilter *cf);
/**
* Peek at the socket and remote ip/port the socket filter is using.
* The filter owns all returned values.
* Returns error if the filter is of invalid type.
*/
CURLcode Curl_cf_socket_peek(struct Curl_cfilter *cf,
curl_socket_t *psock,
const struct Curl_sockaddr_ex **paddr,
const char **premote_ip_str,
int *premote_port);
#endif /* HEADER_CURL_CF_SOCKET_H */

View File

@ -34,9 +34,6 @@
#include "multiif.h"
#include "progress.h"
#include "warnless.h"
#include "http_proxy.h"
#include "socks.h"
#include "vtls/vtls.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
@ -47,6 +44,13 @@
#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
#endif
#define DEBUG_CF 0
#if DEBUG_CF
#define CF_DEBUGF(x) x
#else
#define CF_DEBUGF(x) do { } while(0)
#endif
void Curl_cf_def_destroy_this(struct Curl_cfilter *cf, struct Curl_easy *data)
{
@ -54,89 +58,116 @@ void Curl_cf_def_destroy_this(struct Curl_cfilter *cf, struct Curl_easy *data)
(void)data;
}
CURLcode Curl_cf_def_setup(struct Curl_cfilter *cf,
struct Curl_easy *data,
const struct Curl_dns_entry *remotehost)
{
DEBUGASSERT(cf->next);
return cf->next->cft->setup(cf->next, data, remotehost);
}
void Curl_cf_def_attach_data(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
(void)cf;
(void)data;
}
void Curl_cf_def_detach_data(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
(void)cf;
(void)data;
}
void Curl_cf_def_close(struct Curl_cfilter *cf, struct Curl_easy *data)
{
DEBUGASSERT(cf->next);
cf->connected = FALSE;
cf->next->cft->close(cf->next, data);
if(cf->next)
cf->next->cft->close(cf->next, data);
}
CURLcode Curl_cf_def_connect(struct Curl_cfilter *cf,
struct Curl_easy *data,
bool blocking, bool *done)
{
DEBUGASSERT(cf->next);
return cf->next->cft->connect(cf->next, data, blocking, done);
CURLcode result;
if(cf->connected) {
*done = TRUE;
return CURLE_OK;
}
if(cf->next) {
result = cf->next->cft->connect(cf->next, data, blocking, done);
if(!result && *done) {
cf->connected = TRUE;
}
return result;
}
*done = FALSE;
return CURLE_FAILED_INIT;
}
void Curl_cf_def_get_host(struct Curl_cfilter *cf, struct Curl_easy *data,
const char **phost, const char **pdisplay_host,
int *pport)
{
DEBUGASSERT(cf->next);
cf->next->cft->get_host(cf->next, data, phost, pdisplay_host, pport);
if(cf->next)
cf->next->cft->get_host(cf->next, data, phost, pdisplay_host, pport);
else {
*phost = cf->conn->host.name;
*pdisplay_host = cf->conn->host.dispname;
*pport = cf->conn->port;
}
}
int Curl_cf_def_get_select_socks(struct Curl_cfilter *cf,
struct Curl_easy *data,
curl_socket_t *socks)
{
DEBUGASSERT(cf->next);
return cf->next->cft->get_select_socks(cf->next, data, socks);
return cf->next?
cf->next->cft->get_select_socks(cf->next, data, socks) : 0;
}
bool Curl_cf_def_data_pending(struct Curl_cfilter *cf,
const struct Curl_easy *data)
{
DEBUGASSERT(cf->next);
return cf->next->cft->has_data_pending(cf->next, data);
return cf->next?
cf->next->cft->has_data_pending(cf->next, data) : FALSE;
}
ssize_t Curl_cf_def_send(struct Curl_cfilter *cf, struct Curl_easy *data,
const void *buf, size_t len, CURLcode *err)
{
DEBUGASSERT(cf->next);
return cf->next->cft->do_send(cf->next, data, buf, len, err);
return cf->next?
cf->next->cft->do_send(cf->next, data, buf, len, err) :
CURLE_RECV_ERROR;
}
ssize_t Curl_cf_def_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
char *buf, size_t len, CURLcode *err)
{
DEBUGASSERT(cf->next);
return cf->next->cft->do_recv(cf->next, data, buf, len, err);
return cf->next?
cf->next->cft->do_recv(cf->next, data, buf, len, err) :
CURLE_SEND_ERROR;
}
void Curl_conn_cf_discard_all(struct Curl_easy *data,
struct connectdata *conn, int index)
bool Curl_cf_def_conn_is_alive(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
struct Curl_cfilter *cfn, *cf = conn->cfilter[index];
return cf->next?
cf->next->cft->is_alive(cf->next, data) :
FALSE; /* pessimistic in absence of data */
}
CURLcode Curl_cf_def_conn_keep_alive(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
return cf->next?
cf->next->cft->keep_alive(cf->next, data) :
CURLE_OK;
}
CURLcode Curl_cf_def_query(struct Curl_cfilter *cf,
struct Curl_easy *data,
int query, int *pres1, void **pres2)
{
return cf->next?
cf->next->cft->query(cf->next, data, query, pres1, pres2) :
CURLE_UNKNOWN_OPTION;
}
void Curl_conn_cf_discard_chain(struct Curl_cfilter **pcf,
struct Curl_easy *data)
{
struct Curl_cfilter *cfn, *cf = *pcf;
if(cf) {
conn->cfilter[index] = NULL;
*pcf = NULL;
while(cf) {
cfn = cf->next;
/* prevent destroying filter to mess with its sub-chain, since
* we have the reference now and will call destroy on it.
*/
cf->next = NULL;
cf->cft->destroy(cf, data);
free(cf);
cf = cfn;
@ -144,6 +175,12 @@ void Curl_conn_cf_discard_all(struct Curl_easy *data,
}
}
void Curl_conn_cf_discard_all(struct Curl_easy *data,
struct connectdata *conn, int index)
{
Curl_conn_cf_discard_chain(&conn->cfilter[index], data);
}
void Curl_conn_close(struct Curl_easy *data, int index)
{
struct Curl_cfilter *cf;
@ -170,12 +207,11 @@ ssize_t Curl_conn_recv(struct Curl_easy *data, int num, char *buf,
}
if(cf) {
nread = cf->cft->do_recv(cf, data, buf, len, code);
/* DEBUGF(infof(data, "Curl_conn_recv(handle=%p, index=%d)"
"-> %ld, err=%d", data, num, nread, *code));*/
CF_DEBUGF(infof(data, "Curl_conn_recv(handle=%p, index=%d)"
"-> %ld, err=%d", data, num, nread, *code));
return nread;
}
failf(data, "no filter connected, conn=%ld, sockindex=%d",
data->conn->connection_id, num);
failf(data, CMSGI(data->conn, num, "recv: no filter connected"));
*code = CURLE_FAILED_INIT;
return -1;
}
@ -194,12 +230,12 @@ ssize_t Curl_conn_send(struct Curl_easy *data, int num,
}
if(cf) {
nwritten = cf->cft->do_send(cf, data, mem, len, code);
/* DEBUGF(infof(data, "Curl_conn_send(handle=%p, index=%d, len=%ld)"
" -> %ld, err=%d", data, num, len, nwritten, *code));*/
CF_DEBUGF(infof(data, "Curl_conn_send(handle=%p, index=%d, len=%ld)"
" -> %ld, err=%d", data, num, len, nwritten, *code));
return nwritten;
}
failf(data, "no filter connected, conn=%ld, sockindex=%d",
data->conn->connection_id, num);
failf(data, CMSGI(data->conn, num, "send: no filter connected"));
DEBUGASSERT(0);
*code = CURLE_FAILED_INIT;
return -1;
}
@ -234,12 +270,31 @@ void Curl_conn_cf_add(struct Curl_easy *data,
DEBUGASSERT(!cf->conn);
DEBUGASSERT(!cf->next);
DEBUGF(infof(data, CMSGI(conn, index, "cf_add(filter=%s)"),
cf->cft->name));
cf->next = conn->cfilter[index];
cf->conn = conn;
cf->sockindex = index;
conn->cfilter[index] = cf;
CF_DEBUGF(infof(data, CFMSG(cf, "added")));
}
void Curl_conn_cf_insert_after(struct Curl_cfilter *cf_at,
struct Curl_cfilter *cf_new)
{
struct Curl_cfilter *tail, **pnext;
DEBUGASSERT(cf_at);
DEBUGASSERT(cf_new);
DEBUGASSERT(!cf_new->conn);
tail = cf_at->next;
cf_at->next = cf_new;
do {
cf_new->conn = cf_at->conn;
cf_new->sockindex = cf_at->sockindex;
pnext = &cf_new->next;
cf_new = cf_new->next;
} while(cf_new);
*pnext = tail;
}
void Curl_conn_cf_discard(struct Curl_cfilter *cf, struct Curl_easy *data)
@ -259,96 +314,46 @@ void Curl_conn_cf_discard(struct Curl_cfilter *cf, struct Curl_easy *data)
free(cf);
}
CURLcode Curl_conn_cf_connect(struct Curl_cfilter *cf,
struct Curl_easy *data,
bool blocking, bool *done)
{
if(cf)
return cf->cft->connect(cf, data, blocking, done);
return CURLE_FAILED_INIT;
}
void Curl_conn_cf_close(struct Curl_cfilter *cf, struct Curl_easy *data)
{
if(cf)
cf->cft->close(cf, data);
}
int Curl_conn_cf_get_select_socks(struct Curl_cfilter *cf,
struct Curl_easy *data,
curl_socket_t *socks)
{
if(cf)
return cf->cft->get_select_socks(cf, data, socks);
return 0;
}
ssize_t Curl_conn_cf_send(struct Curl_cfilter *cf, struct Curl_easy *data,
const void *buf, size_t len, CURLcode *err)
{
return cf->cft->do_send(cf, data, buf, len, err);
if(cf)
return cf->cft->do_send(cf, data, buf, len, err);
*err = CURLE_SEND_ERROR;
return -1;
}
ssize_t Curl_conn_cf_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
char *buf, size_t len, CURLcode *err)
{
return cf->cft->do_recv(cf, data, buf, len, err);
}
CURLcode Curl_conn_setup(struct Curl_easy *data,
struct connectdata *conn,
int sockindex,
const struct Curl_dns_entry *remotehost,
int ssl_mode)
{
struct Curl_cfilter *cf;
CURLcode result;
DEBUGASSERT(data);
/* If no filter is set, we have the "default" setup of connection filters.
* The filter chain from bottom to top will be:
* - SOCKET socket filter for outgoing connection to remotehost
* if http_proxy tunneling is engaged:
* - SSL if proxytype is CURLPROXY_HTTPS
* - HTTP_PROXY_TUNNEL
* otherwise, if socks_proxy is engaged:
* - SOCKS_PROXY_TUNNEL
* - SSL if conn->handler has PROTOPT_SSL
*/
if(!conn->cfilter[sockindex]) {
DEBUGF(infof(data, DMSGI(data, sockindex, "setup, init filter chain")));
result = Curl_conn_socket_set(data, conn, sockindex);
if(result)
goto out;
#ifndef CURL_DISABLE_PROXY
if(conn->bits.socksproxy) {
result = Curl_conn_socks_proxy_add(data, conn, sockindex);
if(result)
goto out;
}
if(conn->bits.httpproxy) {
#ifdef USE_SSL
if(conn->http_proxy.proxytype == CURLPROXY_HTTPS) {
result = Curl_ssl_cfilter_proxy_add(data, conn, sockindex);
if(result)
goto out;
}
#endif /* USE_SSL */
#if !defined(CURL_DISABLE_HTTP)
if(conn->bits.tunnel_proxy) {
result = Curl_conn_http_proxy_add(data, conn, sockindex);
if(result)
goto out;
}
}
#endif /* !CURL_DISABLE_HTTP */
/* HAProxy protocol comes *before* SSL, see #10165 */
if(data->set.haproxyprotocol) {
result = Curl_conn_haproxy_add(data, conn, sockindex);
if(result)
goto out;
}
#endif /* !CURL_DISABLE_PROXY */
#ifdef USE_SSL
if(ssl_mode == CURL_CF_SSL_ENABLE
|| (ssl_mode != CURL_CF_SSL_DISABLE
&& conn->handler->flags & PROTOPT_SSL)) {
result = Curl_ssl_cfilter_add(data, conn, sockindex);
if(result)
goto out;
}
#else
(void)ssl_mode;
#endif /* USE_SSL */
}
DEBUGASSERT(conn->cfilter[sockindex]);
cf = data->conn->cfilter[sockindex];
result = cf->cft->setup(cf, data, remotehost);
out:
return result;
if(cf)
return cf->cft->do_recv(cf, data, buf, len, err);
*err = CURLE_RECV_ERROR;
return -1;
}
CURLcode Curl_conn_connect(struct Curl_easy *data,
@ -357,16 +362,33 @@ CURLcode Curl_conn_connect(struct Curl_easy *data,
bool *done)
{
struct Curl_cfilter *cf;
CURLcode result;
CURLcode result = CURLE_OK;
DEBUGASSERT(data);
DEBUGASSERT(data->conn);
cf = data->conn->cfilter[sockindex];
DEBUGASSERT(cf);
result = cf->cft->connect(cf, data, blocking, done);
*done = cf->connected;
if(!*done) {
result = cf->cft->connect (cf, data, blocking, done);
if(!result && *done) {
data->conn->keepalive = Curl_now();
}
}
DEBUGF(infof(data, DMSGI(data, sockindex, "connect(block=%d)-> %d, done=%d"),
blocking, result, *done));
#ifdef DEBUGBUILD
if(result) {
CF_DEBUGF(infof(data, DMSGI(data, sockindex, "connect()-> %d, done=%d"),
result, *done));
}
else if(!*done) {
while(cf->next && !cf->next->connected)
cf = cf->next;
CF_DEBUGF(infof(data, DMSGI(data, sockindex, "connect()-> waiting for %s"),
cf->cft->name));
}
#endif
return result;
}
@ -393,11 +415,10 @@ bool Curl_conn_is_ip_connected(struct Curl_easy *data, int sockindex)
return FALSE;
}
bool Curl_conn_is_ssl(struct Curl_easy *data, int sockindex)
bool Curl_conn_is_ssl(struct connectdata *conn, int sockindex)
{
struct Curl_cfilter *cf = data->conn? data->conn->cfilter[sockindex] : NULL;
struct Curl_cfilter *cf = conn? conn->cfilter[sockindex] : NULL;
(void)data;
for(; cf; cf = cf->next) {
if(cf->cft->flags & CF_TYPE_SSL)
return TRUE;
@ -407,6 +428,19 @@ bool Curl_conn_is_ssl(struct Curl_easy *data, int sockindex)
return FALSE;
}
bool Curl_conn_is_multiplex(struct connectdata *conn, int sockindex)
{
struct Curl_cfilter *cf = conn? conn->cfilter[sockindex] : NULL;
for(; cf; cf = cf->next) {
if(cf->cft->flags & CF_TYPE_MULTIPLEX)
return TRUE;
if(cf->cft->flags & CF_TYPE_IP_CONNECT
|| cf->cft->flags & CF_TYPE_SSL)
return FALSE;
}
return FALSE;
}
bool Curl_conn_data_pending(struct Curl_easy *data, int sockindex)
{
@ -446,40 +480,6 @@ int Curl_conn_get_select_socks(struct Curl_easy *data, int sockindex,
return GETSOCK_BLANK;
}
void Curl_conn_attach_data(struct connectdata *conn,
struct Curl_easy *data)
{
size_t i;
struct Curl_cfilter *cf;
for(i = 0; i < ARRAYSIZE(conn->cfilter); ++i) {
cf = conn->cfilter[i];
if(cf) {
while(cf) {
cf->cft->attach_data(cf, data);
cf = cf->next;
}
}
}
}
void Curl_conn_detach_data(struct connectdata *conn,
struct Curl_easy *data)
{
size_t i;
struct Curl_cfilter *cf;
for(i = 0; i < ARRAYSIZE(conn->cfilter); ++i) {
cf = conn->cfilter[i];
if(cf) {
while(cf) {
cf->cft->detach_data(cf, data);
cf = cf->next;
}
}
}
}
void Curl_conn_get_host(struct Curl_easy *data, int sockindex,
const char **phost, const char **pdisplay_host,
int *pport)
@ -502,4 +502,130 @@ void Curl_conn_get_host(struct Curl_easy *data, int sockindex,
}
}
CURLcode Curl_cf_def_cntrl(struct Curl_cfilter *cf,
struct Curl_easy *data,
int event, int arg1, void *arg2)
{
(void)cf;
(void)data;
(void)event;
(void)arg1;
(void)arg2;
return CURLE_OK;
}
CURLcode Curl_conn_cf_cntrl(struct Curl_cfilter *cf,
struct Curl_easy *data,
bool ignore_result,
int event, int arg1, void *arg2)
{
CURLcode result = CURLE_OK;
for(; cf; cf = cf->next) {
if(Curl_cf_def_cntrl == cf->cft->cntrl)
continue;
result = cf->cft->cntrl(cf, data, event, arg1, arg2);
if(!ignore_result && result)
break;
}
return result;
}
static CURLcode cf_cntrl_all(struct connectdata *conn,
struct Curl_easy *data,
bool ignore_result,
int event, int arg1, void *arg2)
{
CURLcode result = CURLE_OK;
size_t i;
for(i = 0; i < ARRAYSIZE(conn->cfilter); ++i) {
result = Curl_conn_cf_cntrl(conn->cfilter[i], data, ignore_result,
event, arg1, arg2);
if(!ignore_result && result)
break;
}
return result;
}
void Curl_conn_ev_data_attach(struct connectdata *conn,
struct Curl_easy *data)
{
cf_cntrl_all(conn, data, TRUE, CF_CTRL_DATA_ATTACH, 0, NULL);
}
void Curl_conn_ev_data_detach(struct connectdata *conn,
struct Curl_easy *data)
{
cf_cntrl_all(conn, data, TRUE, CF_CTRL_DATA_DETACH, 0, NULL);
}
CURLcode Curl_conn_ev_data_setup(struct Curl_easy *data)
{
return cf_cntrl_all(data->conn, data, FALSE,
CF_CTRL_DATA_SETUP, 0, NULL);
}
CURLcode Curl_conn_ev_data_idle(struct Curl_easy *data)
{
return cf_cntrl_all(data->conn, data, FALSE,
CF_CTRL_DATA_IDLE, 0, NULL);
}
/**
* Notify connection filters that the transfer represented by `data`
* is donw with sending data (e.g. has uploaded everything).
*/
void Curl_conn_ev_data_done_send(struct Curl_easy *data)
{
cf_cntrl_all(data->conn, data, TRUE, CF_CTRL_DATA_DONE_SEND, 0, NULL);
}
/**
* Notify connection filters that the transfer represented by `data`
* is finished - eventually premature, e.g. before being complete.
*/
void Curl_conn_ev_data_done(struct Curl_easy *data, bool premature)
{
cf_cntrl_all(data->conn, data, TRUE, CF_CTRL_DATA_DONE, premature, NULL);
}
CURLcode Curl_conn_ev_data_pause(struct Curl_easy *data, bool do_pause)
{
return cf_cntrl_all(data->conn, data, FALSE,
CF_CTRL_DATA_PAUSE, do_pause, NULL);
}
void Curl_conn_ev_update_info(struct Curl_easy *data,
struct connectdata *conn)
{
cf_cntrl_all(conn, data, TRUE, CF_CTRL_CONN_INFO_UPDATE, 0, NULL);
}
bool Curl_conn_is_alive(struct Curl_easy *data, struct connectdata *conn)
{
struct Curl_cfilter *cf = conn->cfilter[FIRSTSOCKET];
return !cf->conn->bits.close && cf && cf->cft->is_alive(cf, data);
}
CURLcode Curl_conn_keep_alive(struct Curl_easy *data,
struct connectdata *conn,
int sockindex)
{
struct Curl_cfilter *cf = conn->cfilter[sockindex];
return cf? cf->cft->keep_alive(cf, data) : CURLE_OK;
}
size_t Curl_conn_get_max_concurrent(struct Curl_easy *data,
struct connectdata *conn,
int sockindex)
{
CURLcode result;
int n = 0;
struct Curl_cfilter *cf = conn->cfilter[sockindex];
result = cf? cf->cft->query(cf, data, CF_QUERY_MAX_CONCURRENT,
&n, NULL) : CURLE_UNKNOWN_OPTION;
return (result || n <= 0)? 1 : (size_t)n;
}

View File

@ -36,11 +36,6 @@ struct connectdata;
typedef void Curl_cft_destroy_this(struct Curl_cfilter *cf,
struct Curl_easy *data);
/* Setup the connection for `data`, using destination `remotehost`.
*/
typedef CURLcode Curl_cft_setup(struct Curl_cfilter *cf,
struct Curl_easy *data,
const struct Curl_dns_entry *remotehost);
typedef void Curl_cft_close(struct Curl_cfilter *cf,
struct Curl_easy *data);
@ -89,29 +84,77 @@ typedef ssize_t Curl_cft_recv(struct Curl_cfilter *cf,
size_t len, /* amount to read */
CURLcode *err); /* error to return */
typedef void Curl_cft_attach_data(struct Curl_cfilter *cf,
struct Curl_easy *data);
typedef void Curl_cft_detach_data(struct Curl_cfilter *cf,
struct Curl_easy *data);
typedef bool Curl_cft_conn_is_alive(struct Curl_cfilter *cf,
struct Curl_easy *data);
typedef CURLcode Curl_cft_conn_keep_alive(struct Curl_cfilter *cf,
struct Curl_easy *data);
/**
* The easy handle `data` is being detached (no longer served)
* by connection `conn`. All filters are informed to release any resources
* related to `data`.
* Note: there may be several `data` attached to a connection at the same
* time.
* Events/controls for connection filters, their arguments and
* return code handling. Filter callbacks are invoked "top down".
* Return code handling:
* "first fail" meaning that the first filter returning != CURLE_OK, will
* abort further event distribution and determine the result.
* "ignored" meaning return values are ignored and the event is distributed
* to all filters in the chain. Overall result is always CURLE_OK.
*/
void Curl_conn_detach(struct connectdata *conn, struct Curl_easy *data);
/* data event arg1 arg2 return */
#define CF_CTRL_DATA_ATTACH 1 /* 0 NULL ignored */
#define CF_CTRL_DATA_DETACH 2 /* 0 NULL ignored */
#define CF_CTRL_DATA_SETUP 4 /* 0 NULL first fail */
#define CF_CTRL_DATA_IDLE 5 /* 0 NULL first fail */
#define CF_CTRL_DATA_PAUSE 6 /* on/off NULL first fail */
#define CF_CTRL_DATA_DONE 7 /* premature NULL ignored */
#define CF_CTRL_DATA_DONE_SEND 8 /* 0 NULL ignored */
/* update conn info at connection and data */
#define CF_CTRL_CONN_INFO_UPDATE (256+0) /* 0 NULL ignored */
/**
* Handle event/control for the filter.
* Implementations MUST NOT chain calls to cf->next.
*/
typedef CURLcode Curl_cft_cntrl(struct Curl_cfilter *cf,
struct Curl_easy *data,
int event, int arg1, void *arg2);
/**
* Queries to ask via a `Curl_cft_query *query` method on a cfilter chain.
* - MAX_CONCURRENT: the maximum number of parallel transfers the filter
* chain expects to handle at the same time.
* default: 1 if no filter overrides.
*/
/* query res1 res2 */
#define CF_QUERY_MAX_CONCURRENT 1 /* number - */
/**
* Query the cfilter for properties. Filters ignorant of a query will
* pass it "down" the filter chain.
*/
typedef CURLcode Curl_cft_query(struct Curl_cfilter *cf,
struct Curl_easy *data,
int query, int *pres1, void **pres2);
/**
* Type flags for connection filters. A filter can have none, one or
* many of those. Use to evaluate state/capabilities of a filter chain.
*
* CF_TYPE_IP_CONNECT: provides an IP connection or sth equivalent, like
* a CONNECT tunnel, a UNIX domain socket, a QUIC
* connection, etc.
* CF_TYPE_SSL: provide SSL/TLS
* CF_TYPE_MULTIPLEX: provides multiplexing of easy handles
*/
#define CF_TYPE_IP_CONNECT (1 << 0)
#define CF_TYPE_SSL (1 << 1)
#define CF_TYPE_MULTIPLEX (1 << 2)
/* A connection filter type, e.g. specific implementation. */
struct Curl_cftype {
const char *name; /* name of the filter type */
long flags; /* flags of filter type */
Curl_cft_destroy_this *destroy; /* destroy resources of this cf */
Curl_cft_setup *setup; /* setup for a connection */
Curl_cft_connect *connect; /* establish connection */
Curl_cft_close *close; /* close conn */
Curl_cft_get_host *get_host; /* host filter talks to */
@ -119,8 +162,10 @@ struct Curl_cftype {
Curl_cft_data_pending *has_data_pending;/* conn has data pending */
Curl_cft_send *do_send; /* send data */
Curl_cft_recv *do_recv; /* receive data */
Curl_cft_attach_data *attach_data; /* data is being handled here */
Curl_cft_detach_data *detach_data; /* data is no longer handled here */
Curl_cft_cntrl *cntrl; /* events/control */
Curl_cft_conn_is_alive *is_alive; /* FALSE if conn is dead, Jim! */
Curl_cft_conn_keep_alive *keep_alive; /* try to keep it alive */
Curl_cft_query *query; /* query filter chain */
};
/* A connection filter instance, e.g. registered at a connection */
@ -129,7 +174,7 @@ struct Curl_cfilter {
struct Curl_cfilter *next; /* next filter in chain */
void *ctx; /* filter type specific settings */
struct connectdata *conn; /* the connection this filter belongs to */
int sockindex; /* TODO: like to get rid off this */
int sockindex; /* the index the filter is installed at */
BIT(connected); /* != 0 iff this filter is connected */
};
@ -139,9 +184,6 @@ void Curl_cf_def_destroy_this(struct Curl_cfilter *cf,
/* Default implementations for the type functions, implementing pass-through
* the filter chain. */
CURLcode Curl_cf_def_setup(struct Curl_cfilter *cf,
struct Curl_easy *data,
const struct Curl_dns_entry *remotehost);
void Curl_cf_def_close(struct Curl_cfilter *cf, struct Curl_easy *data);
CURLcode Curl_cf_def_connect(struct Curl_cfilter *cf,
struct Curl_easy *data,
@ -158,10 +200,16 @@ ssize_t Curl_cf_def_send(struct Curl_cfilter *cf, struct Curl_easy *data,
const void *buf, size_t len, CURLcode *err);
ssize_t Curl_cf_def_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
char *buf, size_t len, CURLcode *err);
void Curl_cf_def_attach_data(struct Curl_cfilter *cf,
struct Curl_easy *data);
void Curl_cf_def_detach_data(struct Curl_cfilter *cf,
struct Curl_easy *data);
CURLcode Curl_cf_def_cntrl(struct Curl_cfilter *cf,
struct Curl_easy *data,
int event, int arg1, void *arg2);
bool Curl_cf_def_conn_is_alive(struct Curl_cfilter *cf,
struct Curl_easy *data);
CURLcode Curl_cf_def_conn_keep_alive(struct Curl_cfilter *cf,
struct Curl_easy *data);
CURLcode Curl_cf_def_query(struct Curl_cfilter *cf,
struct Curl_easy *data,
int query, int *pres1, void **pres2);
/**
* Create a new filter instance, unattached to the filter chain.
@ -176,7 +224,7 @@ CURLcode Curl_cf_create(struct Curl_cfilter **pcf,
/**
* Add a filter instance to the `sockindex` filter chain at connection
* `data->conn`. The filter must not already be attached. It is inserted at
* `conn`. The filter must not already be attached. It is inserted at
* the start of the chain (top).
*/
void Curl_conn_cf_add(struct Curl_easy *data,
@ -185,11 +233,11 @@ void Curl_conn_cf_add(struct Curl_easy *data,
struct Curl_cfilter *cf);
/**
* Remove and destroy all filters at chain `sockindex` on connection `conn`.
* Insert a filter (chain) after `cf_at`.
* `cf_new` must not already be attached.
*/
void Curl_conn_cf_discard_all(struct Curl_easy *data,
struct connectdata *conn,
int sockindex);
void Curl_conn_cf_insert_after(struct Curl_cfilter *cf_at,
struct Curl_cfilter *cf_new);
/**
* Discard, e.g. remove and destroy a specific filter instance.
@ -198,28 +246,42 @@ void Curl_conn_cf_discard_all(struct Curl_easy *data,
*/
void Curl_conn_cf_discard(struct Curl_cfilter *cf, struct Curl_easy *data);
/**
* Discard all cfilters starting with `*pcf` and clearing it afterwards.
*/
void Curl_conn_cf_discard_chain(struct Curl_cfilter **pcf,
struct Curl_easy *data);
/**
* Remove and destroy all filters at chain `sockindex` on connection `conn`.
*/
void Curl_conn_cf_discard_all(struct Curl_easy *data,
struct connectdata *conn,
int sockindex);
CURLcode Curl_conn_cf_connect(struct Curl_cfilter *cf,
struct Curl_easy *data,
bool blocking, bool *done);
void Curl_conn_cf_close(struct Curl_cfilter *cf, struct Curl_easy *data);
int Curl_conn_cf_get_select_socks(struct Curl_cfilter *cf,
struct Curl_easy *data,
curl_socket_t *socks);
ssize_t Curl_conn_cf_send(struct Curl_cfilter *cf, struct Curl_easy *data,
const void *buf, size_t len, CURLcode *err);
ssize_t Curl_conn_cf_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
char *buf, size_t len, CURLcode *err);
CURLcode Curl_conn_cf_cntrl(struct Curl_cfilter *cf,
struct Curl_easy *data,
bool ignore_result,
int event, int arg1, void *arg2);
#define CURL_CF_SSL_DEFAULT -1
#define CURL_CF_SSL_DISABLE 0
#define CURL_CF_SSL_ENABLE 1
/**
* Setup the filter chain at `sockindex` in connection `conn`, invoking
* the instance `setup(remotehost)` methods. If no filter chain is
* installed yet, inspects the configuration in `data` to install a
* suitable filter chain.
*/
CURLcode Curl_conn_setup(struct Curl_easy *data,
struct connectdata *conn,
int sockindex,
const struct Curl_dns_entry *remotehost,
int ssl_mode);
/**
* Bring the filter chain at `sockindex` for connection `data->conn` into
* connected state. Which will set `*done` to TRUE.
@ -248,7 +310,12 @@ bool Curl_conn_is_ip_connected(struct Curl_easy *data, int sockindex);
* (or will be once connected). This will return FALSE, if SSL
* is only used in proxying and not for the tunnel itself.
*/
bool Curl_conn_is_ssl(struct Curl_easy *data, int sockindex);
bool Curl_conn_is_ssl(struct connectdata *conn, int sockindex);
/**
* Connection provides multiplexing of easy handles at `socketindex`.
*/
bool Curl_conn_is_multiplex(struct connectdata *conn, int sockindex);
/**
* Close the filter chain at `sockindex` for connection `data->conn`.
@ -289,13 +356,12 @@ ssize_t Curl_conn_send(struct Curl_easy *data, int sockindex,
const void *buf, size_t len, CURLcode *code);
/**
* The easy handle `data` is being attached (served) by connection `conn`.
* All filters are informed to adapt to handling `data`.
* Note: there may be several `data` attached to a connection at the same
* time.
* The easy handle `data` is being attached to `conn`. This does
* not mean that data will actually do a transfer. Attachment is
* also used for temporary actions on the connection.
*/
void Curl_conn_attach_data(struct connectdata *conn,
struct Curl_easy *data);
void Curl_conn_ev_data_attach(struct connectdata *conn,
struct Curl_easy *data);
/**
* The easy handle `data` is being detached (no longer served)
@ -304,12 +370,66 @@ void Curl_conn_attach_data(struct connectdata *conn,
* Note: there may be several `data` attached to a connection at the same
* time.
*/
void Curl_conn_detach_data(struct connectdata *conn,
struct Curl_easy *data);
void Curl_conn_ev_data_detach(struct connectdata *conn,
struct Curl_easy *data);
/**
* Notify connection filters that they need to setup data for
* a transfer.
*/
CURLcode Curl_conn_ev_data_setup(struct Curl_easy *data);
/**
* Notify connection filters that now would be a good time to
* perform any idle, e.g. time related, actions.
*/
CURLcode Curl_conn_ev_data_idle(struct Curl_easy *data);
/**
* Notify connection filters that the transfer represented by `data`
* is donw with sending data (e.g. has uploaded everything).
*/
void Curl_conn_ev_data_done_send(struct Curl_easy *data);
/**
* Notify connection filters that the transfer represented by `data`
* is finished - eventually premature, e.g. before being complete.
*/
void Curl_conn_ev_data_done(struct Curl_easy *data, bool premature);
/**
* Notify connection filters that the transfer of data is paused/unpaused.
*/
CURLcode Curl_conn_ev_data_pause(struct Curl_easy *data, bool do_pause);
/**
* Inform connection filters to update their info in `conn`.
*/
void Curl_conn_ev_update_info(struct Curl_easy *data,
struct connectdata *conn);
/**
* Check if FIRSTSOCKET's cfilter chain deems connection alive.
*/
bool Curl_conn_is_alive(struct Curl_easy *data, struct connectdata *conn);
/**
* Try to upkeep the connection filters at sockindex.
*/
CURLcode Curl_conn_keep_alive(struct Curl_easy *data,
struct connectdata *conn,
int sockindex);
void Curl_conn_get_host(struct Curl_easy *data, int sockindex,
const char **phost, const char **pdisplay_host,
int *pport);
/**
* Get the maximum number of parallel transfers the connection
* expects to be able to handle at `sockindex`.
*/
size_t Curl_conn_get_max_concurrent(struct Curl_easy *data,
struct connectdata *conn,
int sockindex);
#endif /* HEADER_CURL_CFILTERS_H */

File diff suppressed because it is too large Load Diff

View File

@ -29,9 +29,7 @@
#include "sockaddr.h"
#include "timeval.h"
CURLcode Curl_connecthost(struct Curl_easy *data,
struct connectdata *conn,
const struct Curl_dns_entry *host);
struct Curl_dns_entry;
/* generic function that returns how much time there's left to run, according
to the timeouts set */
@ -53,67 +51,8 @@ curl_socket_t Curl_getconnectinfo(struct Curl_easy *data,
bool Curl_addr2string(struct sockaddr *sa, curl_socklen_t salen,
char *addr, int *port);
/*
* Check if a connection seems to be alive.
*/
bool Curl_connalive(struct Curl_easy *data, struct connectdata *conn);
#ifdef USE_WINSOCK
/* When you run a program that uses the Windows Sockets API, you may
experience slow performance when you copy data to a TCP server.
https://support.microsoft.com/kb/823764
Work-around: Make the Socket Send Buffer Size Larger Than the Program Send
Buffer Size
*/
void Curl_sndbufset(curl_socket_t sockfd);
#else
#define Curl_sndbufset(y) Curl_nop_stmt
#endif
void Curl_updateconninfo(struct Curl_easy *data, struct connectdata *conn,
curl_socket_t sockfd);
void Curl_conninfo_remote(struct Curl_easy *data, struct connectdata *conn,
curl_socket_t sockfd);
void Curl_conninfo_local(struct Curl_easy *data, curl_socket_t sockfd,
char *local_ip, int *local_port);
void Curl_persistconninfo(struct Curl_easy *data, struct connectdata *conn,
char *local_ip, int local_port);
int Curl_closesocket(struct Curl_easy *data, struct connectdata *conn,
curl_socket_t sock);
/*
* The Curl_sockaddr_ex structure is basically libcurl's external API
* curl_sockaddr structure with enough space available to directly hold any
* protocol-specific address structures. The variable declared here will be
* used to pass / receive data to/from the fopensocket callback if this has
* been set, before that, it is initialized from parameters.
*/
struct Curl_sockaddr_ex {
int family;
int socktype;
int protocol;
unsigned int addrlen;
union {
struct sockaddr addr;
struct Curl_sockaddr_storage buff;
} _sa_ex_u;
};
#define sa_addr _sa_ex_u.addr
/*
* Create a socket based on info from 'conn' and 'ai'.
*
* Fill in 'addr' and 'sockfd' accordingly if OK is returned. If the open
* socket callback is set, used that!
*
*/
CURLcode Curl_socket(struct Curl_easy *data,
const struct Curl_addrinfo *ai,
struct Curl_sockaddr_ex *addr,
curl_socket_t *sockfd);
/*
* Curl_conncontrol() marks the end of a connection/stream. The 'closeit'
@ -148,13 +87,50 @@ void Curl_conncontrol(struct connectdata *conn,
#define connkeep(x,y) Curl_conncontrol(x, CONNCTRL_KEEP)
#endif
CURLcode Curl_conn_socket_set(struct Curl_easy *data,
struct connectdata *conn,
int sockindex);
/**
* Create a cfilter for making an "ip" connection to the
* given address, using paramters from `conn`. The "ip" connection
* can be a TCP socket, a UDP socket or even a QUIC connection.
*
* It MUST use only the supplied `ai` for its connection attempt.
*
* Such a filter may be used in "happy eyeball" scenarios, and its
* `connect` implementation needs to support non-blocking. Once connected,
* it MAY be installed in the connection filter chain to serve transfers.
*/
typedef CURLcode cf_ip_connect_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
struct connectdata *conn,
const struct Curl_addrinfo *ai);
/**
* Create a happy eyeball connection filter that uses the, once resolved,
* address information to connect on ip families based on connection
* configuration.
* @param pcf output, the created cfilter
* @param data easy handle used in creation
* @param conn connection the filter is created for
* @param cf_create method to create the sub-filters performing the
* actual connects.
*/
CURLcode
Curl_cf_happy_eyeballs_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
struct connectdata *conn,
cf_ip_connect_create *cf_create,
const struct Curl_dns_entry *remotehost);
/**
* Setup the cfilters at `sockindex` in connection `conn`, invoking
* the instance `setup(remotehost)` methods. If no filter chain is
* installed yet, inspects the configuration in `data` to install a
* suitable filter chain.
*/
CURLcode Curl_conn_setup(struct Curl_easy *data,
struct connectdata *conn,
int sockindex,
const struct Curl_dns_entry *remotehost,
int ssl_mode);
CURLcode Curl_conn_socket_accepted_set(struct Curl_easy *data,
struct connectdata *conn,
int sockindex,
curl_socket_t *s);
#endif /* HEADER_CURL_CONNECT_H */

View File

@ -65,6 +65,7 @@
#include "easyif.h"
#include "multiif.h"
#include "select.h"
#include "cfilters.h"
#include "sendf.h" /* for failf function prototype */
#include "connect.h" /* for Curl_getconnectinfo */
#include "slist.h"
@ -1099,7 +1100,7 @@ CURLcode curl_easy_pause(struct Curl_easy *data, int action)
k->keepon = newstate;
if(!(newstate & KEEP_RECV_PAUSE)) {
Curl_http2_stream_pause(data, FALSE);
Curl_conn_ev_data_pause(data, FALSE);
if(data->state.tempcount) {
/* there are buffers for sending that can be delivered as the receive
@ -1292,29 +1293,34 @@ static int conn_upkeep(struct Curl_easy *data,
struct connectdata *conn,
void *param)
{
/* Param is unused. */
(void)param;
struct curltime *now = param;
if(Curl_timediff(*now, conn->keepalive) <= data->set.upkeep_interval_ms)
return 0;
/* briefly attach for action */
Curl_attach_connection(data, conn);
if(conn->handler->connection_check) {
/* briefly attach the connection to this transfer for the purpose of
checking it */
Curl_attach_connection(data, conn);
/* Do a protocol-specific keepalive check on the connection. */
conn->handler->connection_check(data, conn, CONNCHECK_KEEPALIVE);
/* detach the connection again */
Curl_detach_connection(data);
}
else {
/* Do the generic action on the FIRSTSOCKE filter chain */
Curl_conn_keep_alive(data, conn, FIRSTSOCKET);
}
Curl_detach_connection(data);
conn->keepalive = *now;
return 0; /* continue iteration */
}
static CURLcode upkeep(struct conncache *conn_cache, void *data)
{
struct curltime now = Curl_now();
/* Loop over every connection and make connection alive. */
Curl_conncache_foreach(data,
conn_cache,
data,
&now,
conn_upkeep);
return CURLE_OK;
}

View File

@ -61,6 +61,7 @@
#include "strcase.h"
#include "vtls/vtls.h"
#include "cfilters.h"
#include "cf-socket.h"
#include "connect.h"
#include "strerror.h"
#include "inet_ntop.h"
@ -285,8 +286,8 @@ static CURLcode AcceptServerConnect(struct Curl_easy *data)
conn->bits.do_more = FALSE;
(void)curlx_nonblock(s, TRUE); /* enable non-blocking */
/* Replace any filter on SECONDARY with one listening on this socket */
result = Curl_conn_socket_accepted_set(data, conn, SECONDARYSOCKET, &s);
/* Replace any filter on SECONDARY with one listeing on this socket */
result = Curl_conn_tcp_accepted_set(data, conn, SECONDARYSOCKET, &s);
if(result)
return result;
@ -819,26 +820,11 @@ static int ftp_domore_getsock(struct Curl_easy *data,
if(FTP_STOP == ftpc->state) {
int bits = GETSOCK_READSOCK(0);
bool any = FALSE;
/* if stopped and still in this state, then we're also waiting for a
connect on the secondary connection */
socks[0] = conn->sock[FIRSTSOCKET];
if(!data->set.ftp_use_port) {
int s;
int i;
/* PORT is used to tell the server to connect to us, and during that we
don't do happy eyeballs, but we do if we connect to the server */
for(s = 1, i = 0; i<2; i++) {
if(conn->tempsock[i] != CURL_SOCKET_BAD) {
socks[s] = conn->tempsock[i];
bits |= GETSOCK_WRITESOCK(s++);
any = TRUE;
}
}
}
if(!any) {
if(conn->sock[SECONDARYSOCKET] != CURL_SOCKET_BAD) {
socks[1] = conn->sock[SECONDARYSOCKET];
bits |= GETSOCK_WRITESOCK(1) | GETSOCK_READSOCK(1);
}
@ -1097,7 +1083,7 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
portsock = CURL_SOCKET_BAD;
error = 0;
for(ai = res; ai; ai = ai->ai_next) {
if(Curl_socket(data, ai, NULL, &portsock)) {
if(Curl_socket_open(data, ai, NULL, conn->transport, &portsock)) {
error = SOCKERRNO;
continue;
}
@ -1266,9 +1252,8 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
/* store which command was sent */
ftpc->count1 = fcmd;
/* Replace any filter on SECONDARY with one listening on this socket */
result = Curl_conn_socket_accepted_set(data, conn, SECONDARYSOCKET,
&portsock);
/* Replace any filter on SECONDARY with one listeing on this socket */
result = Curl_conn_tcp_listen_set(data, conn, SECONDARYSOCKET, &portsock);
if(result)
goto out;
portsock = CURL_SOCKET_BAD; /* now held in filter */
@ -1279,7 +1264,7 @@ out:
state(data, FTP_STOP);
}
if(portsock != CURL_SOCKET_BAD)
Curl_closesocket(data, conn, portsock);
Curl_socket_close(data, conn, portsock);
free(addr);
return result;
}
@ -1954,7 +1939,7 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data,
/* postponed address resolution in case of tcp fastopen */
if(conn->bits.tcp_fastopen && !conn->bits.reuse && !ftpc->newhost[0]) {
Curl_conninfo_remote(data, conn, conn->sock[FIRSTSOCKET]);
Curl_conn_ev_update_info(data, conn);
Curl_safefree(ftpc->newhost);
ftpc->newhost = strdup(control_address(conn));
if(!ftpc->newhost)
@ -2742,7 +2727,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data,
if((ftpcode == 234) || (ftpcode == 334)) {
/* this was BLOCKING, keep it so for now */
bool done;
if(!Curl_conn_is_ssl(data, FIRSTSOCKET)) {
if(!Curl_conn_is_ssl(conn, FIRSTSOCKET)) {
result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET);
if(result) {
/* we failed and bail out */

View File

@ -62,6 +62,7 @@
#include "cookie.h"
#include "vauth/vauth.h"
#include "vtls/vtls.h"
#include "vquic/vquic.h"
#include "http_digest.h"
#include "http_ntlm.h"
#include "curl_ntlm_wb.h"
@ -241,10 +242,7 @@ static CURLcode h3_setup_conn(struct Curl_easy *data,
}
#endif
DEBUGF(infof(data, "HTTP/3 direct conn setup(conn #%ld, index=%d)",
conn->connection_id, FIRSTSOCKET));
return Curl_conn_socket_set(data, conn, FIRSTSOCKET);
return CURLE_OK;
#else /* ENABLE_QUIC */
(void)conn;
(void)data;
@ -275,12 +273,6 @@ static CURLcode http_setup_conn(struct Curl_easy *data,
if(conn->transport == TRNSPRT_QUIC) {
return h3_setup_conn(data, conn);
}
else {
if(!CONN_INUSE(conn))
/* if not already multi-using, setup connection details */
Curl_http2_setup_conn(conn);
Curl_http2_setup_req(data);
}
return CURLE_OK;
}
@ -1610,8 +1602,6 @@ CURLcode Curl_http_done(struct Curl_easy *data,
return CURLE_OK;
Curl_dyn_free(&http->send_buffer);
Curl_http2_done(data, premature);
Curl_quic_done(data, premature);
Curl_mime_cleanpart(&http->form);
Curl_dyn_reset(&data->state.headerb);
Curl_hyper_done(data);
@ -1664,17 +1654,10 @@ bool Curl_use_http_1_1plus(const struct Curl_easy *data,
static const char *get_http_string(const struct Curl_easy *data,
const struct connectdata *conn)
{
#ifdef ENABLE_QUIC
if((data->state.httpwant == CURL_HTTP_VERSION_3) ||
(conn->httpversion == 30))
if(Curl_conn_is_http3(data, conn, FIRSTSOCKET))
return "3";
#endif
#ifdef USE_NGHTTP2
if(conn->proto.httpc.h2)
if(Curl_conn_is_http2(data, conn, FIRSTSOCKET))
return "2";
#endif
if(Curl_use_http_1_1plus(data, conn))
return "1.1";
@ -2561,7 +2544,7 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn,
/* In HTTP2, we send request body in DATA frame regardless of
its size. */
if(conn->httpversion != 20 &&
if(conn->httpversion < 20 &&
!data->state.expect100header &&
(http->postsize < MAX_INITIAL_POST_SIZE)) {
/* if we don't use expect: 100 AND
@ -3021,50 +3004,35 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
the rest of the request in the PERFORM phase. */
*done = TRUE;
if(conn->transport != TRNSPRT_QUIC) {
if(conn->httpversion < 20) { /* unless the connection is re-used and
already http2 */
switch(conn->alpn) {
case CURL_HTTP_VERSION_2:
conn->httpversion = 20; /* we know we're on HTTP/2 now */
result = Curl_http2_switched(data, NULL, 0);
if(result)
return result;
break;
case CURL_HTTP_VERSION_1_1:
/* continue with HTTP/1.1 when explicitly requested */
break;
default:
/* Check if user wants to use HTTP/2 with clear TCP */
#ifdef USE_NGHTTP2
if(data->state.httpwant == CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE) {
#ifndef CURL_DISABLE_PROXY
if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) {
/* We don't support HTTP/2 proxies yet. Also it's debatable
whether or not this setting should apply to HTTP/2 proxies. */
infof(data, "Ignoring HTTP/2 prior knowledge due to proxy");
break;
}
#endif
DEBUGF(infof(data, "HTTP/2 over clean TCP"));
conn->httpversion = 20;
result = Curl_http2_switched(data, NULL, 0);
if(result)
return result;
}
#endif
break;
}
}
else {
/* prepare for an http2 request */
result = Curl_http2_setup(data, conn);
if(Curl_conn_is_http3(data, conn, FIRSTSOCKET)
|| Curl_conn_is_http2(data, conn, FIRSTSOCKET)
|| conn->httpversion == 20 /* like to get rid of this */) {
/* all fine, we are set */
}
else { /* undecided */
switch(conn->alpn) {
case CURL_HTTP_VERSION_2:
result = Curl_http2_switch(data, conn, FIRSTSOCKET, NULL, 0);
if(result)
return result;
break;
case CURL_HTTP_VERSION_1_1:
/* continue with HTTP/1.1 when explicitly requested */
break;
default:
/* Check if user wants to use HTTP/2 with clear TCP */
if(Curl_http2_may_switch(data, conn, FIRSTSOCKET)) {
DEBUGF(infof(data, "HTTP/2 over clean TCP"));
result = Curl_http2_switch(data, conn, FIRSTSOCKET, NULL, 0);
if(result)
return result;
}
break;
}
}
http = data->req.p.http;
DEBUGASSERT(http);
@ -3224,7 +3192,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
}
if(!(conn->handler->flags&PROTOPT_SSL) &&
conn->httpversion != 20 &&
conn->httpversion < 20 &&
(data->state.httpwant == CURL_HTTP_VERSION_2)) {
/* append HTTP2 upgrade magic stuff to the HTTP request if it isn't done
over SSL */
@ -3282,7 +3250,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
}
}
if((conn->httpversion == 20) && data->req.upload_chunky)
if((conn->httpversion >= 20) && data->req.upload_chunky)
/* upload_chunky was set above to set up the request in a chunky fashion,
but is disabled here again to avoid that the chunked encoded version is
actually used when sending the request body over h2 */
@ -3669,7 +3637,8 @@ CURLcode Curl_http_header(struct Curl_easy *data, struct connectdata *conn,
#endif
)) {
/* the ALPN of the current request */
enum alpnid id = (conn->httpversion == 20) ? ALPN_h2 : ALPN_h1;
enum alpnid id = (conn->httpversion == 30)? ALPN_h3 :
(conn->httpversion == 20) ? ALPN_h2 : ALPN_h1;
result = Curl_altsvc_parse(data, data->asi,
headp + strlen("Alt-Svc:"),
id, conn->host.name,
@ -3963,7 +3932,8 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
/* switch to http2 now. The bytes after response headers
are also processed here, otherwise they are lost. */
result = Curl_http2_switched(data, k->str, *nread);
result = Curl_http2_switch(data, conn, FIRSTSOCKET,
k->str, *nread);
if(result)
return result;
*nread = 0;
@ -4191,11 +4161,8 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
stream. In order to do this, we keep reading until we
close the stream. */
if(0 == k->maxdownload
#if defined(USE_NGHTTP2)
&& !((conn->handler->protocol & PROTO_FAMILY_HTTP) &&
conn->httpversion == 20)
#endif
)
&& !Curl_conn_is_http2(data, conn, FIRSTSOCKET)
&& !Curl_conn_is_http3(data, conn, FIRSTSOCKET))
*stop_reading = TRUE;
if(*stop_reading) {

View File

@ -24,6 +24,11 @@
*
***************************************************************************/
#include "curl_setup.h"
#if defined(USE_MSH3) && !defined(_WIN32)
#include <pthread.h>
#endif
#include "ws.h"
typedef enum {
@ -37,10 +42,6 @@ typedef enum {
#ifndef CURL_DISABLE_HTTP
#ifdef USE_NGHTTP2
#include <nghttp2/nghttp2.h>
#endif
#if defined(_WIN32) && defined(ENABLE_QUIC)
#include <stdint.h>
#endif
@ -179,29 +180,6 @@ CURLcode Curl_http_auth_act(struct Curl_easy *data);
struct h3out; /* see ngtcp2 */
#endif
#ifdef USE_MSH3
#ifdef _WIN32
#define msh3_lock CRITICAL_SECTION
#define msh3_lock_initialize(lock) InitializeCriticalSection(lock)
#define msh3_lock_uninitialize(lock) DeleteCriticalSection(lock)
#define msh3_lock_acquire(lock) EnterCriticalSection(lock)
#define msh3_lock_release(lock) LeaveCriticalSection(lock)
#else /* !_WIN32 */
#include <pthread.h>
#define msh3_lock pthread_mutex_t
#define msh3_lock_initialize(lock) { \
pthread_mutexattr_t attr; \
pthread_mutexattr_init(&attr); \
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); \
pthread_mutex_init(lock, &attr); \
pthread_mutexattr_destroy(&attr); \
}
#define msh3_lock_uninitialize(lock) pthread_mutex_destroy(lock)
#define msh3_lock_acquire(lock) pthread_mutex_lock(lock)
#define msh3_lock_release(lock) pthread_mutex_unlock(lock)
#endif /* _WIN32 */
#endif /* USE_MSH3 */
/****************************************************************************
* HTTP unique setup
***************************************************************************/
@ -278,17 +256,22 @@ struct HTTP {
bool firstheader; /* FALSE until headers arrive */
bool firstbody; /* FALSE until body arrives */
bool h3req; /* FALSE until request is issued */
#endif
#endif /* !USE_MSH3 */
bool upload_done;
#endif
#endif /* ENABLE_QUIC */
#ifdef USE_NGHTTP3
size_t unacked_window;
struct h3out *h3out; /* per-stream buffers for upload */
struct dynbuf overflow; /* excess data received during a single Curl_read */
#endif
#endif /* USE_NGHTTP3 */
#ifdef USE_MSH3
struct MSH3_REQUEST *req;
msh3_lock recv_lock;
#ifdef _WIN32
CRITICAL_SECTION recv_lock;
#else /* !_WIN32 */
pthread_mutex_t recv_lock;
#endif /* _WIN32 */
/* Receive Buffer (Headers and Data) */
uint8_t* recv_buf;
size_t recv_buf_alloc;
@ -300,53 +283,7 @@ struct HTTP {
bool recv_data_complete;
/* General Receive Error */
CURLcode recv_error;
#endif
};
#ifdef USE_NGHTTP2
/* h2 settings for this connection */
struct h2settings {
uint32_t max_concurrent_streams;
bool enable_push;
};
#endif
struct http_conn {
#ifdef USE_NGHTTP2
#define H2_BINSETTINGS_LEN 80
uint8_t binsettings[H2_BINSETTINGS_LEN];
size_t binlen; /* length of the binsettings data */
/* We associate the connectdata struct with the connection, but we need to
make sure we can identify the current "driving" transfer. This is a
work-around for the lack of nghttp2_session_set_user_data() in older
nghttp2 versions that we want to support. (Added in 1.31.0) */
struct Curl_easy *trnsfr;
nghttp2_session *h2;
Curl_send *send_underlying; /* underlying send Curl_send callback */
Curl_recv *recv_underlying; /* underlying recv Curl_recv callback */
char *inbuf; /* buffer to receive data from underlying socket */
size_t inbuflen; /* number of bytes filled in inbuf */
size_t nread_inbuf; /* number of bytes read from in inbuf */
/* We need separate buffer for transmission and reception because we
may call nghttp2_session_send() after the
nghttp2_session_mem_recv() but mem buffer is still not full. In
this case, we wrongly sends the content of mem buffer if we share
them for both cases. */
int32_t pause_stream_id; /* stream ID which paused
nghttp2_session_mem_recv */
size_t drain_total; /* sum of all stream's UrlState.drain */
/* this is a hash of all individual streams (Curl_easy structs) */
struct h2settings settings;
/* list of settings that will be sent */
nghttp2_settings_entry local_settings[3];
size_t local_settings_num;
#else
int unused; /* prevent a compiler warning */
#endif
#endif /* USE_MSH3 */
};
CURLcode Curl_http_size(struct Curl_easy *data);

File diff suppressed because it is too large Load Diff

View File

@ -40,44 +40,31 @@ void Curl_http2_ver(char *p, size_t len);
const char *Curl_http2_strerror(uint32_t err);
CURLcode Curl_http2_init(struct connectdata *conn);
void Curl_http2_init_state(struct UrlState *state);
void Curl_http2_init_userset(struct UserDefined *set);
CURLcode Curl_http2_request_upgrade(struct dynbuf *req,
struct Curl_easy *data);
CURLcode Curl_http2_setup(struct Curl_easy *data, struct connectdata *conn);
CURLcode Curl_http2_switched(struct Curl_easy *data,
const char *ptr, size_t nread);
/* called from http_setup_conn */
void Curl_http2_setup_conn(struct connectdata *conn);
void Curl_http2_setup_req(struct Curl_easy *data);
void Curl_http2_done(struct Curl_easy *data, bool premature);
CURLcode Curl_http2_done_sending(struct Curl_easy *data,
struct connectdata *conn);
CURLcode Curl_http2_add_child(struct Curl_easy *parent,
struct Curl_easy *child,
bool exclusive);
void Curl_http2_remove_child(struct Curl_easy *parent,
struct Curl_easy *child);
void Curl_http2_cleanup_dependencies(struct Curl_easy *data);
CURLcode Curl_http2_stream_pause(struct Curl_easy *data, bool pause);
/* returns true if the HTTP/2 stream error was HTTP_1_1_REQUIRED */
bool Curl_h2_http_1_1_error(struct Curl_easy *data);
bool Curl_conn_is_http2(const struct Curl_easy *data,
const struct connectdata *conn,
int sockindex);
bool Curl_http2_may_switch(struct Curl_easy *data,
struct connectdata *conn,
int sockindex);
CURLcode Curl_http2_switch(struct Curl_easy *data,
struct connectdata *conn, int sockindex,
const char *ptr, size_t nread);
#else /* USE_NGHTTP2 */
#define Curl_conn_is_http2(a,b,c) FALSE
#define Curl_http2_may_switch(a,b,c) FALSE
#define Curl_http2_request_upgrade(x,y) CURLE_UNSUPPORTED_PROTOCOL
#define Curl_http2_setup(x,y) CURLE_UNSUPPORTED_PROTOCOL
#define Curl_http2_switched(x,y,z) CURLE_UNSUPPORTED_PROTOCOL
#define Curl_http2_setup_conn(x) Curl_nop_stmt
#define Curl_http2_setup_req(x)
#define Curl_http2_init_state(x)
#define Curl_http2_init_userset(x)
#define Curl_http2_done(x,y)
#define Curl_http2_done_sending(x,y) (void)y
#define Curl_http2_add_child(x, y, z)
#define Curl_http2_remove_child(x, y)
#define Curl_http2_cleanup_dependencies(x)
#define Curl_http2_stream_pause(x, y)
#define Curl_http2_switch(a,b,c,d,e) CURLE_UNSUPPORTED_PROTOCOL
#define Curl_h2_http_1_1_error(x) 0
#endif

View File

@ -26,7 +26,7 @@
#include "http_proxy.h"
#if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP)
#if !defined(CURL_DISABLE_PROXY)
#include <curl/curl.h>
#ifdef USE_HYPER
@ -49,6 +49,17 @@
#include "curl_memory.h"
#include "memdebug.h"
#define DEBUG_CF 0
#if DEBUG_CF
#define CF_DEBUGF(x) x
#else
#define CF_DEBUGF(x) do { } while(0)
#endif
#if !defined(CURL_DISABLE_HTTP)
typedef enum {
TUNNEL_INIT, /* init/default/no tunnel state */
TUNNEL_CONNECT, /* CONNECT request is being send */
@ -183,29 +194,35 @@ static void tunnel_go_state(struct Curl_cfilter *cf,
/* entering this one */
switch(new_state) {
case TUNNEL_INIT:
CF_DEBUGF(infof(data, CFMSG(cf, "new tunnel state 'init'")));
tunnel_reinit(ts, cf->conn, data);
break;
case TUNNEL_CONNECT:
CF_DEBUGF(infof(data, CFMSG(cf, "new tunnel state 'connect'")));
ts->tunnel_state = TUNNEL_CONNECT;
ts->keepon = KEEPON_CONNECT;
Curl_dyn_reset(&ts->rcvbuf);
break;
case TUNNEL_RECEIVE:
CF_DEBUGF(infof(data, CFMSG(cf, "new tunnel state 'receive'")));
ts->tunnel_state = TUNNEL_RECEIVE;
break;
case TUNNEL_RESPONSE:
CF_DEBUGF(infof(data, CFMSG(cf, "new tunnel state 'response'")));
ts->tunnel_state = TUNNEL_RESPONSE;
break;
case TUNNEL_ESTABLISHED:
CF_DEBUGF(infof(data, CFMSG(cf, "new tunnel state 'established'")));
infof(data, "CONNECT phase completed");
data->state.authproxy.done = TRUE;
data->state.authproxy.multipass = FALSE;
/* FALLTHROUGH */
case TUNNEL_FAILED:
CF_DEBUGF(infof(data, CFMSG(cf, "new tunnel state 'failed'")));
ts->tunnel_state = new_state;
Curl_dyn_reset(&ts->rcvbuf);
Curl_dyn_reset(&ts->req);
@ -416,7 +433,7 @@ static CURLcode on_resp_header(struct Curl_easy *data,
if(!auth)
return CURLE_OUT_OF_MEMORY;
DEBUGF(infof(data, "CONNECT: fwd auth header '%s'",
CF_DEBUGF(infof(data, "CONNECT: fwd auth header '%s'",
header));
result = Curl_http_input_auth(data, proxy, auth);
@ -634,7 +651,7 @@ static CURLcode recv_CONNECT_resp(struct Curl_easy *data,
/* without content-length or chunked encoding, we
can't keep the connection alive since the close is
the end signal so we bail out at once instead */
DEBUGF(infof(data, "CONNECT: no content-length or chunked"));
CF_DEBUGF(infof(data, "CONNECT: no content-length or chunked"));
ts->keepon = KEEPON_DONE;
}
}
@ -972,6 +989,7 @@ static CURLcode CONNECT(struct Curl_cfilter *cf,
switch(ts->tunnel_state) {
case TUNNEL_INIT:
/* Prepare the CONNECT request and make a first attempt to send. */
CF_DEBUGF(infof(data, CFMSG(cf, "CONNECT start")));
result = start_CONNECT(data, cf->conn, ts);
if(result)
goto out;
@ -980,6 +998,7 @@ static CURLcode CONNECT(struct Curl_cfilter *cf,
case TUNNEL_CONNECT:
/* see that the request is completely sent */
CF_DEBUGF(infof(data, CFMSG(cf, "CONNECT send")));
result = send_CONNECT(data, cf->conn, ts, &done);
if(result || !done)
goto out;
@ -988,6 +1007,7 @@ static CURLcode CONNECT(struct Curl_cfilter *cf,
case TUNNEL_RECEIVE:
/* read what is there */
CF_DEBUGF(infof(data, CFMSG(cf, "CONNECT receive")));
result = recv_CONNECT_resp(data, cf->conn, ts, &done);
if(Curl_pgrsUpdate(data)) {
result = CURLE_ABORTED_BY_CALLBACK;
@ -1001,24 +1021,29 @@ static CURLcode CONNECT(struct Curl_cfilter *cf,
/* FALLTHROUGH */
case TUNNEL_RESPONSE:
CF_DEBUGF(infof(data, CFMSG(cf, "CONNECT response")));
if(data->req.newurl) {
/* not the "final" response, we need to do a follow up request.
* If the other side indicated a connection close, or if someone
* else told us to close this connection, do so now. */
* else told us to close this connection, do so now.
*/
if(ts->close_connection || conn->bits.close) {
/* Close the filter chain and trigger connect, non-blocking
* again, so the process is ongoing. This will
* a) the close resets our tunnel state
* b) the connect makes sure that there will be a socket
* to select on again.
* We return and expect to be called again. */
/* Close this filter and the sub-chain, re-connect the
* sub-chain and continue. Closing this filter will
* reset our tunnel state. To avoid recursion, we return
* and expect to be called again.
*/
CF_DEBUGF(infof(data, CFMSG(cf, "CONNECT need to close+open")));
infof(data, "Connect me again please");
Curl_conn_close(data, cf->sockindex);
result = cf->next->cft->connect(cf->next, data, FALSE, &done);
Curl_conn_cf_close(cf, data);
connkeep(conn, "HTTP proxy CONNECT");
result = Curl_conn_cf_connect(cf->next, data, FALSE, &done);
goto out;
}
/* staying on this connection, reset state */
tunnel_go_state(cf, ts, TUNNEL_INIT, data);
else {
/* staying on this connection, reset state */
tunnel_go_state(cf, ts, TUNNEL_INIT, data);
}
}
break;
@ -1063,10 +1088,12 @@ static CURLcode http_proxy_cf_connect(struct Curl_cfilter *cf,
return CURLE_OK;
}
CF_DEBUGF(infof(data, CFMSG(cf, "connect")));
result = cf->next->cft->connect(cf->next, data, blocking, done);
if(result || !*done)
return result;
CF_DEBUGF(infof(data, CFMSG(cf, "subchain is connected")));
/* TODO: can we do blocking? */
/* We want "seamless" operations through HTTP proxy tunnel */
@ -1140,24 +1167,18 @@ static int http_proxy_cf_get_select_socks(struct Curl_cfilter *cf,
return fds;
}
static void http_proxy_cf_detach_data(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
if(cf->ctx) {
tunnel_free(cf, data);
}
}
static void http_proxy_cf_destroy(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
http_proxy_cf_detach_data(cf, data);
CF_DEBUGF(infof(data, CFMSG(cf, "destroy")));
tunnel_free(cf, data);
}
static void http_proxy_cf_close(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
DEBUGASSERT(cf->next);
CF_DEBUGF(infof(data, CFMSG(cf, "close")));
cf->connected = FALSE;
cf->next->cft->close(cf->next, data);
if(cf->ctx) {
@ -1170,7 +1191,6 @@ static const struct Curl_cftype cft_http_proxy = {
"HTTP-PROXY",
CF_TYPE_IP_CONNECT,
http_proxy_cf_destroy,
Curl_cf_def_setup,
http_proxy_cf_connect,
http_proxy_cf_close,
http_proxy_cf_get_host,
@ -1178,8 +1198,10 @@ static const struct Curl_cftype cft_http_proxy = {
Curl_cf_def_data_pending,
Curl_cf_def_send,
Curl_cf_def_recv,
Curl_cf_def_attach_data,
http_proxy_cf_detach_data,
Curl_cf_def_cntrl,
Curl_cf_def_conn_is_alive,
Curl_cf_def_conn_keep_alive,
Curl_cf_def_query,
};
CURLcode Curl_conn_http_proxy_add(struct Curl_easy *data,
@ -1195,28 +1217,67 @@ CURLcode Curl_conn_http_proxy_add(struct Curl_easy *data,
return result;
}
#endif /* !CURL_DISABLE_PROXY &6 ! CURL_DISABLE_HTTP */
#if !defined(CURL_DISABLE_PROXY)
static CURLcode send_haproxy_header(struct Curl_cfilter*cf,
struct Curl_easy *data)
CURLcode Curl_cf_http_proxy_insert_after(struct Curl_cfilter *cf_at,
struct Curl_easy *data)
{
struct dynbuf req;
struct Curl_cfilter *cf;
CURLcode result;
(void)data;
result = Curl_cf_create(&cf, &cft_http_proxy, NULL);
if(!result)
Curl_conn_cf_insert_after(cf_at, cf);
return result;
}
#endif /* ! CURL_DISABLE_HTTP */
typedef enum {
HAPROXY_INIT, /* init/default/no tunnel state */
HAPROXY_SEND, /* data_out being sent */
HAPROXY_DONE /* all work done */
} haproxy_state;
struct cf_haproxy_ctx {
int state;
struct dynbuf data_out;
};
static void cf_haproxy_ctx_reset(struct cf_haproxy_ctx *ctx)
{
DEBUGASSERT(ctx);
ctx->state = HAPROXY_INIT;
Curl_dyn_reset(&ctx->data_out);
}
static void cf_haproxy_ctx_free(struct cf_haproxy_ctx *ctx)
{
if(ctx) {
Curl_dyn_free(&ctx->data_out);
free(ctx);
}
}
static CURLcode cf_haproxy_date_out_set(struct Curl_cfilter*cf,
struct Curl_easy *data)
{
struct cf_haproxy_ctx *ctx = cf->ctx;
CURLcode result;
const char *tcp_version;
Curl_dyn_init(&req, DYN_HAXPROXY);
DEBUGASSERT(ctx);
DEBUGASSERT(ctx->state == HAPROXY_INIT);
#ifdef USE_UNIX_SOCKETS
if(cf->conn->unix_domain_socket)
/* the buffer is large enough to hold this! */
result = Curl_dyn_addn(&req, STRCONST("PROXY UNKNOWN\r\n"));
result = Curl_dyn_addn(&ctx->data_out, STRCONST("PROXY UNKNOWN\r\n"));
else {
#endif /* USE_UNIX_SOCKETS */
/* Emit the correct prefix for IPv6 */
tcp_version = cf->conn->bits.ipv6 ? "TCP6" : "TCP4";
result = Curl_dyn_addf(&req, "PROXY %s %s %s %i %i\r\n",
result = Curl_dyn_addf(&ctx->data_out, "PROXY %s %s %s %i %i\r\n",
tcp_version,
data->info.conn_local_ip,
data->info.conn_primary_ip,
@ -1226,19 +1287,18 @@ static CURLcode send_haproxy_header(struct Curl_cfilter*cf,
#ifdef USE_UNIX_SOCKETS
}
#endif /* USE_UNIX_SOCKETS */
if(!result)
result = Curl_buffer_send(&req, data, &data->info.request_size,
0, FIRSTSOCKET);
return result;
}
static CURLcode haproxy_cf_connect(struct Curl_cfilter *cf,
static CURLcode cf_haproxy_connect(struct Curl_cfilter *cf,
struct Curl_easy *data,
bool blocking, bool *done)
{
struct cf_haproxy_ctx *ctx = cf->ctx;
CURLcode result;
size_t len;
DEBUGASSERT(ctx);
if(cf->connected) {
*done = TRUE;
return CURLE_OK;
@ -1248,28 +1308,121 @@ static CURLcode haproxy_cf_connect(struct Curl_cfilter *cf,
if(result || !*done)
return result;
result = send_haproxy_header(cf, data);
*done = (!result);
switch(ctx->state) {
case HAPROXY_INIT:
result = cf_haproxy_date_out_set(cf, data);
if(result)
goto out;
ctx->state = HAPROXY_SEND;
/* FALLTHROUGH */
case HAPROXY_SEND:
len = Curl_dyn_len(&ctx->data_out);
if(len > 0) {
ssize_t written = Curl_conn_send(data, cf->sockindex,
Curl_dyn_ptr(&ctx->data_out),
len, &result);
if(written < 0)
goto out;
Curl_dyn_tail(&ctx->data_out, len - (size_t)written);
if(Curl_dyn_len(&ctx->data_out) > 0) {
result = CURLE_OK;
goto out;
}
}
ctx->state = HAPROXY_DONE;
/* FALLTHROUGH */
default:
Curl_dyn_free(&ctx->data_out);
break;
}
out:
*done = (!result) && (ctx->state == HAPROXY_DONE);
cf->connected = *done;
return result;
}
static void cf_haproxy_destroy(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
(void)data;
CF_DEBUGF(infof(data, CFMSG(cf, "destroy")));
cf_haproxy_ctx_free(cf->ctx);
}
static void cf_haproxy_close(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
CF_DEBUGF(infof(data, CFMSG(cf, "close")));
cf->connected = FALSE;
cf_haproxy_ctx_reset(cf->ctx);
if(cf->next)
cf->next->cft->close(cf->next, data);
}
static int cf_haproxy_get_select_socks(struct Curl_cfilter *cf,
struct Curl_easy *data,
curl_socket_t *socks)
{
struct connectdata *conn = cf->conn;
int fds;
DEBUGASSERT(conn);
fds = cf->next->cft->get_select_socks(cf->next, data, socks);
if(!fds && cf->next->connected && !cf->connected) {
/* If we are not connected, but the filter "below" is
* and not waiting on something, we are sending. */
socks[0] = conn->sock[cf->sockindex];
return GETSOCK_WRITESOCK(0);
}
return fds;
}
static const struct Curl_cftype cft_haproxy = {
"HAPROXY",
0,
Curl_cf_def_destroy_this,
Curl_cf_def_setup,
haproxy_cf_connect,
Curl_cf_def_close,
cf_haproxy_destroy,
cf_haproxy_connect,
cf_haproxy_close,
Curl_cf_def_get_host,
Curl_cf_def_get_select_socks,
cf_haproxy_get_select_socks,
Curl_cf_def_data_pending,
Curl_cf_def_send,
Curl_cf_def_recv,
Curl_cf_def_attach_data,
Curl_cf_def_detach_data,
Curl_cf_def_cntrl,
Curl_cf_def_conn_is_alive,
Curl_cf_def_conn_keep_alive,
Curl_cf_def_query,
};
static CURLcode cf_haproxy_create(struct Curl_cfilter **pcf,
struct Curl_easy *data)
{
struct Curl_cfilter *cf = NULL;
struct cf_haproxy_ctx *ctx;
CURLcode result;
(void)data;
ctx = calloc(sizeof(*ctx), 1);
if(!ctx) {
result = CURLE_OUT_OF_MEMORY;
goto out;
}
ctx->state = HAPROXY_INIT;
Curl_dyn_init(&ctx->data_out, DYN_HAXPROXY);
result = Curl_cf_create(&cf, &cft_haproxy, ctx);
if(result)
goto out;
ctx = NULL;
out:
cf_haproxy_ctx_free(ctx);
*pcf = result? NULL : cf;
return result;
}
CURLcode Curl_conn_haproxy_add(struct Curl_easy *data,
struct connectdata *conn,
int sockindex)
@ -1277,9 +1430,27 @@ CURLcode Curl_conn_haproxy_add(struct Curl_easy *data,
struct Curl_cfilter *cf;
CURLcode result;
result = Curl_cf_create(&cf, &cft_haproxy, NULL);
if(!result)
Curl_conn_cf_add(data, conn, sockindex, cf);
result = cf_haproxy_create(&cf, data);
if(result)
goto out;
Curl_conn_cf_add(data, conn, sockindex, cf);
out:
return result;
}
CURLcode Curl_cf_haproxy_insert_after(struct Curl_cfilter *cf_at,
struct Curl_easy *data)
{
struct Curl_cfilter *cf;
CURLcode result;
result = cf_haproxy_create(&cf, data);
if(result)
goto out;
Curl_conn_cf_insert_after(cf_at, cf);
out:
return result;
}

View File

@ -36,12 +36,18 @@
CURLcode Curl_conn_http_proxy_add(struct Curl_easy *data,
struct connectdata *conn,
int sockindex);
CURLcode Curl_cf_http_proxy_insert_after(struct Curl_cfilter *cf_at,
struct Curl_easy *data);
#endif /* !CURL_DISABLE_HTTP */
CURLcode Curl_conn_haproxy_add(struct Curl_easy *data,
struct connectdata *conn,
int sockindex);
CURLcode Curl_cf_haproxy_insert_after(struct Curl_cfilter *cf_at,
struct Curl_easy *data);
#endif /* !CURL_DISABLE_PROXY */
#endif /* HEADER_CURL_HTTP_PROXY_H */

View File

@ -476,7 +476,7 @@ static CURLcode imap_perform_upgrade_tls(struct Curl_easy *data,
struct imap_conn *imapc = &conn->proto.imapc;
CURLcode result;
if(!Curl_conn_is_ssl(data, FIRSTSOCKET)) {
if(!Curl_conn_is_ssl(conn, FIRSTSOCKET)) {
result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET);
if(result)
goto out;
@ -952,7 +952,7 @@ static CURLcode imap_state_capability_resp(struct Curl_easy *data,
line += wordlen;
}
}
else if(data->set.use_ssl && !Curl_conn_is_ssl(data, FIRSTSOCKET)) {
else if(data->set.use_ssl && !Curl_conn_is_ssl(conn, FIRSTSOCKET)) {
/* PREAUTH is not compatible with STARTTLS. */
if(imapcode == IMAP_RESP_OK && imapc->tls_supported && !imapc->preauth) {
/* Switch to TLS connection now */

View File

@ -655,6 +655,9 @@ static CURLcode multi_done(struct Curl_easy *data,
result = CURLE_ABORTED_BY_CALLBACK;
}
/* Inform connection filters that this transfer is done */
Curl_conn_ev_data_done(data, premature);
process_pending_handles(data->multi); /* connection / multiplex */
CONNCACHE_LOCK(data);
@ -709,12 +712,12 @@ static CURLcode multi_done(struct Curl_easy *data,
conn->proxy_negotiate_state == GSS_AUTHRECV)
#endif
) || conn->bits.close
|| (premature && !(conn->handler->flags & PROTOPT_STREAM))) {
|| (premature && !Curl_conn_is_multiplex(conn, FIRSTSOCKET))) {
DEBUGF(infof(data, "multi_done, not re-using connection=%ld, forbid=%d"
", close=%d, premature=%d, stream=%d",
", close=%d, premature=%d, conn_multiplex=%d",
conn->connection_id,
data->set.reuse_forbid, conn->bits.close, premature,
(conn->handler->flags & PROTOPT_STREAM)));
Curl_conn_is_multiplex(conn, FIRSTSOCKET)));
connclose(conn, "disconnecting");
Curl_conncache_remove_conn(data, conn, FALSE);
CONNCACHE_UNLOCK(data);
@ -954,7 +957,7 @@ void Curl_detach_connection(struct Curl_easy *data)
{
struct connectdata *conn = data->conn;
if(conn) {
Curl_conn_detach_data(conn, data);
Curl_conn_ev_data_detach(conn, data);
Curl_llist_remove(&conn->easyq, &data->conn_queue, NULL);
}
data->conn = NULL;
@ -973,9 +976,9 @@ void Curl_attach_connection(struct Curl_easy *data,
data->conn = conn;
Curl_llist_insert_next(&conn->easyq, conn->easyq.tail, data,
&data->conn_queue);
Curl_conn_attach_data(conn, data);
if(conn->handler->attach)
conn->handler->attach(data, conn);
Curl_conn_ev_data_attach(conn, data);
}
static int domore_getsock(struct Curl_easy *data,
@ -1002,11 +1005,7 @@ static int protocol_getsock(struct Curl_easy *data,
{
if(conn->handler->proto_getsock)
return conn->handler->proto_getsock(data, conn, socks);
/* Backup getsock logic. Since there is a live socket in use, we must wait
for it or it will be removed from watching when the multi_socket API is
used. */
socks[0] = conn->sock[FIRSTSOCKET];
return GETSOCK_READSOCK(0) | GETSOCK_WRITESOCK(0);
return Curl_conn_get_select_socks(data, FIRSTSOCKET, socks);
}
/* returns bitmapped flags for this handle and its sockets. The 'socks[]'

View File

@ -371,7 +371,7 @@ static CURLcode pop3_perform_upgrade_tls(struct Curl_easy *data,
struct pop3_conn *pop3c = &conn->proto.pop3c;
CURLcode result;
if(!Curl_conn_is_ssl(data, FIRSTSOCKET)) {
if(!Curl_conn_is_ssl(conn, FIRSTSOCKET)) {
result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET);
if(result)
goto out;
@ -769,7 +769,7 @@ static CURLcode pop3_state_capa_resp(struct Curl_easy *data, int pop3code,
if(pop3code != '+')
pop3c->authtypes |= POP3_TYPE_CLEARTEXT;
if(!data->set.use_ssl || Curl_conn_is_ssl(data, FIRSTSOCKET))
if(!data->set.use_ssl || Curl_conn_is_ssl(conn, FIRSTSOCKET))
result = pop3_perform_authentication(data, conn);
else if(pop3code == '+' && pop3c->tls_supported)
/* Switch to TLS connection now */

View File

@ -1,68 +0,0 @@
#ifndef HEADER_CURL_QUIC_H
#define HEADER_CURL_QUIC_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 1998 - 2022, 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"
#ifdef ENABLE_QUIC
#ifdef USE_NGTCP2
#include "vquic/ngtcp2.h"
#endif
#ifdef USE_QUICHE
#include "vquic/quiche.h"
#endif
#ifdef USE_MSH3
#include "vquic/msh3.h"
#endif
#include "urldata.h"
/* functions provided by the specific backends */
CURLcode Curl_quic_connect(struct Curl_easy *data,
struct connectdata *conn,
curl_socket_t sockfd,
int sockindex,
const struct sockaddr *addr,
socklen_t addrlen);
CURLcode Curl_quic_is_connected(struct Curl_easy *data,
struct connectdata *conn,
int sockindex,
bool *connected);
void Curl_quic_ver(char *p, size_t len);
CURLcode Curl_quic_done_sending(struct Curl_easy *data);
void Curl_quic_done(struct Curl_easy *data, bool premature);
bool Curl_quic_data_pending(const struct Curl_easy *data);
void Curl_quic_disconnect(struct Curl_easy *data,
struct connectdata *conn, int tempindex);
CURLcode Curl_quic_idle(struct Curl_easy *data);
#else /* ENABLE_QUIC */
#define Curl_quic_done_sending(x)
#define Curl_quic_done(x,y)
#define Curl_quic_data_pending(x)
#define Curl_quic_disconnect(x,y,z)
#endif /* !ENABLE_QUIC */
#endif /* HEADER_CURL_QUIC_H */

View File

@ -38,6 +38,7 @@
#include "strcase.h"
#include "select.h"
#include "connect.h"
#include "cfilters.h"
#include "strdup.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
@ -133,36 +134,6 @@ static CURLcode rtsp_setup_connection(struct Curl_easy *data,
}
/*
* The server may send us RTP data at any point, and RTSPREQ_RECEIVE does not
* want to block the application forever while receiving a stream. Therefore,
* we cannot assume that an RTSP socket is dead just because it is readable.
*
* Instead, if it is readable, run Curl_connalive() to peek at the socket
* and distinguish between closed and data.
*/
static bool rtsp_connisdead(struct Curl_easy *data, struct connectdata *check)
{
int sval;
bool ret_val = TRUE;
sval = SOCKET_READABLE(check->sock[FIRSTSOCKET], 0);
if(sval == 0) {
/* timeout */
ret_val = FALSE;
}
else if(sval & CURL_CSELECT_ERR) {
/* socket is in an error state */
ret_val = TRUE;
}
else if(sval & CURL_CSELECT_IN) {
/* readable with no error. could still be closed */
ret_val = !Curl_connalive(data, check);
}
return ret_val;
}
/*
* Function to check on various aspects of a connection.
*/
@ -174,7 +145,7 @@ static unsigned int rtsp_conncheck(struct Curl_easy *data,
(void)data;
if(checks_to_perform & CONNCHECK_ISDEAD) {
if(rtsp_connisdead(data, conn))
if(!Curl_conn_is_alive(data, conn))
ret_val |= CONNRESULT_DEAD;
}

View File

@ -301,7 +301,7 @@ CURLcode Curl_write(struct Curl_easy *data,
DEBUGASSERT(data);
DEBUGASSERT(data->conn);
conn = data->conn;
num = (sockfd == conn->sock[SECONDARYSOCKET]);
num = (sockfd != CURL_SOCKET_BAD && sockfd == conn->sock[SECONDARYSOCKET]);
#ifdef CURLDEBUG
{
@ -498,8 +498,7 @@ static CURLcode pausewrite(struct Curl_easy *data,
unsigned int i;
bool newtype = TRUE;
/* If this transfers over HTTP/2, pause the stream! */
Curl_http2_stream_pause(data, TRUE);
Curl_conn_ev_data_pause(data, TRUE);
if(s->tempcount) {
for(i = 0; i< s->tempcount; i++) {

View File

@ -2974,29 +2974,23 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
data->set.pipewait = (0 != va_arg(param, long)) ? TRUE : FALSE;
break;
case CURLOPT_STREAM_WEIGHT:
#ifndef USE_NGHTTP2
return CURLE_NOT_BUILT_IN;
#else
#if defined(USE_HTTP2) || defined(USE_HTTP3)
arg = va_arg(param, long);
if((arg >= 1) && (arg <= 256))
data->set.stream_weight = (int)arg;
data->set.priority.weight = (int)arg;
break;
#else
return CURLE_NOT_BUILT_IN;
#endif
case CURLOPT_STREAM_DEPENDS:
case CURLOPT_STREAM_DEPENDS_E:
{
#ifndef USE_NGHTTP2
return CURLE_NOT_BUILT_IN;
#else
struct Curl_easy *dep = va_arg(param, struct Curl_easy *);
if(!dep || GOOD_EASY_HANDLE(dep)) {
if(data->set.stream_depends_on) {
Curl_http2_remove_child(data->set.stream_depends_on, data);
}
Curl_http2_add_child(dep, data, (option == CURLOPT_STREAM_DEPENDS_E));
return Curl_data_priority_add_child(dep, data,
option == CURLOPT_STREAM_DEPENDS_E);
}
break;
#endif
}
case CURLOPT_CONNECT_TO:
data->set.connect_to = va_arg(param, struct curl_slist *);

View File

@ -399,7 +399,7 @@ static CURLcode smtp_perform_upgrade_tls(struct Curl_easy *data)
struct smtp_conn *smtpc = &conn->proto.smtpc;
CURLcode result;
if(!Curl_conn_is_ssl(data, FIRSTSOCKET)) {
if(!Curl_conn_is_ssl(conn, FIRSTSOCKET)) {
result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET);
if(result)
goto out;
@ -891,7 +891,7 @@ static CURLcode smtp_state_ehlo_resp(struct Curl_easy *data,
if(smtpcode/100 != 2 && smtpcode != 1) {
if(data->set.use_ssl <= CURLUSESSL_TRY
|| Curl_conn_is_ssl(data, FIRSTSOCKET))
|| Curl_conn_is_ssl(conn, FIRSTSOCKET))
result = smtp_perform_helo(data, conn);
else {
failf(data, "Remote access denied: %d", smtpcode);
@ -956,7 +956,7 @@ static CURLcode smtp_state_ehlo_resp(struct Curl_easy *data,
}
if(smtpcode != 1) {
if(data->set.use_ssl && !Curl_conn_is_ssl(data, FIRSTSOCKET)) {
if(data->set.use_ssl && !Curl_conn_is_ssl(conn, FIRSTSOCKET)) {
/* We don't have a SSL/TLS connection yet, but SSL is requested */
if(smtpc->tls_supported)
/* Switch to TLS connection now */

View File

@ -1151,7 +1151,6 @@ static CURLcode socks_proxy_cf_connect(struct Curl_cfilter *cf,
result = connect_SOCKS(cf, sx, data);
if(!result && sx->state == CONNECT_DONE) {
cf->connected = TRUE;
Curl_updateconninfo(data, conn, conn->sock[cf->sockindex]);
Curl_verboseconnect(data, conn);
socks_proxy_cf_free(cf);
}
@ -1205,13 +1204,6 @@ static void socks_proxy_cf_destroy(struct Curl_cfilter *cf,
socks_proxy_cf_free(cf);
}
static void socks_proxy_cf_detach_data(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
(void)data;
socks_proxy_cf_free(cf);
}
static void socks_cf_get_host(struct Curl_cfilter *cf,
struct Curl_easy *data,
const char **phost,
@ -1233,7 +1225,6 @@ static const struct Curl_cftype cft_socks_proxy = {
"SOCKS-PROXYY",
CF_TYPE_IP_CONNECT,
socks_proxy_cf_destroy,
Curl_cf_def_setup,
socks_proxy_cf_connect,
socks_proxy_cf_close,
socks_cf_get_host,
@ -1241,8 +1232,10 @@ static const struct Curl_cftype cft_socks_proxy = {
Curl_cf_def_data_pending,
Curl_cf_def_send,
Curl_cf_def_recv,
Curl_cf_def_attach_data,
socks_proxy_cf_detach_data,
Curl_cf_def_cntrl,
Curl_cf_def_conn_is_alive,
Curl_cf_def_conn_keep_alive,
Curl_cf_def_query,
};
CURLcode Curl_conn_socks_proxy_add(struct Curl_easy *data,
@ -1258,4 +1251,17 @@ CURLcode Curl_conn_socks_proxy_add(struct Curl_easy *data,
return result;
}
CURLcode Curl_cf_socks_proxy_insert_after(struct Curl_cfilter *cf_at,
struct Curl_easy *data)
{
struct Curl_cfilter *cf;
CURLcode result;
(void)data;
result = Curl_cf_create(&cf, &cft_socks_proxy, NULL);
if(!result)
Curl_conn_cf_insert_after(cf_at, cf);
return result;
}
#endif /* CURL_DISABLE_PROXY */

View File

@ -55,6 +55,9 @@ CURLcode Curl_conn_socks_proxy_add(struct Curl_easy *data,
struct connectdata *conn,
int sockindex);
CURLcode Curl_cf_socks_proxy_insert_after(struct Curl_cfilter *cf_at,
struct Curl_easy *data);
#endif /* CURL_DISABLE_PROXY */
#endif /* HEADER_CURL_SOCKS_H */

View File

@ -73,6 +73,7 @@
#include "url.h"
#include "getinfo.h"
#include "vtls/vtls.h"
#include "vquic/vquic.h"
#include "select.h"
#include "multiif.h"
#include "connect.h"
@ -367,27 +368,12 @@ static int data_pending(struct Curl_easy *data)
{
struct connectdata *conn = data->conn;
#ifdef ENABLE_QUIC
if(conn->transport == TRNSPRT_QUIC)
return Curl_quic_data_pending(data);
#endif
if(conn->handler->protocol&PROTO_FAMILY_FTP)
return Curl_conn_data_pending(data, SECONDARYSOCKET);
/* in the case of libssh2, we can never be really sure that we have emptied
its internal buffers so we MUST always try until we get EAGAIN back */
return conn->handler->protocol&(CURLPROTO_SCP|CURLPROTO_SFTP) ||
#ifdef USE_NGHTTP2
/* For HTTP/2, we may read up everything including response body
with header fields in Curl_http_readwrite_headers. If no
content-length is provided, curl waits for the connection
close, which we emulate it using conn->proto.httpc.closed =
TRUE. The thing is if we read everything, then http2_recv won't
be called and we cannot signal the HTTP/2 stream has closed. As
a workaround, we return nonzero here to call http2_recv. */
((conn->handler->protocol&PROTO_FAMILY_HTTP) && conn->httpversion >= 20) ||
#endif
Curl_conn_data_pending(data, FIRSTSOCKET);
}
@ -454,29 +440,16 @@ static CURLcode readwrite_data(struct Curl_easy *data,
bool is_empty_data = FALSE;
size_t buffersize = data->set.buffer_size;
size_t bytestoread = buffersize;
#ifdef USE_NGHTTP2
bool is_http2 = ((conn->handler->protocol & PROTO_FAMILY_HTTP) &&
(conn->httpversion == 20));
#endif
bool is_http3 =
#ifdef ENABLE_QUIC
((conn->handler->protocol & PROTO_FAMILY_HTTP) &&
(conn->httpversion == 30));
#else
FALSE;
#endif
/* For HTTP/2 and HTTP/3, read data without caring about the content
length. This is safe because body in HTTP/2 is always segmented
thanks to its framing layer. Meanwhile, we have to call Curl_read
to ensure that http2_handle_stream_close is called when we read all
incoming bytes for a particular stream. */
bool is_http3 = Curl_conn_is_http3(data, conn, FIRSTSOCKET);
bool data_eof_handled = is_http3
|| Curl_conn_is_http2(data, conn, FIRSTSOCKET);
if(
#ifdef USE_NGHTTP2
/* For HTTP/2, read data without caring about the content length. This
is safe because body in HTTP/2 is always segmented thanks to its
framing layer. Meanwhile, we have to call Curl_read to ensure that
http2_handle_stream_close is called when we read all incoming bytes
for a particular stream. */
!is_http2 &&
#endif
!is_http3 && /* Same reason mentioned above. */
k->size != -1 && !k->header) {
if(!data_eof_handled && k->size != -1 && !k->header) {
/* make sure we don't read too much */
curl_off_t totalleft = k->size - k->bytecount;
if(totalleft < (curl_off_t)bytestoread)
@ -499,7 +472,7 @@ static CURLcode readwrite_data(struct Curl_easy *data,
else {
/* read nothing but since we wanted nothing we consider this an OK
situation to proceed from */
DEBUGF(infof(data, "readwrite_data: we're done"));
DEBUGF(infof(data, DMSG(data, "readwrite_data: we're done")));
nread = 0;
}
@ -518,14 +491,9 @@ static CURLcode readwrite_data(struct Curl_easy *data,
buf[nread] = 0;
}
else {
/* if we receive 0 or less here, either the http2 stream is closed or the
/* if we receive 0 or less here, either the data transfer is done or the
server closed the connection and we bail out from this! */
#ifdef USE_NGHTTP2
if(is_http2 && !nread)
DEBUGF(infof(data, "nread == 0, stream closed, bailing"));
else
#endif
if(is_http3 && !nread)
if(data_eof_handled)
DEBUGF(infof(data, "nread == 0, stream closed, bailing"));
else
DEBUGF(infof(data, "nread <= 0, server closed connection, bailing"));
@ -799,19 +767,18 @@ static CURLcode readwrite_data(struct Curl_easy *data,
}
out:
DEBUGF(infof(data, "readwrite_data(handle=%p) -> %d", data, result));
if(result)
DEBUGF(infof(data, DMSG(data, "readwrite_data() -> %d"), result));
return result;
}
CURLcode Curl_done_sending(struct Curl_easy *data,
struct SingleRequest *k)
{
struct connectdata *conn = data->conn;
k->keepon &= ~KEEP_SEND; /* we're done writing */
/* These functions should be moved into the handler struct! */
Curl_http2_done_sending(data, conn);
Curl_quic_done_sending(data);
Curl_conn_ev_data_done_send(data);
return CURLE_OK;
}
@ -1182,13 +1149,9 @@ CURLcode Curl_readwrite(struct connectdata *conn,
}
}
#ifdef ENABLE_QUIC
if(conn->transport == TRNSPRT_QUIC) {
result = Curl_quic_idle(data);
if(result)
goto out;
}
#endif
result = Curl_conn_ev_data_idle(data);
if(result)
goto out;
}
if(Curl_pgrsUpdate(data))
@ -1264,7 +1227,8 @@ CURLcode Curl_readwrite(struct connectdata *conn,
KEEP_RECV_PAUSE|KEEP_SEND_PAUSE))) ? TRUE : FALSE;
result = CURLE_OK;
out:
DEBUGF(infof(data, "Curl_readwrite(handle=%p) -> %d", data, result));
if(result)
DEBUGF(infof(data, DMSG(data, "Curl_readwrite() -> %d"), result));
return result;
}
@ -1377,6 +1341,7 @@ CURLcode Curl_pretransfer(struct Curl_easy *data)
data->state.authhost.want = data->set.httpauth;
data->state.authproxy.want = data->set.proxyauth;
Curl_safefree(data->info.wouldredirect);
Curl_data_priority_clear_state(data);
if(data->state.httpreq == HTTPREQ_PUT)
data->state.infilesize = data->set.filesize;
@ -1434,7 +1399,6 @@ CURLcode Curl_pretransfer(struct Curl_easy *data)
}
}
#endif
Curl_http2_init_state(&data->state);
result = Curl_hsts_loadcb(data, data->hsts);
}
@ -1872,7 +1836,7 @@ Curl_setup_transfer(
httpsending = ((conn->handler->protocol&PROTO_FAMILY_HTTP) &&
(http->sending == HTTPSEND_REQUEST));
if(conn->bits.multiplex || conn->httpversion == 20 || httpsending) {
if(conn->bits.multiplex || conn->httpversion >= 20 || httpsending) {
/* when multiplexing, the read/write sockets need to be the same! */
conn->sockfd = sockindex == -1 ?
((writesockindex == -1 ? CURL_SOCKET_BAD : conn->sock[writesockindex])) :

166
lib/url.c
View File

@ -452,7 +452,7 @@ CURLcode Curl_close(struct Curl_easy **datap)
Curl_resolver_cancel(data);
Curl_resolver_cleanup(data->state.async.resolver);
Curl_http2_cleanup_dependencies(data);
Curl_data_priority_cleanup(data);
/* No longer a dirty share, if it exists */
if(data->share) {
@ -640,14 +640,15 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data)
set->maxage_conn = 118;
set->maxlifetime_conn = 0;
set->http09_allowed = FALSE;
set->httpwant =
#ifdef USE_HTTP2
CURL_HTTP_VERSION_2TLS
set->httpwant = CURL_HTTP_VERSION_2TLS
#else
CURL_HTTP_VERSION_1_1
set->httpwant = CURL_HTTP_VERSION_1_1
#endif
;
Curl_http2_init_userset(set);
#if defined(USE_HTTP2) || defined(USE_HTTP3)
memset(&set->priority, 0, sizeof(set->priority));
#endif
set->quick_exit = 0L;
return result;
}
@ -877,24 +878,6 @@ void Curl_disconnect(struct Curl_easy *data,
conn_free(data, conn);
}
/*
* This function should return TRUE if the socket is to be assumed to
* be dead. Most commonly this happens when the server has closed the
* connection due to inactivity.
*/
static bool SocketIsDead(curl_socket_t sock)
{
int sval;
bool ret_val = TRUE;
sval = SOCKET_READABLE(sock, 0);
if(sval == 0)
/* timeout */
ret_val = FALSE;
return ret_val;
}
/*
* IsMultiplexingPossible()
*
@ -1025,8 +1008,7 @@ static bool extract_if_dead(struct connectdata *conn,
}
else {
/* Use the general method for determining the death of a connection */
dead = SocketIsDead(conn->sock[FIRSTSOCKET]);
dead = !Curl_conn_is_alive(data, conn);
}
if(dead) {
@ -1355,8 +1337,10 @@ ConnectionExists(struct Curl_easy *data,
/* If multiplexing isn't enabled on the h2 connection and h1 is
explicitly requested, handle it: */
if((needle->handler->protocol & PROTO_FAMILY_HTTP) &&
(check->httpversion >= 20) &&
(data->state.httpwant < CURL_HTTP_VERSION_2_0))
(((check->httpversion >= 20) &&
(data->state.httpwant < CURL_HTTP_VERSION_2_0))
|| ((check->httpversion >= 30) &&
(data->state.httpwant < CURL_HTTP_VERSION_3))))
continue;
if(get_protocol_family(needle->handler) == PROTO_FAMILY_SSH) {
@ -1478,9 +1462,8 @@ ConnectionExists(struct Curl_easy *data,
#ifdef USE_NGHTTP2
/* If multiplexed, make sure we don't go over concurrency limit */
if(check->bits.multiplex) {
/* Multiplexed connections can only be HTTP/2 for now */
struct http_conn *httpc = &check->proto.httpc;
if(multiplexed >= httpc->settings.max_concurrent_streams) {
if(multiplexed >= Curl_conn_get_max_concurrent(data, check,
FIRSTSOCKET)) {
infof(data, "MAX_CONCURRENT_STREAMS reached, skip (%zu)",
multiplexed);
continue;
@ -1562,8 +1545,6 @@ static struct connectdata *allocate_conn(struct Curl_easy *data)
conn->sock[FIRSTSOCKET] = CURL_SOCKET_BAD; /* no file descriptor */
conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD; /* no file descriptor */
conn->tempsock[0] = CURL_SOCKET_BAD; /* no file descriptor */
conn->tempsock[1] = CURL_SOCKET_BAD; /* no file descriptor */
conn->connection_id = -1; /* no ID */
conn->port = -1; /* unknown at this point */
conn->remote_port = -1; /* unknown at this point */
@ -3356,12 +3337,6 @@ static void reuse_conn(struct Curl_easy *data,
struct connectdata *temp,
struct connectdata *existing)
{
/* 'local_ip' and 'local_port' get filled with local's numerical
ip address and port number whenever an outgoing connection is
**established** from the primary socket to a remote address. */
char local_ip[MAX_IPADR_LEN] = "";
int local_port = -1;
/* get the user+password information from the temp struct since it may
* be new for this request even when we re-use an existing connection */
if(temp->user) {
@ -3409,12 +3384,8 @@ static void reuse_conn(struct Curl_easy *data,
existing->hostname_resolve = temp->hostname_resolve;
temp->hostname_resolve = NULL;
/* persist connection info in session handle */
if(existing->transport == TRNSPRT_TCP) {
Curl_conninfo_local(data, existing->sock[FIRSTSOCKET],
local_ip, &local_port);
}
Curl_persistconninfo(data, existing, local_ip, local_port);
/* persist existing connection info in data */
Curl_conn_ev_update_info(data, existing);
conn_reset_all_postponed_data(temp); /* free buffers */
@ -3891,6 +3862,13 @@ static CURLcode create_conn(struct Curl_easy *data,
* Resolve the address of the server or proxy
*************************************************************/
result = resolve_server(data, conn, async);
if(result)
goto out;
/* Everything general done, inform filters that they need
* to prepare for a data transfer.
*/
result = Curl_conn_ev_data_setup(data);
out:
return result;
@ -4029,3 +4007,103 @@ CURLcode Curl_init_do(struct Curl_easy *data, struct connectdata *conn)
return CURLE_OK;
}
#if defined(USE_HTTP2) || defined(USE_HTTP3)
#ifdef USE_NGHTTP2
static void priority_remove_child(struct Curl_easy *parent,
struct Curl_easy *child)
{
struct Curl_data_prio_node **pnext = &parent->set.priority.children;
struct Curl_data_prio_node *pnode = parent->set.priority.children;
DEBUGASSERT(child->set.priority.parent == parent);
while(pnode && pnode->data != child) {
pnext = &pnode->next;
pnode = pnode->next;
}
DEBUGASSERT(pnode);
if(pnode) {
*pnext = pnode->next;
free(pnode);
}
child->set.priority.parent = 0;
child->set.priority.exclusive = FALSE;
}
CURLcode Curl_data_priority_add_child(struct Curl_easy *parent,
struct Curl_easy *child,
bool exclusive)
{
if(child->set.priority.parent) {
priority_remove_child(child->set.priority.parent, child);
}
if(parent) {
struct Curl_data_prio_node **tail;
struct Curl_data_prio_node *pnode;
pnode = calloc(1, sizeof(*pnode));
if(!pnode)
return CURLE_OUT_OF_MEMORY;
pnode->data = child;
if(parent->set.priority.children && exclusive) {
/* exclusive: move all existing children underneath the new child */
struct Curl_data_prio_node *node = parent->set.priority.children;
while(node) {
node->data->set.priority.parent = child;
node = node->next;
}
tail = &child->set.priority.children;
while(*tail)
tail = &(*tail)->next;
DEBUGASSERT(!*tail);
*tail = parent->set.priority.children;
parent->set.priority.children = 0;
}
tail = &parent->set.priority.children;
while(*tail) {
(*tail)->data->set.priority.exclusive = FALSE;
tail = &(*tail)->next;
}
DEBUGASSERT(!*tail);
*tail = pnode;
}
child->set.priority.parent = parent;
child->set.priority.exclusive = exclusive;
return CURLE_OK;
}
#endif /* USE_NGHTTP2 */
void Curl_data_priority_cleanup(struct Curl_easy *data)
{
#ifdef USE_NGHTTP2
while(data->set.priority.children) {
struct Curl_easy *tmp = data->set.priority.children->data;
priority_remove_child(data, tmp);
if(data->set.priority.parent)
Curl_data_priority_add_child(data->set.priority.parent, tmp, FALSE);
}
if(data->set.priority.parent)
priority_remove_child(data->set.priority.parent, data);
#endif
(void)data;
}
void Curl_data_priority_clear_state(struct Curl_easy *data)
{
memset(&data->state.priority, 0, sizeof(data->state.priority));
}
#endif /* defined(USE_HTTP2) || defined(USE_HTTP3) */

View File

@ -59,4 +59,20 @@ const struct Curl_handler *Curl_builtin_scheme(const char *scheme,
void Curl_verboseconnect(struct Curl_easy *data, struct connectdata *conn);
#endif
#if defined(USE_HTTP2) || defined(USE_HTTP3)
void Curl_data_priority_cleanup(struct Curl_easy *data);
void Curl_data_priority_clear_state(struct Curl_easy *data);
#else
#define Curl_data_priority_cleanup(x)
#define Curl_data_priority_clear_state(x)
#endif /* !(defined(USE_HTTP2) || defined(USE_HTTP3)) */
#ifdef USE_NGHTTP2
CURLcode Curl_data_priority_add_child(struct Curl_easy *parent,
struct Curl_easy *child,
bool exclusive);
#else
#define Curl_data_priority_add_child(x, y, z) CURLE_NOT_BUILT_IN
#endif
#endif /* HEADER_CURL_URL_H */

View File

@ -123,9 +123,9 @@ typedef unsigned int curl_prot_t;
#define DMSGI(d,i,msg) \
"[CONN-%ld-%d] "msg, (d)->conn->connection_id, (i)
#define CMSG(c,msg) \
"[CONN-%ld] "msg, (conn)->connection_id
"[CONN-%ld] "msg, (c)->connection_id
#define CMSGI(c,i,msg) \
"[CONN-%ld-%d] "msg, (conn)->connection_id, (i)
"[CONN-%ld-%d] "msg, (c)->connection_id, (i)
#define CFMSG(cf,msg) \
"[CONN-%ld-%d][CF-%s] "msg, (cf)->conn->connection_id, \
(cf)->sockindex, (cf)->cft->name
@ -187,7 +187,6 @@ typedef CURLcode (*Curl_datastream)(struct Curl_easy *data,
#include "mqtt.h"
#include "wildcard.h"
#include "multihandle.h"
#include "quic.h"
#include "c-hyper.h"
#ifdef HAVE_GSSAPI
@ -830,7 +829,7 @@ struct Curl_handler {
#define PROTOPT_CREDSPERREQUEST (1<<7) /* requires login credentials per
request instead of per connection */
#define PROTOPT_ALPN (1<<8) /* set ALPN for this */
#define PROTOPT_STREAM (1<<9) /* a protocol with individual logical streams */
/* (1<<9) was PROTOPT_STREAM, now free */
#define PROTOPT_URLOPTIONS (1<<10) /* allow options part in the userinfo field
of the URL */
#define PROTOPT_PROXY_AS_HTTP (1<<11) /* allow this non-HTTP scheme over a
@ -912,13 +911,7 @@ struct connectdata {
/* 'ip_addr' is the particular IP we connected to. It points to a struct
within the DNS cache, so this pointer is only valid as long as the DNS
cache entry remains locked. It gets unlocked in multi_done() */
struct Curl_addrinfo *ip_addr;
struct Curl_addrinfo *tempaddr[2]; /* for happy eyeballs */
#ifdef ENABLE_QUIC
struct quicsocket hequic[2]; /* two, for happy eyeballs! */
struct quicsocket *quic;
#endif
const struct Curl_addrinfo *ip_addr;
struct hostname host;
char *hostname_resolve; /* host name to resolve to address, allocated */
@ -947,8 +940,6 @@ struct connectdata {
struct curltime lastused; /* when returned to the connection cache */
curl_socket_t sock[2]; /* two sockets, the second is used for the data
transfer when doing FTP */
curl_socket_t tempsock[2]; /* temporary sockets for happy eyeballs */
int tempfamily[2]; /* family used for the temp sockets */
Curl_recv *recv[2];
Curl_send *send[2];
struct Curl_cfilter *cfilter[2]; /* connection filters */
@ -962,16 +953,6 @@ struct connectdata {
#endif
struct ConnectBits bits; /* various state-flags for this connection */
/* connecttime: when connect() is called on the current IP address. Used to
be able to track when to move on to try next IP - but only when the multi
interface is used. */
struct curltime connecttime;
/* The field below gets set in Curl_connecthost */
/* how long time in milliseconds to spend on trying to connect to each IP
address, per family */
timediff_t timeoutms_per_addr[2];
const struct Curl_handler *handler; /* Connection's protocol handler */
const struct Curl_handler *given; /* The protocol first given */
@ -1043,9 +1024,6 @@ struct connectdata {
#ifndef CURL_DISABLE_FTP
struct ftp_conn ftpc;
#endif
#ifndef CURL_DISABLE_HTTP
struct http_conn httpc;
#endif
#ifdef USE_SSH
struct ssh_conn sshc;
#endif
@ -1094,7 +1072,7 @@ struct connectdata {
#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
int socks5_gssapi_enctype;
#endif
/* The field below gets set in Curl_connecthost */
/* The field below gets set in connect.c:connecthost() */
int num_addr; /* number of addresses to try to connect to */
int port; /* which port to use locally - to connect to */
int remote_port; /* the remote port, not the proxy port! */
@ -1240,10 +1218,29 @@ struct auth {
should be RFC compliant */
};
struct Curl_http2_dep {
struct Curl_http2_dep *next;
#ifdef USE_NGHTTP2
struct Curl_data_prio_node {
struct Curl_data_prio_node *next;
struct Curl_easy *data;
};
#endif
/**
* Priority information for an easy handle in relation to others
* on the same connection.
* TODO: we need to adapt it to the new priority scheme as defined in RFC 9218
*/
struct Curl_data_priority {
#ifdef USE_NGHTTP2
/* tree like dependencies only implemented in nghttp2 */
struct Curl_easy *parent;
struct Curl_data_prio_node *children;
#endif
int weight;
#ifdef USE_NGHTTP2
BIT(exclusive);
#endif
};
/*
* This struct is for holding data that was attempted to get sent to the user's
@ -1391,14 +1388,11 @@ struct UrlState {
size_t drain; /* Increased when this stream has data to read, even if its
socket is not necessarily is readable. Decreased when
checked. */
struct Curl_data_priority priority; /* shallow coyp of data->set */
#endif
curl_read_callback fread_func; /* read callback/function */
void *in; /* CURLOPT_READDATA */
#ifdef USE_HTTP2
struct Curl_easy *stream_depends_on;
int stream_weight;
#endif
CURLU *uh; /* URL handle for the current parsed URL */
struct urlpieces up;
unsigned char httpreq; /* Curl_HttpReq; what kind of HTTP request (if any)
@ -1469,7 +1463,6 @@ struct UrlState {
BIT(done); /* set to FALSE when Curl_init_do() is called and set to TRUE
when multi_done() is called, to prevent multi_done() to get
invoked twice when the multi interface is used. */
BIT(stream_depends_e); /* set or don't set the Exclusive bit */
BIT(previouslypending); /* this transfer WAS in the multi->pending queue */
BIT(cookie_engine);
BIT(prefer_ascii); /* ASCII rather than binary */
@ -1789,10 +1782,8 @@ struct UserDefined {
size_t maxconnects; /* Max idle connections in the connection cache */
long expect_100_timeout; /* in milliseconds */
#ifdef USE_HTTP2
struct Curl_easy *stream_depends_on;
int stream_weight;
struct Curl_http2_dep *stream_dependents;
#if defined(USE_HTTP2) || defined(USE_HTTP3)
struct Curl_data_priority priority;
#endif
curl_resolver_start_callback resolver_start; /* optional callback called
before resolver start */
@ -1884,7 +1875,6 @@ struct UserDefined {
BIT(suppress_connect_headers); /* suppress proxy CONNECT response headers
from user callbacks */
BIT(dns_shuffle_addresses); /* whether to shuffle addresses before use */
BIT(stream_depends_e); /* set or don't set the Exclusive bit */
BIT(haproxyprotocol); /* whether to send HAProxy PROXY protocol v1
header */
BIT(abstract_unix_socket);

View File

@ -24,12 +24,16 @@
#include "curl_setup.h"
#ifdef USE_NGHTTP2
#include <nghttp2/nghttp2.h>
#endif
#include <curl/curl.h>
#include "urldata.h"
#include "vtls/vtls.h"
#include "http2.h"
#include "vssh/ssh.h"
#include "quic.h"
#include "vquic/vquic.h"
#include "curl_printf.h"
#include "easy_lock.h"

View File

@ -30,6 +30,7 @@
#include "timeval.h"
#include "multiif.h"
#include "sendf.h"
#include "cfilters.h"
#include "connect.h"
#include "h2h3.h"
#include "msh3.h"
@ -48,17 +49,28 @@
#define MSH3_REQ_INIT_BUF_LEN 8192
static CURLcode msh3_do_it(struct Curl_easy *data, bool *done);
static int msh3_getsock(struct Curl_easy *data,
struct connectdata *conn, curl_socket_t *socks);
static CURLcode msh3_disconnect(struct Curl_easy *data,
struct connectdata *conn,
bool dead_connection);
static unsigned int msh3_conncheck(struct Curl_easy *data,
struct connectdata *conn,
unsigned int checks_to_perform);
static Curl_recv msh3_stream_recv;
static Curl_send msh3_stream_send;
#ifdef _WIN32
#define msh3_lock CRITICAL_SECTION
#define msh3_lock_initialize(lock) InitializeCriticalSection(lock)
#define msh3_lock_uninitialize(lock) DeleteCriticalSection(lock)
#define msh3_lock_acquire(lock) EnterCriticalSection(lock)
#define msh3_lock_release(lock) LeaveCriticalSection(lock)
#else /* !_WIN32 */
#include <pthread.h>
#define msh3_lock pthread_mutex_t
#define msh3_lock_initialize(lock) do { \
pthread_mutexattr_t attr; \
pthread_mutexattr_init(&attr); \
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); \
pthread_mutex_init(lock, &attr); \
pthread_mutexattr_destroy(&attr); \
}while(0)
#define msh3_lock_uninitialize(lock) pthread_mutex_destroy(lock)
#define msh3_lock_acquire(lock) pthread_mutex_lock(lock)
#define msh3_lock_release(lock) pthread_mutex_unlock(lock)
#endif /* _WIN32 */
static void MSH3_CALL msh3_header_received(MSH3_REQUEST *Request,
void *IfContext,
const MSH3_HEADER *Header);
@ -71,27 +83,17 @@ static void MSH3_CALL msh3_shutdown(MSH3_REQUEST *Request, void *IfContext);
static void MSH3_CALL msh3_send_complete(MSH3_REQUEST *Request,
void *IfContext, void *SendContext);
static const struct Curl_handler msh3_curl_handler_http3 = {
"HTTPS", /* scheme */
ZERO_NULL, /* setup_connection */
msh3_do_it, /* do_it */
Curl_http_done, /* done */
ZERO_NULL, /* do_more */
ZERO_NULL, /* connect_it */
ZERO_NULL, /* connecting */
ZERO_NULL, /* doing */
msh3_getsock, /* proto_getsock */
msh3_getsock, /* doing_getsock */
ZERO_NULL, /* domore_getsock */
msh3_getsock, /* perform_getsock */
msh3_disconnect, /* disconnect */
ZERO_NULL, /* readwrite */
msh3_conncheck, /* connection_check */
ZERO_NULL, /* attach connection */
PORT_HTTP, /* defport */
CURLPROTO_HTTPS, /* protocol */
CURLPROTO_HTTP, /* family */
PROTOPT_SSL | PROTOPT_STREAM /* flags */
void Curl_msh3_ver(char *p, size_t len)
{
uint32_t v[4];
MsH3Version(v);
(void)msnprintf(p, len, "msh3/%d.%d.%d.%d", v[0], v[1], v[2], v[3]);
}
struct cf_msh3_ctx {
MSH3_API *api;
MSH3_CONNECTION *qconn;
};
static const MSH3_REQUEST_IF msh3_request_if = {
@ -102,107 +104,13 @@ static const MSH3_REQUEST_IF msh3_request_if = {
msh3_send_complete
};
void Curl_quic_ver(char *p, size_t len)
{
uint32_t v[4];
MsH3Version(v);
(void)msnprintf(p, len, "msh3/%d.%d.%d.%d", v[0], v[1], v[2], v[3]);
}
CURLcode Curl_quic_connect(struct Curl_easy *data,
struct connectdata *conn,
curl_socket_t sockfd,
int sockindex,
const struct sockaddr *addr,
socklen_t addrlen)
{
struct quicsocket *qs = &conn->hequic[sockindex];
bool insecure = !conn->ssl_config.verifypeer;
memset(qs, 0, sizeof(*qs));
(void)sockfd;
(void)addr; /* TODO - Pass address along */
(void)addrlen;
H3BUGF(infof(data, "creating new api/connection"));
qs->api = MsH3ApiOpen();
if(!qs->api) {
failf(data, "can't create msh3 api");
return CURLE_FAILED_INIT;
}
qs->conn = MsH3ConnectionOpen(qs->api,
conn->host.name,
(uint16_t)conn->remote_port,
insecure);
if(!qs->conn) {
failf(data, "can't create msh3 connection");
if(qs->api) {
MsH3ApiClose(qs->api);
}
return CURLE_FAILED_INIT;
}
return CURLE_OK;
}
CURLcode Curl_quic_is_connected(struct Curl_easy *data,
struct connectdata *conn,
int sockindex,
bool *connected)
{
struct quicsocket *qs = &conn->hequic[sockindex];
MSH3_CONNECTION_STATE state;
state = MsH3ConnectionGetState(qs->conn, false);
if(state == MSH3_CONN_HANDSHAKE_FAILED || state == MSH3_CONN_DISCONNECTED) {
failf(data, "failed to connect, state=%u", (uint32_t)state);
return CURLE_COULDNT_CONNECT;
}
if(state == MSH3_CONN_CONNECTED) {
H3BUGF(infof(data, "connection connected"));
*connected = true;
conn->quic = qs;
conn->recv[sockindex] = msh3_stream_recv;
conn->send[sockindex] = msh3_stream_send;
conn->handler = &msh3_curl_handler_http3;
conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
conn->httpversion = 30;
conn->bundle->multiuse = BUNDLE_MULTIPLEX;
/* TODO - Clean up other happy-eyeballs connection(s)? */
}
return CURLE_OK;
}
static int msh3_getsock(struct Curl_easy *data,
struct connectdata *conn, curl_socket_t *socks)
static CURLcode msh3_data_setup(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
struct HTTP *stream = data->req.p.http;
int bitmap = GETSOCK_BLANK;
(void)cf;
socks[0] = conn->sock[FIRSTSOCKET];
if(stream->recv_error) {
bitmap |= GETSOCK_READSOCK(FIRSTSOCKET);
data->state.drain++;
}
else if(stream->recv_header_len || stream->recv_data_len) {
bitmap |= GETSOCK_READSOCK(FIRSTSOCKET);
data->state.drain++;
}
H3BUGF(infof(data, "msh3_getsock %u", (uint32_t)data->state.drain));
return bitmap;
}
static CURLcode msh3_do_it(struct Curl_easy *data, bool *done)
{
struct HTTP *stream = data->req.p.http;
H3BUGF(infof(data, "msh3_do_it"));
H3BUGF(infof(data, "msh3_data_setup"));
stream->recv_buf = malloc(MSH3_REQ_INIT_BUF_LEN);
if(!stream->recv_buf) {
return CURLE_OUT_OF_MEMORY;
@ -215,52 +123,9 @@ static CURLcode msh3_do_it(struct Curl_easy *data, bool *done)
stream->recv_data_len = 0;
stream->recv_data_complete = false;
stream->recv_error = CURLE_OK;
return Curl_http(data, done);
}
static unsigned int msh3_conncheck(struct Curl_easy *data,
struct connectdata *conn,
unsigned int checks_to_perform)
{
(void)data;
(void)conn;
(void)checks_to_perform;
H3BUGF(infof(data, "msh3_conncheck"));
return CONNRESULT_NONE;
}
static void disconnect(struct quicsocket *qs)
{
if(qs->conn) {
MsH3ConnectionClose(qs->conn);
qs->conn = ZERO_NULL;
}
if(qs->api) {
MsH3ApiClose(qs->api);
qs->api = ZERO_NULL;
}
}
static CURLcode msh3_disconnect(struct Curl_easy *data,
struct connectdata *conn, bool dead_connection)
{
(void)data;
(void)dead_connection;
H3BUGF(infof(data, "disconnecting (msh3)"));
disconnect(conn->quic);
return CURLE_OK;
}
void Curl_quic_disconnect(struct Curl_easy *data, struct connectdata *conn,
int tempindex)
{
(void)data;
if(conn->transport == TRNSPRT_QUIC) {
H3BUGF(infof(data, "disconnecting QUIC index %u", tempindex));
disconnect(&conn->hequic[tempindex]);
}
}
/* Requires stream->recv_lock to be held */
static bool msh3request_ensure_room(struct HTTP *stream, size_t len)
{
@ -309,7 +174,7 @@ static void MSH3_CALL msh3_header_received(MSH3_REQUEST *Request,
}
msnprintf((char *)stream->recv_buf + stream->recv_header_len,
stream->recv_buf_alloc - stream->recv_header_len,
"HTTP/3 %.*s\n", (int)Header->ValueLength, Header->Value);
"HTTP/3 %.*s \r\n", (int)Header->ValueLength, Header->Value);
}
else {
total_len = Header->NameLength + 4 + Header->ValueLength;
@ -319,7 +184,7 @@ static void MSH3_CALL msh3_header_received(MSH3_REQUEST *Request,
}
msnprintf((char *)stream->recv_buf + stream->recv_header_len,
stream->recv_buf_alloc - stream->recv_header_len,
"%.*s: %.*s\n",
"%.*s: %.*s\r\n",
(int)Header->NameLength, Header->Name,
(int)Header->ValueLength, Header->Value);
}
@ -393,93 +258,25 @@ static void MSH3_CALL msh3_send_complete(MSH3_REQUEST *Request,
(void)SendContext;
}
static ssize_t msh3_stream_send(struct Curl_easy *data,
int sockindex,
const void *mem,
size_t len,
CURLcode *curlcode)
{
struct connectdata *conn = data->conn;
struct HTTP *stream = data->req.p.http;
struct quicsocket *qs = conn->quic;
struct h2h3req *hreq;
size_t hdrlen = 0;
size_t sentlen = 0;
(void)sockindex;
/* Sizes must match for cast below to work" */
DEBUGASSERT(sizeof(MSH3_HEADER) == sizeof(struct h2h3pseudo));
H3BUGF(infof(data, "msh3_stream_send %zu", len));
if(!stream->req) {
/* The first send on the request contains the headers and possibly some
data. Parse out the headers and create the request, then if there is
any data left over go ahead and send it too. */
*curlcode = Curl_pseudo_headers(data, mem, len, &hdrlen, &hreq);
if(*curlcode) {
failf(data, "Curl_pseudo_headers failed");
return -1;
}
H3BUGF(infof(data, "starting request with %zu headers", hreq->entries));
stream->req = MsH3RequestOpen(qs->conn, &msh3_request_if, stream,
(MSH3_HEADER*)hreq->header, hreq->entries,
hdrlen == len ? MSH3_REQUEST_FLAG_FIN :
MSH3_REQUEST_FLAG_NONE);
Curl_pseudo_free(hreq);
if(!stream->req) {
failf(data, "request open failed");
*curlcode = CURLE_SEND_ERROR;
return -1;
}
*curlcode = CURLE_OK;
return len;
}
H3BUGF(infof(data, "send %zd body bytes on request %p", len,
(void *)stream->req));
if(len > 0xFFFFFFFF) {
/* msh3 doesn't support size_t sends currently. */
*curlcode = CURLE_SEND_ERROR;
return -1;
}
/* TODO - Need an explicit signal to know when to FIN. */
if(!MsH3RequestSend(stream->req, MSH3_REQUEST_FLAG_FIN, mem, (uint32_t)len,
stream)) {
*curlcode = CURLE_SEND_ERROR;
return -1;
}
/* TODO - msh3/msquic will hold onto this memory until the send complete
event. How do we make sure curl doesn't free it until then? */
sentlen += len;
*curlcode = CURLE_OK;
return sentlen;
}
static ssize_t msh3_stream_recv(struct Curl_easy *data,
int sockindex,
char *buf,
size_t buffersize,
CURLcode *curlcode)
static ssize_t cf_msh3_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
char *buf, size_t len, CURLcode *err)
{
struct HTTP *stream = data->req.p.http;
size_t outsize = 0;
(void)sockindex;
H3BUGF(infof(data, "msh3_stream_recv %zu", buffersize));
(void)cf;
H3BUGF(infof(data, "msh3_stream_recv %zu", len));
if(stream->recv_error) {
failf(data, "request aborted");
*curlcode = stream->recv_error;
*err = stream->recv_error;
return -1;
}
msh3_lock_acquire(&stream->recv_lock);
if(stream->recv_header_len) {
outsize = buffersize;
outsize = len;
if(stream->recv_header_len < outsize) {
outsize = stream->recv_header_len;
}
@ -492,7 +289,7 @@ static ssize_t msh3_stream_recv(struct Curl_easy *data,
H3BUGF(infof(data, "returned %zu bytes of headers", outsize));
}
else if(stream->recv_data_len) {
outsize = buffersize;
outsize = len;
if(stream->recv_data_len < outsize) {
outsize = stream->recv_data_len;
}
@ -513,52 +310,300 @@ static ssize_t msh3_stream_recv(struct Curl_easy *data,
return (ssize_t)outsize;
}
CURLcode Curl_quic_done_sending(struct Curl_easy *data)
static ssize_t cf_msh3_send(struct Curl_cfilter *cf, struct Curl_easy *data,
const void *buf, size_t len, CURLcode *err)
{
struct connectdata *conn = data->conn;
H3BUGF(infof(data, "Curl_quic_done_sending"));
if(conn->handler == &msh3_curl_handler_http3) {
struct HTTP *stream = data->req.p.http;
stream->upload_done = TRUE;
struct cf_msh3_ctx *ctx = cf->ctx;
struct HTTP *stream = data->req.p.http;
struct h2h3req *hreq;
size_t hdrlen = 0;
size_t sentlen = 0;
/* Sizes must match for cast below to work" */
DEBUGASSERT(sizeof(MSH3_HEADER) == sizeof(struct h2h3pseudo));
H3BUGF(infof(data, "msh3_stream_send %zu", len));
if(!stream->req) {
/* The first send on the request contains the headers and possibly some
data. Parse out the headers and create the request, then if there is
any data left over go ahead and send it too. */
*err = Curl_pseudo_headers(data, buf, len, &hdrlen, &hreq);
if(*err) {
failf(data, "Curl_pseudo_headers failed");
return -1;
}
H3BUGF(infof(data, "starting request with %zu headers", hreq->entries));
stream->req = MsH3RequestOpen(ctx->qconn, &msh3_request_if, stream,
(MSH3_HEADER*)hreq->header, hreq->entries,
hdrlen == len ? MSH3_REQUEST_FLAG_FIN :
MSH3_REQUEST_FLAG_NONE);
Curl_pseudo_free(hreq);
if(!stream->req) {
failf(data, "request open failed");
*err = CURLE_SEND_ERROR;
return -1;
}
*err = CURLE_OK;
return len;
}
H3BUGF(infof(data, "send %zd body bytes on request %p", len,
(void *)stream->req));
if(len > 0xFFFFFFFF) {
/* msh3 doesn't support size_t sends currently. */
*err = CURLE_SEND_ERROR;
return -1;
}
return CURLE_OK;
}
void Curl_quic_done(struct Curl_easy *data, bool premature)
{
struct HTTP *stream = data->req.p.http;
(void)premature;
H3BUGF(infof(data, "Curl_quic_done"));
if(stream) {
if(stream->recv_buf) {
Curl_safefree(stream->recv_buf);
msh3_lock_uninitialize(&stream->recv_lock);
}
if(stream->req) {
MsH3RequestClose(stream->req);
stream->req = ZERO_NULL;
}
/* TODO - Need an explicit signal to know when to FIN. */
if(!MsH3RequestSend(stream->req, MSH3_REQUEST_FLAG_FIN, buf, (uint32_t)len,
stream)) {
*err = CURLE_SEND_ERROR;
return -1;
}
/* TODO - msh3/msquic will hold onto this memory until the send complete
event. How do we make sure curl doesn't free it until then? */
sentlen += len;
*err = CURLE_OK;
return sentlen;
}
bool Curl_quic_data_pending(const struct Curl_easy *data)
static int cf_msh3_get_select_socks(struct Curl_cfilter *cf,
struct Curl_easy *data,
curl_socket_t *socks)
{
struct HTTP *stream = data->req.p.http;
int bitmap = GETSOCK_BLANK;
socks[0] = cf->conn->sock[FIRSTSOCKET];
if(stream->recv_error) {
bitmap |= GETSOCK_READSOCK(0);
data->state.drain++;
}
else if(stream->recv_header_len || stream->recv_data_len) {
bitmap |= GETSOCK_READSOCK(0);
data->state.drain++;
}
H3BUGF(infof(data, "msh3_getsock %u", (uint32_t)data->state.drain));
return bitmap;
}
static bool cf_msh3_data_pending(struct Curl_cfilter *cf,
const struct Curl_easy *data)
{
struct HTTP *stream = data->req.p.http;
(void)data;
(void)cf;
H3BUGF(infof((struct Curl_easy *)data, "Curl_quic_data_pending"));
return stream->recv_header_len || stream->recv_data_len;
}
/*
* Called from transfer.c:Curl_readwrite when neither HTTP level read
* nor write is performed. It is a good place to handle timer expiry
* for QUIC transport.
*/
CURLcode Curl_quic_idle(struct Curl_easy *data)
static CURLcode cf_msh3_data_event(struct Curl_cfilter *cf,
struct Curl_easy *data,
int event, int arg1, void *arg2)
{
(void)data;
H3BUGF(infof(data, "Curl_quic_idle"));
struct HTTP *stream = data->req.p.http;
CURLcode result = CURLE_OK;
(void)arg1;
(void)arg2;
switch(event) {
case CF_CTRL_DATA_SETUP:
result = msh3_data_setup(cf, data);
break;
case CF_CTRL_DATA_DONE:
H3BUGF(infof(data, "Curl_quic_done"));
if(stream) {
if(stream->recv_buf) {
Curl_safefree(stream->recv_buf);
msh3_lock_uninitialize(&stream->recv_lock);
}
if(stream->req) {
MsH3RequestClose(stream->req);
stream->req = ZERO_NULL;
}
}
break;
case CF_CTRL_DATA_DONE_SEND:
H3BUGF(infof(data, "Curl_quic_done_sending"));
stream->upload_done = TRUE;
break;
default:
break;
}
return result;
}
static CURLcode cf_connect_start(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
struct cf_msh3_ctx *ctx = cf->ctx;
bool insecure = !cf->conn->ssl_config.verifypeer;
H3BUGF(infof(data, "creating new api/connection"));
ctx->api = MsH3ApiOpen();
if(!ctx->api) {
failf(data, "can't create msh3 api");
return CURLE_FAILED_INIT;
}
ctx->qconn = MsH3ConnectionOpen(ctx->api,
cf->conn->host.name,
(uint16_t)cf->conn->remote_port,
insecure);
if(!ctx->qconn) {
failf(data, "can't create msh3 connection");
if(ctx->api) {
MsH3ApiClose(ctx->api);
ctx->api = NULL;
}
return CURLE_FAILED_INIT;
}
return CURLE_OK;
}
static CURLcode cf_msh3_connect(struct Curl_cfilter *cf,
struct Curl_easy *data,
bool blocking, bool *done)
{
struct cf_msh3_ctx *ctx = cf->ctx;
MSH3_CONNECTION_STATE state;
CURLcode result = CURLE_OK;
(void)blocking;
if(cf->connected) {
*done = TRUE;
return CURLE_OK;
}
*done = FALSE;
if(!ctx->qconn) {
result = cf_connect_start(cf, data);
if(result)
goto out;
}
state = MsH3ConnectionGetState(ctx->qconn, FALSE);
if(state == MSH3_CONN_HANDSHAKE_FAILED || state == MSH3_CONN_DISCONNECTED) {
failf(data, "failed to connect, state=%u", (uint32_t)state);
result = CURLE_COULDNT_CONNECT;
goto out;
}
if(state == MSH3_CONN_CONNECTED) {
DEBUGF(infof(data, "msh3 established connection"));
cf->connected = TRUE;
cf->conn->alpn = CURL_HTTP_VERSION_3;
*done = TRUE;
connkeep(cf->conn, "HTTP/3 default");
}
out:
return result;
}
static void cf_msh3_ctx_clear(struct cf_msh3_ctx *ctx)
{
if(ctx) {
if(ctx->qconn)
MsH3ConnectionClose(ctx->qconn);
if(ctx->api)
MsH3ApiClose(ctx->api);
memset(ctx, 0, sizeof(*ctx));
}
}
static void cf_msh3_close(struct Curl_cfilter *cf, struct Curl_easy *data)
{
struct cf_msh3_ctx *ctx = cf->ctx;
(void)data;
if(ctx) {
cf_msh3_ctx_clear(ctx);
}
}
static void cf_msh3_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
{
struct cf_msh3_ctx *ctx = cf->ctx;
(void)data;
cf_msh3_ctx_clear(ctx);
free(ctx);
cf->ctx = NULL;
}
static const struct Curl_cftype cft_msh3 = {
"HTTP/3-MSH3",
CF_TYPE_IP_CONNECT | CF_TYPE_SSL | CF_TYPE_MULTIPLEX,
cf_msh3_destroy,
cf_msh3_connect,
cf_msh3_close,
Curl_cf_def_get_host,
cf_msh3_get_select_socks,
cf_msh3_data_pending,
cf_msh3_send,
cf_msh3_recv,
cf_msh3_data_event,
Curl_cf_def_conn_is_alive,
Curl_cf_def_conn_keep_alive,
Curl_cf_def_query,
};
CURLcode Curl_cf_msh3_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
struct connectdata *conn,
const struct Curl_addrinfo *ai)
{
struct cf_msh3_ctx *ctx = NULL;
struct Curl_cfilter *cf = NULL;
CURLcode result;
(void)data;
(void)conn;
(void)ai; /* TODO: msh3 resolves itself? */
ctx = calloc(sizeof(*ctx), 1);
if(!ctx) {
result = CURLE_OUT_OF_MEMORY;
goto out;
}
result = Curl_cf_create(&cf, &cft_msh3, ctx);
out:
*pcf = (!result)? cf : NULL;
if(result) {
Curl_safefree(cf);
Curl_safefree(ctx);
}
return result;
}
bool Curl_conn_is_msh3(const struct Curl_easy *data,
const struct connectdata *conn,
int sockindex)
{
struct Curl_cfilter *cf = conn? conn->cfilter[sockindex] : NULL;
(void)data;
for(; cf; cf = cf->next) {
if(cf->cft == &cft_msh3)
return TRUE;
if(cf->cft->flags & CF_TYPE_IP_CONNECT)
return FALSE;
}
return FALSE;
}
#endif /* USE_MSH3 */

View File

@ -30,10 +30,16 @@
#include <msh3.h>
struct quicsocket {
MSH3_API* api;
MSH3_CONNECTION* conn;
};
void Curl_msh3_ver(char *p, size_t len);
CURLcode Curl_cf_msh3_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
struct connectdata *conn,
const struct Curl_addrinfo *ai);
bool Curl_conn_is_msh3(const struct Curl_easy *data,
const struct connectdata *conn,
int sockindex);
#endif /* USE_MSQUIC */

File diff suppressed because it is too large Load Diff

View File

@ -42,52 +42,20 @@
#include <wolfssl/quic.h>
#endif
struct gtls_instance;
struct blocked_pkt {
const uint8_t *pkt;
size_t pktlen;
size_t gsolen;
};
struct quicsocket {
struct connectdata *conn; /* point back to the connection */
ngtcp2_conn *qconn;
ngtcp2_cid dcid;
ngtcp2_cid scid;
uint32_t version;
ngtcp2_settings settings;
ngtcp2_transport_params transport_params;
ngtcp2_connection_close_error last_error;
ngtcp2_crypto_conn_ref conn_ref;
#ifdef USE_OPENSSL
SSL_CTX *sslctx;
SSL *ssl;
#elif defined(USE_GNUTLS)
struct gtls_instance *gtls;
#elif defined(USE_WOLFSSL)
WOLFSSL_CTX *sslctx;
WOLFSSL *ssl;
#endif
struct sockaddr_storage local_addr;
socklen_t local_addrlen;
bool no_gso;
uint8_t *pktbuf;
size_t pktbuflen;
/* the number of entries in blocked_pkt */
size_t num_blocked_pkt;
/* the number of processed entries in blocked_pkt */
size_t num_blocked_pkt_sent;
/* the packets blocked by sendmsg (EAGAIN or EWOULDBLOCK) */
struct blocked_pkt blocked_pkt[2];
nghttp3_conn *h3conn;
nghttp3_settings h3settings;
int qlogfd;
};
struct Curl_cfilter;
#include "urldata.h"
void Curl_ngtcp2_ver(char *p, size_t len);
CURLcode Curl_cf_ngtcp2_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
struct connectdata *conn,
const struct Curl_addrinfo *ai);
bool Curl_conn_is_ngtcp2(const struct Curl_easy *data,
const struct connectdata *conn,
int sockindex);
#endif
#endif /* HEADER_CURL_VQUIC_NGTCP2_H */

File diff suppressed because it is too large Load Diff

View File

@ -31,27 +31,19 @@
#include <quiche.h>
#include <openssl/ssl.h>
struct quic_handshake {
char *buf; /* pointer to the buffer */
size_t alloclen; /* size of allocation */
size_t len; /* size of content in buffer */
size_t nread; /* how many bytes have been read */
};
struct Curl_cfilter;
struct Curl_easy;
struct quicsocket {
quiche_config *cfg;
quiche_conn *conn;
quiche_h3_conn *h3c;
quiche_h3_config *h3config;
uint8_t scid[QUICHE_MAX_CONN_ID_LEN];
curl_socket_t sockfd;
uint32_t version;
SSL_CTX *sslctx;
SSL *ssl;
bool h3_recving; /* TRUE when in h3-body-reading state */
struct sockaddr_storage local_addr;
socklen_t local_addrlen;
};
void Curl_quiche_ver(char *p, size_t len);
CURLcode Curl_cf_quiche_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
struct connectdata *conn,
const struct Curl_addrinfo *ai);
bool Curl_conn_is_quiche(const struct Curl_easy *data,
const struct connectdata *conn,
int sockindex);
#endif

View File

@ -32,6 +32,9 @@
#include "urldata.h"
#include "dynbuf.h"
#include "curl_printf.h"
#include "msh3.h"
#include "ngtcp2.h"
#include "quiche.h"
#include "vquic.h"
#ifdef O_BINARY
@ -40,6 +43,17 @@
#define QLOGMODE O_WRONLY|O_CREAT
#endif
void Curl_quic_ver(char *p, size_t len)
{
#ifdef USE_NGTCP2
Curl_ngtcp2_ver(p, len);
#elif defined(USE_QUICHE)
Curl_quiche_ver(p, len);
#elif defined(USE_MSH3)
Curl_msh3_ver(p, len);
#endif
}
/*
* If the QLOGDIR environment variable is set, open and return a file
* descriptor to write the log to.
@ -84,4 +98,41 @@ CURLcode Curl_qlogdir(struct Curl_easy *data,
return CURLE_OK;
}
CURLcode Curl_cf_quic_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
struct connectdata *conn,
const struct Curl_addrinfo *ai)
{
#ifdef USE_NGTCP2
return Curl_cf_ngtcp2_create(pcf, data, conn, ai);
#elif defined(USE_QUICHE)
return Curl_cf_quiche_create(pcf, data, conn, ai);
#elif defined(USE_MSH3)
return Curl_cf_msh3_create(pcf, data, conn, ai);
#else
*pcf = NULL;
(void)data;
(void)conn;
(void)ai;
return CURLE_NOT_BUILT_IN;
#endif
}
bool Curl_conn_is_http3(const struct Curl_easy *data,
const struct connectdata *conn,
int sockindex)
{
#ifdef USE_NGTCP2
return Curl_conn_is_ngtcp2(data, conn, sockindex);
#elif defined(USE_QUICHE)
return Curl_conn_is_quiche(data, conn, sockindex);
#elif defined(USE_MSH3)
return Curl_conn_is_msh3(data, conn, sockindex);
#else
return ((conn->handler->protocol & PROTO_FAMILY_HTTP) &&
(conn->httpversion == 30));
#endif
}
#endif

View File

@ -27,10 +27,32 @@
#include "curl_setup.h"
#ifdef ENABLE_QUIC
struct Curl_cfilter;
struct Curl_easy;
struct connectdata;
struct Curl_addrinfo;
void Curl_quic_ver(char *p, size_t len);
CURLcode Curl_qlogdir(struct Curl_easy *data,
unsigned char *scid,
size_t scidlen,
int *qlogfdp);
#endif
CURLcode Curl_cf_quic_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
struct connectdata *conn,
const struct Curl_addrinfo *ai);
bool Curl_conn_is_http3(const struct Curl_easy *data,
const struct connectdata *conn,
int sockindex);
#else /* ENABLE_QUIC */
#define Curl_conn_is_http3(a,b,c) FALSE
#endif /* !ENABLE_QUIC */
#endif /* HEADER_CURL_VQUIC_QUIC_H */

33
lib/vquic/vquic_int.h Normal file
View File

@ -0,0 +1,33 @@
#ifndef HEADER_CURL_VQUIC_QUIC_INT_H
#define HEADER_CURL_VQUIC_QUIC_INT_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 1998 - 2022, 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"
#ifdef ENABLE_QUIC
#endif /* !ENABLE_QUIC */
#endif /* HEADER_CURL_VQUIC_QUIC_INT_H */

View File

@ -144,7 +144,7 @@ const struct Curl_handler Curl_handler_scp = {
scp_disconnect, /* disconnect */
ZERO_NULL, /* readwrite */
ZERO_NULL, /* connection_check */
ssh_attach,
ssh_attach, /* attach */
PORT_SSH, /* defport */
CURLPROTO_SCP, /* protocol */
CURLPROTO_SCP, /* family */
@ -173,7 +173,7 @@ const struct Curl_handler Curl_handler_sftp = {
sftp_disconnect, /* disconnect */
ZERO_NULL, /* readwrite */
ZERO_NULL, /* connection_check */
ssh_attach,
ssh_attach, /* attach */
PORT_SSH, /* defport */
CURLPROTO_SFTP, /* protocol */
CURLPROTO_SFTP, /* family */

View File

@ -96,6 +96,10 @@
#include "curl_memory.h"
#include "memdebug.h"
#define DEBUG_ME 0
/* Uncomment the ALLOW_RENEG line to a real #define if you want to allow TLS
renegotiations when built with BoringSSL. Renegotiating is non-compliant
with HTTP/2 and "an extremely dangerous protocol feature". Beware.
@ -706,8 +710,10 @@ static int bio_cf_out_write(BIO *bio, const char *buf, int blen)
DEBUGASSERT(data);
nwritten = Curl_conn_cf_send(cf->next, data, buf, blen, &result);
/* DEBUGF(infof(data, CFMSG(cf, "bio_cf_out_write(len=%d) -> %d, err=%d"),
blen, (int)nwritten, result)); */
#if DEBUG_ME
DEBUGF(infof(data, CFMSG(cf, "bio_cf_out_write(len=%d) -> %d, err=%d"),
blen, (int)nwritten, result));
#endif
BIO_clear_retry_flags(bio);
connssl->backend->io_result = result;
if(nwritten < 0) {
@ -731,8 +737,10 @@ static int bio_cf_in_read(BIO *bio, char *buf, int blen)
return 0;
nread = Curl_conn_cf_recv(cf->next, data, buf, blen, &result);
/* DEBUGF(infof(data, CFMSG(cf, "bio_cf_in_read(len=%d) -> %d, err=%d"),
blen, (int)nread, result)); */
#if DEBUG_ME
DEBUGF(infof(data, CFMSG(cf, "bio_cf_in_read(len=%d) -> %d, err=%d"),
blen, (int)nread, result));
#endif
BIO_clear_retry_flags(bio);
connssl->backend->io_result = result;
if(nread < 0) {
@ -2630,13 +2638,13 @@ static void ossl_trace(int direction, int ssl_ver, int content_type,
const void *buf, size_t len, SSL *ssl,
void *userp)
{
char unknown[32];
const char *verstr = NULL;
const char *verstr = "???";
struct connectdata *conn = userp;
int cf_idx = ossl_get_ssl_cf_index();
struct ssl_connect_data *connssl;
struct Curl_easy *data = NULL;
struct Curl_cfilter *cf;
char unknown[32];
DEBUGASSERT(cf_idx >= 0);
cf = (struct Curl_cfilter*) SSL_get_ex_data(ssl, cf_idx);
@ -2646,8 +2654,8 @@ static void ossl_trace(int direction, int ssl_ver, int content_type,
DEBUGASSERT(connssl->backend);
data = connssl->call_data;
if(!conn || !data || !data->set.fdebug ||
(direction != 0 && direction != 1))
if(!conn || !data || !data->set.fdebug
|| (direction != 0 && direction != 1))
return;
switch(ssl_ver) {
@ -3448,6 +3456,7 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf,
struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
BIO *bio;
int cf_idx = ossl_get_ssl_cf_index();
#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
bool sni;
@ -3473,6 +3482,9 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf,
DEBUGASSERT(ssl_connect_1 == connssl->connecting_state);
DEBUGASSERT(backend);
if(cf_idx < 0)
return CURLE_FAILED_INIT;
/* Make funny stuff to get random input */
result = ossl_seed(data);
if(result)
@ -3785,6 +3797,8 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf,
return CURLE_OUT_OF_MEMORY;
}
SSL_set_ex_data(backend->handle, cf_idx, cf);
#if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_TLSEXT) && \
!defined(OPENSSL_NO_OCSP)
if(conn_config->verifystatus)

View File

@ -685,20 +685,6 @@ void Curl_ssl_version(char *buffer, size_t size)
#endif
}
/*
* This function tries to determine connection status.
*
* Return codes:
* 1 means the connection is still in place
* 0 means the connection has been closed
* -1 means the connection status is unknown
*/
int Curl_ssl_check_cxn(struct Curl_easy *data, struct connectdata *conn)
{
struct Curl_cfilter *cf = Curl_ssl_cf_get_ssl(conn->cfilter[FIRSTSOCKET]);
return cf? Curl_ssl->check_cxn(cf, data) : -1;
}
void Curl_ssl_free_certinfo(struct Curl_easy *data)
{
struct curl_certinfo *ci = &data->info.certs;
@ -1589,31 +1575,50 @@ static int ssl_cf_get_select_socks(struct Curl_cfilter *cf,
return result;
}
static void ssl_cf_attach_data(struct Curl_cfilter *cf,
struct Curl_easy *data)
static CURLcode ssl_cf_cntrl(struct Curl_cfilter *cf,
struct Curl_easy *data,
int event, int arg1, void *arg2)
{
if(Curl_ssl->attach_data) {
cf_ctx_set_data(cf, data);
Curl_ssl->attach_data(cf, data);
cf_ctx_set_data(cf, NULL);
(void)arg1;
(void)arg2;
switch(event) {
case CF_CTRL_DATA_ATTACH:
if(Curl_ssl->attach_data) {
cf_ctx_set_data(cf, data);
Curl_ssl->attach_data(cf, data);
cf_ctx_set_data(cf, NULL);
}
break;
case CF_CTRL_DATA_DETACH:
if(Curl_ssl->detach_data) {
cf_ctx_set_data(cf, data);
Curl_ssl->detach_data(cf, data);
cf_ctx_set_data(cf, NULL);
}
break;
default:
break;
}
return CURLE_OK;
}
static void ssl_cf_detach_data(struct Curl_cfilter *cf,
struct Curl_easy *data)
static bool cf_ssl_is_alive(struct Curl_cfilter *cf, struct Curl_easy *data)
{
if(Curl_ssl->detach_data) {
cf_ctx_set_data(cf, data);
Curl_ssl->detach_data(cf, data);
cf_ctx_set_data(cf, NULL);
}
/*
* This function tries to determine connection status.
*
* Return codes:
* 1 means the connection is still in place
* 0 means the connection has been closed
* -1 means the connection status is unknown
*/
return Curl_ssl->check_cxn(cf, data) != 0;
}
static const struct Curl_cftype cft_ssl = {
"SSL",
CF_TYPE_SSL,
ssl_cf_destroy,
Curl_cf_def_setup,
ssl_cf_connect,
ssl_cf_close,
Curl_cf_def_get_host,
@ -1621,15 +1626,16 @@ static const struct Curl_cftype cft_ssl = {
ssl_cf_data_pending,
ssl_cf_send,
ssl_cf_recv,
ssl_cf_attach_data,
ssl_cf_detach_data,
ssl_cf_cntrl,
cf_ssl_is_alive,
Curl_cf_def_conn_keep_alive,
Curl_cf_def_query,
};
static const struct Curl_cftype cft_ssl_proxy = {
"SSL-PROXY",
CF_TYPE_SSL,
ssl_cf_destroy,
Curl_cf_def_setup,
ssl_cf_connect,
ssl_cf_close,
Curl_cf_def_get_host,
@ -1637,15 +1643,16 @@ static const struct Curl_cftype cft_ssl_proxy = {
ssl_cf_data_pending,
ssl_cf_send,
ssl_cf_recv,
ssl_cf_attach_data,
ssl_cf_detach_data,
ssl_cf_cntrl,
cf_ssl_is_alive,
Curl_cf_def_conn_keep_alive,
Curl_cf_def_query,
};
CURLcode Curl_ssl_cfilter_add(struct Curl_easy *data,
struct connectdata *conn,
int sockindex)
static CURLcode cf_ssl_create(struct Curl_cfilter **pcf,
struct Curl_easy *data)
{
struct Curl_cfilter *cf;
struct Curl_cfilter *cf = NULL;
struct ssl_connect_data *ctx;
CURLcode result;
@ -1657,25 +1664,44 @@ CURLcode Curl_ssl_cfilter_add(struct Curl_easy *data,
}
result = Curl_cf_create(&cf, &cft_ssl, ctx);
if(result)
goto out;
Curl_conn_cf_add(data, conn, sockindex, cf);
result = CURLE_OK;
out:
if(result)
cf_ctx_free(ctx);
*pcf = result? NULL : cf;
return result;
}
CURLcode Curl_ssl_cfilter_add(struct Curl_easy *data,
struct connectdata *conn,
int sockindex)
{
struct Curl_cfilter *cf;
CURLcode result;
result = cf_ssl_create(&cf, data);
if(!result)
Curl_conn_cf_add(data, conn, sockindex, cf);
return result;
}
CURLcode Curl_cf_ssl_insert_after(struct Curl_cfilter *cf_at,
struct Curl_easy *data)
{
struct Curl_cfilter *cf;
CURLcode result;
result = cf_ssl_create(&cf, data);
if(!result)
Curl_conn_cf_insert_after(cf_at, cf);
return result;
}
#ifndef CURL_DISABLE_PROXY
CURLcode Curl_ssl_cfilter_proxy_add(struct Curl_easy *data,
struct connectdata *conn,
int sockindex)
static CURLcode cf_ssl_proxy_create(struct Curl_cfilter **pcf,
struct Curl_easy *data)
{
struct Curl_cfilter *cf;
struct Curl_cfilter *cf = NULL;
struct ssl_connect_data *ctx;
CURLcode result;
@ -1686,16 +1712,36 @@ CURLcode Curl_ssl_cfilter_proxy_add(struct Curl_easy *data,
}
result = Curl_cf_create(&cf, &cft_ssl_proxy, ctx);
if(result)
goto out;
Curl_conn_cf_add(data, conn, sockindex, cf);
result = CURLE_OK;
out:
if(result)
cf_ctx_free(ctx);
*pcf = result? NULL : cf;
return result;
}
CURLcode Curl_ssl_cfilter_proxy_add(struct Curl_easy *data,
struct connectdata *conn,
int sockindex)
{
struct Curl_cfilter *cf;
CURLcode result;
result = cf_ssl_proxy_create(&cf, data);
if(!result)
Curl_conn_cf_add(data, conn, sockindex, cf);
return result;
}
CURLcode Curl_cf_ssl_proxy_insert_after(struct Curl_cfilter *cf_at,
struct Curl_easy *data)
{
struct Curl_cfilter *cf;
CURLcode result;
result = cf_ssl_proxy_create(&cf, data);
if(!result)
Curl_conn_cf_insert_after(cf_at, cf);
return result;
}

View File

@ -53,6 +53,7 @@ struct Curl_ssl_session;
/* Curl_multi SSL backend-specific data; declared differently by each SSL
backend */
struct multi_ssl_backend_data;
struct Curl_cfilter;
CURLsslset Curl_init_sslset_nolock(curl_sslbackend id, const char *name,
const curl_ssl_backend ***avail);
@ -95,7 +96,6 @@ struct curl_slist *Curl_ssl_engines_list(struct Curl_easy *data);
/* init the SSL session ID cache */
CURLcode Curl_ssl_initsessions(struct Curl_easy *, size_t);
void Curl_ssl_version(char *buffer, size_t size);
int Curl_ssl_check_cxn(struct Curl_easy *data, struct connectdata *conn);
/* Certificate information list handling. */
@ -156,6 +156,9 @@ CURLcode Curl_ssl_cfilter_add(struct Curl_easy *data,
struct connectdata *conn,
int sockindex);
CURLcode Curl_cf_ssl_insert_after(struct Curl_cfilter *cf_at,
struct Curl_easy *data);
CURLcode Curl_ssl_cfilter_remove(struct Curl_easy *data,
int sockindex);
@ -163,6 +166,8 @@ CURLcode Curl_ssl_cfilter_remove(struct Curl_easy *data,
CURLcode Curl_ssl_cfilter_proxy_add(struct Curl_easy *data,
struct connectdata *conn,
int sockindex);
CURLcode Curl_cf_ssl_proxy_insert_after(struct Curl_cfilter *cf_at,
struct Curl_easy *data);
#endif /* !CURL_DISABLE_PROXY */
/**
@ -218,7 +223,6 @@ void *Curl_ssl_get_internals(struct Curl_easy *data, int sockindex,
#define Curl_ssl_set_engine_default(x) CURLE_NOT_BUILT_IN
#define Curl_ssl_engines_list(x) NULL
#define Curl_ssl_initsessions(x,y) CURLE_OK
#define Curl_ssl_check_cxn(d,x) 0
#define Curl_ssl_free_certinfo(x) Curl_nop_stmt
#define Curl_ssl_kill_session(x) Curl_nop_stmt
#define Curl_ssl_random(x,y,z) ((void)x, CURLE_NOT_BUILT_IN)

View File

@ -246,7 +246,7 @@ test2300 test2301 test2302 test2303 test2304 \
\
test2400 test2401 test2402 \
\
test2500 \
test2500 test2501 test2502 \
\
test3000 test3001 test3002 test3003 test3004 test3005 test3006 test3007 \
test3008 test3009 test3010 test3011 test3012 test3013 test3014 test3015 \

70
tests/data/test2501 Normal file
View File

@ -0,0 +1,70 @@
<testcase>
<info>
<keywords>
HTTP
HTTP POST
HTTP/3
HTTPS
</keywords>
</info>
#
# Server-side
<reply>
<data nocheck="yes">
HTTP/1.1 201 OK
Date: Tue, 09 Nov 2010 14:49:00 GMT
Connection: close
Content-Length: 0
Funny-head: yesyes
</data>
</reply>
#
# Client-side
<client>
<features>
debug
http
http/3
</features>
<server>
http
http/3
</server>
<name>
HTTP/3 POST
</name>
<setenv>
</setenv>
<command>
-k --http3 "https://%HOSTIP:%HTTP3PORT/%TESTNUMBER" -d "moo"
</command>
</client>
#
# Verify data after the test has been "shot"
<verify>
<stdout>
HTTP/3 201
date: Tue, 09 Nov 2010 14:49:00 GMT
content-length: 0
funny-head: yesyes
via: 1.1 nghttpx
</stdout>
<protocol nonewline="yes">
POST https://%HOSTIP:%HTTP3PORT/2501 HTTP/1.1
Host: %HOSTIP:%HTTP3PORT
User-Agent: curl/%VERSION
Accept: */*
Content-Length: 3
Content-Type: application/x-www-form-urlencoded
Via: 3 nghttpx
moo
</protocol>
</verify>
</testcase>

104
tests/data/test2502 Normal file
View File

@ -0,0 +1,104 @@
<testcase>
<info>
<keywords>
HTTP
HTTP/3
multi
verbose logs
</keywords>
</info>
# Server-side
<reply>
<data1 crlf=yes">
HTTP/1.1 200 OK
Date: Tue, 09 Nov 2010 14:49:00 GMT
Server: server.example.com
Content-Length: 47
file contents should appear once for each file
</data1>
<data2>
HTTP/1.1 200 OK
Date: Tue, 09 Nov 2010 14:49:00 GMT
Server: server.example.com
Content-Length: 47
file contents should appear once for each file
</data2>
<data3>
HTTP/1.1 200 OK
Date: Tue, 09 Nov 2010 14:49:00 GMT
Server: server.example.com
Content-Length: 47
file contents should appear once for each file
</data3>
<data4>
HTTP/1.1 200 OK
Date: Tue, 09 Nov 2010 14:49:00 GMT
Server: server.example.com
Content-Length: 47
file contents should appear once for each file
</data4>
</reply>
# Client-side
<client>
<features>
http/3
</features>
<server>
http
http/3
</server>
<tool>
lib%TESTNUMBER
</tool>
<name>
HTTP GET multiple over HTTP/3
</name>
<command>
https://%HOSTIP:%HTTP3PORT/path/%TESTNUMBER %HOSTIP %HTTP3PORT
</command>
</client>
# Verify data after the test has been "shot"
<verify>
<protocol crlf="yes">
GET https://localhost:%HTTP3PORT/path/%TESTNUMBER0001 HTTP/1.1
Host: %HOSTIP:%HTTPPORT
Accept: */*
Via: 3 nghttpx
GET https://localhost:%HTTP3PORT/path/%TESTNUMBER0002 HTTP/1.1
Host: %HOSTIP:%HTTPPORT
Accept: */*
Via: 3 nghttpx
GET https://localhost:%HTTP3PORT/path/%TESTNUMBER0003 HTTP/1.1
Host: %HOSTIP:%HTTPPORT
Accept: */*
Via: 3 nghttpx
GET https://localhost:%HTTP3PORT/path/%TESTNUMBER0004 HTTP/1.1
Host: %HOSTIP:%HTTPPORT
Accept: */*
Via: 3 nghttpx
</protocol>
<strip>
^Host:.*
</strip>
<file name="log/stderr%TESTNUMBER" mode="text">
* Connection #0 to host localhost left intact
* Connection #0 to host localhost left intact
* Connection #0 to host localhost left intact
* Connection #0 to host localhost left intact
</file>
<stripfile>
$_ = '' if (($_ !~ /left intact/) && ($_ !~ /Closing connection/))
</stripfile>
</verify>
</testcase>

View File

@ -71,6 +71,7 @@ noinst_PROGRAMS = chkhostname libauthretry libntlmconnect \
lib1945 lib1946 lib1947 lib1948 lib1955 lib1956 lib1957 lib1958 lib1959 \
lib2301 lib2302 lib2304 \
lib2402 \
lib2502 \
lib3010 lib3025 lib3026 lib3027 \
lib3100 lib3101
@ -803,6 +804,10 @@ lib2402_SOURCES = lib2402.c $(SUPPORTFILES) $(TESTUTIL) $(WARNLESS)
lib2402_LDADD = $(TESTUTIL_LIBS)
lib2402_CPPFLAGS = $(AM_CPPFLAGS) -DLIB2402
lib2502_SOURCES = lib2502.c $(SUPPORTFILES) $(TESTUTIL) $(WARNLESS)
lib2502_LDADD = $(TESTUTIL_LIBS)
lib2502_CPPFLAGS = $(AM_CPPFLAGS) -DLIB2502
lib3010_SOURCES = lib3010.c $(SUPPORTFILES) $(TESTUTIL) $(WARNLESS)
lib3010_LDADD = $(TESTUTIL_LIBS)
lib3010_CPPFLAGS = $(AM_CPPFLAGS)

141
tests/libtest/lib2502.c Normal file
View File

@ -0,0 +1,141 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 2013 - 2022, 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 "test.h"
#include "testutil.h"
#include "warnless.h"
#include "memdebug.h"
#define TEST_HANG_TIMEOUT 60 * 1000
#define NUM_HANDLES 4
int test(char *URL)
{
int res = 0;
CURL *curl[NUM_HANDLES] = {0};
int running;
CURLM *m = NULL;
int i;
char target_url[256];
char dnsentry[256];
struct curl_slist *slist = NULL;
char *port = libtest_arg3;
char *address = libtest_arg2;
(void)URL;
msnprintf(dnsentry, sizeof(dnsentry), "localhost:%s:%s",
port, address);
printf("%s\n", dnsentry);
slist = curl_slist_append(slist, dnsentry);
if(!slist) {
fprintf(stderr, "curl_slist_append() failed\n");
goto test_cleanup;
}
start_test_timing();
global_init(CURL_GLOBAL_ALL);
multi_init(m);
multi_setopt(m, CURLMOPT_MAXCONNECTS, 1L);
/* get NUM_HANDLES easy handles */
for(i = 0; i < NUM_HANDLES; i++) {
/* get an easy handle */
easy_init(curl[i]);
/* specify target */
msnprintf(target_url, sizeof(target_url),
"https://localhost:%s/path/2502%04i",
port, i + 1);
target_url[sizeof(target_url) - 1] = '\0';
easy_setopt(curl[i], CURLOPT_URL, target_url);
/* go http2 */
easy_setopt(curl[i], CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_3);
easy_setopt(curl[i], CURLOPT_CONNECTTIMEOUT_MS, (long)5000);
easy_setopt(curl[i], CURLOPT_CAINFO, "./certs/EdelCurlRoot-ca.cacert");
/* wait for first connection establised to see if we can share it */
easy_setopt(curl[i], CURLOPT_PIPEWAIT, 1L);
/* go verbose */
easy_setopt(curl[i], CURLOPT_VERBOSE, 1L);
/* include headers */
easy_setopt(curl[i], CURLOPT_HEADER, 1L);
easy_setopt(curl[i], CURLOPT_RESOLVE, slist);
}
fprintf(stderr, "Start at URL 0\n");
for(i = 0; i < NUM_HANDLES; i++) {
/* add handle to multi */
multi_add_handle(m, curl[i]);
for(;;) {
struct timeval interval;
fd_set rd, wr, exc;
int maxfd = -99;
interval.tv_sec = 1;
interval.tv_usec = 0;
multi_perform(m, &running);
abort_on_test_timeout();
if(!running)
break; /* done */
FD_ZERO(&rd);
FD_ZERO(&wr);
FD_ZERO(&exc);
multi_fdset(m, &rd, &wr, &exc, &maxfd);
/* At this point, maxfd is guaranteed to be greater or equal than -1. */
select_test(maxfd + 1, &rd, &wr, &exc, &interval);
abort_on_test_timeout();
}
wait_ms(1); /* to ensure different end times */
}
test_cleanup:
/* proper cleanup sequence - type PB */
for(i = 0; i < NUM_HANDLES; i++) {
curl_multi_remove_handle(m, curl[i]);
curl_easy_cleanup(curl[i]);
}
curl_slist_free_all(slist);
curl_multi_cleanup(m);
curl_global_cleanup();
return res;
}