Curl_resolv_unlink(data, &data->state.dns[sockindex]);
return result;
}
+
+void Curl_conn_set_multiplex(struct connectdata *conn)
+{
+ if(!conn->bits.multiplex) {
+ conn->bits.multiplex = TRUE;
+ if(conn->attached_multi) {
+ Curl_multi_connchanged(conn->attached_multi);
+ }
+ }
+}
struct Curl_dns_entry *dns,
int ssl_mode);
+/* Set conn to allow multiplexing. */
+void Curl_conn_set_multiplex(struct connectdata *conn);
+
extern struct Curl_cftype Curl_cft_setup;
#endif /* HEADER_CURL_CONNECT_H */
/* disables SMTP */
#cmakedefine CURL_DISABLE_SMTP 1
-/* disabled WebSockets */
+/* disabled WebSocket */
#cmakedefine CURL_DISABLE_WEBSOCKETS 1
/* disables use of socketpair for curl_multi_poll */
*announced_exp100 = FALSE;
/* Avoid Expect: 100-continue if Upgrade: is used */
- if(data->req.upgr101 != UPGR101_INIT)
+ if(data->req.upgr101 != UPGR101_NONE)
return CURLE_OK;
/* For really small puts we do not use Expect: headers at all, and for
info_version = "HTTP/2";
/* There is no ALPN here, but the connection is now definitely h2 */
conn->httpversion_seen = 20;
+ Curl_conn_set_multiplex(conn);
}
else
info_version = "HTTP/1.x";
infof(data, "HTTP 1.0, assume close after body");
connclose(conn, "HTTP/1.0 close after body");
}
- else if(k->httpversion == 20 ||
- (k->upgr101 == UPGR101_H2 && k->httpcode == 101)) {
- DEBUGF(infof(data, "HTTP/2 found, allow multiplexing"));
- }
k->http_bodyless = k->httpcode >= 100 && k->httpcode < 200;
switch(k->httpcode) {
struct connectdata *conn = data->conn;
CURLcode result = CURLE_OK;
struct SingleRequest *k = &data->req;
+ bool conn_changed = FALSE;
(void)buf; /* not used without HTTP2 enabled */
*pconsumed = 0;
*/
http_exp100_got100(data);
break;
- case 101:
- /* Switching Protocols only allowed from HTTP/1.1 */
+ case 101: {
+ int upgr101_requested = k->upgr101;
+
if(k->httpversion_sent != 11) {
/* invalid for other HTTP versions */
- failf(data, "unexpected 101 response code");
+ failf(data, "server sent 101 response while not talking HTTP/1.1");
result = CURLE_WEIRD_SERVER_REPLY;
goto out;
}
- if(k->upgr101 == UPGR101_H2) {
- /* Switching to HTTP/2, where we will get more responses */
+
+ /* Whatever the success, upgrade was selected. */
+ k->upgr101 = UPGR101_RECEIVED;
+ data->conn->bits.upgrade_in_progress = FALSE;
+ conn_changed = TRUE;
+
+ /* To be fully conform, we would check the "Upgrade:" response header
+ * to mention the protocol we requested. */
+ switch(upgr101_requested) {
+ case UPGR101_H2:
+ /* Switch to HTTP/2, where we will get more responses.
+ * blen bytes in bug are already h2 protocol bytes */
infof(data, "Received 101, Switching to HTTP/2");
- k->upgr101 = UPGR101_RECEIVED;
- data->conn->bits.asks_multiplex = FALSE;
- /* We expect more response from HTTP/2 later */
- k->header = TRUE;
- k->headerline = 0; /* restart the header line counter */
- k->httpversion_sent = 20; /* It's an HTTP/2 request now */
- /* Any remaining `buf` bytes are already HTTP/2 and passed to
- * be processed. */
result = Curl_http2_upgrade(data, conn, FIRSTSOCKET, buf, blen);
if(result)
goto out;
*pconsumed += blen;
- }
+ break;
#ifndef CURL_DISABLE_WEBSOCKETS
- else if(k->upgr101 == UPGR101_WS) {
- /* verify the response. Any passed `buf` bytes are already in
- * WebSockets format and taken in by the protocol handler. */
+ case UPGR101_WS:
+ /* Switch to WebSocket, where we now stream ws frames.
+ * blen bytes in bug are already ws protocol bytes */
+ infof(data, "Received 101, Switching to WebSocket");
result = Curl_ws_accept(data, buf, blen);
if(result)
goto out;
*pconsumed += blen; /* ws accept handled the data */
- }
+ break;
#endif
- else {
+ default:
/* We silently accept this as the final response. What are we
* switching to if we did not ask for an Upgrade? Maybe the
* application provided an `Upgrade: xxx` header? */
k->header = FALSE;
+ break;
}
+ /* processed 101 */
break;
+ }
default:
/* The server may send us other 1xx responses, like informative
* 103. This have no influence on request processing and we expect
/* k->httpcode >= 200, final response */
k->header = FALSE;
-
- if(k->upgr101 == UPGR101_H2) {
- /* A requested upgrade was denied, poke the multi handle to possibly
- allow a pending pipewait to continue */
- data->conn->bits.asks_multiplex = FALSE;
- Curl_multi_connchanged(data->multi);
+ if(data->conn->bits.upgrade_in_progress) {
+ /* Asked for protocol upgrade, but it was not selected by the server */
+ data->conn->bits.upgrade_in_progress = FALSE;
+ conn_changed = TRUE;
}
if((k->size == -1) && !k->chunk && !conn->bits.close &&
#endif
#ifndef CURL_DISABLE_WEBSOCKETS
- /* All >=200 HTTP status codes are errors when wanting WebSockets */
+ /* All >=200 HTTP status codes are errors when wanting WebSocket */
if(data->req.upgr101 == UPGR101_WS) {
- failf(data, "Refused WebSockets upgrade: %d", k->httpcode);
+ failf(data, "Refused WebSocket upgrade: %d", k->httpcode);
result = CURLE_HTTP_RETURNED_ERROR;
goto out;
}
result = Curl_1st_err(
result, http_write_header(data, last_hd, last_hd_len));
}
+ if(conn_changed) {
+ /* poke the multi handle to allow any pending pipewait to retry now */
+ Curl_multi_connchanged(data->multi);
+ }
return result;
}
free(base64);
k->upgr101 = UPGR101_H2;
- data->conn->bits.asks_multiplex = TRUE;
+ data->conn->bits.upgrade_in_progress = TRUE;
return result;
}
case CF_CTRL_DATA_DONE:
http2_data_done(cf, data);
break;
+ case CF_CTRL_CONN_INFO_UPDATE:
+ if(!cf->sockindex && cf->connected) {
+ cf->conn->httpversion_seen = 20;
+ Curl_conn_set_multiplex(cf->conn);
+ }
+ break;
default:
break;
}
return result;
CURL_TRC_CF(data, cf, "switching connection to HTTP/2");
- data->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
- Curl_multi_connchanged(data->multi);
-
if(cf->next) {
bool done;
return Curl_conn_cf_connect(cf, data, &done);
return result;
cf_h2 = cf->next;
- cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
- Curl_multi_connchanged(data->multi);
if(cf_h2->next) {
bool done;
CURLcode result;
DEBUGASSERT(Curl_conn_http_version(data, conn) < 20);
- DEBUGASSERT(data->req.upgr101 == UPGR101_RECEIVED);
result = http2_cfilter_add(&cf, data, conn, sockindex, TRUE);
if(result)
DEBUGASSERT(cf->cft == &Curl_cft_nghttp2);
ctx = cf->ctx;
+ data->req.httpversion_sent = 20; /* it is an h2 request now */
+ data->req.header = TRUE; /* we expect the real response to come in h2 */
+ data->req.headerline = 0; /* restart the header line counter */
+
if(nread > 0) {
/* Remaining data from the protocol switch reply is already using
* the switched protocol, ie. HTTP/2. We add that to the network
" after upgrade: len=%zu", nread);
}
- conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
- Curl_multi_connchanged(data->multi);
-
if(cf->next) {
bool done;
- return Curl_conn_cf_connect(cf, data, &done);
+ result = Curl_conn_cf_connect(cf, data, &done);
+ if(!result)
+ cf->cft->cntrl(cf, data, CF_CTRL_CONN_INFO_UPDATE, 0, NULL);
}
- return CURLE_OK;
+ return result;
}
/* Only call this function for a transfer that already got an HTTP/2
req->offset = 0;
req->httpcode = 0;
req->keepon = 0;
- req->upgr101 = UPGR101_INIT;
+ req->upgr101 = UPGR101_NONE;
req->sendbuf_hds_len = 0;
req->timeofdoc = 0;
req->location = NULL;
};
enum upgrade101 {
- UPGR101_INIT, /* default state */
- UPGR101_WS, /* upgrade to WebSockets requested */
+ UPGR101_NONE, /* default state */
+ UPGR101_WS, /* upgrade to WebSocket requested */
UPGR101_H2, /* upgrade to HTTP/2 requested */
- UPGR101_RECEIVED, /* 101 response received */
- UPGR101_WORKING /* talking upgraded protocol */
+ UPGR101_RECEIVED /* 101 response received */
};
struct url_conn_match *m)
{
if(!Curl_conn_is_connected(conn, FIRSTSOCKET) ||
- conn->bits.asks_multiplex) {
- /* Not yet connected, or not yet decided if it multiplexes. The later
+ conn->bits.upgrade_in_progress) {
+ /* Not yet connected, or a protocol upgrade is in progress. The later
* happens for HTTP/2 Upgrade: requests that need a response. */
if(m->may_multiplex) {
m->seen_pending_conn = TRUE;
if(!url_match_connect_config(conn, m))
return FALSE;
+ /* match for destination and protocol? */
if(!url_match_destination(conn, m))
return FALSE;
#endif
BIT(bound); /* set true if bind() has already been done on this socket/
connection */
- BIT(asks_multiplex); /* connection asks for multiplexing, but is not yet */
+ BIT(upgrade_in_progress); /* protocol upgrade is in progress */
BIT(multiplex); /* connection is multiplexed */
BIT(tcp_fastopen); /* use TCP Fast Open */
BIT(tls_enable_alpn); /* TLS ALPN extension? */
ctx->handshake_at = curlx_now();
ctx->tls_handshake_complete = TRUE;
- cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
Curl_vquic_report_handshake(&ctx->tls, cf, data);
ctx->tls_vrfy_result = Curl_vquic_tls_verify_peer(&ctx->tls, cf,
break;
}
case CF_CTRL_CONN_INFO_UPDATE:
- if(!cf->sockindex && cf->connected)
+ if(!cf->sockindex && cf->connected) {
cf->conn->httpversion_seen = 30;
+ Curl_conn_set_multiplex(cf->conn);
+ }
break;
default:
break;
struct Curl_easy *data)
{
struct cf_osslq_ctx *ctx = cf->ctx;
-
- cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
-
return Curl_vquic_tls_verify_peer(&ctx->tls, cf, data, &ctx->peer);
}
break;
}
case CF_CTRL_CONN_INFO_UPDATE:
- if(!cf->sockindex && cf->connected)
+ if(!cf->sockindex && cf->connected) {
cf->conn->httpversion_seen = 30;
+ Curl_conn_set_multiplex(cf->conn);
+ }
break;
default:
break;
break;
}
case CF_CTRL_CONN_INFO_UPDATE:
- if(!cf->sockindex && cf->connected)
+ if(!cf->sockindex && cf->connected) {
cf->conn->httpversion_seen = 30;
+ Curl_conn_set_multiplex(cf->conn);
+ }
break;
default:
break;
struct Curl_easy *data)
{
struct cf_quiche_ctx *ctx = cf->ctx;
-
- cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
-
return Curl_vquic_tls_verify_peer(&ctx->tls, cf, data, &ctx->peer);
}
ws->recvframe.bytesleft = bytesleft;
}
-/* WebSockets decoding client writer */
+/* WebSocket decoding client writer */
struct ws_cw_ctx {
struct Curl_cwriter super;
struct bufq buf;
}
data->state.http_hd_upgrade = TRUE;
k->upgr101 = UPGR101_WS;
+ data->conn->bits.upgrade_in_progress = TRUE;
return result;
}
goto out;
ws_dec_writer = NULL; /* owned by transfer now */
+ k->header = FALSE; /* we will not get more response headers */
+
if(data->set.connect_only) {
size_t nwritten;
/* In CONNECT_ONLY setup, the payloads from `mem` need to be received
static CURLcode ws_setup_conn(struct Curl_easy *data,
struct connectdata *conn)
{
- /* WebSockets is 1.1 only (for now) */
+ /* WebSocket is 1.1 only (for now) */
data->state.http_neg.accept_09 = FALSE;
data->state.http_neg.only_10 = FALSE;
data->state.http_neg.wanted = CURL_HTTP_V1x;