mirror of
https://github.com/warmcat/libwebsockets.git
synced 2025-03-09 00:00:04 +01:00
ws proxy: also proxy h1 ws to h1 and h2
lws has been able to proxy h2 or h1 inbound connections to an h1 onward connection for a while now. It's simple to use just build with LWS_WITH_HTTP_PROXY and make a mount where the origin is the onward connection details. Unix sockets can also be used as the onward connection. This patch extends the support to be able to also do the same for inbound h2 or h1 ws upgrades to an h1 ws onward connection as well. This allows you to offer completely different services in a common URL space, including ones that connect back by ws / wss.
This commit is contained in:
parent
668a3f440f
commit
d1d313b4bf
12 changed files with 557 additions and 280 deletions
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
"<h1>Service Temporarily Unavailable</h1>"
|
||||
"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,
|
||||
"<h1>Service Temporarily Unavailable</h1>"
|
||||
"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
|
||||
|
||||
/*
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Add table
Reference in a new issue