diff --git a/lib/core-net/client/connect.c b/lib/core-net/client/connect.c index 1e18cbbe6..2298fa21b 100644 --- a/lib/core-net/client/connect.c +++ b/lib/core-net/client/connect.c @@ -60,7 +60,7 @@ lws_http_client_connect_via_info2(struct lws *wsi) for (n = 0; n < (int)LWS_ARRAY_SIZE(hnames); n++) if (hnames[n] && stash->cis[n] && lws_hdr_simple_create(wsi, hnames[n], stash->cis[n])) - goto bail1; + goto bail; #if defined(LWS_WITH_SOCKS5) if (!wsi->a.vhost->socks_proxy_port) @@ -70,15 +70,58 @@ lws_http_client_connect_via_info2(struct lws *wsi) no_ah: return lws_client_connect_2_dnsreq(wsi); -bail1: +bail: #if defined(LWS_WITH_SOCKS5) if (!wsi->a.vhost->socks_proxy_port) lws_free_set_NULL(wsi->stash); #endif + lws_free_set_NULL(wsi->stash); + return NULL; } +int +lws_client_stash_create(struct lws *wsi, const char **cisin) +{ + size_t size; + char *pc; + int n; + + size = sizeof(*wsi->stash); + + /* + * Let's overallocate the stash object with space for all the args + * in one hit. + */ + for (n = 0; n < CIS_COUNT; n++) + if (cisin[n]) + size += strlen(cisin[n]) + 1; + + if (wsi->stash) + lws_free_set_NULL(wsi->stash); + + wsi->stash = lws_malloc(size, "client stash"); + if (!wsi->stash) + return 1; + + /* all the pointers default to NULL, but no need to zero the args */ + memset(wsi->stash, 0, sizeof(*wsi->stash)); + + pc = (char *)&wsi->stash[1]; + + for (n = 0; n < CIS_COUNT; n++) + if (cisin[n]) { + size_t mm; + wsi->stash->cis[n] = pc; + mm = strlen(cisin[n]) + 1; + memcpy(pc, cisin[n], mm); + pc += mm; + } + + return 0; +} + struct lws * lws_client_connect_via_info(const struct lws_client_connect_info *i) { @@ -86,10 +129,8 @@ lws_client_connect_via_info(const struct lws_client_connect_info *i) struct lws *wsi, *safe = NULL; const struct lws_protocols *p; const char *cisin[CIS_COUNT]; - struct lws_vhost *vh, *v; - size_t size; - int n, tsi; - char *pc; + struct lws_vhost *vh; + int tsi; if (i->context->requested_stop_internal_loops) return NULL; @@ -118,11 +159,10 @@ lws_client_connect_via_info(const struct lws_client_connect_info *i) wsi = __lws_wsi_create_with_role(i->context, tsi, NULL); lws_context_unlock(i->context); if (wsi == NULL) - goto bail; + return NULL; vh = i->vhost; if (!vh) { - #if defined(LWS_WITH_TLS_JIT_TRUST) if (lws_tls_jit_trust_vhost_bind(i->context, i->address, &vh)) #endif @@ -199,23 +239,11 @@ lws_client_connect_via_info(const struct lws_client_connect_info *i) if (i->ssl_connection & LCCSCF_WAKE_SUSPEND__VALIDITY) wsi->conn_validity_wakesuspend = 1; - if (!i->vhost) { - v = i->context->vhost_list; - - if (!v) { /* coverity */ - lwsl_err("%s: no vhost\n", __func__); - goto bail; - } - if (!strcmp(v->name, "system")) - v = v->vhost_next; - } else - v = i->vhost; - - lws_vhost_bind_wsi(v, wsi); + lws_vhost_bind_wsi(vh, wsi); #if defined(LWS_WITH_SYS_FAULT_INJECTION) /* additionally inerit from vhost we bound to */ - lws_fi_inherit_copy(&wsi->fic, &v->fic, "wsi", i->fi_wsi_name); + lws_fi_inherit_copy(&wsi->fic, &vh->fic, "wsi", i->fi_wsi_name); #endif if (!wsi->a.vhost) { @@ -324,23 +352,13 @@ lws_client_connect_via_info(const struct lws_client_connect_info *i) cisin[CIS_IFACE] = i->iface; cisin[CIS_ALPN] = i->alpn; - size = sizeof(*wsi->stash); + if (lws_client_stash_create(wsi, cisin)) + goto bail; - /* - * Let's overallocate the stash object with space for all the args - * in one hit. - */ - for (n = 0; n < CIS_COUNT; n++) - if (cisin[n]) - size += strlen(cisin[n]) + 1; - - wsi->stash = lws_malloc(size, "client stash"); - if (!wsi->stash) { - lwsl_err("%s: OOM\n", __func__); - goto bail1; - } - /* all the pointers default to NULL, but no need to zero the args */ - memset(wsi->stash, 0, sizeof(*wsi->stash)); +#if defined(LWS_WITH_TLS) + if (i->alpn) + lws_strncpy(wsi->alpn, i->alpn, sizeof(wsi->alpn)); +#endif wsi->a.opaque_user_data = wsi->stash->opaque_user_data = i->opaque_user_data; @@ -382,17 +400,6 @@ lws_client_connect_via_info(const struct lws_client_connect_info *i) lws_metrics_tag_wsi_add(wsi, "vh", wsi->a.vhost->name); - pc = (char *)&wsi->stash[1]; - - for (n = 0; n < CIS_COUNT; n++) - if (cisin[n]) { - size_t mm; - wsi->stash->cis[n] = pc; - mm = strlen(cisin[n]) + 1; - memcpy(pc, cisin[n], mm); - pc += mm; - } - /* * at this point user callbacks like * LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER will be interested to @@ -519,10 +526,8 @@ bail3: return NULL; #endif -bail1: - lws_free_set_NULL(wsi->stash); - bail: + lws_free_set_NULL(wsi->stash); lws_fi_destroy(&wsi->fic); lws_free(wsi); #if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) diff --git a/lib/core-net/client/connect2.c b/lib/core-net/client/connect2.c index d08f89818..394283530 100644 --- a/lib/core-net/client/connect2.c +++ b/lib/core-net/client/connect2.c @@ -144,6 +144,26 @@ lws_client_connect_2_dnsreq(struct lws *wsi) return wsi; } + /* + * clients who will create their own fresh connection keep a copy of + * the hostname they originally connected to, in case other connections + * want to use it too + */ + + if (!wsi->cli_hostname_copy) { + if (wsi->stash && wsi->stash->cis[CIS_HOST]) + wsi->cli_hostname_copy = + lws_strdup(wsi->stash->cis[CIS_HOST]); +#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) + else { + char *pa = lws_hdr_simple_ptr(wsi, + _WSI_TOKEN_CLIENT_PEER_ADDRESS); + if (pa) + wsi->cli_hostname_copy = lws_strdup(pa); + } +#endif + } + /* * The first job is figure out if we want to pipeline on or just join * an existing "active connection" to the same place @@ -213,26 +233,6 @@ lws_client_connect_2_dnsreq(struct lws *wsi) solo: - /* - * clients who will create their own fresh connection keep a copy of - * the hostname they originally connected to, in case other connections - * want to use it too - */ - - if (!wsi->cli_hostname_copy) { - if (wsi->stash && wsi->stash->cis[CIS_HOST]) - wsi->cli_hostname_copy = - lws_strdup(wsi->stash->cis[CIS_HOST]); -#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) - else { - char *pa = lws_hdr_simple_ptr(wsi, - _WSI_TOKEN_CLIENT_PEER_ADDRESS); - if (pa) - wsi->cli_hostname_copy = lws_strdup(pa); - } -#endif - } - /* * If we made our own connection, and we're doing a method that can * take a pipeline, we are an "active client connection". diff --git a/lib/core-net/client/connect3.c b/lib/core-net/client/connect3.c index 0f6b901b9..98fff5bac 100644 --- a/lib/core-net/client/connect3.c +++ b/lib/core-net/client/connect3.c @@ -475,7 +475,7 @@ ads_known: errno_copy = 999; #endif - lwsl_debug("%s: connect: errno: %d\n", __func__, errno_copy); + lwsl_debug("%s: connect: fd %d errno: %d\n", __func__, wsi->desc.sockfd, errno_copy); if (errno_copy && errno_copy != LWS_EALREADY && diff --git a/lib/core-net/client/connect4.c b/lib/core-net/client/connect4.c index 89611202f..8e13dc03e 100644 --- a/lib/core-net/client/connect4.c +++ b/lib/core-net/client/connect4.c @@ -1,7 +1,7 @@ /* * libwebsockets - small server side websockets and web server implementation * - * Copyright (C) 2010 - 2020 Andy Green + * Copyright (C) 2010 - 2021 Andy Green * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to @@ -95,7 +95,8 @@ lws_client_connect_4_established(struct lws *wsi, struct lws *wsi_piggyback, goto failed; wsi->c_port = (uint16_t)wsi->a.vhost->http.http_proxy_port; - n = (int)send(wsi->desc.sockfd, (char *)pt->serv_buf, (unsigned int)plen, + n = (int)send(wsi->desc.sockfd, (char *)pt->serv_buf, + (unsigned int)plen, MSG_NOSIGNAL); if (n < 0) { lwsl_debug("ERROR writing to proxy socket\n"); @@ -161,14 +162,18 @@ send_hs: __func__, wsi->lc.gutag, lwsi_state(wsi_piggyback)); } else { lwsl_info("%s: %s: %s %s client created own conn " - "(raw %d) vh %sm st 0x%x\n", - __func__, wsi->lc.gutag, wsi->role_ops->name, - wsi->a.protocol->name, rawish, wsi->a.vhost->name, - lwsi_state(wsi)); + "(raw %d) vh %sm st 0x%x\n", __func__, wsi->lc.gutag, + wsi->role_ops->name, wsi->a.protocol->name, rawish, + wsi->a.vhost->name, lwsi_state(wsi)); /* we are making our own connection */ - if (!rawish) { + if (!rawish +#if defined(LWS_WITH_TLS) + // && (!(wsi->tls.use_ssl & LCCSCF_USE_SSL) || wsi->tls.ssl) +#endif + ) { + if (lwsi_state(wsi) != LRS_H1C_ISSUE_HANDSHAKE2) lwsi_set_state(wsi, LRS_H1C_ISSUE_HANDSHAKE); } else { @@ -183,20 +188,24 @@ send_hs: (wsi->tls.use_ssl & LCCSCF_USE_SSL)) { int result; + //lwsi_set_state(wsi, LRS_WAITING_SSL); + /* * We can retry this... just cook the SSL BIO * the first time */ result = lws_client_create_tls(wsi, &cce, 1); - lwsl_debug("%s: create_tls said %d\n", - __func__, result); switch (result) { case CCTLS_RETURN_DONE: break; case CCTLS_RETURN_RETRY: + lwsl_debug("%s: create_tls RETRY\n", + __func__); return wsi; default: + lwsl_debug("%s: create_tls FAIL\n", + __func__); goto failed; } @@ -207,9 +216,10 @@ send_hs: * LRS_H2_WAITING_TO_SEND_HEADERS already. */ - lwsl_notice("%s: %s: " - "tls established st 0x%x\n", - __func__, wsi->lc.gutag, lwsi_state(wsi)); + lwsl_notice("%s: %s: tls established st 0x%x, " + "client_h2_alpn %d\n", __func__, + wsi->lc.gutag, lwsi_state(wsi), + wsi->client_h2_alpn); if (lwsi_state(wsi) != LRS_H2_WAITING_TO_SEND_HEADERS) @@ -217,7 +227,13 @@ send_hs: LRS_H1C_ISSUE_HANDSHAKE2); lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_CLIENT_HS_SEND, - (int)wsi->a.context->timeout_secs); + (int)wsi->a.context->timeout_secs); +#if 0 + /* ensure pollin enabled */ + if (lws_change_pollfd(wsi, 0, LWS_POLLIN)) + lwsl_notice("%s: unable to set POLLIN\n", + __func__); +#endif goto provoke_service; } @@ -230,7 +246,8 @@ send_hs: if (m) { n = user_callback_handle_rxflow( wsi->a.protocol->callback, wsi, - (enum lws_callback_reasons)m, wsi->user_space, NULL, 0); + (enum lws_callback_reasons)m, + wsi->user_space, NULL, 0); if (n < 0) { lwsl_info("RAW_PROXY_CLI_ADOPT err\n"); goto failed; @@ -240,7 +257,7 @@ send_hs: /* service.c pollout processing wants this */ wsi->hdr_parsing_completed = 1; #if defined(LWS_ROLE_MQTT) - if (!strcmp(meth, "MQTT")) { + if (meth && !strcmp(meth, "MQTT")) { #if defined(LWS_WITH_TLS) if (wsi->tls.use_ssl & LCCSCF_USE_SSL) { lwsi_set_state(wsi, LRS_WAITING_SSL); @@ -320,7 +337,7 @@ provoke_service: failed: lws_inform_client_conn_fail(wsi, (void *)cce, strlen(cce)); - lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "client_connect2"); + lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "client_connect4"); return NULL; } diff --git a/lib/core-net/close.c b/lib/core-net/close.c index 06e1266b3..45c88c9ee 100644 --- a/lib/core-net/close.c +++ b/lib/core-net/close.c @@ -123,6 +123,8 @@ __lws_reset_wsi(struct lws *wsi) #if defined(LWS_WITH_CLIENT) lws_dll2_remove(&wsi->dll2_cli_txn_queue); lws_dll2_remove(&wsi->dll_cli_active_conns); + if (wsi->cli_hostname_copy) + lws_free_set_NULL(wsi->cli_hostname_copy); #endif #if defined(LWS_WITH_SYS_ASYNC_DNS) @@ -143,7 +145,7 @@ __lws_reset_wsi(struct lws *wsi) __lws_same_vh_protocol_remove(wsi); #if defined(LWS_WITH_CLIENT) - lws_free_set_NULL(wsi->stash); + //lws_free_set_NULL(wsi->stash); lws_free_set_NULL(wsi->cli_hostname_copy); #endif @@ -166,6 +168,34 @@ __lws_reset_wsi(struct lws *wsi) #if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) __lws_header_table_detach(wsi, 0); #endif + +#if defined(LWS_ROLE_H2) + /* + * Let's try to clean out the h2-ness of the wsi + */ + + memset(&wsi->h2, 0, sizeof(wsi->h2)); + + wsi->hdr_parsing_completed = wsi->mux_substream = + wsi->upgraded_to_http2 = wsi->mux_stream_immortal = + wsi->h2_acked_settings = wsi->seen_nonpseudoheader = + wsi->socket_is_permanently_unusable = wsi->favoured_pollin = + wsi->already_did_cce = wsi->told_user_closed = + wsi->waiting_to_send_close_frame = wsi->close_needs_ack = + wsi->parent_pending_cb_on_writable = wsi->seen_zero_length_recv = + wsi->close_when_buffered_out_drained = wsi->could_have_pending = 0; +#endif + +#if defined(LWS_WITH_CLIENT) + wsi->do_ws = wsi->chunked = wsi->client_rx_avail = + wsi->client_http_body_pending = wsi->transaction_from_pipeline_queue = + wsi->keepalive_active = wsi->keepalive_rejected = + wsi->redirected_to_get = wsi->client_pipeline = wsi->client_h2_alpn = + wsi->client_mux_substream = wsi->client_mux_migrated = + wsi->tls_session_reused = wsi->perf_done = 0; + + wsi->immortal_substream_count = 0; +#endif } /* req cx lock */ @@ -194,17 +224,23 @@ __lws_free_wsi(struct lws *wsi) } #endif + vh = wsi->a.vhost; + __lws_reset_wsi(wsi); __lws_wsi_remove_from_sul(wsi); - vh = wsi->a.vhost; + if (vh) + /* this may destroy vh */ + __lws_vhost_unbind_wsi(wsi); /* req cx + vh lock */ + +#if defined(LWS_WITH_CLIENT) + if (wsi->stash) + lws_free_set_NULL(wsi->stash); +#endif if (wsi->a.context->event_loop_ops->destroy_wsi) wsi->a.context->event_loop_ops->destroy_wsi(wsi); - if (vh) - __lws_vhost_unbind_wsi(wsi); /* req cx + vh lock */ - lwsl_debug("%s: %s, tsi fds count %d\n", __func__, lws_wsi_tag(wsi), wsi->a.context->pt[(int)wsi->tsi].fds_count); @@ -339,6 +375,7 @@ __lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason, #if defined(LWS_WITH_CLIENT) lws_free_set_NULL(wsi->cli_hostname_copy); + wsi->client_mux_substream_was = wsi->client_mux_substream; lws_addrinfo_clean(wsi); #endif @@ -407,7 +444,8 @@ __lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason, #endif #if defined(LWS_WITH_CLIENT) - lws_free_set_NULL(wsi->stash); + if (!wsi->close_is_redirect) + lws_free_set_NULL(wsi->stash); #endif if (wsi->role_ops == &role_ops_raw_skt) { @@ -565,7 +603,8 @@ just_kill_connection: #endif lwsi_state(wsi) == LRS_WAITING_DNS || lwsi_state(wsi) == LRS_WAITING_CONNECT) && - !wsi->already_did_cce && wsi->a.protocol) { + !wsi->already_did_cce && wsi->a.protocol && + !wsi->close_is_redirect) { static const char _reason[] = "closed before established"; lwsl_debug("%s: closing in unestablished state 0x%x\n", @@ -624,6 +663,9 @@ just_kill_connection: #if !defined(_WIN32_WCE) && !defined(LWS_PLAT_FREERTOS) /* libuv: no event available to guarantee completion */ if (!wsi->socket_is_permanently_unusable && +#if defined(LWS_WITH_CLIENT) + !wsi->close_is_redirect && +#endif lws_socket_is_valid(wsi->desc.sockfd) && lwsi_state(wsi) != LRS_SHUTDOWN && (context->event_loop_ops->flags & LELOF_ISPOLL)) { @@ -696,7 +738,7 @@ just_kill_connection: /* * He's a guy who go started with dns, but failed or is * caught with a shutdown before he got the result. We have - * to issue him a close cb + * to issclient_mux_substream_wasue him a close cb */ ccb = 1; @@ -712,12 +754,17 @@ just_kill_connection: ccb = 0; #if defined(LWS_WITH_CLIENT) - if (!ccb && (lwsi_state_PRE_CLOSE(wsi) & LWSIFS_NOT_EST) && + if (!wsi->close_is_redirect && !ccb && + (lwsi_state_PRE_CLOSE(wsi) & LWSIFS_NOT_EST) && lwsi_role_client(wsi)) { lws_inform_client_conn_fail(wsi, "Closed before conn", 18); } #endif - if (ccb) { + if (ccb +#if defined(LWS_WITH_CLIENT) + && !wsi->close_is_redirect +#endif + ) { if (!wsi->a.protocol && wsi->a.vhost && wsi->a.vhost->protocols) pro = &wsi->a.vhost->protocols[0]; @@ -831,10 +878,80 @@ __lws_close_free_wsi_final(struct lws *wsi) #endif sanity_assert_no_sockfd_traces(wsi->a.context, wsi->desc.sockfd); - - wsi->desc.sockfd = LWS_SOCK_INVALID; } + wsi->desc.sockfd = LWS_SOCK_INVALID; + +#if defined(LWS_WITH_CLIENT) + if (wsi->close_is_redirect) { + + wsi->close_is_redirect = 0; + + lwsl_info("%s: picking up redirection %s\n", __func__, + wsi->lc.gutag); + + lws_role_transition(wsi, LWSIFR_CLIENT, LRS_UNCONNECTED, + &role_ops_h1); + +#if defined(LWS_WITH_HTTP2) + if (wsi->client_mux_substream_was) + wsi->h2.END_STREAM = wsi->h2.END_HEADERS = 0; +#endif +#if defined(LWS_ROLE_H2) || defined(LWS_ROLE_MQTT) + if (wsi->mux.parent_wsi) { + lws_wsi_mux_sibling_disconnect(wsi); + wsi->mux.parent_wsi = NULL; + } +#endif + +#if defined(LWS_WITH_TLS) + memset(&wsi->tls, 0, sizeof(wsi->tls)); +#endif + + // wsi->a.protocol = NULL; + if (wsi->a.protocol) + lws_bind_protocol(wsi, wsi->a.protocol, "client_reset"); + wsi->pending_timeout = NO_PENDING_TIMEOUT; + wsi->hdr_parsing_completed = 0; + +#if defined(LWS_WITH_TLS) + if (wsi->stash->cis[CIS_ALPN]) + lws_strncpy(wsi->alpn, wsi->stash->cis[CIS_ALPN], + sizeof(wsi->alpn)); +#endif + + if (lws_header_table_attach(wsi, 0)) { + lwsl_err("%s: failed to get ah\n", __func__); + return; + } +// } + //_lws_header_table_reset(wsi->http.ah); + +#if defined(LWS_WITH_TLS) + wsi->tls.use_ssl = wsi->flags & LCCSCF_USE_SSL; +#endif + +#if defined(LWS_WITH_TLS_JIT_TRUST) + if (wsi->stash && wsi->stash->cis[CIS_ADDRESS]) { + struct lws_vhost *vh = NULL; + lws_tls_jit_trust_vhost_bind(wsi->a.context, + wsi->stash->cis[CIS_ADDRESS], + &vh); + if (vh) { + if (!vh->count_bound_wsi && vh->grace_after_unref) { + lwsl_info("%s: %s: in use\n", __func__, vh->lc.gutag); + lws_sul_cancel(&vh->sul_unref); + } + vh->count_bound_wsi++; + wsi->a.vhost = vh; + } + } +#endif + + return; + } +#endif + /* outermost destroy notification for wsi (user_space still intact) */ if (wsi->a.vhost) wsi->a.vhost->protocols[0].callback(wsi, LWS_CALLBACK_WSI_DESTROY, diff --git a/lib/core-net/private-lib-core-net.h b/lib/core-net/private-lib-core-net.h index eec38e60d..38073baeb 100644 --- a/lib/core-net/private-lib-core-net.h +++ b/lib/core-net/private-lib-core-net.h @@ -540,7 +540,7 @@ struct lws_vhost { #endif #if defined(LWS_WITH_SECURE_STREAMS_STATIC_POLICY_ONLY) - int8_t ss_refcount; + int8_t ss_refcount; /**< refcount of number of ss connections with streamtypes using this * trust store */ #endif @@ -742,6 +742,7 @@ struct lws { #if defined(LWS_WITH_TLS) struct lws_lws_tls tls; + char alpn[24]; #endif lws_sock_file_fd_type desc; /* .filefd / .sockfd */ @@ -759,6 +760,8 @@ struct lws { #endif unsigned int cache_secs; + short bugcatcher; + unsigned int hdr_parsing_completed:1; unsigned int mux_substream:1; unsigned int upgraded_to_http2:1; @@ -837,6 +840,8 @@ struct lws { * this activity, and will report the failure */ unsigned int tls_session_reused:1; unsigned int perf_done:1; + unsigned int close_is_redirect:1; + unsigned int client_mux_substream_was:1; #endif #ifdef _WIN32 @@ -1351,6 +1356,9 @@ __lws_same_vh_protocol_remove(struct lws *wsi); void lws_same_vh_protocol_insert(struct lws *wsi, int n); +int +lws_client_stash_create(struct lws *wsi, const char **cisin); + void lws_seq_destroy_all_on_pt(struct lws_context_per_thread *pt); diff --git a/lib/core-net/vhost.c b/lib/core-net/vhost.c index 5e751a21a..7654faaf9 100644 --- a/lib/core-net/vhost.c +++ b/lib/core-net/vhost.c @@ -557,6 +557,7 @@ lws_create_vhost(struct lws_context *context, #endif struct lws_protocols *lwsp; int m, f = !info->pvo, fx = 0, abs_pcol_count = 0, sec_pcol_count = 0; + const char *name = "default"; char buf[96]; char *p; #if defined(LWS_WITH_SYS_ASYNC_DNS) @@ -564,10 +565,13 @@ lws_create_vhost(struct lws_context *context, #endif int n; + if (info->vhost_name) + name = info->vhost_name; + if (lws_fi(&info->fic, "vh_create_oom")) vh = NULL; else - vh = lws_zalloc(sizeof(*vh) + vh = lws_zalloc(sizeof(*vh) + strlen(name) + 1 #if defined(LWS_WITH_EVENT_LIBS) + context->event_loop_ops->evlib_size_vh #endif @@ -577,7 +581,12 @@ lws_create_vhost(struct lws_context *context, #if defined(LWS_WITH_EVENT_LIBS) vh->evlib_vh = (void *)&vh[1]; + vh->name = (const char *)vh->evlib_vh + + context->event_loop_ops->evlib_size_vh; +#else + vh->name = (const char *)&vh[1]; #endif + memcpy((char *)vh->name, name, strlen(name) + 1); #if LWS_MAX_SMP > 1 lws_mutex_refcount_init(&vh->mr); @@ -587,10 +596,6 @@ lws_create_vhost(struct lws_context *context, pcols = &protocols_dummy[0]; vh->context = context; - if (!info->vhost_name) - vh->name = "default"; - else - vh->name = info->vhost_name; { char *end = buf + sizeof(buf) - 1; p = buf; diff --git a/lib/core-net/wsi.c b/lib/core-net/wsi.c index e67a98be2..07d605542 100644 --- a/lib/core-net/wsi.c +++ b/lib/core-net/wsi.c @@ -43,10 +43,12 @@ void lwsi_set_role(struct lws *wsi, lws_wsi_state_t role) void lwsi_set_state(struct lws *wsi, lws_wsi_state_t lrs) { - wsi->wsistate = (wsi->wsistate & (unsigned int)(~LRS_MASK)) | lrs; + lws_wsi_state_t old = wsi->wsistate; - lwsl_debug("lwsi_set_state(%s, 0x%lx)\n", lws_wsi_tag(wsi), - (unsigned long)wsi->wsistate); + wsi->wsistate = (old & (unsigned int)(~LRS_MASK)) | lrs; + + lwsl_debug("lwsi_set_state(%s): 0x%lx -> 0x%lx)\n", lws_wsi_tag(wsi), + (unsigned long)old, (unsigned long)wsi->wsistate); } #endif @@ -56,6 +58,7 @@ lws_vhost_bind_wsi(struct lws_vhost *vh, struct lws *wsi) { if (wsi->a.vhost == vh) return; + lws_context_lock(vh->context, __func__); /* ---------- context { */ wsi->a.vhost = vh; @@ -68,6 +71,7 @@ lws_vhost_bind_wsi(struct lws_vhost *vh, struct lws *wsi) vh->count_bound_wsi++; lws_context_unlock(vh->context); /* } context ---------- */ + lwsl_debug("%s: vh %s: wsi %s/%s, count_bound_wsi %d\n", __func__, vh->name, wsi->role_ops ? wsi->role_ops->name : "none", wsi->a.protocol ? wsi->a.protocol->name : "none", diff --git a/lib/roles/h2/http2.c b/lib/roles/h2/http2.c index d71214d0e..aeb1383c7 100644 --- a/lib/roles/h2/http2.c +++ b/lib/roles/h2/http2.c @@ -395,12 +395,17 @@ lws_h2_issue_preface(struct lws *wsi) return 1; } + if (h2n->sent_preface) + return 1; + lwsl_debug("%s: %s: fd %d\n", __func__, lws_wsi_tag(wsi), (int)wsi->desc.sockfd); if (lws_issue_raw(wsi, (uint8_t *)preface, strlen(preface)) != (int)strlen(preface)) return 1; + h2n->sent_preface = 1; + lws_role_transition(wsi, LWSIFR_CLIENT, LRS_H2_WAITING_TO_SEND_HEADERS, &role_ops_h2); @@ -1534,6 +1539,11 @@ lws_h2_parse_end_of_frame(struct lws *wsi) h2n->swsi->txc.manual_initial_tx_credit = wsi->txc.manual_initial_tx_credit; +#if defined(LWS_WITH_TLS) + lws_strncpy(h2n->swsi->alpn, wsi->alpn, + sizeof(wsi->alpn)); +#endif + wsi->user_space = NULL; if (h2n->swsi->http.ah) @@ -1631,8 +1641,17 @@ lws_h2_parse_end_of_frame(struct lws *wsi) #if defined(LWS_WITH_CLIENT) if (h2n->swsi->client_mux_substream && lws_client_interpret_server_handshake(h2n->swsi)) { - lwsl_info("%s: cli int serv hs closed it\n", __func__); - break; + /* + * This is more complicated than it looks, one exit from + * interpret_server_handshake() is to do a close that + * turns into a redirect. + * + * In that case, the wsi survives having being reset + * and detached from any h2 identity. We need to get + * our parents out from touching it any more + */ + lwsl_info("%s: cli int serv hs closed, or redir\n", __func__); + return 2; } #endif @@ -2032,7 +2051,7 @@ lws_h2_parser(struct lws *wsi, unsigned char *in, lws_filepos_t _inlen, h2n->count++; if (h2n->type == LWS_H2_FRAME_TYPE_COUNT) { /* IGNORING FRAME */ - lwsl_debug("%s: consuming for ignored %u %u\n", __func__, (unsigned int)h2n->count, (unsigned int)h2n->length); + //lwsl_debug("%s: consuming for ignored %u %u\n", __func__, (unsigned int)h2n->count, (unsigned int)h2n->length); goto frame_end; } @@ -2372,7 +2391,7 @@ do_windows: break; case LWS_H2_FRAME_TYPE_COUNT: /* IGNORING FRAME */ - lwsl_debug("%s: consuming for ignored %u %u\n", __func__, (unsigned int)h2n->count, (unsigned int)h2n->length); + //lwsl_debug("%s: consuming for ignored %u %u\n", __func__, (unsigned int)h2n->count, (unsigned int)h2n->length); h2n->count++; break; @@ -2396,7 +2415,13 @@ frame_end: /* * end of frame just happened */ - if (lws_h2_parse_end_of_frame(wsi)) + n = lws_h2_parse_end_of_frame(wsi); + if (n == 2) { + *inused = (lws_filepos_t)lws_ptr_diff_size_t(in, oldin); + + return 2; + } + if (n) goto fail; break; @@ -2442,7 +2467,7 @@ try_frame_start: default: if (h2n->type == LWS_H2_FRAME_TYPE_COUNT) { /* IGNORING FRAME */ - lwsl_debug("%s: consuming for ignored %u %u\n", __func__, (unsigned int)h2n->count, (unsigned int)h2n->length); + //lwsl_debug("%s: consuming for ignored %u %u\n", __func__, (unsigned int)h2n->count, (unsigned int)h2n->length); h2n->count++; } break; @@ -2480,6 +2505,7 @@ lws_h2_client_handshake(struct lws *wsi) char *meth = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_METHOD), *uri = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_URI), *simp; struct lws *nwsi = lws_get_network_wsi(wsi); + const char *path = "/"; int n, m; /* * The identifier of a newly established stream MUST be numerically @@ -2517,6 +2543,8 @@ lws_h2_client_handshake(struct lws *wsi) if (!meth) meth = "GET"; + /* h2 pseudoheaders must be in a bunch at the start */ + if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_COLON_METHOD, (unsigned char *)meth, @@ -2529,31 +2557,49 @@ lws_h2_client_handshake(struct lws *wsi) &p, end)) goto fail_length; + n = lws_hdr_total_length(wsi, _WSI_TOKEN_CLIENT_URI); + if (n) + path = uri; + else + if (wsi->stash && wsi->stash->cis[CIS_PATH]) { + path = wsi->stash->cis[CIS_PATH]; + n = (int)strlen(path); + } else + n = 1; + + if (n > 1 && path[0] == '/' && path[1] == '/') { + path++; + n--; + } + if (n && lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_COLON_PATH, - (unsigned char *)uri, n, &p, end)) - goto fail_length; - - n = lws_hdr_total_length(wsi, _WSI_TOKEN_CLIENT_ORIGIN); - simp = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_ORIGIN); - if (n && simp && lws_add_http_header_by_token(wsi, - WSI_TOKEN_HTTP_COLON_AUTHORITY, - (unsigned char *)simp, n, &p, end)) + (unsigned char *)path, n, &p, end)) goto fail_length; n = lws_hdr_total_length(wsi, _WSI_TOKEN_CLIENT_HOST); simp = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_HOST); + if (!n && wsi->stash && wsi->stash->cis[CIS_ADDRESS]) { + n = (int)strlen(wsi->stash->cis[CIS_ADDRESS]); + simp = wsi->stash->cis[CIS_ADDRESS]; + } - if (!wsi->client_h2_alpn && n && simp && +// n = lws_hdr_total_length(wsi, _WSI_TOKEN_CLIENT_ORIGIN); +// simp = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_ORIGIN); +#if 0 + if (n && simp && lws_add_http_header_by_token(wsi, + WSI_TOKEN_HTTP_COLON_AUTHORITY, + (unsigned char *)simp, n, &p, end)) + goto fail_length; +#endif + + + if (/*!wsi->client_h2_alpn && */n && simp && lws_add_http_header_by_token(wsi, WSI_TOKEN_HOST, (unsigned char *)simp, n, &p, end)) goto fail_length; - if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_USER_AGENT, - (unsigned char *)"lwsss", 5, - &p, end)) - goto fail_length; if (wsi->flags & LCCSCF_HTTP_MULTIPART_MIME) { p1 = lws_http_multipart_headers(wsi, p); diff --git a/lib/roles/h2/ops-h2.c b/lib/roles/h2/ops-h2.c index 7ac517f89..d02394a65 100644 --- a/lib/roles/h2/ops-h2.c +++ b/lib/roles/h2/ops-h2.c @@ -113,8 +113,20 @@ rops_handle_POLLIN_h2(struct lws_context_per_thread *pt, struct lws *wsi, } #endif - lwsl_info("%s: wsistate 0x%x, pollout %d\n", __func__, - (unsigned int)wsi->wsistate, pollfd->revents & LWS_POLLOUT); + lwsl_info("%s: %s wsistate 0x%x, events %d, revents %d, pollout %d\n", __func__, + wsi->lc.gutag, (unsigned int)wsi->wsistate, + pollfd->events, pollfd->revents, + pollfd->revents & LWS_POLLOUT); + + /* !!! */ + if (wsi->wsistate == 0x10000013) { + wsi->bugcatcher++; + if (wsi->bugcatcher == 250) { + lwsl_err("%s: BUGCATCHER\n", __func__); + return LWS_HPI_RET_PLEASE_CLOSE_ME; + } + } else + wsi->bugcatcher = 0; /* * something went wrong with parsing the handshake, and @@ -170,7 +182,9 @@ rops_handle_POLLIN_h2(struct lws_context_per_thread *pt, struct lws *wsi, if (wsi->mux_substream || wsi->upgraded_to_http2) { wsi1 = lws_get_network_wsi(wsi); - if (wsi1 && lws_has_buffered_out(wsi1)) + if (wsi1 && lws_has_buffered_out(wsi1)) { + + lwsl_info("%s: has buffered out\n", __func__); /* * We cannot deal with any kind of new RX * because we are dealing with a partial send @@ -178,6 +192,7 @@ rops_handle_POLLIN_h2(struct lws_context_per_thread *pt, struct lws *wsi, * expect to be able to send) */ return LWS_HPI_RET_HANDLED; + } } read: @@ -205,8 +220,11 @@ read: !(pollfd->revents & pollfd->events & LWS_POLLIN)) return LWS_HPI_RET_HANDLED; + /* We have something to read... */ + if (!(lwsi_role_client(wsi) && (lwsi_state(wsi) != LRS_ESTABLISHED && + // lwsi_state(wsi) != LRS_H1C_ISSUE_HANDSHAKE2 && lwsi_state(wsi) != LRS_H2_WAITING_TO_SEND_HEADERS))) { ebuf.token = pt->serv_buf; @@ -228,7 +246,8 @@ read: // lwsl_notice("%s: Actual RX %d\n", __func__, ebuf.len); // if (ebuf.len > 0) // lwsl_hexdump_notice(ebuf.token, ebuf.len); - } + } else + lwsl_info("%s: skipped read\n", __func__); if (ebuf.len < 0) return LWS_HPI_RET_PLEASE_CLOSE_ME; @@ -484,9 +503,9 @@ rops_write_role_protocol_h2(struct lws *wsi, unsigned char *buf, size_t len, } if (base == LWS_WRITE_HTTP_FINAL || ((*wp) & LWS_WRITE_H2_STREAM_END)) { - lwsl_info("%s: %s: setting END_STREAM\n", __func__, - lws_wsi_tag(wsi)); flags |= LWS_H2_FLAG_END_STREAM; + lwsl_info("%s: %s: setting END_STREAM, 0x%x\n", __func__, + lws_wsi_tag(wsi), flags); wsi->h2.send_END_STREAM = 1; } diff --git a/lib/roles/h2/private-lib-roles-h2.h b/lib/roles/h2/private-lib-roles-h2.h index 9bce1d9c0..98b2aea44 100644 --- a/lib/roles/h2/private-lib-roles-h2.h +++ b/lib/roles/h2/private-lib-roles-h2.h @@ -272,6 +272,7 @@ struct lws_h2_netconn { unsigned int is_first_header_char:1; unsigned int zero_huff_padding:1; unsigned int last_action_dyntable_resize:1; + unsigned int sent_preface:1; uint32_t hdr_idx; uint32_t hpack_len; diff --git a/lib/roles/http/client/client-http.c b/lib/roles/http/client/client-http.c index 3cca39540..381e9fd58 100644 --- a/lib/roles/http/client/client-http.c +++ b/lib/roles/http/client/client-http.c @@ -217,7 +217,9 @@ start_ws_handshake: #endif #if defined (LWS_WITH_HTTP2) - if (wsi->client_h2_alpn && lwsi_state(wsi) != LRS_H1C_ISSUE_HANDSHAKE2) { + if (wsi->client_h2_alpn //&& + //lwsi_state(wsi) != LRS_H1C_ISSUE_HANDSHAKE2 + ) { /* * We connected to the server and set up tls and * negotiated "h2" or connected as clear text @@ -227,16 +229,18 @@ start_ws_handshake: * now, not an h1 client connection. */ -#if defined(LWS_WITH_TLS) - if (wsi->tls.use_ssl & LCCSCF_USE_SSL) - lws_tls_server_conn_alpn(wsi); -#endif + lwsl_info("%s: doing h2 hello path\n", __func__); - /* send the H2 preface to legitimize the connection */ - if (lws_h2_issue_preface(wsi)) { - cce = "error sending h2 preface"; - goto bail3; - } + /* + * send the H2 preface to legitimize the connection + * + * transitions us to LRS_H2_WAITING_TO_SEND_HEADERS + */ + if (wsi->client_h2_alpn) + if (lws_h2_issue_preface(wsi)) { + cce = "error sending h2 preface"; + goto bail3; + } // lwsi_set_state(wsi, LRS_H1C_ISSUE_HANDSHAKE2); lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_CLIENT_HS_SEND, @@ -800,7 +804,18 @@ lws_client_interpret_server_handshake(struct lws *wsi) /* wsi has closed */ return 1; } - return 0; + + /* + * We are redirecting, let's close in order to extricate + * ourselves from the current wsi usage, eg, h2 mux cleanly. + * + * We will notice close_is_redirect and switch to redirect + * flow late in the close action. + */ + + lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "redir"); + + return -1; } /* if h1 KA is allowed, enable the queued pipeline guys */ @@ -1094,7 +1109,7 @@ char * lws_generate_client_handshake(struct lws *wsi, char *pkt) { const char *meth, *pp = lws_hdr_simple_ptr(wsi, - _WSI_TOKEN_CLIENT_SENT_PROTOCOLS); + _WSI_TOKEN_CLIENT_SENT_PROTOCOLS), *path; char *p = pkt, *p1, *end = p + wsi->a.context->pt_serv_buf_size; meth = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_METHOD); @@ -1147,9 +1162,17 @@ lws_generate_client_handshake(struct lws *wsi, char *pkt) * Sec-WebSocket-Version: 4 */ + path = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_URI); + if (!path) { + if (wsi->stash && wsi->stash->cis[CIS_PATH] && + wsi->stash->cis[CIS_PATH][0]) + path = wsi->stash->cis[CIS_PATH]; + else + path = "/"; + } + p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), - "%s %s HTTP/1.1\x0d\x0a", meth, - lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_URI)); + "%s %s HTTP/1.1\x0d\x0a", meth, path); p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "Pragma: no-cache\x0d\x0a" @@ -1168,7 +1191,9 @@ lws_generate_client_handshake(struct lws *wsi, char *pkt) _WSI_TOKEN_CLIENT_ORIGIN)); else p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), - "Origin: http://%s\x0d\x0a", + "Origin: %s://%s\x0d\x0a", + wsi->flags & LCCSCF_USE_SSL ? + "https" : "http", lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_ORIGIN)); } @@ -1523,8 +1548,7 @@ static uint8_t hnames2[] = { _WSI_TOKEN_CLIENT_ORIGIN, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS, _WSI_TOKEN_CLIENT_METHOD, - _WSI_TOKEN_CLIENT_IFACE, - _WSI_TOKEN_CLIENT_ALPN + _WSI_TOKEN_CLIENT_IFACE }; /** @@ -1545,9 +1569,9 @@ lws_client_reset(struct lws **pwsi, int ssl, const char *address, int port, #if defined(LWS_ROLE_WS) struct _lws_websocket_related *ws; #endif - char *stash, *p; + const char *cisin[CIS_COUNT]; struct lws *wsi; - size_t size = 0; + size_t o; int n; if (!pwsi) @@ -1559,7 +1583,7 @@ lws_client_reset(struct lws **pwsi, int ssl, const char *address, int port, lwsl_debug("%s: %s: redir %d: %s\n", __func__, lws_wsi_tag(wsi), wsi->redirects, address); - if (wsi->redirects == 3) { + if (wsi->redirects == 4) { lwsl_err("%s: Too many redirects\n", __func__); return NULL; } @@ -1570,52 +1594,24 @@ lws_client_reset(struct lws **pwsi, int ssl, const char *address, int port, * but leave our wsi extant and still bound to whatever vhost it was */ + o = path[0] == '/' && path[1] == '/'; + + memset((char *)cisin, 0, sizeof(cisin)); + + cisin[CIS_ADDRESS] = address; + cisin[CIS_PATH] = path + o; + cisin[CIS_HOST] = host; + for (n = 0; n < (int)LWS_ARRAY_SIZE(hnames2); n++) - size += (unsigned int)lws_hdr_total_length(wsi, hnames2[n]) + 1u; + cisin[n + 3] = lws_hdr_simple_ptr(wsi, hnames2[n]); - if (size < (size_t)lws_hdr_total_length(wsi, _WSI_TOKEN_CLIENT_URI) + 1) - size = (unsigned int)lws_hdr_total_length(wsi, _WSI_TOKEN_CLIENT_URI) + 1u; +#if defined(LWS_WITH_TLS) + cisin[CIS_ALPN] = wsi->alpn; +#endif - /* - * The incoming address and host can be from inside the existing ah - * we are going to detach and reattch - */ - - size += strlen(path) + 1 + strlen(address) + 1 + strlen(host) + 1 + 1; - - p = stash = lws_malloc(size, __func__); - if (!stash) + if (lws_client_stash_create(wsi, cisin)) return NULL; - /* - * _WSI_TOKEN_CLIENT_ORIGIN, - * _WSI_TOKEN_CLIENT_SENT_PROTOCOLS, - * _WSI_TOKEN_CLIENT_METHOD, - * _WSI_TOKEN_CLIENT_IFACE, - * _WSI_TOKEN_CLIENT_ALPN - * address - * host - * path - */ - - for (n = 0; n < (int)LWS_ARRAY_SIZE(hnames2); n++) - if (lws_hdr_total_length(wsi, hnames2[n]) && - lws_hdr_simple_ptr(wsi, hnames2[n])) { - memcpy(p, lws_hdr_simple_ptr(wsi, hnames2[n]), (size_t)( - lws_hdr_total_length(wsi, hnames2[n]) + 1)); - p += (size_t)(lws_hdr_total_length(wsi, hnames2[n]) + 1); - } else - *p++ = '\0'; - - memcpy(p, address, strlen(address) + (size_t)1); - address = p; - p += strlen(address) + 1; - memcpy(p, host, strlen(host) + (size_t)1); - host = p; - p += strlen(host) + 1; - memcpy(p, path, strlen(path) + (size_t)1); - path = p; - if (!port) { lwsl_info("%s: forcing port 443\n", __func__); @@ -1623,8 +1619,16 @@ lws_client_reset(struct lws **pwsi, int ssl, const char *address, int port, ssl = 1; } - lwsl_info("redirect ads='%s', port=%d, path='%s', ssl = %d, pifds %d\n", - address, port, path, ssl, wsi->position_in_fds_table); + wsi->c_port = (uint16_t)port; + + wsi->flags = (wsi->flags & (~LCCSCF_USE_SSL)) | + (ssl ? LCCSCF_USE_SSL : 0); + + lwsl_notice("%s: REDIRECT %s:%d, path='%s', ssl = %d, alpn='%s'\n", + __func__, address, port, path, ssl, cisin[CIS_ALPN]); + + if (!cisin[CIS_ALPN][0]) + assert(0); lws_pt_lock(pt, __func__); __remove_wsi_socket_from_fds(wsi); @@ -1636,6 +1640,14 @@ lws_client_reset(struct lws **pwsi, int ssl, const char *address, int port, wsi->ws = NULL; } #endif + + /* + * After this point we can't trust the incoming strings like address, + * path any more, since they may have been pointing into the old ah. + * + * We must use the copies in the wsi->stash instead if we want them. + */ + __lws_reset_wsi(wsi); /* detaches ah here */ #if defined(LWS_ROLE_WS) if (weak) @@ -1643,102 +1655,11 @@ lws_client_reset(struct lws **pwsi, int ssl, const char *address, int port, #endif wsi->client_pipeline = 1; - /* close the connection by hand */ - -#if defined(LWS_WITH_TLS) - lws_ssl_close(wsi); -#endif - - if (wsi->role_ops && - lws_rops_fidx(wsi->role_ops, LWS_ROPS_close_kill_connection)) - lws_rops_func_fidx(wsi->role_ops, LWS_ROPS_close_kill_connection). - close_kill_connection(wsi, 1); - - if (wsi->a.context->event_loop_ops->close_handle_manually) - wsi->a.context->event_loop_ops->close_handle_manually(wsi); - else - if (wsi->desc.sockfd != LWS_SOCK_INVALID) - compatible_close(wsi->desc.sockfd); - -#if defined(LWS_WITH_TLS) - if (!ssl) - wsi->tls.use_ssl &= (unsigned int)~LCCSCF_USE_SSL; - else - wsi->tls.use_ssl |= LCCSCF_USE_SSL; -#else - if (ssl) { - lwsl_err("%s: not configured for ssl\n", __func__); - goto bail; - } -#endif - - if (wsi->a.protocol && wsi->role_ops && wsi->protocol_bind_balance) { - wsi->a.protocol->callback(wsi, - wsi->role_ops->protocol_unbind_cb[ - !!lwsi_role_server(wsi)], - wsi->user_space, (void *)__func__, 0); - - wsi->protocol_bind_balance = 0; - } - - wsi->desc.sockfd = LWS_SOCK_INVALID; - lws_role_transition(wsi, LWSIFR_CLIENT, LRS_UNCONNECTED, &role_ops_h1); -// wsi->a.protocol = NULL; - if (wsi->a.protocol) - lws_bind_protocol(wsi, wsi->a.protocol, "client_reset"); - wsi->pending_timeout = NO_PENDING_TIMEOUT; - wsi->c_port = (uint16_t)port; - wsi->hdr_parsing_completed = 0; - - if (lws_header_table_attach(wsi, 0)) { - lwsl_err("%s: failed to get ah\n", __func__); - goto bail; - } - //_lws_header_table_reset(wsi->http.ah); - - if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS, address)) - goto bail; - - if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_HOST, host)) - goto bail; - /* - * _WSI_TOKEN_CLIENT_ORIGIN, - * _WSI_TOKEN_CLIENT_SENT_PROTOCOLS, - * _WSI_TOKEN_CLIENT_METHOD, - * _WSI_TOKEN_CLIENT_IFACE, - * _WSI_TOKEN_CLIENT_ALPN - * address - * host - * path + * Will complete at close flow */ - p = stash; - for (n = 0; n < (int)LWS_ARRAY_SIZE(hnames2); n++) { - if (lws_hdr_simple_create(wsi, hnames2[n], p)) - goto bail; - p += lws_hdr_total_length(wsi, hnames2[n]) + 1; - } - - stash[0] = '/'; - memmove(&stash[1], path, size - 1 < strlen(path) + 1 ? - size - 1 : strlen(path) + (size_t)1); - if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_URI, stash)) - goto bail; - - lws_free_set_NULL(stash); - -#if defined(LWS_WITH_HTTP2) - if (wsi->client_mux_substream) - wsi->h2.END_STREAM = wsi->h2.END_HEADERS = 0; -#endif - - *pwsi = lws_client_connect_2_dnsreq(wsi); + wsi->close_is_redirect = 1; return *pwsi; - -bail: - lws_free_set_NULL(stash); - - return NULL; } diff --git a/lib/tls/tls-client.c b/lib/tls/tls-client.c index 9b5003f12..f2b10f783 100644 --- a/lib/tls/tls-client.c +++ b/lib/tls/tls-client.c @@ -74,7 +74,7 @@ lws_ssl_client_connect2(struct lws *wsi, char *errbuf, size_t len) lwsi_set_state(wsi, LRS_WAITING_SSL); /* fallthru */ case LWS_SSL_CAPABLE_MORE_SERVICE: - return 0; + return 0; /* retry */ } } @@ -89,7 +89,7 @@ lws_ssl_client_connect2(struct lws *wsi, char *errbuf, size_t len) (lws_now_usecs() - wsi->conmon_datum); #endif - return 1; + return 1; /* connected */ } @@ -176,7 +176,6 @@ int lws_context_init_client_ssl(const struct lws_context_creation_info *info, int lws_client_create_tls(struct lws *wsi, const char **pcce, int do_c1) { - /* we can retry this... just cook the SSL BIO the first time */ if (wsi->tls.use_ssl & LCCSCF_USE_SSL) { @@ -198,7 +197,7 @@ lws_client_create_tls(struct lws *wsi, const char **pcce, int do_c1) } if (!do_c1) - return 0; + return CCTLS_RETURN_DONE; lws_metrics_caliper_report(wsi->cal_conn, METRES_GO); lws_metrics_caliper_bind(wsi->cal_conn, wsi->a.context->mt_conn_tls); @@ -211,39 +210,18 @@ lws_client_create_tls(struct lws *wsi, const char **pcce, int do_c1) lwsl_debug("%s: lws_ssl_client_connect1: %d\n", __func__, n); if (!n) return CCTLS_RETURN_RETRY; /* caller should return 0 */ + if (n < 0) { *pcce = (const char *)wsi->a.context->pt[(int)wsi->tsi].serv_buf; lws_metrics_caliper_report(wsi->cal_conn, METRES_NOGO); return CCTLS_RETURN_ERROR; } /* ...connect1 already handled caliper if SSL_accept done */ + + lws_tls_server_conn_alpn(wsi); + } else wsi->tls.ssl = NULL; -#if 0 -#if defined (LWS_WITH_HTTP2) - if (wsi->client_h2_alpn) { - /* - * We connected to the server and set up tls, and - * negotiated "h2". - * - * So this is it, we are an h2 nwsi client connection - * now, not an h1 client connection. - */ -#if defined(LWS_WITH_TLS) - lws_tls_server_conn_alpn(wsi); -#endif - - /* send the H2 preface to legitimize the connection */ - if (lws_h2_issue_preface(wsi)) { - *pcce = "error sending h2 preface"; - return CCTLS_RETURN_ERROR; - } - - lwsi_set_state(wsi, LRS_H1C_ISSUE_HANDSHAKE2); - } -#endif -#endif - return CCTLS_RETURN_DONE; /* OK */ } diff --git a/lib/tls/tls.c b/lib/tls/tls.c index cd7a8bbca..e85cc84ef 100644 --- a/lib/tls/tls.c +++ b/lib/tls/tls.c @@ -116,8 +116,12 @@ lws_tls_server_conn_alpn(struct lws *wsi) char cstr[10]; unsigned len; - if (!wsi->tls.ssl) + lwsl_info("%s\n", __func__); + + if (!wsi->tls.ssl) { + lwsl_err("%s: non-ssl\n", __func__); return 0; + } SSL_get0_alpn_selected(wsi->tls.ssl, &name, &len); if (!len) { @@ -131,10 +135,12 @@ lws_tls_server_conn_alpn(struct lws *wsi) memcpy(cstr, name, len); cstr[len] = '\0'; - lwsl_info("negotiated '%s' using ALPN\n", cstr); + lwsl_info("%s: negotiated '%s' using ALPN\n", __func__, cstr); wsi->tls.use_ssl |= LCCSCF_USE_SSL; return lws_role_call_alpn_negotiated(wsi, (const char *)cstr); +#else + lwsl_err("%s: openssl too old\n", __func__); #endif // OPENSSL_VERSION_NUMBER >= 0x10002000L return 0; diff --git a/minimal-examples/http-client/minimal-http-client-multi/CMakeLists.txt b/minimal-examples/http-client/minimal-http-client-multi/CMakeLists.txt index 9122a9e4c..c484e8186 100644 --- a/minimal-examples/http-client/minimal-http-client-multi/CMakeLists.txt +++ b/minimal-examples/http-client/minimal-http-client-multi/CMakeLists.txt @@ -119,17 +119,17 @@ endif() add_test(NAME http-client-multi-h1 COMMAND lws-minimal-http-client-multi --h1 -l --port ${PORT_HCM_SRV} -d1151) add_test(NAME http-client-multi-pipe COMMAND lws-minimal-http-client-multi - -p -l --port ${PORT_HCM_SRV}) + -p -l --port ${PORT_HCM_SRV} -d1151) add_test(NAME http-client-multi-h1-pipe COMMAND lws-minimal-http-client-multi - --h1 -p -l --port ${PORT_HCM_SRV}) + --h1 -p -l --port ${PORT_HCM_SRV} -d1151) add_test(NAME http-client-multi-stag COMMAND lws-minimal-http-client-multi - -s -l --port ${PORT_HCM_SRV}) + -s -l --port ${PORT_HCM_SRV} -d1151) add_test(NAME http-client-multi-stag-h1 COMMAND lws-minimal-http-client-multi - --h1 -s -l --port ${PORT_HCM_SRV}) + --h1 -s -l --port ${PORT_HCM_SRV} -d1151) add_test(NAME http-client-multi-stag-pipe COMMAND lws-minimal-http-client-multi - -p -s -l --port ${PORT_HCM_SRV}) + -p -s -l --port ${PORT_HCM_SRV} -d1151) add_test(NAME http-client-multi-stag-h1-pipe COMMAND lws-minimal-http-client-multi - --h1 -p -s -l --port ${PORT_HCM_SRV}) + --h1 -p -s -l --port ${PORT_HCM_SRV} -d1151) # confirm that the pipelined mode really is doing it in one connection add_test(NAME http-client-multi-restrict-pipe COMMAND lws-minimal-http-client-multi -d1151 --limit 1 -p -l --port ${PORT_HCM_SRV}) @@ -137,9 +137,9 @@ endif() add_test(NAME http-client-multi-restrict-stag-pipe COMMAND lws-minimal-http-client-multi -d1151 --limit 1 -p -s -l --port ${PORT_HCM_SRV}) add_test(NAME http-client-multi-restrict-stag-h1-pipe COMMAND lws-minimal-http-client-multi -d1151 --limit 1 --h1 -p -s -l --port ${PORT_HCM_SRV}) # confirm that we do fail with a one connection limit and no pipelining - add_test(NAME http-client-multi-restrict-nopipe-fail COMMAND lws-minimal-http-client-multi --limit 1 -l --port ${PORT_HCM_SRV}) + add_test(NAME http-client-multi-restrict-nopipe-fail COMMAND lws-minimal-http-client-multi --limit 1 -l --port ${PORT_HCM_SRV} -d1151) set_property(TEST http-client-multi-restrict-nopipe-fail PROPERTY WILL_FAIL TRUE) - add_test(NAME http-client-multi-restrict-h1-nopipe-fail COMMAND lws-minimal-http-client-multi --limit 1 --h1 -l --port ${PORT_HCM_SRV}) + add_test(NAME http-client-multi-restrict-h1-nopipe-fail COMMAND lws-minimal-http-client-multi --limit 1 --h1 -l --port ${PORT_HCM_SRV} -d1151) set_property(TEST http-client-multi-restrict-h1-nopipe-fail PROPERTY WILL_FAIL TRUE) set_tests_properties(http-client-multi-restrict-pipe @@ -163,21 +163,21 @@ endif() # POSTs against local http-server-form-post add_test(NAME http-client-multi-post COMMAND lws-minimal-http-client-multi - --post -l --port 1${PORT_HCM_SRV}) + --post -l --port 1${PORT_HCM_SRV} -d1151) add_test(NAME http-client-multi-post-h1 COMMAND lws-minimal-http-client-multi - --post --h1 -l --port 1${PORT_HCM_SRV}) + --post --h1 -l --port 1${PORT_HCM_SRV} -d1151) add_test(NAME http-client-multi-post-pipe COMMAND lws-minimal-http-client-multi - --post -p -l --port 1${PORT_HCM_SRV}) + --post -p -l --port 1${PORT_HCM_SRV} -d1151) add_test(NAME http-client-multi-post-h1-pipe COMMAND lws-minimal-http-client-multi - --post --h1 -p -l --port 1${PORT_HCM_SRV}) + --post --h1 -p -l --port 1${PORT_HCM_SRV} -d1151) add_test(NAME http-client-multi-post-stag COMMAND lws-minimal-http-client-multi - --post -s -l -d1151 --port 1${PORT_HCM_SRV}) + --post -s -l -d1151 --port 1${PORT_HCM_SRV} -d1151) add_test(NAME http-client-multi-post-stag-h1 COMMAND lws-minimal-http-client-multi - --post --h1 -d1151 -s -l --port 1${PORT_HCM_SRV}) + --post --h1 -d1151 -s -l --port 1${PORT_HCM_SRV} -d1151) add_test(NAME http-client-multi-post-stag-pipe COMMAND lws-minimal-http-client-multi - --post -p -s -l --port 1${PORT_HCM_SRV}) + --post -p -s -l --port 1${PORT_HCM_SRV} -d1151) add_test(NAME http-client-multi-post-stag-h1-pipe COMMAND lws-minimal-http-client-multi - --post --h1 -p -s -l --port 1${PORT_HCM_SRV}) + --post --h1 -p -s -l --port 1${PORT_HCM_SRV} -d1151) set_tests_properties(http-client-multi-post http-client-multi-post-h1 http-client-multi-post-pipe diff --git a/minimal-examples/http-client/minimal-http-client/minimal-http-client.c b/minimal-examples/http-client/minimal-http-client/minimal-http-client.c index 4ef278d80..c254b9586 100644 --- a/minimal-examples/http-client/minimal-http-client/minimal-http-client.c +++ b/minimal-examples/http-client/minimal-http-client/minimal-http-client.c @@ -259,7 +259,7 @@ system_notify_cb(lws_state_manager_t *mgr, lws_state_notify_link_t *link, LCCSCF_ACCEPT_TLS_DOWNGRADE_REDIRECTS | LCCSCF_H2_QUIRK_NGHTTP2_END_STREAM; - i.alpn = "h2"; + i.alpn = "h2,http/1.1"; if (lws_cmdline_option(a->argc, a->argv, "--h1")) i.alpn = "http/1.1";