diff --git a/include/libwebsockets/lws-http.h b/include/libwebsockets/lws-http.h index fce21d8c1..17df7bac0 100644 --- a/include/libwebsockets/lws-http.h +++ b/include/libwebsockets/lws-http.h @@ -788,8 +788,19 @@ lws_h2_client_stream_long_poll_rxonly(struct lws *wsi); * LWS_WITH_HTTP_STREAM_COMPRESSION set, then a NOP is provided for this api, * allowing user code to build either way and use compression if available. */ -LWS_VISIBLE int +LWS_VISIBLE LWS_EXTERN int lws_http_compression_apply(struct lws *wsi, const char *name, unsigned char **p, unsigned char *end, char decomp); + +/** + * lws_http_is_redirected_to_get() - true if redirected to GET + * + * \param wsi: the wsi to check + * + * Check if the wsi is currently in GET mode, after, eg, doing a POST and + * receiving a 303. + */ +LWS_VISIBLE LWS_EXTERN int +lws_http_is_redirected_to_get(struct lws *wsi); ///@} diff --git a/lib/core-net/close.c b/lib/core-net/close.c index b3d8ad513..cbc6bcac7 100644 --- a/lib/core-net/close.c +++ b/lib/core-net/close.c @@ -79,8 +79,10 @@ __lws_reset_wsi(struct lws *wsi) * or by specified the user. We should only free what we allocated. */ if (wsi->protocol && wsi->protocol->per_session_data_size && - wsi->user_space && !wsi->user_space_externally_allocated) + wsi->user_space && !wsi->user_space_externally_allocated) { lws_free(wsi->user_space); + wsi->user_space = NULL; + } lws_buflist_destroy_all_segments(&wsi->buflist); lws_buflist_destroy_all_segments(&wsi->buflist_out); diff --git a/lib/core-net/private-lib-core-net.h b/lib/core-net/private-lib-core-net.h index 7bff03b12..f4452c0ed 100644 --- a/lib/core-net/private-lib-core-net.h +++ b/lib/core-net/private-lib-core-net.h @@ -747,6 +747,7 @@ struct lws { unsigned int transaction_from_pipeline_queue:1; unsigned int keepalive_active:1; unsigned int keepalive_rejected:1; + unsigned int redirected_to_get:1; unsigned int client_pipeline:1; unsigned int client_h2_alpn:1; unsigned int client_h2_substream:1; diff --git a/lib/roles/http/client/client-handshake.c b/lib/roles/http/client/client-handshake.c index 8a29e6899..3713e6f7a 100644 --- a/lib/roles/http/client/client-handshake.c +++ b/lib/roles/http/client/client-handshake.c @@ -322,7 +322,8 @@ lws_client_connect_3_connect(struct lws *wsi, const char *ads, goto conn_good; } - lwsl_debug("%s: getsockopt says err %d\n", __func__, e); + lwsl_debug("%s: getsockopt fd %d says err %d\n", __func__, + wsi->desc.sockfd, e); } lwsl_debug("%s: getsockopt check: conn fail: errno %d\n", @@ -963,6 +964,17 @@ lws_client_reset(struct lws **pwsi, int ssl, const char *address, int port, if (!stash) 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])) { memcpy(p, lws_hdr_simple_ptr(wsi, hnames2[n]), @@ -981,6 +993,8 @@ lws_client_reset(struct lws **pwsi, int ssl, const char *address, int port, path = p; if (!port) { + lwsl_info("%s: forcing port 443\n", __func__); + port = 443; ssl = 1; } @@ -1008,7 +1022,10 @@ lws_client_reset(struct lws **pwsi, int ssl, const char *address, int port, compatible_close(wsi->desc.sockfd); #if defined(LWS_WITH_TLS) - wsi->tls.use_ssl = ssl; + if (!ssl) + wsi->tls.use_ssl &= LCCSCF_USE_SSL; + else + wsi->tls.use_ssl |= LCCSCF_USE_SSL; #else if (ssl) { lwsl_err("%s: not configured for ssl\n", __func__); @@ -1046,6 +1063,17 @@ lws_client_reset(struct lws **pwsi, int ssl, const char *address, int port, 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 + */ + p = stash; for (n = 0; n < (int)LWS_ARRAY_SIZE(hnames2); n++) { if (lws_hdr_simple_create(wsi, hnames2[n], p)) @@ -1060,6 +1088,11 @@ lws_client_reset(struct lws **pwsi, int ssl, const char *address, int port, lws_free_set_NULL(stash); +#if defined(LWS_WITH_HTTP2) + if (wsi->client_h2_substream) + wsi->h2.END_STREAM = wsi->h2.END_HEADERS = 0; +#endif + *pwsi = lws_client_connect_2_dnsreq(wsi); return *pwsi; @@ -1258,9 +1291,10 @@ lws_http_client_connect_via_info2(struct lws *wsi) * allocated stash */ for (n = 0; n < (int)LWS_ARRAY_SIZE(hnames); n++) - if (hnames[n] && stash->cis[n]) + if (hnames[n] && stash->cis[n]) { if (lws_hdr_simple_create(wsi, hnames[n], stash->cis[n])) goto bail1; + } #if defined(LWS_WITH_SOCKS5) if (!wsi->vhost->socks_proxy_port) diff --git a/lib/roles/http/client/client-http.c b/lib/roles/http/client/client-http.c index b08d1dc44..4ead44cf6 100644 --- a/lib/roles/http/client/client-http.c +++ b/lib/roles/http/client/client-http.c @@ -702,6 +702,13 @@ lws_http_client_http_response(struct lws *_wsi) #endif #if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) + +int +lws_http_is_redirected_to_get(struct lws *wsi) +{ + return wsi->redirected_to_get; +} + int lws_client_interpret_server_handshake(struct lws *wsi) { @@ -710,6 +717,7 @@ lws_client_interpret_server_handshake(struct lws *wsi) const char *prot, *ads = NULL, *path, *cce = NULL; struct allocated_headers *ah; struct lws *w = lws_client_wsi_effective(wsi); + struct lws *nwsi = lws_get_network_wsi(wsi); char *p, *q; char new_path[300]; @@ -797,16 +805,37 @@ lws_client_interpret_server_handshake(struct lws *wsi) goto bail3; } + /* + * Some redirect codes imply we have to change the method + * used for the subsequent transaction, commonly POST -> + * 303 -> GET. + */ + + if (n == 303) { + char *mp = lws_hdr_simple_ptr(wsi,_WSI_TOKEN_CLIENT_METHOD); + int ml = lws_hdr_total_length(wsi, _WSI_TOKEN_CLIENT_METHOD); + + if (ml >= 3 && mp) { + lwsl_info("%s: 303 switching to GET\n", __func__); + memcpy(mp, "GET", 4); + wsi->redirected_to_get = 1; + wsi->http.ah->frags[wsi->http.ah->frag_index[ + _WSI_TOKEN_CLIENT_METHOD]].len = 3; + } + } + /* Relative reference absolute path */ - if (p[0] == '/') { + if (p[0] == '/' || !strchr(p, ':')) { #if defined(LWS_WITH_TLS) - ssl = wsi->tls.use_ssl & LCCSCF_USE_SSL; + ssl = nwsi->tls.use_ssl & LCCSCF_USE_SSL; #endif ads = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS); - port = wsi->c_port; - /* +1 as lws_client_reset expects leading / omitted */ - path = p + 1; + port = nwsi->c_port; + path = p; + /* lws_client_reset expects leading / omitted */ + if (*path == '/') + path++; } /* Absolute (Full) URI */ else if (strchr(p, ':')) { @@ -816,14 +845,14 @@ lws_client_interpret_server_handshake(struct lws *wsi) } if (!strcmp(prot, "wss") || !strcmp(prot, "https")) - ssl = 1; + ssl = LCCSCF_USE_SSL; } /* Relative reference relative path */ else { /* This doesn't try to calculate an absolute path, * that will be left to the server */ #if defined(LWS_WITH_TLS) - ssl = wsi->tls.use_ssl & LCCSCF_USE_SSL; + ssl = nwsi->tls.use_ssl & LCCSCF_USE_SSL; #endif ads = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS); diff --git a/lib/tls/mbedtls/mbedtls-client.c b/lib/tls/mbedtls/mbedtls-client.c index d50ee4d97..8a1b63e61 100644 --- a/lib/tls/mbedtls/mbedtls-client.c +++ b/lib/tls/mbedtls/mbedtls-client.c @@ -177,8 +177,8 @@ lws_tls_client_confirm_peer_cert(struct lws *wsi, char *ebuf, int ebuf_len) return 0; } lws_snprintf(ebuf, ebuf_len, - "server's cert didn't look good, X509_V_ERR = %d: %s\n", - n, ERR_error_string(n, sb)); + "server's cert didn't look good, (use_ssl 0x%x) X509_V_ERR = %d: %s\n", + (unsigned int)wsi->tls.use_ssl, n, ERR_error_string(n, sb)); lwsl_info("%s\n", ebuf); lws_tls_err_describe_clear(); diff --git a/minimal-examples/http-client/minimal-http-client-post/minimal-http-client-post.c b/minimal-examples/http-client/minimal-http-client-post/minimal-http-client-post.c index 3f139f1e1..b291efb77 100644 --- a/minimal-examples/http-client/minimal-http-client-post/minimal-http-client-post.c +++ b/minimal-examples/http-client/minimal-http-client-post/minimal-http-client-post.c @@ -96,11 +96,17 @@ callback_http(struct lws *wsi, enum lws_callback_reasons reason, /* * Tell lws we are going to send the body next... */ - lws_client_http_body_pending(wsi, 1); - lws_callback_on_writable(wsi); + if (!lws_http_is_redirected_to_get(wsi)) { + lwsl_user("%s: doing POST flow\n", __func__); + lws_client_http_body_pending(wsi, 1); + lws_callback_on_writable(wsi); + } else + lwsl_user("%s: doing GET flow\n", __func__); break; case LWS_CALLBACK_CLIENT_HTTP_WRITEABLE: + if (lws_http_is_redirected_to_get(wsi)) + break; lwsl_user("LWS_CALLBACK_CLIENT_HTTP_WRITEABLE\n"); n = LWS_WRITE_HTTP; @@ -236,6 +242,9 @@ int main(int argc, const char **argv) i.path = "/testserver/formtest"; } + if (lws_cmdline_option(argc, argv, "--form1")) + i.path = "/form1"; + i.host = i.address; i.origin = i.address; i.method = "POST"; diff --git a/minimal-examples/http-server/minimal-http-server-form-post/minimal-http-server-form-post.c b/minimal-examples/http-server/minimal-http-server-form-post/minimal-http-server-form-post.c index c7d09439e..4711fc261 100644 --- a/minimal-examples/http-server/minimal-http-server-form-post/minimal-http-server-form-post.c +++ b/minimal-examples/http-server/minimal-http-server-form-post/minimal-http-server-form-post.c @@ -24,7 +24,7 @@ struct pss { struct lws_spa *spa; }; -static int interrupted; +static int interrupted, use303; static const char * const param_names[] = { "text1", @@ -81,6 +81,11 @@ callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user, return -1; break; + case LWS_CALLBACK_CLOSED_CLIENT_HTTP: + if (pss->spa && lws_spa_destroy(pss->spa)) + return -1; + break; + case LWS_CALLBACK_HTTP_BODY_COMPLETION: /* inform the spa no more payload data coming */ @@ -90,22 +95,27 @@ callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user, /* we just dump the decoded things to the log */ - for (n = 0; n < (int)LWS_ARRAY_SIZE(param_names); n++) { - if (!lws_spa_get_string(pss->spa, n)) - lwsl_user("%s: undefined\n", param_names[n]); - else - lwsl_user("%s: (len %d) '%s'\n", - param_names[n], - lws_spa_get_length(pss->spa, n), - lws_spa_get_string(pss->spa, n)); - } + if (pss->spa) + for (n = 0; n < (int)LWS_ARRAY_SIZE(param_names); n++) { + if (!lws_spa_get_string(pss->spa, n)) + lwsl_user("%s: undefined\n", param_names[n]); + else + lwsl_user("%s: (len %d) '%s'\n", + param_names[n], + lws_spa_get_length(pss->spa, n), + lws_spa_get_string(pss->spa, n)); + } + + if (pss->spa && lws_spa_destroy(pss->spa)) + return -1; /* * Our response is to redirect to a static page. We could * have generated a dynamic html page here instead. */ - if (lws_http_redirect(wsi, HTTP_STATUS_MOVED_PERMANENTLY, + if (lws_http_redirect(wsi, use303 ? HTTP_STATUS_SEE_OTHER : + HTTP_STATUS_MOVED_PERMANENTLY, (unsigned char *)"after-form1.html", 16, &p, end) < 0) return -1; @@ -192,6 +202,11 @@ int main(int argc, const char **argv) info.ssl_private_key_filepath = "localhost-100y.key"; } + if (lws_cmdline_option(argc, argv, "--303")) { + lwsl_user("%s: using 303 redirect\n", __func__); + use303 = 1; + } + context = lws_create_context(&info); if (!context) { lwsl_err("lws init failed\n");