proxy rewrite
If you enable -DLWS_WITH_HTTP_PROXY=1 at cmake, the test server has a new URI path http://localhost:7681/proxytest If you visit here, a client connection to http://example.com:80 is spawned, and the results piped on to your original connection. Also with LWS_WITH_HTTP_PROXY enabled at cmake, lws wants to link to an additional library, "libhubbub". This allows lws to do html rewriting on the fly, adjusting proxied urls in a lightweight and fast way.
This commit is contained in:
parent
5c8906e931
commit
1e5a9ad2dc
13 changed files with 515 additions and 172 deletions
|
@ -88,6 +88,8 @@ option(LWS_WITH_HTTP2 "Compile with support for http2" OFF)
|
|||
option(LWS_MBED3 "Platform is MBED3" OFF)
|
||||
option(LWS_SSL_SERVER_WITH_ECDH_CERT "Include SSL server use ECDH certificate" OFF)
|
||||
option(LWS_WITH_CGI "Include CGI (spawn process with network-connected stdin/out/err) APIs" OFF)
|
||||
option(LWS_WITH_HTTP_PROXY "Support for rewriting HTTP proxying" OFF)
|
||||
|
||||
|
||||
if (DEFINED YOTTA_WEBSOCKETS_VERSION_STRING)
|
||||
|
||||
|
@ -108,6 +110,9 @@ if (WIN32)
|
|||
set(LWS_MAX_SMP 1)
|
||||
endif()
|
||||
|
||||
if (LWS_WITH_HTTP_PROXY AND (LWS_WITHOUT_CLIENT OR LWS_WITHOUT_SERVER))
|
||||
message(FATAL_ERROR "You have to enable both client and server for http proxy")
|
||||
endif()
|
||||
|
||||
# Allow the user to override installation directories.
|
||||
set(LWS_INSTALL_LIB_DIR lib CACHE PATH "Installation directory for libraries")
|
||||
|
@ -211,6 +216,7 @@ if (LWS_WITH_LIBUV)
|
|||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
# FIXME: This must be runtime-only option.
|
||||
# The base dir where the test-apps look for the SSL certs.
|
||||
set(LWS_OPENSSL_CLIENT_CERTS ../share CACHE PATH "Server SSL certificate directory")
|
||||
|
@ -489,6 +495,11 @@ if (NOT LWS_WITHOUT_EXTENSIONS)
|
|||
lib/extension-permessage-deflate.c)
|
||||
endif()
|
||||
|
||||
if (LWS_WITH_HTTP_PROXY)
|
||||
list(APPEND SOURCES
|
||||
lib/rewrite.c)
|
||||
endif()
|
||||
|
||||
if (LWS_WITH_LIBEV)
|
||||
list(APPEND SOURCES
|
||||
lib/libev.c)
|
||||
|
@ -734,6 +745,11 @@ if (LWS_WITH_LIBUV)
|
|||
include_directories("${LIBUV_INCLUDE_DIRS}")
|
||||
list(APPEND LIB_LIST ${LIBUV_LIBRARIES})
|
||||
endif()
|
||||
if (LWS_WITH_HTTP_PROXY)
|
||||
find_library(LIBHUBBUB_LIBRARIES NAMES libhubbub)
|
||||
list(APPEND LIB_LIST ${LIBHUBBUB_LIBRARIES} )
|
||||
endif()
|
||||
|
||||
|
||||
#
|
||||
# Platform specific libs.
|
||||
|
@ -1227,6 +1243,8 @@ message(" LWS_SSL_SERVER_WITH_ECDH_CERT = ${LWS_SSL_SERVER_WITH_ECDH_CERT}")
|
|||
message(" LWS_MAX_SMP = ${LWS_MAX_SMP}")
|
||||
message(" LWS_WITH_CGI = ${LWS_WITH_CGI}")
|
||||
message(" LWS_HAVE_OPENSSL_ECDH_H = ${LWS_HAVE_OPENSSL_ECDH_H}")
|
||||
message(" LWS_WITH_HTTP_PROXY = ${LWS_WITH_HTTP_PROXY}")
|
||||
message(" LIBHUBBUB_LIBRARIES = ${LIBHUBBUB_LIBRARIES}")
|
||||
message("---------------------------------------------------------------------")
|
||||
|
||||
# These will be available to parent projects including libwebsockets using add_subdirectory()
|
||||
|
|
11
changelog
11
changelog
|
@ -81,9 +81,14 @@ protocol string to its argument in URL format. If so, it stays in http[s]
|
|||
client mode and doesn't upgrade to ws[s], allowing you to do generic http client
|
||||
operations. Receiving transfer-encoding: chunked is supported.
|
||||
|
||||
9) The test server has a new URI path http://localhost:7681/proxytest
|
||||
If you visit here, a client connection to http://example.com:80 is spawned,
|
||||
and the results piped on to your original connection.
|
||||
9) If you enable -DLWS_WITH_HTTP_PROXY=1 at cmake, the test server has a
|
||||
new URI path http://localhost:7681/proxytest If you visit here, a client
|
||||
connection to http://example.com:80 is spawned, and the results piped on
|
||||
to your original connection.
|
||||
|
||||
10) Also with LWS_WITH_HTTP_PROXY enabled at cmake, lws wants to link to an
|
||||
additional library, "libhubbub". This allows lws to do html rewriting on the
|
||||
fly, adjusting proxied urls in a lightweight and fast way.
|
||||
|
||||
|
||||
User API additions
|
||||
|
|
|
@ -338,6 +338,115 @@ lws_client_reset(struct lws *wsi, int ssl, const char *address, int port, const
|
|||
return lws_client_connect_2(wsi);
|
||||
}
|
||||
|
||||
|
||||
static hubbub_error
|
||||
html_parser_cb(const hubbub_token *token, void *pw)
|
||||
{
|
||||
struct lws_rewrite *r = (struct lws_rewrite *)pw;
|
||||
char buf[1024], *start = buf + LWS_PRE, *p = start,
|
||||
*end = &buf[sizeof(buf) - 1];
|
||||
size_t i;
|
||||
|
||||
switch (token->type) {
|
||||
case HUBBUB_TOKEN_DOCTYPE:
|
||||
|
||||
p += snprintf(p, end - p, "<!DOCTYPE %.*s %s ",
|
||||
(int) token->data.doctype.name.len,
|
||||
token->data.doctype.name.ptr,
|
||||
token->data.doctype.force_quirks ?
|
||||
"(force-quirks) " : "");
|
||||
|
||||
if (token->data.doctype.public_missing)
|
||||
printf("\tpublic: missing\n");
|
||||
else
|
||||
p += snprintf(p, end - p, "PUBLIC \"%.*s\"\n",
|
||||
(int) token->data.doctype.public_id.len,
|
||||
token->data.doctype.public_id.ptr);
|
||||
|
||||
if (token->data.doctype.system_missing)
|
||||
printf("\tsystem: missing\n");
|
||||
else
|
||||
p += snprintf(p, end - p, " \"%.*s\">\n",
|
||||
(int) token->data.doctype.system_id.len,
|
||||
token->data.doctype.system_id.ptr);
|
||||
|
||||
break;
|
||||
case HUBBUB_TOKEN_START_TAG:
|
||||
p += snprintf(p, end - p, "<%.*s", (int)token->data.tag.name.len,
|
||||
token->data.tag.name.ptr);
|
||||
|
||||
/* (token->data.tag.self_closing) ?
|
||||
"(self-closing) " : "",
|
||||
(token->data.tag.n_attributes > 0) ?
|
||||
"attributes:" : "");
|
||||
*/
|
||||
for (i = 0; i < token->data.tag.n_attributes; i++) {
|
||||
if (!hstrcmp(&token->data.tag.attributes[i].name, "href", 4) ||
|
||||
!hstrcmp(&token->data.tag.attributes[i].name, "action", 6) ||
|
||||
!hstrcmp(&token->data.tag.attributes[i].name, "src", 3)) {
|
||||
const char *pp = (const char *)token->data.tag.attributes[i].value.ptr;
|
||||
int plen = (int) token->data.tag.attributes[i].value.len;
|
||||
|
||||
if (!hstrcmp(&token->data.tag.attributes[i].value,
|
||||
r->from, r->from_len)) {
|
||||
pp += r->from_len;
|
||||
plen -= r->from_len;
|
||||
}
|
||||
p += snprintf(p, end - p, " %.*s=\"%s/%.*s\"",
|
||||
(int) token->data.tag.attributes[i].name.len,
|
||||
token->data.tag.attributes[i].name.ptr,
|
||||
r->to, plen, pp);
|
||||
|
||||
} else
|
||||
|
||||
p += snprintf(p, end - p, " %.*s=\"%.*s\"",
|
||||
(int) token->data.tag.attributes[i].name.len,
|
||||
token->data.tag.attributes[i].name.ptr,
|
||||
(int) token->data.tag.attributes[i].value.len,
|
||||
token->data.tag.attributes[i].value.ptr);
|
||||
}
|
||||
p += snprintf(p, end - p, ">\n");
|
||||
break;
|
||||
case HUBBUB_TOKEN_END_TAG:
|
||||
p += snprintf(p, end - p, "</%.*s", (int) token->data.tag.name.len,
|
||||
token->data.tag.name.ptr);
|
||||
/*
|
||||
(token->data.tag.self_closing) ?
|
||||
"(self-closing) " : "",
|
||||
(token->data.tag.n_attributes > 0) ?
|
||||
"attributes:" : "");
|
||||
*/
|
||||
for (i = 0; i < token->data.tag.n_attributes; i++) {
|
||||
p += snprintf(p, end - p, " %.*s='%.*s'\n",
|
||||
(int) token->data.tag.attributes[i].name.len,
|
||||
token->data.tag.attributes[i].name.ptr,
|
||||
(int) token->data.tag.attributes[i].value.len,
|
||||
token->data.tag.attributes[i].value.ptr);
|
||||
}
|
||||
p += snprintf(p, end - p, ">\n");
|
||||
break;
|
||||
case HUBBUB_TOKEN_COMMENT:
|
||||
p += snprintf(p, end - p, "<!-- %.*s -->\n",
|
||||
(int) token->data.comment.len,
|
||||
token->data.comment.ptr);
|
||||
break;
|
||||
case HUBBUB_TOKEN_CHARACTER:
|
||||
p += snprintf(p, end - p, "%.*s", (int) token->data.character.len,
|
||||
token->data.character.ptr);
|
||||
break;
|
||||
case HUBBUB_TOKEN_EOF:
|
||||
p += snprintf(p, end - p, "\n");
|
||||
break;
|
||||
}
|
||||
|
||||
if (user_callback_handle_rxflow(r->wsi->protocol->callback,
|
||||
r->wsi, LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ,
|
||||
r->wsi->user_space, start, p - start))
|
||||
return -1;
|
||||
|
||||
return HUBBUB_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* lws_client_connect_via_info() - Connect to another websocket server
|
||||
* @i:pointer to lws_client_connect_info struct
|
||||
|
@ -452,6 +561,11 @@ lws_client_connect_via_info(struct lws_client_connect_info *i)
|
|||
i->parent_wsi->child_list = wsi;
|
||||
}
|
||||
|
||||
if (i->uri_replace_to)
|
||||
wsi->rw = lws_rewrite_create(wsi, html_parser_cb,
|
||||
i->uri_replace_from,
|
||||
i->uri_replace_to);
|
||||
|
||||
return wsi;
|
||||
|
||||
bail:
|
||||
|
|
|
@ -622,6 +622,15 @@ lws_client_interpret_server_handshake(struct lws *wsi)
|
|||
goto bail2;
|
||||
}
|
||||
|
||||
#ifndef LWS_NO_CLIENT
|
||||
wsi->perform_rewrite = 0;
|
||||
if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE)) {
|
||||
if (!strncmp(lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE),
|
||||
"text/html", 9))
|
||||
wsi->perform_rewrite = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* allocate the per-connection user memory (if any) */
|
||||
if (lws_ensure_user_space(wsi)) {
|
||||
lwsl_err("Problem allocating wsi user mem\n");
|
||||
|
|
|
@ -158,12 +158,18 @@ lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason)
|
|||
if (wsi->child_list) {
|
||||
wsi2 = wsi->child_list;
|
||||
while (wsi2) {
|
||||
lwsl_notice("%s: closing %p: close child %p\n",
|
||||
__func__, wsi, wsi2);
|
||||
//lwsl_notice("%s: closing %p: close child %p\n",
|
||||
// __func__, wsi, wsi2);
|
||||
wsi1 = wsi2->sibling_list;
|
||||
//lwsl_notice("%s: closing %p: next sibling %p\n",
|
||||
// __func__, wsi2, wsi1);
|
||||
wsi2->parent = NULL;
|
||||
/* stop it doing shutdown processing */
|
||||
wsi2->socket_is_permanently_unusable = 1;
|
||||
lws_close_free_wsi(wsi2, reason);
|
||||
wsi2 = wsi1;
|
||||
}
|
||||
wsi->child_list = NULL;
|
||||
}
|
||||
|
||||
#ifdef LWS_WITH_CGI
|
||||
|
@ -186,7 +192,6 @@ lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason)
|
|||
|
||||
if (wsi->mode == LWSCM_HTTP_SERVING_ACCEPTED &&
|
||||
wsi->u.http.fd != LWS_INVALID_FILE) {
|
||||
lwsl_debug("closing http file\n");
|
||||
lws_plat_file_close(wsi, wsi->u.http.fd);
|
||||
wsi->u.http.fd = LWS_INVALID_FILE;
|
||||
context->protocols[0].callback(wsi, LWS_CALLBACK_CLOSED_HTTP,
|
||||
|
@ -363,7 +368,7 @@ just_kill_connection:
|
|||
if (wsi->state != LWSS_SHUTDOWN &&
|
||||
reason != LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY &&
|
||||
!wsi->socket_is_permanently_unusable) {
|
||||
lwsl_info("%s: shutting down connection: %p\n", __func__, wsi);
|
||||
lwsl_info("%s: shutting down connection: %p (sock %d)\n", __func__, wsi, wsi->sock);
|
||||
n = shutdown(wsi->sock, SHUT_WR);
|
||||
if (n)
|
||||
lwsl_debug("closing: shutdown ret %d\n", LWS_ERRNO);
|
||||
|
@ -384,7 +389,13 @@ just_kill_connection:
|
|||
}
|
||||
#endif
|
||||
|
||||
lwsl_info("%s: real just_kill_connection: %p\n", __func__, wsi);
|
||||
lwsl_info("%s: real just_kill_connection: %p (sockfd %d)\n", __func__,
|
||||
wsi, wsi->sock);
|
||||
|
||||
if (wsi->rw) {
|
||||
lws_rewrite_destroy(wsi->rw);
|
||||
wsi->rw = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* we won't be servicing or receiving anything further from this guy
|
||||
|
@ -502,6 +513,7 @@ lws_close_free_wsi_final(struct lws *wsi)
|
|||
|
||||
if (!lws_ssl_close(wsi) && lws_socket_is_valid(wsi->sock)) {
|
||||
#if LWS_POSIX
|
||||
//lwsl_err("*** closing sockfd %d\n", wsi->sock);
|
||||
n = compatible_close(wsi->sock);
|
||||
if (n)
|
||||
lwsl_debug("closing: close ret %d\n", LWS_ERRNO);
|
||||
|
@ -1742,12 +1754,11 @@ lws_cgi_write_split_stdout_headers(struct lws *wsi)
|
|||
|
||||
/* ran out of input, ended the headers, or filled up the headers buf */
|
||||
if (!n || wsi->hdr_state == LHCS_PAYLOAD || (p + 4) == end) {
|
||||
lwsl_err("a\n");
|
||||
|
||||
m = lws_write(wsi, (unsigned char *)start,
|
||||
p - start, LWS_WRITE_HTTP_HEADERS);
|
||||
if (m < 0)
|
||||
return -1;
|
||||
lwsl_err("b\n");
|
||||
/* writeability becomes uncertain now we wrote
|
||||
* something, we must return to the event loop
|
||||
*/
|
||||
|
@ -1755,7 +1766,7 @@ lwsl_err("b\n");
|
|||
return 0;
|
||||
}
|
||||
}
|
||||
lwsl_err("%s: stdout\n", __func__);
|
||||
//lwsl_err("%s: stdout\n", __func__);
|
||||
n = read(lws_get_socket_fd(wsi->cgi->stdwsi[LWS_STDOUT]),
|
||||
start, sizeof(buf) - LWS_PRE);
|
||||
|
||||
|
|
|
@ -364,6 +364,7 @@ enum lws_callback_reasons {
|
|||
LWS_CALLBACK_CLOSED_CLIENT_HTTP = 45,
|
||||
LWS_CALLBACK_RECEIVE_CLIENT_HTTP = 46,
|
||||
LWS_CALLBACK_COMPLETED_CLIENT_HTTP = 47,
|
||||
LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ = 48,
|
||||
|
||||
/****** add new things just above ---^ ******/
|
||||
|
||||
|
@ -1415,6 +1416,9 @@ struct lws_context_creation_info {
|
|||
* @parent_wsi: if another wsi is responsible for this connection, give it here.
|
||||
* this is used to make sure if the parent closes so do any
|
||||
* child connections first.
|
||||
* @uri_replace_from: if non-NULL, when this string is found in URIs in
|
||||
* text/html content-encoding, it's replaced with @uri_replace_to
|
||||
* @uri_replace_to: see above
|
||||
*/
|
||||
|
||||
struct lws_client_connect_info {
|
||||
|
@ -1431,6 +1435,8 @@ struct lws_client_connect_info {
|
|||
const struct lws_extension *client_exts;
|
||||
const char *method;
|
||||
struct lws *parent_wsi;
|
||||
const char *uri_replace_from;
|
||||
const char *uri_replace_to;
|
||||
|
||||
/* Add new things just above here ---^
|
||||
* This is part of the ABI, don't needlessly break compatibility
|
||||
|
@ -1893,6 +1899,9 @@ LWS_VISIBLE LWS_EXTERN int
|
|||
lws_cgi_kill(struct lws *wsi);
|
||||
#endif
|
||||
|
||||
LWS_VISIBLE LWS_EXTERN int
|
||||
lws_http_client_read(struct lws *wsi, char **buf, int *len);
|
||||
|
||||
/*
|
||||
* Wsi-associated File Operations access helpers
|
||||
*
|
||||
|
|
|
@ -600,7 +600,6 @@ LWS_VISIBLE int lws_serve_http_file_fragment(struct lws *wsi)
|
|||
all_sent:
|
||||
if (!wsi->trunc_len && wsi->u.http.filepos == wsi->u.http.filelen) {
|
||||
wsi->state = LWSS_HTTP;
|
||||
|
||||
/* we might be in keepalive, so close it off here */
|
||||
lws_plat_file_close(wsi, wsi->u.http.fd);
|
||||
wsi->u.http.fd = LWS_INVALID_FILE;
|
||||
|
|
|
@ -112,6 +112,10 @@
|
|||
#include <netdb.h>
|
||||
#include <signal.h>
|
||||
#include <sys/socket.h>
|
||||
#ifdef LWS_WITH_HTTP_PROXY
|
||||
#include <hubbub/hubbub.h>
|
||||
#include <hubbub/parser.h>
|
||||
#endif
|
||||
#ifdef LWS_BUILTIN_GETIFADDRS
|
||||
#include <getifaddrs.h>
|
||||
#else
|
||||
|
@ -157,7 +161,7 @@
|
|||
#define LWS_POLLHUP (POLLHUP|POLLERR)
|
||||
#define LWS_POLLIN (POLLIN)
|
||||
#define LWS_POLLOUT (POLLOUT)
|
||||
#define compatible_close(fd) close(fd)
|
||||
static inline int compatible_close(int fd) { return close(fd); }
|
||||
#define lws_set_blocking_send(wsi)
|
||||
|
||||
#ifdef MBED_OPERATORS
|
||||
|
@ -1068,6 +1072,8 @@ enum lws_chunk_parser {
|
|||
};
|
||||
#endif
|
||||
|
||||
struct lws_rewrite;
|
||||
|
||||
struct lws {
|
||||
|
||||
/* structs */
|
||||
|
@ -1118,6 +1124,9 @@ struct lws {
|
|||
BIO *client_bio;
|
||||
struct lws *pending_read_list_prev, *pending_read_list_next;
|
||||
#endif
|
||||
#ifndef LWS_NO_CLIENT
|
||||
struct lws_rewrite *rw;
|
||||
#endif
|
||||
#ifdef LWS_LATENCY
|
||||
unsigned long action_start;
|
||||
unsigned long latency_start;
|
||||
|
@ -1144,6 +1153,8 @@ struct lws {
|
|||
#ifndef LWS_NO_CLIENT
|
||||
unsigned int do_ws:1; /* whether we are doing http or ws flow */
|
||||
unsigned int chunked:1; /* if the clientside connection is chunked */
|
||||
unsigned int client_rx_avail:1;
|
||||
unsigned int perform_rewrite:1;
|
||||
#endif
|
||||
#ifndef LWS_NO_EXTENSIONS
|
||||
unsigned int extension_data_pending:1;
|
||||
|
@ -1502,10 +1513,35 @@ lws_ssl_capable_write_no_ssl(struct lws *wsi, unsigned char *buf, int len);
|
|||
LWS_EXTERN int LWS_WARN_UNUSED_RESULT
|
||||
lws_ssl_pending_no_ssl(struct lws *wsi);
|
||||
|
||||
#ifndef LWS_NO_CLIENT
|
||||
#ifdef LWS_WITH_HTTP_PROXY
|
||||
struct lws_rewrite {
|
||||
hubbub_parser *parser;
|
||||
hubbub_parser_optparams params;
|
||||
const char *from, *to;
|
||||
int from_len, to_len;
|
||||
unsigned char *p, *end;
|
||||
struct lws *wsi;
|
||||
};
|
||||
static LWS_INLINE int hstrcmp(hubbub_string *s, const char *p, int len)
|
||||
{
|
||||
if (s->len != len)
|
||||
return 1;
|
||||
|
||||
return strncmp((const char *)s->ptr, p, len);
|
||||
}
|
||||
typedef hubbub_error (*hubbub_callback_t)(const hubbub_token *token, void *pw);
|
||||
LWS_EXTERN int lws_client_socket_service(struct lws_context *context,
|
||||
struct lws *wsi,
|
||||
struct lws_pollfd *pollfd);
|
||||
LWS_EXTERN struct lws_rewrite *
|
||||
lws_rewrite_create(struct lws *wsi, hubbub_callback_t cb, const char *from, const char *to);
|
||||
LWS_EXTERN void
|
||||
lws_rewrite_destroy(struct lws_rewrite *r);
|
||||
LWS_EXTERN int
|
||||
lws_rewrite_parse(struct lws_rewrite *r, const unsigned char *in, int in_len);
|
||||
#endif
|
||||
|
||||
#ifndef LWS_NO_CLIENT
|
||||
#ifdef LWS_OPENSSL_SUPPORT
|
||||
LWS_EXTERN int
|
||||
lws_context_init_client_ssl(struct lws_context_creation_info *info,
|
||||
|
|
47
lib/rewrite.c
Normal file
47
lib/rewrite.c
Normal file
|
@ -0,0 +1,47 @@
|
|||
#include "private-libwebsockets.h"
|
||||
|
||||
|
||||
LWS_EXTERN struct lws_rewrite *
|
||||
lws_rewrite_create(struct lws *wsi, hubbub_callback_t cb, const char *from, const char *to)
|
||||
{
|
||||
struct lws_rewrite *r = lws_malloc(sizeof(*r));
|
||||
|
||||
if (hubbub_parser_create("UTF-8", false, &r->parser) != HUBBUB_OK) {
|
||||
lws_free(r);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
r->from = from;
|
||||
r->from_len = strlen(from);
|
||||
r->to = to;
|
||||
r->to_len = strlen(to);
|
||||
r->params.token_handler.handler = cb;
|
||||
r->wsi = wsi;
|
||||
r->params.token_handler.pw = (void *)r;
|
||||
if (hubbub_parser_setopt(r->parser, HUBBUB_PARSER_TOKEN_HANDLER,
|
||||
&r->params) != HUBBUB_OK) {
|
||||
lws_free(r);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
LWS_EXTERN int
|
||||
lws_rewrite_parse(struct lws_rewrite *r,
|
||||
const unsigned char *in, int in_len)
|
||||
{
|
||||
if (hubbub_parser_parse_chunk(r->parser, in, in_len) != HUBBUB_OK)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
LWS_EXTERN void
|
||||
lws_rewrite_destroy(struct lws_rewrite *r)
|
||||
{
|
||||
hubbub_parser_destroy(r->parser);
|
||||
lws_free(r);
|
||||
}
|
||||
|
|
@ -779,7 +779,7 @@ lws_adopt_socket(struct lws_context *context, lws_sockfd_type accept_fd)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
lwsl_debug("%s: new wsi %p\n", __func__, new_wsi);
|
||||
lwsl_info("%s: new wsi %p, sockfd %d\n", __func__, new_wsi, accept_fd);
|
||||
|
||||
new_wsi->sock = accept_fd;
|
||||
|
||||
|
|
299
lib/service.c
299
lib/service.c
|
@ -473,6 +473,134 @@ lws_service_flag_pending(struct lws_context *context, int tsi)
|
|||
return forced;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
LWS_VISIBLE int
|
||||
lws_http_client_read(struct lws *wsi, char **buf, int *len)
|
||||
{
|
||||
int rlen, n;
|
||||
|
||||
rlen = lws_ssl_capable_read(wsi, (unsigned char *)*buf, *len);
|
||||
if (rlen < 0)
|
||||
return -1;
|
||||
|
||||
*len = rlen;
|
||||
if (rlen == 0)
|
||||
return 0;
|
||||
|
||||
// lwsl_err("%s: read %d\n", __func__, rlen);
|
||||
|
||||
/* allow the source to signal he has data again next time */
|
||||
wsi->client_rx_avail = 0;
|
||||
lws_change_pollfd(wsi, 0, LWS_POLLIN);
|
||||
|
||||
/*
|
||||
* server may insist on transfer-encoding: chunked,
|
||||
* so http client must deal with it
|
||||
*/
|
||||
spin_chunks:
|
||||
while (wsi->chunked && (wsi->chunk_parser != ELCP_CONTENT) && *len) {
|
||||
switch (wsi->chunk_parser) {
|
||||
case ELCP_HEX:
|
||||
if ((*buf)[0] == '\x0d') {
|
||||
wsi->chunk_parser = ELCP_CR;
|
||||
break;
|
||||
}
|
||||
n = char_to_hex((*buf)[0]);
|
||||
if (n < 0)
|
||||
return -1;
|
||||
wsi->chunk_remaining <<= 4;
|
||||
wsi->chunk_remaining |= n;
|
||||
break;
|
||||
case ELCP_CR:
|
||||
if ((*buf)[0] != '\x0a')
|
||||
return -1;
|
||||
wsi->chunk_parser = ELCP_CONTENT;
|
||||
lwsl_info("chunk %d\n", wsi->chunk_remaining);
|
||||
if (wsi->chunk_remaining)
|
||||
break;
|
||||
lwsl_info("final chunk\n");
|
||||
goto completed;
|
||||
|
||||
case ELCP_CONTENT:
|
||||
break;
|
||||
|
||||
case ELCP_POST_CR:
|
||||
if ((*buf)[0] != '\x0d')
|
||||
return -1;
|
||||
|
||||
wsi->chunk_parser = ELCP_POST_LF;
|
||||
break;
|
||||
|
||||
case ELCP_POST_LF:
|
||||
if ((*buf)[0] != '\x0a')
|
||||
return -1;
|
||||
|
||||
wsi->chunk_parser = ELCP_HEX;
|
||||
wsi->chunk_remaining = 0;
|
||||
break;
|
||||
}
|
||||
(*buf)++;
|
||||
(*len)--;
|
||||
}
|
||||
|
||||
if (wsi->chunked && !wsi->chunk_remaining)
|
||||
return 0;
|
||||
|
||||
if (wsi->u.http.content_remain &&
|
||||
wsi->u.http.content_remain < *len)
|
||||
n = wsi->u.http.content_remain;
|
||||
else
|
||||
n = *len;
|
||||
|
||||
if (wsi->chunked && wsi->chunk_remaining &&
|
||||
wsi->chunk_remaining < n)
|
||||
n = wsi->chunk_remaining;
|
||||
|
||||
/* hubbub */
|
||||
if (wsi->perform_rewrite)
|
||||
lws_rewrite_parse(wsi->rw, (unsigned char *)*buf, n);
|
||||
else
|
||||
|
||||
if (user_callback_handle_rxflow(wsi->protocol->callback,
|
||||
wsi, LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ,
|
||||
wsi->user_space, *buf, n))
|
||||
return -1;
|
||||
|
||||
if (wsi->chunked && wsi->chunk_remaining) {
|
||||
(*buf) += n;
|
||||
wsi->chunk_remaining -= n;
|
||||
*len -= n;
|
||||
}
|
||||
|
||||
if (wsi->chunked && !wsi->chunk_remaining)
|
||||
wsi->chunk_parser = ELCP_POST_CR;
|
||||
|
||||
if (wsi->chunked && *len) {
|
||||
goto spin_chunks;
|
||||
}
|
||||
|
||||
if (wsi->chunked)
|
||||
return 0;
|
||||
|
||||
wsi->u.http.content_remain -= n;
|
||||
if (wsi->u.http.content_remain || !wsi->u.http.content_length)
|
||||
return 0;
|
||||
|
||||
completed:
|
||||
if (user_callback_handle_rxflow(wsi->protocol->callback,
|
||||
wsi, LWS_CALLBACK_COMPLETED_CLIENT_HTTP,
|
||||
wsi->user_space, NULL, 0))
|
||||
return -1;
|
||||
|
||||
if (lws_http_transaction_completed(wsi))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* lws_service_fd() - Service polled socket with something waiting
|
||||
* @context: Websocket context
|
||||
|
@ -716,37 +844,59 @@ lws_service_fd_tsi(struct lws_context *context, struct lws_pollfd *pollfd, int t
|
|||
|
||||
if (!(pollfd->revents & pollfd->events & LWS_POLLIN))
|
||||
break;
|
||||
read:
|
||||
|
||||
read:
|
||||
/* all the union members start with hdr, so even in ws mode
|
||||
* we can deal with the ah via u.hdr
|
||||
*/
|
||||
if (wsi->u.hdr.ah) {
|
||||
lwsl_err("%s: %p: using inherited ah rx\n", __func__, wsi);
|
||||
lwsl_info("%s: %p: inherited ah rx\n", __func__, wsi);
|
||||
eff_buf.token_len = wsi->u.hdr.ah->rxlen -
|
||||
wsi->u.hdr.ah->rxpos;
|
||||
eff_buf.token = (char *)wsi->u.hdr.ah->rx +
|
||||
wsi->u.hdr.ah->rxpos;
|
||||
} else {
|
||||
if (wsi->mode != LWSCM_HTTP_CLIENT_ACCEPTED) {
|
||||
eff_buf.token_len = lws_ssl_capable_read(wsi,
|
||||
pt->serv_buf, pending ? pending :
|
||||
LWS_MAX_SOCKET_IO_BUF);
|
||||
switch (eff_buf.token_len) {
|
||||
case 0:
|
||||
lwsl_info("%s: zero length read\n", __func__);
|
||||
goto close_and_handled;
|
||||
case LWS_SSL_CAPABLE_MORE_SERVICE:
|
||||
lwsl_info("SSL Capable more service\n");
|
||||
n = 0;
|
||||
goto handled;
|
||||
case LWS_SSL_CAPABLE_ERROR:
|
||||
lwsl_info("Closing when error\n");
|
||||
goto close_and_handled;
|
||||
}
|
||||
|
||||
eff_buf.token_len = lws_ssl_capable_read(wsi, pt->serv_buf,
|
||||
pending ? pending : LWS_MAX_SOCKET_IO_BUF);
|
||||
switch (eff_buf.token_len) {
|
||||
case 0:
|
||||
lwsl_info("service_fd: closing due to 0 length read\n");
|
||||
goto close_and_handled;
|
||||
case LWS_SSL_CAPABLE_MORE_SERVICE:
|
||||
lwsl_info("SSL Capable more service\n");
|
||||
n = 0;
|
||||
goto handled;
|
||||
case LWS_SSL_CAPABLE_ERROR:
|
||||
lwsl_info("Closing when error\n");
|
||||
goto close_and_handled;
|
||||
eff_buf.token = (char *)pt->serv_buf;
|
||||
}
|
||||
|
||||
eff_buf.token = (char *)pt->serv_buf;
|
||||
}
|
||||
|
||||
drain:
|
||||
if (wsi->mode == LWSCM_HTTP_CLIENT_ACCEPTED) {
|
||||
|
||||
/*
|
||||
* simply mark ourselves as having readable data
|
||||
* and turn off our POLLIN
|
||||
*/
|
||||
wsi->client_rx_avail = 1;
|
||||
lws_change_pollfd(wsi, LWS_POLLIN, 0);
|
||||
|
||||
/* let user code know, he'll usually ask for writeable
|
||||
* callback and drain / reenable it there
|
||||
*/
|
||||
if (user_callback_handle_rxflow(wsi->protocol->callback,
|
||||
wsi, LWS_CALLBACK_RECEIVE_CLIENT_HTTP,
|
||||
wsi->user_space, NULL, 0))
|
||||
goto close_and_handled;
|
||||
|
||||
|
||||
}
|
||||
/*
|
||||
* give any active extensions a chance to munge the buffer
|
||||
* before parse. We pass in a pointer to an lws_tokens struct
|
||||
|
@ -758,121 +908,6 @@ read:
|
|||
* extension callback handling, just the normal input buffer is
|
||||
* used then so it is efficient.
|
||||
*/
|
||||
drain:
|
||||
if (wsi->mode == LWSCM_HTTP_CLIENT_ACCEPTED) {
|
||||
/*
|
||||
* server may insist on transfer-encoding: chunked,
|
||||
* so http client must deal with it
|
||||
*/
|
||||
spin_chunks:
|
||||
while (wsi->chunked &&
|
||||
(wsi->chunk_parser != ELCP_CONTENT) &&
|
||||
eff_buf.token_len) {
|
||||
switch (wsi->chunk_parser) {
|
||||
case ELCP_HEX:
|
||||
if (eff_buf.token[0] == '\x0d') {
|
||||
wsi->chunk_parser = ELCP_CR;
|
||||
break;
|
||||
}
|
||||
n = char_to_hex(eff_buf.token[0]);
|
||||
if (n < 0)
|
||||
goto close_and_handled;
|
||||
wsi->chunk_remaining <<= 4;
|
||||
wsi->chunk_remaining |= n;
|
||||
break;
|
||||
case ELCP_CR:
|
||||
if (eff_buf.token[0] != '\x0a')
|
||||
goto close_and_handled;
|
||||
wsi->chunk_parser = ELCP_CONTENT;
|
||||
lwsl_info("chunk %d\n",
|
||||
wsi->chunk_remaining);
|
||||
if (wsi->chunk_remaining)
|
||||
break;
|
||||
lwsl_info("final chunk\n");
|
||||
if (user_callback_handle_rxflow(
|
||||
wsi->protocol->callback,
|
||||
wsi, LWS_CALLBACK_COMPLETED_CLIENT_HTTP,
|
||||
wsi->user_space, NULL, 0))
|
||||
goto close_and_handled;
|
||||
if (lws_http_transaction_completed(wsi))
|
||||
goto close_and_handled;
|
||||
n = 0;
|
||||
goto handled;
|
||||
|
||||
case ELCP_CONTENT:
|
||||
break;
|
||||
|
||||
case ELCP_POST_CR:
|
||||
if (eff_buf.token[0] == '\x0d') {
|
||||
wsi->chunk_parser = ELCP_POST_LF;
|
||||
break;
|
||||
}
|
||||
goto close_and_handled;
|
||||
|
||||
case ELCP_POST_LF:
|
||||
if (eff_buf.token[0] == '\x0a') {
|
||||
wsi->chunk_parser = ELCP_HEX;
|
||||
wsi->chunk_remaining = 0;
|
||||
break;
|
||||
}
|
||||
goto close_and_handled;
|
||||
}
|
||||
eff_buf.token++;
|
||||
eff_buf.token_len--;
|
||||
}
|
||||
|
||||
if (wsi->chunked && !wsi->chunk_remaining) {
|
||||
n = 0;
|
||||
goto handled;
|
||||
}
|
||||
|
||||
if (wsi->u.http.content_remain &&
|
||||
wsi->u.http.content_remain < eff_buf.token_len)
|
||||
n = wsi->u.http.content_remain;
|
||||
else
|
||||
n = eff_buf.token_len;
|
||||
|
||||
if (wsi->chunked && wsi->chunk_remaining &&
|
||||
wsi->chunk_remaining < n)
|
||||
n = wsi->chunk_remaining;
|
||||
|
||||
if (user_callback_handle_rxflow(wsi->protocol->callback,
|
||||
wsi, LWS_CALLBACK_RECEIVE_CLIENT_HTTP,
|
||||
wsi->user_space, (void *)eff_buf.token,
|
||||
n))
|
||||
goto close_and_handled;
|
||||
|
||||
if (wsi->chunked && wsi->chunk_remaining) {
|
||||
eff_buf.token += n;
|
||||
wsi->chunk_remaining -= n;
|
||||
eff_buf.token_len -= n;
|
||||
}
|
||||
|
||||
if (wsi->chunked && !wsi->chunk_remaining)
|
||||
wsi->chunk_parser = ELCP_POST_CR;
|
||||
|
||||
if (wsi->chunked && eff_buf.token_len) {
|
||||
goto spin_chunks;
|
||||
}
|
||||
|
||||
if (wsi->chunked) {
|
||||
n = 0;
|
||||
goto handled;
|
||||
}
|
||||
|
||||
wsi->u.http.content_remain -= n;
|
||||
if (wsi->u.http.content_remain)
|
||||
goto handled;
|
||||
|
||||
if (user_callback_handle_rxflow(wsi->protocol->callback,
|
||||
wsi, LWS_CALLBACK_COMPLETED_CLIENT_HTTP,
|
||||
wsi->user_space, NULL, 0))
|
||||
goto close_and_handled;
|
||||
|
||||
if (wsi->u.http.connection_type == HTTP_CONNECTION_CLOSE)
|
||||
goto close_and_handled;
|
||||
goto handled;
|
||||
}
|
||||
do {
|
||||
more = 0;
|
||||
|
||||
|
|
|
@ -80,6 +80,9 @@
|
|||
/* whether the Openssl is recent enough, and / or built with, ecdh */
|
||||
#cmakedefine LWS_HAVE_OPENSSL_ECDH_H
|
||||
|
||||
/* HTTP Proxy support */
|
||||
#cmakedefine LWS_WITH_HTTP_PROXY
|
||||
|
||||
/* Maximum supported service threads */
|
||||
#define LWS_MAX_SMP ${LWS_MAX_SMP}
|
||||
|
||||
|
|
|
@ -126,6 +126,7 @@ int callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user,
|
|||
unsigned char *end;
|
||||
struct timeval tv;
|
||||
unsigned char *p;
|
||||
struct lws *wsi1;
|
||||
char buf[256];
|
||||
char b64[64];
|
||||
int n, m;
|
||||
|
@ -161,15 +162,11 @@ int callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user,
|
|||
goto try_to_reuse;
|
||||
}
|
||||
|
||||
/* this example server has no concept of directories */
|
||||
if (strchr((const char *)in + 1, '/')) {
|
||||
lws_return_http_status(wsi, HTTP_STATUS_FORBIDDEN, NULL);
|
||||
goto try_to_reuse;
|
||||
}
|
||||
|
||||
#ifndef LWS_NO_CLIENT
|
||||
if (!strcmp(in, "/proxytest")) {
|
||||
if (!strncmp(in, "/proxytest", 10)) {
|
||||
struct lws_client_connect_info i;
|
||||
char *rootpath = "/";
|
||||
const char *p = (const char *)in;
|
||||
|
||||
if (lws_get_child(wsi))
|
||||
break;
|
||||
|
@ -177,22 +174,36 @@ int callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user,
|
|||
pss->client_finished = 0;
|
||||
memset(&i,0, sizeof(i));
|
||||
i.context = lws_get_context(wsi);
|
||||
i.address = "example.com";
|
||||
i.address = "git.libwebsockets.org";
|
||||
i.port = 80;
|
||||
i.ssl_connection = 0;
|
||||
i.path = "/";
|
||||
i.host = "example.com";
|
||||
if (p[10])
|
||||
i.path = in + 10;
|
||||
else
|
||||
i.path = rootpath;
|
||||
i.host = "git.libwebsockets.org";
|
||||
i.origin = NULL;
|
||||
i.method = "GET";
|
||||
i.parent_wsi = wsi;
|
||||
i.uri_replace_from = "git.libwebsockets.org/";
|
||||
i.uri_replace_to = "/proxytest/";
|
||||
if (!lws_client_connect_via_info(&i)) {
|
||||
lwsl_err("proxy connect fail\n");
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* this example server has no concept of directories */
|
||||
if (strchr((const char *)in + 1, '/')) {
|
||||
lws_return_http_status(wsi, HTTP_STATUS_FORBIDDEN, NULL);
|
||||
goto try_to_reuse;
|
||||
}
|
||||
|
||||
#ifdef LWS_WITH_CGI
|
||||
if (!strcmp(in, "/cgitest")) {
|
||||
static char *cmd[] = {
|
||||
|
@ -396,11 +407,33 @@ int callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user,
|
|||
if (pss->fd == LWS_INVALID_FILE)
|
||||
goto try_to_reuse;
|
||||
#ifdef LWS_WITH_CGI
|
||||
if (pss->reason_bf) {
|
||||
if (pss->reason_bf & 1) {
|
||||
if (lws_cgi_write_split_stdout_headers(wsi) < 0)
|
||||
goto bail;
|
||||
|
||||
pss->reason_bf = 0;
|
||||
pss->reason_bf &= ~1;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
#ifndef LWS_NO_CLIENT
|
||||
if (pss->reason_bf & 2) {
|
||||
char *px = buf + LWS_PRE;
|
||||
int lenx = sizeof(buf) - LWS_PRE;
|
||||
/*
|
||||
* our sink is writeable and our source has something
|
||||
* to read. So read a lump of source material of
|
||||
* suitable size to send or what's available, whichever
|
||||
* is the smaller.
|
||||
*/
|
||||
pss->reason_bf &= ~2;
|
||||
wsi1 = lws_get_child(wsi);
|
||||
if (!wsi1)
|
||||
break;
|
||||
if (lws_http_client_read(wsi1, &px, &lenx) < 0)
|
||||
goto bail;
|
||||
|
||||
if (pss->client_finished)
|
||||
return -1;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
@ -470,7 +503,7 @@ bail:
|
|||
* callback for confirming to continue with client IP appear in
|
||||
* protocol 0 callback since no websocket protocol has been agreed
|
||||
* yet. You can just ignore this if you won't filter on client IP
|
||||
* since the default uhandled callback return is 0 meaning let the
|
||||
* since the default unhandled callback return is 0 meaning let the
|
||||
* connection continue.
|
||||
*/
|
||||
case LWS_CALLBACK_FILTER_NETWORK_CONNECTION:
|
||||
|
@ -478,7 +511,9 @@ bail:
|
|||
break;
|
||||
|
||||
#ifndef LWS_WITH_CLIENT
|
||||
case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP:
|
||||
case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP: {
|
||||
char ctype[64], ctlen = 0;
|
||||
lwsl_err("LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP\n");
|
||||
p = buffer + LWS_PRE;
|
||||
end = p + sizeof(buffer) - LWS_PRE;
|
||||
if (lws_add_http_header_status(lws_get_parent(wsi), 200, &p, end))
|
||||
|
@ -488,14 +523,17 @@ bail:
|
|||
(unsigned char *)"libwebsockets",
|
||||
13, &p, end))
|
||||
return 1;
|
||||
if (lws_add_http_header_by_token(lws_get_parent(wsi),
|
||||
|
||||
ctlen = lws_hdr_copy(wsi, ctype, sizeof(ctype), WSI_TOKEN_HTTP_CONTENT_TYPE);
|
||||
if (ctlen > 0) {
|
||||
if (lws_add_http_header_by_token(lws_get_parent(wsi),
|
||||
WSI_TOKEN_HTTP_CONTENT_TYPE,
|
||||
(unsigned char *)"text/html", 9, &p, end))
|
||||
return 1;
|
||||
(unsigned char *)ctype, ctlen, &p, end))
|
||||
return 1;
|
||||
}
|
||||
#if 0
|
||||
if (lws_add_http_header_content_length(lws_get_parent(wsi),
|
||||
file_len, &p,
|
||||
end))
|
||||
file_len, &p, end))
|
||||
return 1;
|
||||
#endif
|
||||
if (lws_finalize_http_header(lws_get_parent(wsi), &p, end))
|
||||
|
@ -510,20 +548,39 @@ bail:
|
|||
if (n < 0)
|
||||
return -1;
|
||||
|
||||
break;
|
||||
break; }
|
||||
case LWS_CALLBACK_CLOSED_CLIENT_HTTP:
|
||||
//lwsl_err("LWS_CALLBACK_CLOSED_CLIENT_HTTP\n");
|
||||
return -1;
|
||||
break;
|
||||
case LWS_CALLBACK_RECEIVE_CLIENT_HTTP:
|
||||
m = lws_write(lws_get_parent(wsi), in, len, LWS_WRITE_HTTP);
|
||||
//lwsl_err("LWS_CALLBACK_RECEIVE_CLIENT_HTTP: wsi %p\n", wsi);
|
||||
assert(lws_get_parent(wsi));
|
||||
if (!lws_get_parent(wsi))
|
||||
break;
|
||||
// lwsl_err("LWS_CALLBACK_RECEIVE_CLIENT_HTTP: wsi %p: sock: %d, parent_wsi: %p, parent_sock:%d, len %d\n",
|
||||
// wsi, lws_get_socket_fd(wsi),
|
||||
// lws_get_parent(wsi),
|
||||
// lws_get_socket_fd(lws_get_parent(wsi)), len);
|
||||
pss1 = lws_wsi_user(lws_get_parent(wsi));
|
||||
pss1->reason_bf |= 2;
|
||||
lws_callback_on_writable(lws_get_parent(wsi));
|
||||
break;
|
||||
case LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ:
|
||||
//lwsl_err("LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ len %d\n", len);
|
||||
assert(lws_get_parent(wsi));
|
||||
m = lws_write(lws_get_parent(wsi), (unsigned char *)in,
|
||||
len, LWS_WRITE_HTTP);
|
||||
if (m < 0)
|
||||
return 1;
|
||||
return -1;
|
||||
break;
|
||||
case LWS_CALLBACK_COMPLETED_CLIENT_HTTP:
|
||||
//lwsl_err("LWS_CALLBACK_COMPLETED_CLIENT_HTTP\n");
|
||||
assert(lws_get_parent(wsi));
|
||||
if (!lws_get_parent(wsi))
|
||||
break;
|
||||
pss1 = lws_wsi_user(lws_get_parent(wsi));
|
||||
pss1->client_finished = 1;
|
||||
lws_callback_on_writable(lws_get_parent(wsi));
|
||||
return -1;
|
||||
break;
|
||||
#endif
|
||||
|
||||
|
@ -542,7 +599,7 @@ bail:
|
|||
/* TBD stdin rx flow control */
|
||||
break;
|
||||
case LWS_STDOUT:
|
||||
pss->reason_bf |= 1 << pss->args.ch;
|
||||
pss->reason_bf |= 1;
|
||||
/* when writing to MASTER would not block */
|
||||
lws_callback_on_writable(wsi);
|
||||
break;
|
||||
|
|
Loading…
Add table
Reference in a new issue