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:
parent
297aa86b60
commit
0f7f27801e
8 changed files with 128 additions and 27 deletions
|
@ -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);
|
||||
///@}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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");
|
||||
|
|
Loading…
Add table
Reference in a new issue