diff --git a/changelog b/changelog index 6d883e58..9a34696c 100644 --- a/changelog +++ b/changelog @@ -162,6 +162,10 @@ LWS_SERVER_OPTION_SSL_ECD to build in support and select it at runtime. +6) There's a new api lws_parse_uri() that simplies chopping up +https://xxx:yyy/zzz uris into parts nicely. The test client now uses this +to allow proper uris. + User api changes ---------------- diff --git a/lib/client-handshake.c b/lib/client-handshake.c index d6f637bc..259dcbae 100644 --- a/lib/client-handshake.c +++ b/lib/client-handshake.c @@ -313,6 +313,56 @@ failed: return NULL; } + +/** + * lws_client_reset() - retarget a connected wsi to start over with a new connection (ie, redirect) + * this only works if still in HTTP, ie, not upgraded yet + * wsi: connection to reset + * address: network address of the new server + * port: port to connect to + * path: uri path to connect to on the new server + * host: host header to send to the new server + */ +LWS_VISIBLE struct lws * +lws_client_reset(struct lws *wsi, int ssl, const char *address, int port, const char *path, const char *host) +{ + if (wsi->u.hdr.redirects == 3) { + lwsl_err("%s: Too many redirects\n", __func__); + return NULL; + } + wsi->u.hdr.redirects++; + +#ifdef LWS_OPENSSL_SUPPORT + wsi->use_ssl = ssl; +#else + if (ssl) { + lwsl_err("%s: not configured for ssl\n", __func__); + return NULL; + } +#endif + + lwsl_notice("redirect ads='%s', port=%d, path='%s'\n", address, port, path); + + if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS, address)) + return NULL; + + if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_URI, path)) + return NULL; + + if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_HOST, host)) + return NULL; + + close(wsi->sock); + remove_wsi_socket_from_fds(wsi); + wsi->sock = LWS_SOCK_INVALID; + wsi->state = LWSS_CLIENT_UNCONNECTED; + wsi->protocol = NULL; + wsi->pending_timeout = NO_PENDING_TIMEOUT; + wsi->u.hdr.ah->c_port = port; + + return lws_client_connect_2(wsi); +} + /** * lws_client_connect_via_info() - Connect to another websocket server * @i:pointer to lws_client_connect_info struct diff --git a/lib/client.c b/lib/client.c index a8f36738..bd84015c 100644 --- a/lib/client.c +++ b/lib/client.c @@ -509,8 +509,8 @@ lws_client_interpret_server_handshake(struct lws *wsi) { struct lws_context *context = wsi->context; int close_reason = LWS_CLOSE_STATUS_PROTOCOL_ERR; - int n, len, okay = 0, isErrorCodeReceived = 0; - const char *pc; + int n, len, okay = 0, isErrorCodeReceived = 0, port = 0, ssl = 0; + const char *pc, *prot, *ads = NULL, *path; char *p; #ifndef LWS_NO_EXTENSIONS const struct lws_extension *ext; @@ -532,7 +532,24 @@ lws_client_interpret_server_handshake(struct lws *wsi) lwsl_info("no URI\n"); goto bail3; } + n = atoi(p); + if (n == 301 || n == 302 || n == 303 || n == 307 || n == 308) { + p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_LOCATION); + if (!p) + goto bail3; + if (lws_parse_uri(p, &prot, &ads, &port, &path)) + goto bail3; + + if (!strcmp(prot, "wss://") || !strcmp(prot, "https://")) + ssl = 1; + + if (lws_client_reset(wsi, ssl, ads, port, path, ads)) { + lwsl_err("Redirect failed\n"); + goto bail3; + } + return 0; + } if (lws_hdr_total_length(wsi, WSI_TOKEN_ACCEPT) == 0) { lwsl_info("no ACCEPT\n"); isErrorCodeReceived = 1; diff --git a/lib/libwebsockets.c b/lib/libwebsockets.c index bd2bc0da..d1d95807 100644 --- a/lib/libwebsockets.c +++ b/lib/libwebsockets.c @@ -1096,6 +1096,17 @@ lws_check_utf8(unsigned char *state, unsigned char *buf, size_t len) return 0; } +/** + * lws_parse_uri: cut up https:/xxx:yyy/zzz into pieces + * Notice it does so by dropping '\0' into input string + * + * @p: incoming uri string.. will get written to + * @prot: result pointer for protocol part (https://) + * @ads: result pointer for address part + * @port: result pointer for port part + * @path: result pointer for path part + */ + LWS_VISIBLE LWS_EXTERN int lws_parse_uri(char *p, const char **prot, const char **ads, int *port, const char **path) { diff --git a/lib/private-libwebsockets.h b/lib/private-libwebsockets.h index d7f069cd..77e37c13 100644 --- a/lib/private-libwebsockets.h +++ b/lib/private-libwebsockets.h @@ -836,6 +836,7 @@ struct _lws_header_related { char esc_stash; char post_literal_equal; unsigned char parser_state; /* enum lws_token_indexes */ + char redirects; }; struct _lws_websocket_related { @@ -1034,6 +1035,10 @@ lws_service_timeout_check(struct lws *wsi, unsigned int sec); LWS_EXTERN struct lws * lws_client_connect_2(struct lws *wsi); +LWS_VISIBLE struct lws * +lws_client_reset(struct lws *wsi, int ssl, const char *address, int port, const char *path, const char *host); + + LWS_EXTERN struct lws * lws_create_new_server_wsi(struct lws_context *context); diff --git a/test-server/test-client.c b/test-server/test-client.c index 475b8b14..1458d22d 100644 --- a/test-server/test-client.c +++ b/test-server/test-client.c @@ -274,7 +274,7 @@ int main(int argc, char **argv) struct lws_context_creation_info info; struct lws_client_connect_info i; struct lws_context *context; - const char *address; + const char *prot; memset(&info, 0, sizeof info); @@ -321,7 +321,16 @@ int main(int argc, char **argv) signal(SIGINT, sighandler); - address = argv[optind]; + memset(&i, 0, sizeof(i)); + + i.port = port; + if (lws_parse_uri(argv[optind], &prot, &i.address, &i.port, &i.path)) + goto usage; + + if (!strcmp(prot, "http://") || !strcmp(prot, "ws://")) + use_ssl = 0; + if (!strcmp(prot, "https://") || !strcmp(prot, "wss://")) + use_ssl = 1; /* * create the websockets context. This tracks open connections and @@ -342,15 +351,10 @@ int main(int argc, char **argv) return 1; } - memset(&i, 0, sizeof(i)); - i.context = context; - i.address = address; - i.port = port; i.ssl_connection = use_ssl; - i.path = "/"; - i.host = argv[optind]; - i.origin = argv[optind]; + i.host = i.address; + i.origin = i.address; i.ietf_version_or_minus_one = ietf_version; i.client_exts = exts; /*