diff --git a/lib/core-net/close.c b/lib/core-net/close.c
index 074e1fac0..b72b98a78 100644
--- a/lib/core-net/close.c
+++ b/lib/core-net/close.c
@@ -326,7 +326,6 @@ just_kill_connection:
if (wsi->role_ops->close_kill_connection)
wsi->role_ops->close_kill_connection(wsi, reason);
- lws_remove_child_from_any_parent(wsi);
n = 0;
if (!wsi->told_user_closed && wsi->user_space &&
@@ -469,6 +468,7 @@ just_kill_connection:
}
async_close:
+ lws_remove_child_from_any_parent(wsi);
wsi->socket_is_permanently_unusable = 1;
if (wsi->context->event_loop_ops->wsi_logical_close)
diff --git a/lib/core-net/dummy-callback.c b/lib/core-net/dummy-callback.c
index e66395361..94b28b160 100644
--- a/lib/core-net/dummy-callback.c
+++ b/lib/core-net/dummy-callback.c
@@ -83,6 +83,143 @@ stream_close(struct lws *wsi)
#endif
+struct lws_proxy_pkt {
+ struct lws_dll pkt_list;
+ size_t len;
+ char binary;
+ char first;
+ char final;
+
+ /* data follows */
+};
+
+#if defined(LWS_WITH_HTTP_PROXY) && defined(LWS_ROLE_WS)
+int
+lws_callback_ws_proxy(struct lws *wsi, enum lws_callback_reasons reason,
+ void *user, void *in, size_t len)
+{
+ struct lws_proxy_pkt *pkt;
+ struct lws_dll *dll;
+
+ switch (reason) {
+
+ /* h1 ws proxying... child / client / onward */
+
+ case LWS_CALLBACK_CLIENT_ESTABLISHED:
+ if (!wsi->h1_ws_proxied || !wsi->parent)
+ break;
+
+ lws_process_ws_upgrade2(wsi->parent);
+
+#if defined(LWS_WITH_HTTP2)
+ if (wsi->parent->http2_substream)
+ lwsl_info("%s: proxied h2 -> h1 ws established\n", __func__);
+#endif
+ break;
+
+
+ case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
+ case LWS_CALLBACK_CLIENT_CLOSED:
+ lwsl_user("%s: client closed: parent %p\n", __func__, wsi->parent);
+ if (wsi->parent)
+ lws_set_timeout(wsi->parent, 1, LWS_TO_KILL_ASYNC);
+ break;
+
+ case LWS_CALLBACK_CLIENT_RECEIVE:
+ pkt = lws_malloc(sizeof(*pkt) + LWS_PRE + len, __func__);
+ if (!pkt)
+ return -1;
+
+ pkt->pkt_list.prev = pkt->pkt_list.next = NULL;
+ pkt->len = len;
+ pkt->first = lws_is_first_fragment(wsi);
+ pkt->final = lws_is_final_fragment(wsi);
+ pkt->binary = lws_frame_is_binary(wsi);
+
+ memcpy(((uint8_t *)&pkt[1]) + LWS_PRE, in, len);
+
+ lws_dll_add_tail(&pkt->pkt_list, &wsi->parent->ws->proxy_head);
+ lws_callback_on_writable(wsi->parent);
+ break;
+
+ case LWS_CALLBACK_CLIENT_WRITEABLE:
+ dll = lws_dll_get_tail(&wsi->ws->proxy_head);
+ if (!dll)
+ break;
+
+ pkt = (struct lws_proxy_pkt *)dll;
+ if (lws_write(wsi, ((unsigned char *)&pkt[1]) +
+ LWS_PRE, pkt->len, lws_write_ws_flags(
+ pkt->binary ? LWS_WRITE_BINARY : LWS_WRITE_TEXT,
+ pkt->first, pkt->final)) < 0)
+ return -1;
+
+ lws_dll_remove_track_tail(dll, &wsi->ws->proxy_head);
+ lws_free(pkt);
+
+ if (lws_dll_get_tail(&wsi->ws->proxy_head))
+ lws_callback_on_writable(wsi);
+ break;
+
+ /* h1 ws proxying... parent / server / incoming */
+
+ case LWS_CALLBACK_CLOSED:
+ lwsl_user("%s: closed\n", __func__);
+ return -1;
+
+ case LWS_CALLBACK_RECEIVE:
+ pkt = lws_malloc(sizeof(*pkt) + LWS_PRE + len, __func__);
+ if (!pkt)
+ return -1;
+
+ pkt->pkt_list.prev = pkt->pkt_list.next = NULL;
+ pkt->len = len;
+ pkt->first = lws_is_first_fragment(wsi);
+ pkt->final = lws_is_final_fragment(wsi);
+ pkt->binary = lws_frame_is_binary(wsi);
+
+ memcpy(((uint8_t *)&pkt[1]) + LWS_PRE, in, len);
+
+ lws_dll_add_tail(&pkt->pkt_list, &wsi->child_list->ws->proxy_head);
+ lws_callback_on_writable(wsi->child_list);
+ break;
+
+ case LWS_CALLBACK_SERVER_WRITEABLE:
+ dll = lws_dll_get_tail(&wsi->ws->proxy_head);
+ if (!dll)
+ break;
+
+ pkt = (struct lws_proxy_pkt *)dll;
+ if (lws_write(wsi, ((unsigned char *)&pkt[1]) +
+ LWS_PRE, pkt->len, lws_write_ws_flags(
+ pkt->binary ? LWS_WRITE_BINARY : LWS_WRITE_TEXT,
+ pkt->first, pkt->final)) < 0)
+ return -1;
+
+ lws_dll_remove_track_tail(dll, &wsi->ws->proxy_head);
+ lws_free(pkt);
+
+ if (lws_dll_get_tail(&wsi->ws->proxy_head))
+ lws_callback_on_writable(wsi);
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+const struct lws_protocols lws_ws_proxy = {
+ "lws-ws-proxy",
+ lws_callback_ws_proxy,
+ 0,
+ 8192,
+ 8192, NULL, 0
+};
+
+#endif
+
LWS_VISIBLE int
lws_callback_http_dummy(struct lws *wsi, enum lws_callback_reasons reason,
void *user, void *in, size_t len)
@@ -269,8 +406,8 @@ lws_callback_http_dummy(struct lws *wsi, enum lws_callback_reasons reason,
return -1;
break; }
+ /* h1 http proxying... */
- /* this handles the proxy case... */
case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP: {
unsigned char *start, *p, *end;
diff --git a/lib/core-net/private.h b/lib/core-net/private.h
index ae8c85ab0..feade9f36 100644
--- a/lib/core-net/private.h
+++ b/lib/core-net/private.h
@@ -577,6 +577,8 @@ struct lws {
unsigned int protocol_bind_balance:1;
unsigned int unix_skt:1;
unsigned int close_when_buffered_out_drained:1;
+ unsigned int h1_ws_proxied;
+ unsigned int proxied_ws_parent;
unsigned int could_have_pending:1; /* detect back-to-back writes */
unsigned int outer_will_close:1;
diff --git a/lib/core-net/vhost.c b/lib/core-net/vhost.c
index b66d13a70..2a00c1257 100644
--- a/lib/core-net/vhost.c
+++ b/lib/core-net/vhost.c
@@ -425,7 +425,7 @@ lws_create_vhost(struct lws_context *context,
struct lws_plugin *plugin = context->plugin_list;
#endif
struct lws_protocols *lwsp;
- int m, f = !info->pvo;
+ int m, f = !info->pvo, fx = 0;
char buf[20];
#if !defined(LWS_WITHOUT_CLIENT) && defined(LWS_HAVE_GETENV)
char *p;
@@ -461,6 +461,11 @@ lws_create_vhost(struct lws_context *context,
vh->bind_iface = info->bind_iface;
#endif
+ /*
+ * let's figure out how many protocols the user is handing us, using the
+ * old or new way depending on what he gave us
+ */
+
if (!pcols)
for (vh->count_protocols = 0;
info->pprotocols[vh->count_protocols];
@@ -529,12 +534,16 @@ lws_create_vhost(struct lws_context *context,
}
#endif
+#if defined(LWS_WITH_HTTP_PROXY) && defined(LWS_ROLE_WS)
+ fx = 1;
+#endif
+
/*
* give the vhost a unified list of protocols including the
* ones that came from plugins
*/
lwsp = lws_zalloc(sizeof(struct lws_protocols) * (vh->count_protocols +
- context->plugin_protocol_count + 1),
+ context->plugin_protocol_count + fx + 1),
"vhost-specific plugin table");
if (!lwsp) {
lwsl_err("OOM\n");
@@ -548,7 +557,8 @@ lws_create_vhost(struct lws_context *context,
} else
memcpy(lwsp, pcols, sizeof(struct lws_protocols) * m);
- /* for compatibility, all protocols enabled on vhost if only
+ /*
+ * For compatibility, all protocols enabled on vhost if only
* the default vhost exists. Otherwise only vhosts who ask
* for a protocol get it enabled.
*/
@@ -579,6 +589,11 @@ lws_create_vhost(struct lws_context *context,
}
#endif
+#if defined(LWS_WITH_HTTP_PROXY) && defined(LWS_ROLE_WS)
+ memcpy(&lwsp[m++], &lws_ws_proxy, sizeof(lws_ws_proxy));
+ vh->count_protocols++;
+#endif
+
if (!pcols ||
#ifdef LWS_WITH_PLUGINS
(context->plugin_list) ||
@@ -978,6 +993,7 @@ __lws_vhost_destroy2(struct lws_vhost *vh)
n = 0;
while (n < vh->count_protocols) {
wsi.protocol = protocol;
+
if (protocol->callback)
protocol->callback(&wsi, LWS_CALLBACK_PROTOCOL_DESTROY,
NULL, NULL, 0);
diff --git a/lib/roles/h2/http2.c b/lib/roles/h2/http2.c
index 888eb5375..9bf320ca4 100644
--- a/lib/roles/h2/http2.c
+++ b/lib/roles/h2/http2.c
@@ -2213,22 +2213,36 @@ lws_h2_ws_handshake(struct lws *wsi)
if (lws_hdr_total_length(wsi, WSI_TOKEN_PROTOCOL) > 64)
return -1;
- /* we can only return the protocol header if:
- * - one came in, and ... */
- if (lws_hdr_total_length(wsi, WSI_TOKEN_PROTOCOL) &&
- /* - it is not an empty string */
- wsi->protocol->name && wsi->protocol->name[0]) {
- if (lws_add_http_header_by_token(wsi, WSI_TOKEN_PROTOCOL,
- (unsigned char *)wsi->protocol->name,
- (int)strlen(wsi->protocol->name),
- &p, end))
- return -1;
+ if (wsi->proxied_ws_parent && wsi->child_list) {
+ if (lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL)) {
+ if (lws_add_http_header_by_token(wsi, WSI_TOKEN_PROTOCOL,
+ (uint8_t *)lws_hdr_simple_ptr(wsi,
+ WSI_TOKEN_PROTOCOL),
+ strlen(lws_hdr_simple_ptr(wsi,
+ WSI_TOKEN_PROTOCOL)),
+ &p, end))
+ return -1;
+ }
+ } else {
+
+ /* we can only return the protocol header if:
+ * - one came in, and ... */
+ if (lws_hdr_total_length(wsi, WSI_TOKEN_PROTOCOL) &&
+ /* - it is not an empty string */
+ wsi->protocol->name && wsi->protocol->name[0]) {
+ if (lws_add_http_header_by_token(wsi, WSI_TOKEN_PROTOCOL,
+ (unsigned char *)wsi->protocol->name,
+ (int)strlen(wsi->protocol->name),
+ &p, end))
+ return -1;
+ }
}
if (lws_finalize_http_header(wsi, &p, end))
return -1;
m = lws_ptr_diff(p, start);
+ // lwsl_hexdump_notice(start, m);
n = lws_write(wsi, start, m, LWS_WRITE_HTTP_HEADERS);
if (n != m) {
lwsl_err("_write returned %d from %d\n", n, m);
diff --git a/lib/roles/http/private.h b/lib/roles/http/private.h
index 08fd105de..891409ed4 100644
--- a/lib/roles/http/private.h
+++ b/lib/roles/http/private.h
@@ -298,3 +298,7 @@ _lws_destroy_ah(struct lws_context_per_thread *pt, struct allocated_headers *ah)
int
lws_http_get_uri_and_method(struct lws *wsi, char **puri_ptr, int *puri_len);
+
+int
+lws_http_proxy_start(struct lws *wsi, const struct lws_http_mount *hit,
+ char *uri_ptr, char ws);
diff --git a/lib/roles/http/server/server.c b/lib/roles/http/server/server.c
index cbf83866d..fdc5cf00a 100644
--- a/lib/roles/http/server/server.c
+++ b/lib/roles/http/server/server.c
@@ -982,6 +982,173 @@ lws_check_basic_auth(struct lws *wsi, const char *basic_auth_login_file)
return LCBA_CONTINUE;
}
+#if defined(LWS_WITH_HTTP_PROXY)
+/*
+ * Set up an onward http proxy connection according to the mount this
+ * uri falls under. Notice this can also be starting the proxying of what was
+ * originally an incoming h1 upgrade, or an h2 ws "upgrade".
+ */
+int
+lws_http_proxy_start(struct lws *wsi, const struct lws_http_mount *hit,
+ char *uri_ptr, char ws)
+{
+ char ads[96], rpath[256], host[96], *pcolon, *pslash, unix_skt = 0;
+ struct lws_client_connect_info i;
+ struct lws *cwsi;
+ int n, na;
+
+ if (ws)
+ /*
+ * Neither our inbound ws upgrade request side, nor our onward
+ * ws client connection on our side can bind to the actual
+ * protocol that only the remote inbound side and the remote
+ * onward side understand.
+ *
+ * Instead these are both bound to our built-in "lws-ws-proxy"
+ * protocol, which understands how to proxy between the two
+ * sides.
+ *
+ * We bind the parent, inbound part here and our side of the
+ * onward client connection is bound to the same handler using
+ * the .local_protocol_name.
+ */
+ lws_bind_protocol(wsi, &lws_ws_proxy, __func__);
+
+ memset(&i, 0, sizeof(i));
+ i.context = lws_get_context(wsi);
+
+ if (hit->origin[0] == '+')
+ unix_skt = 1;
+
+ pcolon = strchr(hit->origin, ':');
+ pslash = strchr(hit->origin, '/');
+ if (!pslash) {
+ lwsl_err("Proxy mount origin '%s' must have /\n", hit->origin);
+ return -1;
+ }
+
+ if (unix_skt) {
+ if (!pcolon) {
+ lwsl_err("Proxy mount origin for unix skt must "
+ "have address delimited by :\n");
+
+ return -1;
+ }
+ n = lws_ptr_diff(pcolon, hit->origin);
+ pslash = pcolon;
+ } else {
+ if (pcolon > pslash)
+ pcolon = NULL;
+
+ if (pcolon)
+ n = (int)(pcolon - hit->origin);
+ else
+ n = (int)(pslash - hit->origin);
+
+ if (n >= (int)sizeof(ads) - 2)
+ n = sizeof(ads) - 2;
+ }
+
+ memcpy(ads, hit->origin, n);
+ ads[n] = '\0';
+
+ i.address = ads;
+ i.port = 80;
+ if (hit->origin_protocol == LWSMPRO_HTTPS) {
+ i.port = 443;
+ i.ssl_connection = 1;
+ }
+ if (pcolon)
+ i.port = atoi(pcolon + 1);
+
+ n = lws_snprintf(rpath, sizeof(rpath) - 1, "/%s/%s",
+ pslash + 1, uri_ptr + hit->mountpoint_len) - 2;
+ lws_clean_url(rpath);
+ na = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_URI_ARGS);
+ if (na) {
+ char *p = rpath + n;
+
+ if (na >= (int)sizeof(rpath) - n - 2) {
+ lwsl_info("%s: query string %d longer "
+ "than we can handle\n", __func__,
+ na);
+
+ return -1;
+ }
+
+ *p++ = '?';
+ if (lws_hdr_copy(wsi, p,
+ (int)(&rpath[sizeof(rpath) - 1] - p),
+ WSI_TOKEN_HTTP_URI_ARGS) > 0)
+ while (na--) {
+ if (*p == '\0')
+ *p = '&';
+ p++;
+ }
+ *p = '\0';
+ }
+
+ i.path = rpath;
+ if (i.address[0] != '+' ||
+ !lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST))
+ i.host = i.address;
+ else
+ i.host = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST);
+ i.origin = NULL;
+ if (!ws)
+ i.method = "GET";
+
+ lws_snprintf(host, sizeof(host), "%s:%d", i.address, i.port);
+ i.host = host;
+
+ i.alpn = "http/1.1";
+ i.parent_wsi = wsi;
+ i.pwsi = &cwsi;
+ i.protocol = lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL);
+ if (ws)
+ i.local_protocol_name = "lws-ws-proxy";
+
+// i.uri_replace_from = hit->origin;
+// i.uri_replace_to = hit->mountpoint;
+
+ lwsl_info("proxying to %s port %d url %s, ssl %d, from %s, to %s\n",
+ i.address, i.port, i.path, i.ssl_connection,
+ i.uri_replace_from, i.uri_replace_to);
+
+ if (!lws_client_connect_via_info(&i)) {
+ lwsl_err("proxy connect fail\n");
+
+ /*
+ * ... we can't do the proxy action, but we can
+ * cleanly return him a 503 and a description
+ */
+
+ lws_return_http_status(wsi,
+ HTTP_STATUS_SERVICE_UNAVAILABLE,
+ "
Service Temporarily Unavailable
"
+ "The server is temporarily unable to service "
+ "your request due to maintenance downtime or "
+ "capacity problems. Please try again later.");
+
+ return 1;
+ }
+
+ lwsl_info("%s: setting proxy clientside on %p (parent %p)\n",
+ __func__, cwsi, lws_get_parent(cwsi));
+
+ cwsi->http.proxy_clientside = 1;
+ if (ws) {
+ wsi->proxied_ws_parent = 1;
+ cwsi->h1_ws_proxied = 1;
+ if (i.protocol) {
+ lwsl_debug("%s: (requesting '%s')\n", __func__, i.protocol);
+ }
+ }
+
+ return 0;
+}
+#endif
+
static const char * const oprot[] = {
"http://", "https://"
};
@@ -1238,131 +1405,8 @@ lws_http_action(struct lws *wsi)
// lwsl_notice("%s: origin_protocol: %d\n", __func__, hit->origin_protocol);
if (hit->origin_protocol == LWSMPRO_HTTPS ||
- hit->origin_protocol == LWSMPRO_HTTP) {
- char ads[96], rpath[256], *pcolon, *pslash, unix_skt = 0;
- struct lws_client_connect_info i;
- struct lws *cwsi;
- int n, na;
-
- memset(&i, 0, sizeof(i));
- i.context = lws_get_context(wsi);
-
- if (hit->origin[0] == '+')
- unix_skt = 1;
-
- pcolon = strchr(hit->origin, ':');
- pslash = strchr(hit->origin, '/');
- if (!pslash) {
- lwsl_err("Proxy mount origin '%s' must have /\n",
- hit->origin);
- return -1;
- }
-
- if (unix_skt) {
- if (!pcolon) {
- lwsl_err("Proxy mount origin for unix skt must "
- "have address delimited by :\n");
-
- return -1;
- }
- n = lws_ptr_diff(pcolon, hit->origin);
- pslash = pcolon;
- } else {
- if (pcolon > pslash)
- pcolon = NULL;
-
- if (pcolon)
- n = (int)(pcolon - hit->origin);
- else
- n = (int)(pslash - hit->origin);
-
- if (n >= (int)sizeof(ads) - 2)
- n = sizeof(ads) - 2;
- }
-
- memcpy(ads, hit->origin, n);
- ads[n] = '\0';
-
- i.address = ads;
- i.port = 80;
- if (hit->origin_protocol == LWSMPRO_HTTPS) {
- i.port = 443;
- i.ssl_connection = 1;
- }
- if (pcolon)
- i.port = atoi(pcolon + 1);
-
- n = lws_snprintf(rpath, sizeof(rpath) - 1, "/%s/%s",
- pslash + 1, uri_ptr + hit->mountpoint_len) - 2;
- lws_clean_url(rpath);
- na = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_URI_ARGS);
- if (na) {
- char *p = rpath + n;
-
- if (na >= (int)sizeof(rpath) - n - 2) {
- lwsl_info("%s: query string %d longer "
- "than we can handle\n", __func__,
- na);
-
- return -1;
- }
-
- *p++ = '?';
- if (lws_hdr_copy(wsi, p,
- (int)(&rpath[sizeof(rpath) - 1] - p),
- WSI_TOKEN_HTTP_URI_ARGS) > 0)
- while (na--) {
- if (*p == '\0')
- *p = '&';
- p++;
- }
- *p = '\0';
- }
-
- i.path = rpath;
- if (i.address[0] != '+' ||
- !lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST))
- i.host = i.address;
- else
- i.host = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST);
- i.origin = NULL;
- i.method = "GET";
- i.alpn = "http/1.1";
- i.parent_wsi = wsi;
- i.pwsi = &cwsi;
-
- // i.uri_replace_from = hit->origin;
- // i.uri_replace_to = hit->mountpoint;
-
- lwsl_info("proxying to %s port %d url %s, ssl %d, "
- "from %s, to %s\n",
- i.address, i.port, i.path, i.ssl_connection,
- i.uri_replace_from, i.uri_replace_to);
-
- if (!lws_client_connect_via_info(&i)) {
- lwsl_err("proxy connect fail\n");
-
- /*
- * ... we can't do the proxy action, but we can
- * cleanly return him a 503 and a description
- */
-
- lws_return_http_status(wsi,
- HTTP_STATUS_SERVICE_UNAVAILABLE,
- "Service Temporarily Unavailable
"
- "The server is temporarily unable to service "
- "your request due to maintenance downtime or "
- "capacity problems. Please try again later.");
-
- return 1;
- }
-
- lwsl_info("%s: setting proxy clientside on %p (parent %p)\n",
- __func__, cwsi, lws_get_parent(cwsi));
- cwsi->http.proxy_clientside = 1;
-
- return 0;
- }
+ hit->origin_protocol == LWSMPRO_HTTP)
+ return lws_http_proxy_start(wsi, hit, uri_ptr, 0);
#endif
/*
diff --git a/lib/roles/ws/client-parser-ws.c b/lib/roles/ws/client-parser-ws.c
index f5aaa6dbb..f6802953a 100644
--- a/lib/roles/ws/client-parser-ws.c
+++ b/lib/roles/ws/client-parser-ws.c
@@ -452,7 +452,7 @@ ping_drop:
break;
case LWSWSOPC_PONG:
- lwsl_info("client received pong\n");
+ lwsl_info("%s: client %p received pong\n", __func__, wsi);
lwsl_hexdump(&wsi->ws->rx_ubuf[LWS_PRE],
wsi->ws->rx_ubuf_head);
diff --git a/lib/roles/ws/client-ws.c b/lib/roles/ws/client-ws.c
index d88833f38..cabca0fb7 100644
--- a/lib/roles/ws/client-ws.c
+++ b/lib/roles/ws/client-ws.c
@@ -334,6 +334,11 @@ bad_conn_format:
goto bail2;
}
+#if defined(LWS_WITH_HTTP_PROXY)
+ lws_strncpy(wsi->ws->actual_protocol, p,
+ sizeof(wsi->ws->actual_protocol));
+#endif
+
/*
* identify the selected protocol struct and set it
*/
diff --git a/lib/roles/ws/ops-ws.c b/lib/roles/ws/ops-ws.c
index 6247bdbeb..7f1d5e13e 100644
--- a/lib/roles/ws/ops-ws.c
+++ b/lib/roles/ws/ops-ws.c
@@ -1261,7 +1261,9 @@ int rops_handle_POLLOUT_ws(struct lws *wsi)
if (!wsi->socket_is_permanently_unusable && wsi->ws->send_check_ping) {
- lwsl_info("issuing ping on wsi %p\n", wsi);
+ lwsl_info("%s: issuing ping on wsi %p: %s %s h2: %d\n", __func__, wsi,
+ wsi->role_ops->name, wsi->protocol->name,
+ wsi->http2_substream);
wsi->ws->send_check_ping = 0;
n = lws_write(wsi, &wsi->ws->ping_payload_buf[LWS_PRE],
0, LWS_WRITE_PING);
@@ -1421,7 +1423,7 @@ rops_periodic_checks_ws(struct lws_context *context, int tsi, time_t now)
struct lws *wsi = lws_container_of(d,
struct lws, same_vh_protocol);
- if (lwsi_role_ws(wsi) &&
+ if (lwsi_role_ws(wsi) && !wsi->http2_substream &&
!wsi->socket_is_permanently_unusable &&
!wsi->ws->send_check_ping &&
wsi->ws->time_next_ping_check &&
@@ -1429,7 +1431,7 @@ rops_periodic_checks_ws(struct lws_context *context, int tsi, time_t now)
wsi->ws->time_next_ping_check) >
context->ws_ping_pong_interval) {
- lwsl_info("req pp on wsi %p\n", wsi);
+ lwsl_info("%s: req pp on wsi %p\n", __func__, wsi);
wsi->ws->send_check_ping = 1;
lws_set_timeout(wsi,
PENDING_TIMEOUT_WS_PONG_CHECK_SEND_PING,
diff --git a/lib/roles/ws/private.h b/lib/roles/ws/private.h
index 71ffcaea9..e58264d6d 100644
--- a/lib/roles/ws/private.h
+++ b/lib/roles/ws/private.h
@@ -92,6 +92,12 @@ struct _lws_websocket_related {
struct lws *rx_draining_ext_list;
struct lws *tx_draining_ext_list;
#endif
+
+#if defined(LWS_WITH_HTTP_PROXY)
+ struct lws_dll proxy_head;
+ char actual_protocol[16];
+#endif
+
/* Also used for close content... control opcode == < 128 */
uint8_t ping_payload_buf[128 - 3 + LWS_PRE];
uint8_t mask[4];
@@ -160,5 +166,11 @@ int
handshake_0405(struct lws_context *context, struct lws *wsi);
int
lws_process_ws_upgrade(struct lws *wsi);
+
+int
+lws_process_ws_upgrade2(struct lws *wsi);
+
+extern const struct lws_protocols lws_ws_proxy;
+
int
lws_server_init_wsi_for_ws(struct lws *wsi);
diff --git a/lib/roles/ws/server-ws.c b/lib/roles/ws/server-ws.c
index 688e81cc9..80e3e7794 100644
--- a/lib/roles/ws/server-ws.c
+++ b/lib/roles/ws/server-ws.c
@@ -249,145 +249,11 @@ lws_extension_server_handshake(struct lws *wsi, char **p, int budget)
#endif
int
-lws_process_ws_upgrade(struct lws *wsi)
+lws_process_ws_upgrade2(struct lws *wsi)
{
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
const struct lws_protocol_vhost_options *pvos = NULL;
- const struct lws_protocols *pcol = NULL;
const char *ws_prot_basic_auth = NULL;
- char buf[128], name[64];
- struct lws_tokenize ts;
- lws_tokenize_elem e;
-
- if (!wsi->protocol)
- lwsl_err("NULL protocol at lws_read\n");
-
- /*
- * It's either websocket or h2->websocket
- *
- * If we are on h1, confirm we got the required "connection: upgrade"
- * header. h2 / ws-over-h2 does not have this.
- */
-
-#if defined(LWS_WITH_HTTP2)
- if (!wsi->http2_substream) {
-#endif
-
- lws_tokenize_init(&ts, buf, LWS_TOKENIZE_F_COMMA_SEP_LIST |
- LWS_TOKENIZE_F_DOT_NONTERM |
- LWS_TOKENIZE_F_RFC7230_DELIMS |
- LWS_TOKENIZE_F_MINUS_NONTERM);
- ts.len = lws_hdr_copy(wsi, buf, sizeof(buf) - 1,
- WSI_TOKEN_CONNECTION);
- if (ts.len <= 0)
- goto bad_conn_format;
-
- do {
- e = lws_tokenize(&ts);
- switch (e) {
- case LWS_TOKZE_TOKEN:
- if (!strcasecmp(ts.token, "upgrade"))
- e = LWS_TOKZE_ENDED;
- break;
-
- case LWS_TOKZE_DELIMITER:
- break;
-
- default: /* includes ENDED */
- bad_conn_format:
- lwsl_err("%s: malformed or absent conn hdr\n",
- __func__);
-
- return 1;
- }
- } while (e > 0);
-
-#if defined(LWS_WITH_HTTP2)
- }
-#endif
-
- /*
- * Select the first protocol we support from the list
- * the client sent us.
- */
-
- lws_tokenize_init(&ts, buf, LWS_TOKENIZE_F_COMMA_SEP_LIST |
- LWS_TOKENIZE_F_MINUS_NONTERM |
- LWS_TOKENIZE_F_DOT_NONTERM |
- LWS_TOKENIZE_F_RFC7230_DELIMS);
- ts.len = lws_hdr_copy(wsi, buf, sizeof(buf) - 1, WSI_TOKEN_PROTOCOL);
- if (ts.len < 0) {
- lwsl_err("%s: protocol list too long\n", __func__);
- return 1;
- }
- if (!ts.len) {
- int n = wsi->vhost->default_protocol_index;
- /*
- * Some clients only have one protocol and do not send the
- * protocol list header... allow it and match to the vhost's
- * default protocol (which itself defaults to zero).
- *
- * Setting the vhost default protocol index to -1 or anything
- * more than the actual number of protocols on the vhost causes
- * these "no protocol" ws connections to be rejected.
- */
-
- if (n >= wsi->vhost->count_protocols) {
- lwsl_notice("%s: rejecting ws upg with no protocol\n",
- __func__);
-
- return 1;
- }
-
- lwsl_info("%s: defaulting to prot handler %d\n", __func__, n);
-
- lws_bind_protocol(wsi, &wsi->vhost->protocols[n],
- "ws upgrade default pcol");
-
- goto alloc_ws;
- }
-
- /* otherwise go through the user-provided protocol list */
-
- do {
- e = lws_tokenize(&ts);
- switch (e) {
- case LWS_TOKZE_TOKEN:
-
- if (lws_tokenize_cstr(&ts, name, sizeof(name))) {
- lwsl_err("%s: pcol name too long\n", __func__);
-
- return 1;
- }
- lwsl_debug("checking %s\n", name);
- pcol = lws_vhost_name_to_protocol(wsi->vhost, name);
- if (pcol) {
- /* if we know it, bind to it and stop looking */
- lws_bind_protocol(wsi, pcol, "ws upg pcol");
- e = LWS_TOKZE_ENDED;
- }
- break;
-
- case LWS_TOKZE_DELIMITER:
- case LWS_TOKZE_ENDED:
- break;
-
- default:
- lwsl_err("%s: malformatted protocol list", __func__);
-
- return 1;
- }
- } while (e > 0);
-
- /* we didn't find a protocol he wanted? */
-
- if (!pcol) {
- lwsl_notice("No supported protocol \"%s\"\n", buf);
-
- return 1;
- }
-
-alloc_ws:
/*
* Allow basic auth a look-in now we bound the wsi to the protocol.
@@ -521,6 +387,174 @@ alloc_ws:
return 0;
}
+int
+lws_process_ws_upgrade(struct lws *wsi)
+{
+ const struct lws_protocols *pcol = NULL;
+ char buf[128], name[64];
+ struct lws_tokenize ts;
+ lws_tokenize_elem e;
+
+ if (!wsi->protocol)
+ lwsl_err("NULL protocol at lws_read\n");
+
+ /*
+ * It's either websocket or h2->websocket
+ *
+ * If we are on h1, confirm we got the required "connection: upgrade"
+ * header. h2 / ws-over-h2 does not have this.
+ */
+
+#if defined(LWS_WITH_HTTP2)
+ if (!wsi->http2_substream) {
+#endif
+
+ lws_tokenize_init(&ts, buf, LWS_TOKENIZE_F_COMMA_SEP_LIST |
+ LWS_TOKENIZE_F_DOT_NONTERM |
+ LWS_TOKENIZE_F_RFC7230_DELIMS |
+ LWS_TOKENIZE_F_MINUS_NONTERM);
+ ts.len = lws_hdr_copy(wsi, buf, sizeof(buf) - 1,
+ WSI_TOKEN_CONNECTION);
+ if (ts.len <= 0)
+ goto bad_conn_format;
+
+ do {
+ e = lws_tokenize(&ts);
+ switch (e) {
+ case LWS_TOKZE_TOKEN:
+ if (!strcasecmp(ts.token, "upgrade"))
+ e = LWS_TOKZE_ENDED;
+ break;
+
+ case LWS_TOKZE_DELIMITER:
+ break;
+
+ default: /* includes ENDED */
+ bad_conn_format:
+ lwsl_err("%s: malformed or absent conn hdr\n",
+ __func__);
+
+ return 1;
+ }
+ } while (e > 0);
+
+#if defined(LWS_WITH_HTTP2)
+ }
+#endif
+
+#if defined(LWS_WITH_HTTP_PROXY)
+ {
+ const struct lws_http_mount *hit;
+ int uri_len = 0, meth;
+ char *uri_ptr;
+
+ meth = lws_http_get_uri_and_method(wsi, &uri_ptr, &uri_len);
+ hit = lws_find_mount(wsi, uri_ptr, uri_len);
+
+ if (hit && (meth == 0 || meth == 8) &&
+ (hit->origin_protocol == LWSMPRO_HTTPS ||
+ hit->origin_protocol == LWSMPRO_HTTP))
+ /*
+ * We are an h1 ws upgrade on a urlpath that corresponds
+ * to a proxying mount. Don't try to deal with it
+ * locally, eg, we won't even have the right protocol
+ * handler since we're not the guy handling it, just a
+ * conduit.
+ *
+ * Instead open the related ongoing h1 connection
+ * according to the mount configuration and proxy
+ * whatever that has to say from now on.
+ */
+ return lws_http_proxy_start(wsi, hit, uri_ptr, 1);
+ }
+#endif
+
+ /*
+ * Select the first protocol we support from the list
+ * the client sent us.
+ */
+
+ lws_tokenize_init(&ts, buf, LWS_TOKENIZE_F_COMMA_SEP_LIST |
+ LWS_TOKENIZE_F_MINUS_NONTERM |
+ LWS_TOKENIZE_F_DOT_NONTERM |
+ LWS_TOKENIZE_F_RFC7230_DELIMS);
+ ts.len = lws_hdr_copy(wsi, buf, sizeof(buf) - 1, WSI_TOKEN_PROTOCOL);
+ if (ts.len < 0) {
+ lwsl_err("%s: protocol list too long\n", __func__);
+ return 1;
+ }
+ if (!ts.len) {
+ int n = wsi->vhost->default_protocol_index;
+ /*
+ * Some clients only have one protocol and do not send the
+ * protocol list header... allow it and match to the vhost's
+ * default protocol (which itself defaults to zero).
+ *
+ * Setting the vhost default protocol index to -1 or anything
+ * more than the actual number of protocols on the vhost causes
+ * these "no protocol" ws connections to be rejected.
+ */
+
+ if (n >= wsi->vhost->count_protocols) {
+ lwsl_notice("%s: rejecting ws upg with no protocol\n",
+ __func__);
+
+ return 1;
+ }
+
+ lwsl_info("%s: defaulting to prot handler %d\n", __func__, n);
+
+ lws_bind_protocol(wsi, &wsi->vhost->protocols[n],
+ "ws upgrade default pcol");
+
+ goto alloc_ws;
+ }
+
+ /* otherwise go through the user-provided protocol list */
+
+ do {
+ e = lws_tokenize(&ts);
+ switch (e) {
+ case LWS_TOKZE_TOKEN:
+
+ if (lws_tokenize_cstr(&ts, name, sizeof(name))) {
+ lwsl_err("%s: pcol name too long\n", __func__);
+
+ return 1;
+ }
+ lwsl_debug("checking %s\n", name);
+ pcol = lws_vhost_name_to_protocol(wsi->vhost, name);
+ if (pcol) {
+ /* if we know it, bind to it and stop looking */
+ lws_bind_protocol(wsi, pcol, "ws upg pcol");
+ e = LWS_TOKZE_ENDED;
+ }
+ break;
+
+ case LWS_TOKZE_DELIMITER:
+ case LWS_TOKZE_ENDED:
+ break;
+
+ default:
+ lwsl_err("%s: malformatted protocol list", __func__);
+
+ return 1;
+ }
+ } while (e > 0);
+
+ /* we didn't find a protocol he wanted? */
+
+ if (!pcol) {
+ lwsl_notice("No supported protocol \"%s\"\n", buf);
+
+ return 1;
+ }
+
+alloc_ws:
+
+ return lws_process_ws_upgrade2(wsi);
+}
+
int
handshake_0405(struct lws_context *context, struct lws *wsi)
{
@@ -585,8 +619,15 @@ handshake_0405(struct lws_context *context, struct lws *wsi)
/* - it is not an empty string */
wsi->protocol->name &&
wsi->protocol->name[0]) {
+ const char *prot = wsi->protocol->name;
+
+#if defined(LWS_WITH_HTTP_PROXY)
+ if (wsi->proxied_ws_parent && wsi->child_list)
+ prot = wsi->child_list->ws->actual_protocol;
+#endif
+
LWS_CPYAPP(p, "\x0d\x0aSec-WebSocket-Protocol: ");
- p += lws_snprintf(p, 128, "%s", wsi->protocol->name);
+ p += lws_snprintf(p, 128, "%s", prot);
}
#if !defined(LWS_WITHOUT_EXTENSIONS)