mirror of
https://github.com/curl/curl.git
synced 2025-03-07 15:27:17 +08:00
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:
parent
1c18f8da51
commit
71b7e01610
@ -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
1534
lib/cf-socket.c
Normal file
File diff suppressed because it is too large
Load Diff
169
lib/cf-socket.h
Normal file
169
lib/cf-socket.h
Normal 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 */
|
478
lib/cfilters.c
478
lib/cfilters.c
@ -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;
|
||||
}
|
||||
|
||||
|
224
lib/cfilters.h
224
lib/cfilters.h
@ -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 */
|
||||
|
2355
lib/connect.c
2355
lib/connect.c
File diff suppressed because it is too large
Load Diff
114
lib/connect.h
114
lib/connect.h
@ -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 */
|
||||
|
26
lib/easy.c
26
lib/easy.c
@ -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;
|
||||
}
|
||||
|
35
lib/ftp.c
35
lib/ftp.c
@ -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 */
|
||||
|
109
lib/http.c
109
lib/http.c
@ -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) {
|
||||
|
93
lib/http.h
93
lib/http.h
@ -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);
|
||||
|
1614
lib/http2.c
1614
lib/http2.c
File diff suppressed because it is too large
Load Diff
49
lib/http2.h
49
lib/http2.h
@ -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
|
||||
|
||||
|
277
lib/http_proxy.c
277
lib/http_proxy.c
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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 */
|
||||
|
@ -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 */
|
||||
|
19
lib/multi.c
19
lib/multi.c
@ -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[]'
|
||||
|
@ -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 */
|
||||
|
68
lib/quic.h
68
lib/quic.h
@ -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 */
|
33
lib/rtsp.c
33
lib/rtsp.c
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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++) {
|
||||
|
18
lib/setopt.c
18
lib/setopt.c
@ -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 *);
|
||||
|
@ -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 */
|
||||
|
28
lib/socks.c
28
lib/socks.c
@ -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 */
|
||||
|
@ -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 */
|
||||
|
@ -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
166
lib/url.c
@ -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) */
|
||||
|
16
lib/url.h
16
lib/url.h
@ -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 */
|
||||
|
@ -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);
|
||||
|
@ -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"
|
||||
|
||||
|
611
lib/vquic/msh3.c
611
lib/vquic/msh3.c
@ -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 */
|
||||
|
@ -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 */
|
||||
|
||||
|
1289
lib/vquic/ngtcp2.c
1289
lib/vquic/ngtcp2.c
File diff suppressed because it is too large
Load Diff
@ -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 */
|
||||
|
1344
lib/vquic/quiche.c
1344
lib/vquic/quiche.c
File diff suppressed because it is too large
Load Diff
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
33
lib/vquic/vquic_int.h
Normal 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 */
|
@ -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 */
|
||||
|
@ -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)
|
||||
|
152
lib/vtls/vtls.c
152
lib/vtls/vtls.c
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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
70
tests/data/test2501
Normal 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
104
tests/data/test2502
Normal 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>
|
@ -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
141
tests/libtest/lib2502.c
Normal 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;
|
||||
}
|
Loading…
Reference in New Issue
Block a user