1
0
Fork 0
mirror of https://github.com/warmcat/libwebsockets.git synced 2025-03-09 00:00:04 +01:00

http redirect: 303: force method to GET

This teaches http client stuff how to handle 303 redirects... these
can happen after POST where the server side wants you to come back with
a GET to the Location: mentioned.

lws client will follow the redirect and force GET, this works for both
h1 and h2.  Client protocol handler has to act differently if it finds
it is connecting for the initial POST or the subsequent GET, it can
find out which by checking a new api lws_http_is_redirected_to_get(wsi)
which returns nonzero if in GET mode.

Minimal example for server form-post has a new --303 switch to enable
this behaviour there and the client post example has additions to
check lws_http_is_redirected_to_get().
This commit is contained in:
Andy Green 2019-11-17 10:47:01 +00:00
parent 297aa86b60
commit 0f7f27801e
8 changed files with 128 additions and 27 deletions

View file

@ -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);
///@}

View file

@ -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);

View file

@ -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;

View file

@ -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)

View file

@ -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);

View file

@ -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();

View file

@ -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";

View file

@ -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");