mirror of
https://github.com/curl/curl.git
synced 2026-04-14 22:21:41 +03:00
alpn: query filter
Add a connection filter query to obtained the negotiated ALPN protocol to check in setup/protocols how the connection needs to behave. Remove the members `alpn` and `proxy_alpn` from `connectdata`. Closes #17947
This commit is contained in:
parent
f63bdea790
commit
21e885eb39
16 changed files with 168 additions and 151 deletions
|
|
@ -1534,6 +1534,12 @@ static CURLcode cf_h2_proxy_query(struct Curl_cfilter *cf,
|
|||
}
|
||||
break;
|
||||
}
|
||||
case CF_QUERY_ALPN_NEGOTIATED: {
|
||||
const char **palpn = pres2;
|
||||
DEBUGASSERT(palpn);
|
||||
*palpn = NULL;
|
||||
return CURLE_OK;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -226,24 +226,22 @@ static CURLcode baller_connected(struct Curl_cfilter *cf,
|
|||
cf->next = winner->cf;
|
||||
winner->cf = NULL;
|
||||
|
||||
switch(cf->conn->alpn) {
|
||||
case CURL_HTTP_VERSION_3:
|
||||
break;
|
||||
case CURL_HTTP_VERSION_2:
|
||||
#ifdef USE_NGHTTP2
|
||||
{
|
||||
/* Using nghttp2, we add the filter "below" us, so when the conn
|
||||
* closes, we tear it down for a fresh reconnect */
|
||||
result = Curl_http2_switch_at(cf, data);
|
||||
if(result) {
|
||||
ctx->state = CF_HC_FAILURE;
|
||||
ctx->result = result;
|
||||
return result;
|
||||
const char *alpn = Curl_conn_cf_get_alpn_negotiated(cf->next, data);
|
||||
if(alpn && !strcmp("h2", alpn)) {
|
||||
result = Curl_http2_switch_at(cf, data);
|
||||
if(result) {
|
||||
ctx->state = CF_HC_FAILURE;
|
||||
ctx->result = result;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
ctx->state = CF_HC_SUCCESS;
|
||||
cf->connected = TRUE;
|
||||
return result;
|
||||
|
|
|
|||
|
|
@ -564,7 +564,7 @@ bool Curl_conn_is_ip_connected(struct Curl_easy *data, int sockindex)
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
bool Curl_conn_cf_is_ssl(struct Curl_cfilter *cf)
|
||||
static bool cf_is_ssl(struct Curl_cfilter *cf)
|
||||
{
|
||||
for(; cf; cf = cf->next) {
|
||||
if(cf->cft->flags & CF_TYPE_SSL)
|
||||
|
|
@ -577,7 +577,7 @@ bool Curl_conn_cf_is_ssl(struct Curl_cfilter *cf)
|
|||
|
||||
bool Curl_conn_is_ssl(struct connectdata *conn, int sockindex)
|
||||
{
|
||||
return conn ? Curl_conn_cf_is_ssl(conn->cfilter[sockindex]) : FALSE;
|
||||
return conn ? cf_is_ssl(conn->cfilter[sockindex]) : FALSE;
|
||||
}
|
||||
|
||||
bool Curl_conn_get_ssl_info(struct Curl_easy *data,
|
||||
|
|
@ -613,6 +613,13 @@ unsigned char Curl_conn_get_transport(struct Curl_easy *data,
|
|||
return Curl_conn_cf_get_transport(cf, data);
|
||||
}
|
||||
|
||||
const char *Curl_conn_get_alpn_negotiated(struct Curl_easy *data,
|
||||
struct connectdata *conn)
|
||||
{
|
||||
struct Curl_cfilter *cf = conn->cfilter[FIRSTSOCKET];
|
||||
return Curl_conn_cf_get_alpn_negotiated(cf, data);
|
||||
}
|
||||
|
||||
unsigned char Curl_conn_http_version(struct Curl_easy *data,
|
||||
struct connectdata *conn)
|
||||
{
|
||||
|
|
@ -810,6 +817,17 @@ unsigned char Curl_conn_cf_get_transport(struct Curl_cfilter *cf,
|
|||
return (unsigned char)(data->conn ? data->conn->transport_wanted : 0);
|
||||
}
|
||||
|
||||
const char *Curl_conn_cf_get_alpn_negotiated(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data)
|
||||
{
|
||||
const char *alpn = NULL;
|
||||
CURL_TRC_CF(data, cf, "query ALPN");
|
||||
if(cf && !cf->cft->query(cf, data, CF_QUERY_ALPN_NEGOTIATED, NULL,
|
||||
CURL_UNCONST(&alpn)))
|
||||
return alpn;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const struct Curl_sockaddr_ex *
|
||||
cf_get_remote_addr(struct Curl_cfilter *cf, struct Curl_easy *data)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -158,6 +158,10 @@ typedef CURLcode Curl_cft_cntrl(struct Curl_cfilter *cf,
|
|||
* - CF_QUERY_SSL_CTX_INFO: same as CF_QUERY_SSL_INFO, but give the SSL_CTX
|
||||
* when available, or the same internal pointer
|
||||
* when the TLS stack does not differentiate.
|
||||
* - CF_QUERY_ALPN_NEGOTIATED: The ALPN selected by the server as
|
||||
null-terminated string or NULL if none
|
||||
selected/handshake not done. Implemented by filter
|
||||
types CF_TYPE_SSL or CF_TYPE_IP_CONNECT.
|
||||
*/
|
||||
/* query res1 res2 */
|
||||
#define CF_QUERY_MAX_CONCURRENT 1 /* number - */
|
||||
|
|
@ -176,6 +180,7 @@ typedef CURLcode Curl_cft_cntrl(struct Curl_cfilter *cf,
|
|||
#define CF_QUERY_SSL_INFO 12 /* - struct curl_tlssessioninfo * */
|
||||
#define CF_QUERY_SSL_CTX_INFO 13 /* - struct curl_tlssessioninfo * */
|
||||
#define CF_QUERY_TRANSPORT 14 /* TRNSPRT_* - * */
|
||||
#define CF_QUERY_ALPN_NEGOTIATED 15 /* - const char * */
|
||||
|
||||
/**
|
||||
* Query the cfilter for properties. Filters ignorant of a query will
|
||||
|
|
@ -331,12 +336,6 @@ CURLcode Curl_conn_cf_cntrl(struct Curl_cfilter *cf,
|
|||
bool ignore_result,
|
||||
int event, int arg1, void *arg2);
|
||||
|
||||
/**
|
||||
* Determine if the connection filter chain is using SSL to the remote host
|
||||
* (or will be once connected).
|
||||
*/
|
||||
bool Curl_conn_cf_is_ssl(struct Curl_cfilter *cf);
|
||||
|
||||
/**
|
||||
* Get the socket used by the filter chain starting at `cf`.
|
||||
* Returns CURL_SOCKET_BAD if not available.
|
||||
|
|
@ -354,6 +353,9 @@ bool Curl_conn_cf_needs_flush(struct Curl_cfilter *cf,
|
|||
unsigned char Curl_conn_cf_get_transport(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data);
|
||||
|
||||
const char *Curl_conn_cf_get_alpn_negotiated(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data);
|
||||
|
||||
#define CURL_CF_SSL_DEFAULT -1
|
||||
#define CURL_CF_SSL_DISABLE 0
|
||||
#define CURL_CF_SSL_ENABLE 1
|
||||
|
|
@ -418,6 +420,10 @@ unsigned char Curl_conn_http_version(struct Curl_easy *data,
|
|||
unsigned char Curl_conn_get_transport(struct Curl_easy *data,
|
||||
struct connectdata *conn);
|
||||
|
||||
/* Get the negotiated ALPN protocol or NULL if none in play */
|
||||
const char *Curl_conn_get_alpn_negotiated(struct Curl_easy *data,
|
||||
struct connectdata *conn);
|
||||
|
||||
/**
|
||||
* Close the filter chain at `sockindex` for connection `data->conn`.
|
||||
* Filters remain in place and may be connected again afterwards.
|
||||
|
|
|
|||
18
lib/http.c
18
lib/http.c
|
|
@ -2673,17 +2673,17 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
|
|||
char *altused = NULL;
|
||||
const char *p_accept; /* Accept: string */
|
||||
unsigned char httpversion;
|
||||
const char *alpn;
|
||||
|
||||
/* Always consider the DO phase done after this function call, even if there
|
||||
may be parts of the request that are not yet sent, since we can deal with
|
||||
the rest of the request in the PERFORM phase. */
|
||||
*done = TRUE;
|
||||
|
||||
switch(conn->alpn) {
|
||||
case CURL_HTTP_VERSION_3:
|
||||
alpn = Curl_conn_get_alpn_negotiated(data, conn);
|
||||
if(alpn && !strcmp("h3", alpn)) {
|
||||
DEBUGASSERT(Curl_conn_http_version(data, conn) == 30);
|
||||
break;
|
||||
case CURL_HTTP_VERSION_2:
|
||||
}
|
||||
else if(alpn && !strcmp("h2", alpn)) {
|
||||
#ifndef CURL_DISABLE_PROXY
|
||||
if((Curl_conn_http_version(data, conn) != 20) &&
|
||||
conn->bits.proxy && !conn->bits.tunnel_proxy
|
||||
|
|
@ -2695,11 +2695,8 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
|
|||
else
|
||||
#endif
|
||||
DEBUGASSERT(Curl_conn_http_version(data, conn) == 20);
|
||||
break;
|
||||
case CURL_HTTP_VERSION_1_1:
|
||||
/* continue with HTTP/1.x when explicitly requested */
|
||||
break;
|
||||
default:
|
||||
}
|
||||
else {
|
||||
/* Check if user wants to use HTTP/2 with clear TCP */
|
||||
if(Curl_http2_may_switch(data)) {
|
||||
DEBUGF(infof(data, "HTTP/2 over clean TCP"));
|
||||
|
|
@ -2707,7 +2704,6 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
|
|||
if(result)
|
||||
goto fail;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* Add collecting of headers written to client. For a new connection,
|
||||
|
|
|
|||
|
|
@ -325,36 +325,42 @@ connect_sub:
|
|||
if(!ctx->cf_protocol) {
|
||||
struct Curl_cfilter *cf_protocol = NULL;
|
||||
int httpversion = 0;
|
||||
int alpn = Curl_conn_cf_is_ssl(cf->next) ?
|
||||
cf->conn->proxy_alpn : CURL_HTTP_VERSION_1_1;
|
||||
const char *alpn = Curl_conn_cf_get_alpn_negotiated(cf->next, data);
|
||||
|
||||
/* First time call after the subchain connected */
|
||||
switch(alpn) {
|
||||
case CURL_HTTP_VERSION_NONE:
|
||||
case CURL_HTTP_VERSION_1_0:
|
||||
case CURL_HTTP_VERSION_1_1:
|
||||
CURL_TRC_CF(data, cf, "installing subfilter for HTTP/1.1");
|
||||
infof(data, "CONNECT tunnel: HTTP/1.%d negotiated",
|
||||
(alpn == CURL_HTTP_VERSION_1_0) ? 0 : 1);
|
||||
if(alpn)
|
||||
infof(data, "CONNECT: '%s' negotiated", alpn);
|
||||
else
|
||||
infof(data, "CONNECT: no ALPN negotiated");
|
||||
|
||||
if(alpn && !strcmp(alpn, "http/1.0")) {
|
||||
CURL_TRC_CF(data, cf, "installing subfilter for HTTP/1.0");
|
||||
result = Curl_cf_h1_proxy_insert_after(cf, data);
|
||||
if(result)
|
||||
goto out;
|
||||
cf_protocol = cf->next;
|
||||
httpversion = (alpn == CURL_HTTP_VERSION_1_0) ? 10 : 11;
|
||||
break;
|
||||
httpversion = 10;
|
||||
}
|
||||
else if(!alpn || !strcmp(alpn, "http/1.1")) {
|
||||
CURL_TRC_CF(data, cf, "installing subfilter for HTTP/1.1");
|
||||
result = Curl_cf_h1_proxy_insert_after(cf, data);
|
||||
if(result)
|
||||
goto out;
|
||||
cf_protocol = cf->next;
|
||||
/* Assume that without an ALPN, we are talking to an ancient one */
|
||||
httpversion = 11;
|
||||
}
|
||||
#ifdef USE_NGHTTP2
|
||||
case CURL_HTTP_VERSION_2:
|
||||
else if(!strcmp(alpn, "h2")) {
|
||||
CURL_TRC_CF(data, cf, "installing subfilter for HTTP/2");
|
||||
infof(data, "CONNECT tunnel: HTTP/2 negotiated");
|
||||
result = Curl_cf_h2_proxy_insert_after(cf, data);
|
||||
if(result)
|
||||
goto out;
|
||||
cf_protocol = cf->next;
|
||||
httpversion = 20;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
infof(data, "CONNECT tunnel: unsupported ALPN(%d) negotiated", alpn);
|
||||
else {
|
||||
failf(data, "CONNECT: negotiated ALPN '%s' not supported", alpn);
|
||||
result = CURLE_COULDNT_CONNECT;
|
||||
goto out;
|
||||
}
|
||||
|
|
@ -391,6 +397,12 @@ CURLcode Curl_cf_http_proxy_query(struct Curl_cfilter *cf,
|
|||
*pres1 = (int)cf->conn->http_proxy.port;
|
||||
*((const char **)pres2) = cf->conn->http_proxy.host.name;
|
||||
return CURLE_OK;
|
||||
case CF_QUERY_ALPN_NEGOTIATED: {
|
||||
const char **palpn = pres2;
|
||||
DEBUGASSERT(palpn);
|
||||
*palpn = NULL;
|
||||
return CURLE_OK;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
17
lib/socks.c
17
lib/socks.c
|
|
@ -1195,15 +1195,22 @@ static CURLcode socks_cf_query(struct Curl_cfilter *cf,
|
|||
{
|
||||
struct socks_state *sx = cf->ctx;
|
||||
|
||||
if(sx) {
|
||||
switch(query) {
|
||||
case CF_QUERY_HOST_PORT:
|
||||
switch(query) {
|
||||
case CF_QUERY_HOST_PORT:
|
||||
if(sx) {
|
||||
*pres1 = sx->remote_port;
|
||||
*((const char **)pres2) = sx->hostname;
|
||||
return CURLE_OK;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case CF_QUERY_ALPN_NEGOTIATED: {
|
||||
const char **palpn = pres2;
|
||||
DEBUGASSERT(palpn);
|
||||
*palpn = NULL;
|
||||
return CURLE_OK;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return cf->next ?
|
||||
cf->next->cft->query(cf->next, data, query, pres1, pres2) :
|
||||
|
|
|
|||
18
lib/url.c
18
lib/url.c
|
|
@ -1400,17 +1400,15 @@ void Curl_verboseconnect(struct Curl_easy *data,
|
|||
conn->primary.remote_port);
|
||||
#ifndef CURL_DISABLE_HTTP
|
||||
if(conn->handler->protocol & PROTO_FAMILY_HTTP) {
|
||||
switch(conn->alpn) {
|
||||
case CURL_HTTP_VERSION_3:
|
||||
infof(data, "using HTTP/3");
|
||||
break;
|
||||
case CURL_HTTP_VERSION_2:
|
||||
infof(data, "using HTTP/2");
|
||||
break;
|
||||
default:
|
||||
const char *alpn = Curl_conn_get_alpn_negotiated(data, conn);
|
||||
if(!alpn || !strcmp("http/1.1", alpn) || !strcmp("http/1.0", alpn))
|
||||
infof(data, "using HTTP/1.x");
|
||||
break;
|
||||
}
|
||||
else if(!strcmp("h2", alpn))
|
||||
infof(data, "using HTTP/2");
|
||||
else if(!strcmp("h3", alpn))
|
||||
infof(data, "using HTTP/3");
|
||||
else
|
||||
infof(data, "using ALPN protocol '%s'", alpn);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
|||
|
|
@ -780,11 +780,6 @@ struct connectdata {
|
|||
unsigned short localport;
|
||||
unsigned short secondary_port; /* secondary socket remote port to connect to
|
||||
(ftp) */
|
||||
unsigned char alpn; /* APLN TLS negotiated protocol, a CURL_HTTP_VERSION*
|
||||
value */
|
||||
#ifndef CURL_DISABLE_PROXY
|
||||
unsigned char proxy_alpn; /* APLN of proxy tunnel, CURL_HTTP_VERSION* */
|
||||
#endif
|
||||
unsigned char transport_wanted; /* one of the TRNSPRT_* defines. Not
|
||||
necessarily the transport the connection ends using due to Alt-Svc
|
||||
and happy eyeballing. Use `Curl_conn_get_transport() for actual value
|
||||
|
|
|
|||
|
|
@ -2544,7 +2544,6 @@ static CURLcode cf_ngtcp2_connect(struct Curl_cfilter *cf,
|
|||
if(result)
|
||||
goto out;
|
||||
if(cf->connected) {
|
||||
cf->conn->alpn = CURL_HTTP_VERSION_3;
|
||||
*done = TRUE;
|
||||
goto out;
|
||||
}
|
||||
|
|
@ -2566,7 +2565,6 @@ static CURLcode cf_ngtcp2_connect(struct Curl_cfilter *cf,
|
|||
if(!result) {
|
||||
CURL_TRC_CF(data, cf, "peer verified");
|
||||
cf->connected = TRUE;
|
||||
cf->conn->alpn = CURL_HTTP_VERSION_3;
|
||||
*done = TRUE;
|
||||
connkeep(cf->conn, "HTTP/3 default");
|
||||
}
|
||||
|
|
@ -2665,6 +2663,12 @@ static CURLcode cf_ngtcp2_query(struct Curl_cfilter *cf,
|
|||
return CURLE_OK;
|
||||
break;
|
||||
}
|
||||
case CF_QUERY_ALPN_NEGOTIATED: {
|
||||
const char **palpn = pres2;
|
||||
DEBUGASSERT(palpn);
|
||||
*palpn = cf->connected ? "h3" : NULL;
|
||||
return CURLE_OK;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1802,7 +1802,6 @@ static CURLcode cf_osslq_connect(struct Curl_cfilter *cf,
|
|||
if(!result) {
|
||||
CURL_TRC_CF(data, cf, "peer verified");
|
||||
cf->connected = TRUE;
|
||||
cf->conn->alpn = CURL_HTTP_VERSION_3;
|
||||
*done = TRUE;
|
||||
connkeep(cf->conn, "HTTP/3 default");
|
||||
}
|
||||
|
|
@ -2357,6 +2356,12 @@ static CURLcode cf_osslq_query(struct Curl_cfilter *cf,
|
|||
return CURLE_OK;
|
||||
break;
|
||||
}
|
||||
case CF_QUERY_ALPN_NEGOTIATED: {
|
||||
const char **palpn = pres2;
|
||||
DEBUGASSERT(palpn);
|
||||
*palpn = cf->connected ? "h3" : NULL;
|
||||
return CURLE_OK;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1416,7 +1416,6 @@ static CURLcode cf_quiche_connect(struct Curl_cfilter *cf,
|
|||
goto out;
|
||||
}
|
||||
cf->connected = TRUE;
|
||||
cf->conn->alpn = CURL_HTTP_VERSION_3;
|
||||
*done = TRUE;
|
||||
connkeep(cf->conn, "HTTP/3 default");
|
||||
}
|
||||
|
|
@ -1556,6 +1555,12 @@ static CURLcode cf_quiche_query(struct Curl_cfilter *cf,
|
|||
return CURLE_OK;
|
||||
break;
|
||||
}
|
||||
case CF_QUERY_ALPN_NEGOTIATED: {
|
||||
const char **palpn = pres2;
|
||||
DEBUGASSERT(palpn);
|
||||
*palpn = cf->connected ? "h3" : NULL;
|
||||
return CURLE_OK;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1591,19 +1591,18 @@ schannel_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data)
|
|||
|
||||
if(alpn_result.ProtoNegoStatus ==
|
||||
SecApplicationProtocolNegotiationStatus_Success) {
|
||||
unsigned char prev_alpn = cf->conn->alpn;
|
||||
|
||||
if(backend->recv_renegotiating &&
|
||||
connssl->negotiated.alpn &&
|
||||
strncmp(connssl->negotiated.alpn,
|
||||
(const char *)alpn_result.ProtocolId,
|
||||
alpn_result.ProtocolIdSize)) {
|
||||
/* Renegotiation selected a different protocol now, we cannot
|
||||
* deal with this */
|
||||
failf(data, "schannel: server selected an ALPN protocol too late");
|
||||
return CURLE_SSL_CONNECT_ERROR;
|
||||
}
|
||||
Curl_alpn_set_negotiated(cf, data, connssl, alpn_result.ProtocolId,
|
||||
alpn_result.ProtocolIdSize);
|
||||
if(backend->recv_renegotiating) {
|
||||
if(prev_alpn != cf->conn->alpn &&
|
||||
prev_alpn != CURL_HTTP_VERSION_NONE) {
|
||||
/* Renegotiation selected a different protocol now, we cannot
|
||||
* deal with this */
|
||||
failf(data, "schannel: server selected an ALPN protocol too late");
|
||||
return CURLE_SSL_CONNECT_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(!backend->recv_renegotiating)
|
||||
|
|
|
|||
|
|
@ -1571,15 +1571,24 @@ static CURLcode ssl_cf_query(struct Curl_cfilter *cf,
|
|||
return CURLE_OK;
|
||||
}
|
||||
case CF_QUERY_SSL_INFO:
|
||||
case CF_QUERY_SSL_CTX_INFO: {
|
||||
struct curl_tlssessioninfo *info = pres2;
|
||||
struct cf_call_data save;
|
||||
CF_DATA_SAVE(save, cf, data);
|
||||
info->backend = Curl_ssl_backend();
|
||||
info->internals = connssl->ssl_impl->get_internals(
|
||||
cf->ctx, (query == CF_QUERY_SSL_INFO) ?
|
||||
CURLINFO_TLS_SSL_PTR : CURLINFO_TLS_SESSION);
|
||||
CF_DATA_RESTORE(cf, save);
|
||||
case CF_QUERY_SSL_CTX_INFO:
|
||||
if(!Curl_ssl_cf_is_proxy(cf)) {
|
||||
struct curl_tlssessioninfo *info = pres2;
|
||||
struct cf_call_data save;
|
||||
CF_DATA_SAVE(save, cf, data);
|
||||
info->backend = Curl_ssl_backend();
|
||||
info->internals = connssl->ssl_impl->get_internals(
|
||||
cf->ctx, (query == CF_QUERY_SSL_INFO) ?
|
||||
CURLINFO_TLS_SSL_PTR : CURLINFO_TLS_SESSION);
|
||||
CF_DATA_RESTORE(cf, save);
|
||||
return CURLE_OK;
|
||||
}
|
||||
break;
|
||||
case CF_QUERY_ALPN_NEGOTIATED: {
|
||||
const char **palpn = pres2;
|
||||
DEBUGASSERT(palpn);
|
||||
*palpn = connssl->negotiated.alpn;
|
||||
CURL_TRC_CF(data, cf, "query ALPN: returning '%s'", *palpn);
|
||||
return CURLE_OK;
|
||||
}
|
||||
default:
|
||||
|
|
@ -1636,7 +1645,7 @@ struct Curl_cftype Curl_cft_ssl_proxy = {
|
|||
Curl_cf_def_cntrl,
|
||||
cf_ssl_is_alive,
|
||||
Curl_cf_def_conn_keep_alive,
|
||||
Curl_cf_def_query,
|
||||
ssl_cf_query,
|
||||
};
|
||||
|
||||
#endif /* !CURL_DISABLE_PROXY */
|
||||
|
|
@ -1708,9 +1717,11 @@ static CURLcode cf_ssl_proxy_create(struct Curl_cfilter **pcf,
|
|||
struct Curl_cfilter *cf = NULL;
|
||||
struct ssl_connect_data *ctx;
|
||||
CURLcode result;
|
||||
bool use_alpn = conn->bits.tls_enable_alpn;
|
||||
/* ALPN is default, but if user explicitly disables it, obey */
|
||||
bool use_alpn = data->set.ssl_enable_alpn;
|
||||
http_majors allowed = CURL_HTTP_V1x;
|
||||
|
||||
(void)conn;
|
||||
#ifdef USE_HTTP2
|
||||
if(conn->http_proxy.proxytype == CURLPROXY_HTTPS2) {
|
||||
use_alpn = TRUE;
|
||||
|
|
@ -1939,14 +1950,7 @@ CURLcode Curl_alpn_set_negotiated(struct Curl_cfilter *cf,
|
|||
size_t proto_len)
|
||||
{
|
||||
CURLcode result = CURLE_OK;
|
||||
unsigned char *palpn =
|
||||
#ifndef CURL_DISABLE_PROXY
|
||||
(cf->conn->bits.tunnel_proxy && Curl_ssl_cf_is_proxy(cf)) ?
|
||||
&cf->conn->proxy_alpn : &cf->conn->alpn
|
||||
#else
|
||||
&cf->conn->alpn
|
||||
#endif
|
||||
;
|
||||
(void)cf;
|
||||
|
||||
if(connssl->negotiated.alpn) {
|
||||
/* When we ask for a specific ALPN protocol, we need the confirmation
|
||||
|
|
@ -1988,38 +1992,12 @@ CURLcode Curl_alpn_set_negotiated(struct Curl_cfilter *cf,
|
|||
}
|
||||
|
||||
if(proto && proto_len) {
|
||||
if(proto_len == ALPN_HTTP_1_1_LENGTH &&
|
||||
!memcmp(ALPN_HTTP_1_1, proto, ALPN_HTTP_1_1_LENGTH)) {
|
||||
*palpn = CURL_HTTP_VERSION_1_1;
|
||||
}
|
||||
#ifdef USE_HTTP2
|
||||
else if(proto_len == ALPN_H2_LENGTH &&
|
||||
!memcmp(ALPN_H2, proto, ALPN_H2_LENGTH)) {
|
||||
*palpn = CURL_HTTP_VERSION_2;
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_HTTP3
|
||||
else if(proto_len == ALPN_H3_LENGTH &&
|
||||
!memcmp(ALPN_H3, proto, ALPN_H3_LENGTH)) {
|
||||
*palpn = CURL_HTTP_VERSION_3;
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
*palpn = CURL_HTTP_VERSION_NONE;
|
||||
failf(data, "unsupported ALPN protocol: '%.*s'", (int)proto_len, proto);
|
||||
/* Previous code just ignored it and some vtls backends even ignore the
|
||||
* return code of this function. */
|
||||
/* return CURLE_NOT_BUILT_IN; */
|
||||
goto out;
|
||||
}
|
||||
|
||||
if(connssl->state == ssl_connection_deferred)
|
||||
infof(data, VTLS_INFOF_ALPN_DEFERRED, (int)proto_len, proto);
|
||||
else
|
||||
infof(data, VTLS_INFOF_ALPN_ACCEPTED, (int)proto_len, proto);
|
||||
}
|
||||
else {
|
||||
*palpn = CURL_HTTP_VERSION_NONE;
|
||||
if(connssl->state == ssl_connection_deferred)
|
||||
infof(data, VTLS_INFOF_NO_ALPN_DEFERRED);
|
||||
else
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ class TestProxy:
|
|||
|
||||
def get_tunnel_proto_used(self, r: ExecResult):
|
||||
for line in r.trace_lines:
|
||||
m = re.match(r'.* CONNECT tunnel: (\S+) negotiated$', line)
|
||||
m = re.match(r'.* CONNECT: \'(\S+)\' negotiated$', line)
|
||||
if m:
|
||||
return m.group(1)
|
||||
assert False, f'tunnel protocol not found in:\n{"".join(r.trace_lines)}'
|
||||
|
|
@ -161,8 +161,7 @@ class TestProxy:
|
|||
extra_args=xargs)
|
||||
r.check_response(count=1, http_status=200,
|
||||
protocol='HTTP/2' if proto == 'h2' else 'HTTP/1.1')
|
||||
assert self.get_tunnel_proto_used(r) == 'HTTP/2' \
|
||||
if tunnel == 'h2' else 'HTTP/1.1'
|
||||
assert self.get_tunnel_proto_used(r) == tunnel
|
||||
srcfile = os.path.join(httpd.docs_dir, 'data.json')
|
||||
dfile = curl.download_file(0)
|
||||
assert filecmp.cmp(srcfile, dfile, shallow=False)
|
||||
|
|
@ -193,8 +192,7 @@ class TestProxy:
|
|||
extra_args=xargs)
|
||||
r.check_response(count=count, http_status=200,
|
||||
protocol='HTTP/2' if proto == 'h2' else 'HTTP/1.1')
|
||||
assert self.get_tunnel_proto_used(r) == 'HTTP/2' \
|
||||
if tunnel == 'h2' else 'HTTP/1.1'
|
||||
assert self.get_tunnel_proto_used(r) == tunnel
|
||||
srcfile = os.path.join(httpd.docs_dir, fname)
|
||||
for i in range(count):
|
||||
dfile = curl.download_file(i)
|
||||
|
|
@ -226,8 +224,7 @@ class TestProxy:
|
|||
xargs = curl.get_proxy_args(tunnel=True, proto=tunnel)
|
||||
r = curl.http_upload(urls=[url], data=f'@{srcfile}', alpn_proto=proto,
|
||||
extra_args=xargs)
|
||||
assert self.get_tunnel_proto_used(r) == 'HTTP/2' \
|
||||
if tunnel == 'h2' else 'HTTP/1.1'
|
||||
assert self.get_tunnel_proto_used(r) == tunnel
|
||||
r.check_response(count=count, http_status=200)
|
||||
indata = open(srcfile).readlines()
|
||||
for i in range(count):
|
||||
|
|
@ -249,8 +246,7 @@ class TestProxy:
|
|||
r = curl.http_download(urls=[url1, url2], alpn_proto='http/1.1', with_stats=True,
|
||||
extra_args=xargs)
|
||||
r.check_response(count=2, http_status=200)
|
||||
assert self.get_tunnel_proto_used(r) == 'HTTP/2' \
|
||||
if tunnel == 'h2' else 'HTTP/1.1'
|
||||
assert self.get_tunnel_proto_used(r) == tunnel
|
||||
if tunnel == 'h2':
|
||||
# TODO: we would like to reuse the first connection for the
|
||||
# second URL, but this is currently not possible
|
||||
|
|
@ -276,8 +272,7 @@ class TestProxy:
|
|||
r1 = curl.http_download(urls=[url], alpn_proto='http/1.1', with_stats=True,
|
||||
extra_args=proxy_args)
|
||||
r1.check_response(count=1, http_status=200)
|
||||
assert self.get_tunnel_proto_used(r1) == 'HTTP/2' \
|
||||
if tunnel == 'h2' else 'HTTP/1.1'
|
||||
assert self.get_tunnel_proto_used(r1) == tunnel
|
||||
# get the args, duplicate separated with '--next'
|
||||
x2_args = r1.args[1:]
|
||||
x2_args.append('--next')
|
||||
|
|
@ -302,8 +297,7 @@ class TestProxy:
|
|||
r1 = curl.http_download(urls=[url], alpn_proto='http/1.1', with_stats=True,
|
||||
extra_args=proxy_args)
|
||||
r1.check_response(count=1, http_status=200)
|
||||
assert self.get_tunnel_proto_used(r1) == 'HTTP/2' \
|
||||
if tunnel == 'h2' else 'HTTP/1.1'
|
||||
assert self.get_tunnel_proto_used(r1) == tunnel
|
||||
# get the args, duplicate separated with '--next'
|
||||
x2_args = r1.args[1:]
|
||||
x2_args.append('--next')
|
||||
|
|
@ -329,8 +323,7 @@ class TestProxy:
|
|||
r1 = curl.http_download(urls=[url], alpn_proto='http/1.1', with_stats=True,
|
||||
extra_args=proxy_args)
|
||||
r1.check_response(count=1, http_status=200)
|
||||
assert self.get_tunnel_proto_used(r1) == 'HTTP/2' \
|
||||
if tunnel == 'h2' else 'HTTP/1.1'
|
||||
assert self.get_tunnel_proto_used(r1) == tunnel
|
||||
# get the args, duplicate separated with '--next'
|
||||
x2_args = r1.args[1:]
|
||||
x2_args.append('--next')
|
||||
|
|
@ -356,8 +349,7 @@ class TestProxy:
|
|||
r1 = curl.http_download(urls=[url], alpn_proto='http/1.1', with_stats=True,
|
||||
extra_args=proxy_args)
|
||||
r1.check_response(count=1, http_status=200)
|
||||
assert self.get_tunnel_proto_used(r1) == 'HTTP/2' \
|
||||
if tunnel == 'h2' else 'HTTP/1.1'
|
||||
assert self.get_tunnel_proto_used(r1) == tunnel
|
||||
# get the args, duplicate separated with '--next'
|
||||
x2_args = r1.args[1:]
|
||||
x2_args.append('--next')
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ class TestProxyAuth:
|
|||
|
||||
def get_tunnel_proto_used(self, r: ExecResult):
|
||||
for line in r.trace_lines:
|
||||
m = re.match(r'.* CONNECT tunnel: (\S+) negotiated$', line)
|
||||
m = re.match(r'.* CONNECT: \'(\S+)\' negotiated$', line)
|
||||
if m:
|
||||
return m.group(1)
|
||||
assert False, f'tunnel protocol not found in:\n{"".join(r.trace_lines)}'
|
||||
|
|
@ -133,8 +133,7 @@ class TestProxyAuth:
|
|||
extra_args=xargs)
|
||||
# expect "COULD_NOT_CONNECT"
|
||||
r.check_response(exitcode=56, http_status=None)
|
||||
assert self.get_tunnel_proto_used(r) == 'HTTP/2' \
|
||||
if tunnel == 'h2' else 'HTTP/1.1'
|
||||
assert self.get_tunnel_proto_used(r) == tunnel
|
||||
|
||||
@pytest.mark.skipif(condition=not Env.have_nghttpx(), reason="no nghttpx available")
|
||||
@pytest.mark.skipif(condition=not Env.curl_has_feature('HTTPS-proxy'),
|
||||
|
|
@ -154,8 +153,7 @@ class TestProxyAuth:
|
|||
extra_args=xargs)
|
||||
r.check_response(count=1, http_status=200,
|
||||
protocol='HTTP/2' if proto == 'h2' else 'HTTP/1.1')
|
||||
assert self.get_tunnel_proto_used(r) == 'HTTP/2' \
|
||||
if tunnel == 'h2' else 'HTTP/1.1'
|
||||
assert self.get_tunnel_proto_used(r) == tunnel
|
||||
|
||||
@pytest.mark.skipif(condition=not Env.curl_has_feature('SPNEGO'),
|
||||
reason='curl lacks SPNEGO support')
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue