1
0
Fork 0
mirror of https://github.com/warmcat/libwebsockets.git synced 2025-03-09 00:00:04 +01:00

http proxy: client: unix socket support

This allows the client stuff to understand that addresses beginning with '+'
represent unix sockets.

If the first character after the '+' is '@', it understands that the '@'
should be read as '\0', in order to use Linux "abstract namespace"
sockets.

Further the lws_parse_uri() helper is extended to understand the convention
that an address starting with + is a unix socket, and treats the socket
path as delimited by ':', eg

http://+/var/run/mysocket:/my/path

HTTP Proxy is updated to allow mounts to these unix socket paths.

Proxy connections go out on h1, but are dynamically translated to h1 or h2
on the incoming side.

Proxy usage of libhubbub is separated out... LWS_WITH_HTTP_PROXY is on by
default, and LWS_WITH_HUBBUB is off by default.
This commit is contained in:
Andy Green 2018-09-04 08:06:46 +08:00
parent dc38eea060
commit fd810f198a
27 changed files with 1058 additions and 454 deletions

View file

@ -29,7 +29,7 @@ option(LWS_WITH_CGI "Include CGI (spawn process with network-connected stdin/out
option(LWS_IPV6 "Compile with support for ipv6" OFF)
option(LWS_UNIX_SOCK "Compile with support for UNIX domain socket" ON)
option(LWS_WITH_PLUGINS "Support plugins for protocols and extensions" OFF)
option(LWS_WITH_HTTP_PROXY "Support for rewriting HTTP proxying (requires libhubbub)" OFF)
option(LWS_WITH_HTTP_PROXY "Support for HTTP proxying" ON)
option(LWS_WITH_ZIP_FOPS "Support serving pre-zipped files" OFF)
option(LWS_WITH_SOCKS5 "Allow use of SOCKS5 proxy on client connections" OFF)
option(LWS_WITH_GENERIC_SESSIONS "With the Generic Sessions plugin" OFF)
@ -41,6 +41,7 @@ option(LWS_WITH_THREADPOOL "Managed worker thread pool support (relies on pthrea
option(LWS_WITH_HTTP_STREAM_COMPRESSION "Support HTTP stream compression" OFF)
option(LWS_WITH_HTTP_BROTLI "Also offer brotli http stream compression (requires LWS_WITH_HTTP_STREAM_COMPRESSION)" OFF)
option(LWS_WITH_ACME "Enable support for ACME automatic cert acquisition + maintenance (letsencrypt etc)" OFF)
option(LWS_WITH_HUBBUB "Enable libhubbub rewriting support" OFF)
#
# TLS library options... all except mbedTLS are basically OpenSSL variants.
#
@ -134,6 +135,12 @@ if(NOT DEFINED CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type")
endif()
# microsoft... that's why you can't have nice things
if (WIN32 OR LWS_WITH_ESP32)
set(LWS_UNIX_SOCK 0)
endif()
project(libwebsockets C)
set(PACKAGE "libwebsockets")
@ -333,7 +340,8 @@ 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")
message("You have to enable both client and server for http proxy")
set(LWS_WITH_HTTP_PROXY 0)
endif()
# Allow the user to override installation directories.
@ -744,6 +752,7 @@ set(HDR_PUBLIC
set(SOURCES
lib/core/alloc.c
lib/core/context.c
lib/core/dummy-callback.c
lib/core/libwebsockets.c
lib/core/output.c
lib/core/pollfd.c
@ -1402,7 +1411,7 @@ if (LWS_WITH_SQLITE3)
endif()
if (LWS_WITH_HTTP_PROXY)
if (LWS_WITH_HUBBUB)
find_library(LIBHUBBUB_LIBRARIES NAMES hubbub)
list(APPEND LIB_LIST ${LIBHUBBUB_LIBRARIES} )
endif()

View file

@ -0,0 +1,95 @@
## Unix Domain Sockets Reverse Proxy
### Introduction
lws is able to use a mount to place reverse proxies into the URL space.
These are particularly useful when using Unix Domain Sockets, basically
files in the server filesystem, to communicate between lws and a separate
server process and integrate the result into a coherent URL namespace on
the lws side.
This has the advantage that the actual web server that forwards the
data from the unix socket owner is in a different process than the server
that serves on the unix socket. If it has problems, they do not affect
the actual public-facing web server. The unix domain socket server may
be in a completely different language than the web server.
Compared to CGI, there are no forks to make a connection to the unix
domain socket server.
### Mount origin format
Unix Domain Sockets are effectively "files" in the server filesystem, and
are defined by their filepath. The "server" side that is to be proxied opens
the socket and listens on it, which creates a file in the server filesystem.
The socket understands either http or https protocol.
Lws can be told to act as a proxy for that at a mountpoint in the lws vhost
url space.
If your mount is expressed in C code, then the mount type is LWSMPRO_HTTP or
LWSMPRO_HTTPS depending on the protocol the unix socket understands, and the
origin address has the form `+/path/to/unix/socket:/path/inside/mount`.
The + at the start indicates it is a local unix socket we are proxying, and
the ':' acts as a delimiter for the socket path, since unlike other addresses
the unix socket path can contain '/' itself.
### Connectivity rules and translations
Onward proxy connections from lws to the Unix Domain Socket happen using
http/1.1. That implies `transfer-encoding: chunking` in the case that the
length of the output is not known beforehand.
Lws takes care of stripping any chunking (which is illegal in h2) and
translating between h1 and h2 header formats if the return connection is
actually in http/2.
The h1 onward proxy connection translates the following headers from the return
connection, which may be h1 or h2:
Header|Function
---|---
host|Which vhost
etag|Information on any etag the client has cached for this URI
if-modified-since|Information on the freshness of any etag the client has cached for this URI
accept-language|Which languages the return path client prefers
accept-encoding|Which compression encodings the client can accept
cache-control|Information from the return path client about cache acceptability
x-forwarded-for|The IP address of the return path client
This implies that the proxied connection can
- return 301 etc to say the return path client's etag is still valid
- choose to compress using an acceptable content-encoding
The following headers are translated from the headers replied via the onward
connection (always h1) back to the return path (which may be h1 or h2)
Header|Function
---|---
content-length|If present, an assertion of how much payload is expected
content-type|The mimetype of the payload
etag|The canonical etag for the content at this URI
accept-language|This is returned to the return path client because there is no easy way for the return path client to know what it sent originally. It allows clientside selection of i18n.
content-encoding|Any compression format on the payload (selected from what the client sent in accept-encoding, if anything)
cache-control|The onward server's response about cacheability of its payload
### h1 -> h2 conversion
Chunked encoding that may have been used on the outgoing proxy client connection
is removed for h2 return connections (chunked encoding is illegal for h2).
Headers are converted to all lower-case and hpack format for h2 return connections.
Header and payload proxying is staged according to when the return connection
(which may be an h2 child stream) is writable.
### Behaviour is unix domain socket server unavailable
If the server that listens on the unix domain socket is down or being restarted,
lws understands that it couldn't connect to it and returns a clean 503 response
`HTTP_STATUS_SERVICE_UNAVAILABLE` along with a brief human-readable explanation.

View file

@ -485,7 +485,7 @@ enum lws_callback_reasons {
/**< after your client connection completed the websocket upgrade
* handshake with the remote server */
LWS_CALLBACK_CLIENT_CLOSED = 75,
LWS_CALLBACK_CLIENT_CLOSED = 75,
/**< when a client websocket session ends */
LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER = 24,
@ -497,21 +497,11 @@ enum lws_callback_reasons {
* which is typically some hundreds of bytes. So, to add a canned
* cookie, your handler code might look similar to:
*
* char **p = (char **)in;
* char **p = (char **)in, *end = (*p) + len;
*
* if (len < 100)
* return 1;
*
* *p += sprintf(*p, "Cookie: a=b\x0d\x0a");
*
* return 0;
*
* Notice if you add anything, you just have to take care about
* the CRLF on the line you added. Obviously this callback is
* optional, if you don't handle it everything is fine.
*
* Notice the callback is coming to protocols[0] all the time,
* because there is no specific protocol negotiated yet.
* if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_COOKIE,
* (unsigned char)"a=b", 3, p, end))
* return -1;
*
* See LWS_CALLBACK_ADD_HEADERS for adding headers to server
* transactions.
@ -798,4 +788,6 @@ lws_callback_function(struct lws *wsi, enum lws_callback_reasons reason,
#define LWS_CB_REASON_AUX_BF__PROXY 2
#define LWS_CB_REASON_AUX_BF__CGI_CHUNK_END 4
#define LWS_CB_REASON_AUX_BF__CGI_HEADERS 8
#define LWS_CB_REASON_AUX_BF__PROXY_TRANS_END 16
#define LWS_CB_REASON_AUX_BF__PROXY_HEADERS 32
///@}

View file

@ -45,8 +45,9 @@ enum lws_log_levels {
LLL_CLIENT = 1 << 8,
LLL_LATENCY = 1 << 9,
LLL_USER = 1 << 10,
LLL_THREAD = 1 << 11,
LLL_COUNT = 11 /* set to count of valid flags */
LLL_COUNT = 12 /* set to count of valid flags */
};
LWS_VISIBLE LWS_EXTERN void _lws_log(int filter, const char *format, ...) LWS_FORMAT(2);
@ -92,6 +93,7 @@ lwsl_timestamp(int level, char *p, int len);
#define lwsl_ext(...) _lws_log(LLL_EXT, __VA_ARGS__)
#define lwsl_client(...) _lws_log(LLL_CLIENT, __VA_ARGS__)
#define lwsl_latency(...) _lws_log(LLL_LATENCY, __VA_ARGS__)
#define lwsl_thread(...) _lws_log(LLL_THREAD, __VA_ARGS__)
#else /* no debug */
#if defined(LWS_WITH_NO_LOGS)
@ -105,6 +107,7 @@ lwsl_timestamp(int level, char *p, int len);
#define lwsl_ext(...) do {} while(0)
#define lwsl_client(...) do {} while(0)
#define lwsl_latency(...) do {} while(0)
#define lwsl_thread(...) do {} while(0)
#endif

View file

@ -392,6 +392,17 @@ lws_set_wsi_user(struct lws *wsi, void *user);
* \param ads: result pointer for address part
* \param port: result pointer for port part
* \param path: result pointer for path part
*
* You may also refer to unix socket addresses, using a '+' at the start of
* the address. In this case, the address should end with ':', which is
* treated as the separator between the address and path (the normal separator
* '/' is a valid part of the socket path). Eg,
*
* http://+/var/run/mysocket:/my/path
*
* If the first character after the + is '@', it's interpreted by lws client
* processing as meaning to use linux abstract namespace sockets, the @ is
* replaced with a '\0' before use.
*/
LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT
lws_parse_uri(char *p, const char **prot, const char **ads, int *port,
@ -422,6 +433,12 @@ lws_cmdline_option(int argc, const char **argv, const char *val);
LWS_VISIBLE LWS_EXTERN unsigned long
lws_now_secs(void);
/**
* lws_now_usecs(): return useconds since 1970-1-1
*/
LWS_VISIBLE LWS_EXTERN lws_usec_t
lws_now_usecs(void);
/**
* lws_compare_time_t(): return relationship between two time_t
*

View file

@ -63,6 +63,7 @@ enum pending_timeout {
PENDING_TIMEOUT_LAGGING = 28,
PENDING_TIMEOUT_THREADPOOL = 29,
PENDING_TIMEOUT_THREADPOOL_TASK = 30,
PENDING_TIMEOUT_KILLED_BY_PROXY_CLIENT_CLOSE = 31,
/****** add new things just above ---^ ******/

View file

@ -86,6 +86,14 @@ enum lws_write_protocol {
/* flags */
LWS_WRITE_BUFLIST = 0x20,
/**< Don't actually write it... stick it on the output buflist and
* write it as soon as possible. Useful if you learn you have to
* write something, have the data to write to hand but the timing is
* unrelated as to whether the connection is writable or not, and were
* otherwise going to have to allocate a temp buffer and write it
* later anyway */
LWS_WRITE_NO_FIN = 0x40,
/**< This part of the message is not the end of the message */

View file

@ -241,6 +241,9 @@ bail:
parent->child_list = new_wsi->sibling_list;
if (new_wsi->user_space)
lws_free(new_wsi->user_space);
vh->context->count_wsi_allocated--;
lws_vhost_unbind_wsi(new_wsi);
lws_free(new_wsi);

View file

@ -42,7 +42,7 @@ lws_client_stash_destroy(struct lws *wsi)
LWS_VISIBLE struct lws *
lws_client_connect_via_info(const struct lws_client_connect_info *i)
{
struct lws *wsi;
struct lws *wsi, *safe = NULL;
const struct lws_protocols *p;
const char *local = i->protocol;
#if LWS_MAX_SMP > 1
@ -219,6 +219,21 @@ lws_client_connect_via_info(const struct lws_client_connect_info *i)
goto bail1;
}
/*
* at this point user callbacks like
* LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER will be interested to
* know the parent... eg for proxying we can grab extra headers from
* the parent's incoming ah and add them to the child client handshake
*/
if (i->parent_wsi) {
lwsl_info("%s: created child %p of parent %p\n", __func__,
wsi, i->parent_wsi);
wsi->parent = i->parent_wsi;
safe = wsi->sibling_list = i->parent_wsi->child_list;
i->parent_wsi->child_list = wsi;
}
/*
* PHASE 7: Do any role-specific finalization processing. We can still
* see important info things via wsi->stash
@ -227,6 +242,12 @@ lws_client_connect_via_info(const struct lws_client_connect_info *i)
if (wsi->role_ops->client_bind) {
int n = wsi->role_ops->client_bind(wsi, NULL);
if (n && i->parent_wsi) {
/* unpick from parent */
i->parent_wsi->child_list = safe;
}
if (n < 0)
/* we didn't survive, wsi is freed */
goto bail2;
@ -241,14 +262,8 @@ lws_client_connect_via_info(const struct lws_client_connect_info *i)
if (i->pwsi)
*i->pwsi = wsi;
if (i->parent_wsi) {
lwsl_info("%s: created child %p of parent %p\n", __func__,
wsi, i->parent_wsi);
wsi->parent = i->parent_wsi;
wsi->sibling_list = i->parent_wsi->child_list;
i->parent_wsi->child_list = wsi;
}
#ifdef LWS_WITH_HTTP_PROXY
#if defined(LWS_WITH_HUBBUB)
if (i->uri_replace_to)
wsi->http.rw = lws_rewrite_create(wsi, html_parser_cb,
i->uri_replace_from,

View file

@ -339,347 +339,6 @@ next:
return 0;
}
LWS_VISIBLE int
lws_callback_http_dummy(struct lws *wsi, enum lws_callback_reasons reason,
void *user, void *in, size_t len)
{
struct lws_ssl_info *si;
#ifdef LWS_WITH_CGI
struct lws_cgi_args *args;
#endif
#if defined(LWS_WITH_CGI) || defined(LWS_WITH_HTTP_PROXY)
char buf[128];
int n;
#endif
switch (reason) {
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
case LWS_CALLBACK_HTTP:
#ifndef LWS_NO_SERVER
if (lws_return_http_status(wsi, HTTP_STATUS_NOT_FOUND, NULL))
return -1;
if (lws_http_transaction_completed(wsi))
#endif
return -1;
break;
#if !defined(LWS_NO_SERVER)
case LWS_CALLBACK_HTTP_BODY_COMPLETION:
case LWS_CALLBACK_HTTP_FILE_COMPLETION:
if (lws_http_transaction_completed(wsi))
return -1;
break;
#endif
case LWS_CALLBACK_HTTP_WRITEABLE:
#ifdef LWS_WITH_CGI
if (wsi->reason_bf & (LWS_CB_REASON_AUX_BF__CGI_HEADERS |
LWS_CB_REASON_AUX_BF__CGI)) {
n = lws_cgi_write_split_stdout_headers(wsi);
if (n < 0) {
lwsl_debug("AUX_BF__CGI forcing close\n");
return -1;
}
if (!n)
lws_rx_flow_control(
wsi->http.cgi->stdwsi[LWS_STDOUT], 1);
if (wsi->reason_bf & LWS_CB_REASON_AUX_BF__CGI_HEADERS)
wsi->reason_bf &=
~LWS_CB_REASON_AUX_BF__CGI_HEADERS;
else
wsi->reason_bf &= ~LWS_CB_REASON_AUX_BF__CGI;
if (wsi->http.cgi && wsi->http.cgi->cgi_transaction_over)
return -1;
break;
}
if (wsi->reason_bf & LWS_CB_REASON_AUX_BF__CGI_CHUNK_END) {
if (!wsi->http2_substream) {
memcpy(buf + LWS_PRE, "0\x0d\x0a\x0d\x0a", 5);
lwsl_debug("writing chunk term and exiting\n");
n = lws_write(wsi, (unsigned char *)buf +
LWS_PRE, 5, LWS_WRITE_HTTP);
} else
n = lws_write(wsi, (unsigned char *)buf +
LWS_PRE, 0,
LWS_WRITE_HTTP_FINAL);
/* always close after sending it */
return -1;
}
#endif
#if defined(LWS_WITH_HTTP_PROXY)
if (wsi->reason_bf & LWS_CB_REASON_AUX_BF__PROXY) {
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.
*/
wsi->reason_bf &= ~LWS_CB_REASON_AUX_BF__PROXY;
if (!lws_get_child(wsi))
break;
if (lws_http_client_read(lws_get_child(wsi), &px,
&lenx) < 0)
return -1;
break;
}
#endif
break;
#if defined(LWS_WITH_HTTP_PROXY)
case LWS_CALLBACK_RECEIVE_CLIENT_HTTP:
assert(lws_get_parent(wsi));
if (!lws_get_parent(wsi))
break;
lws_get_parent(wsi)->reason_bf |= LWS_CB_REASON_AUX_BF__PROXY;
lws_callback_on_writable(lws_get_parent(wsi));
break;
case LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ:
assert(lws_get_parent(wsi));
n = lws_write(lws_get_parent(wsi), (unsigned char *)in,
len, LWS_WRITE_HTTP);
if (n < 0)
return -1;
break;
case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP: {
unsigned char *p, *end;
char ctype[64], ctlen = 0;
p = (unsigned char *)buf + LWS_PRE;
end = p + sizeof(buf) - LWS_PRE;
if (lws_add_http_header_status(lws_get_parent(wsi),
HTTP_STATUS_OK, &p, end))
return 1;
if (lws_add_http_header_by_token(lws_get_parent(wsi),
WSI_TOKEN_HTTP_SERVER,
(unsigned char *)"libwebsockets",
13, &p, end))
return 1;
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 *)ctype, ctlen, &p, end))
return 1;
}
if (lws_finalize_http_header(lws_get_parent(wsi), &p, end))
return 1;
*p = '\0';
n = lws_write(lws_get_parent(wsi),
(unsigned char *)buf + LWS_PRE,
p - ((unsigned char *)buf + LWS_PRE),
LWS_WRITE_HTTP_HEADERS);
if (n < 0)
return -1;
break; }
#endif
#ifdef LWS_WITH_CGI
/* CGI IO events (POLLIN/OUT) appear here, our default policy is:
*
* - POST data goes on subprocess stdin
* - subprocess stdout goes on http via writeable callback
* - subprocess stderr goes to the logs
*/
case LWS_CALLBACK_CGI:
args = (struct lws_cgi_args *)in;
switch (args->ch) { /* which of stdin/out/err ? */
case LWS_STDIN:
/* TBD stdin rx flow control */
break;
case LWS_STDOUT:
/* quench POLLIN on STDOUT until MASTER got writeable */
lws_rx_flow_control(args->stdwsi[LWS_STDOUT], 0);
wsi->reason_bf |= LWS_CB_REASON_AUX_BF__CGI;
/* when writing to MASTER would not block */
lws_callback_on_writable(wsi);
break;
case LWS_STDERR:
n = lws_get_socket_fd(args->stdwsi[LWS_STDERR]);
if (n < 0)
break;
n = read(n, buf, sizeof(buf) - 2);
if (n > 0) {
if (buf[n - 1] != '\n')
buf[n++] = '\n';
buf[n] = '\0';
lwsl_notice("CGI-stderr: %s\n", buf);
}
break;
}
break;
case LWS_CALLBACK_CGI_TERMINATED:
lwsl_debug("LWS_CALLBACK_CGI_TERMINATED: %d %" PRIu64 "\n",
wsi->http.cgi->explicitly_chunked,
(uint64_t)wsi->http.cgi->content_length);
if (!wsi->http.cgi->explicitly_chunked &&
!wsi->http.cgi->content_length) {
/* send terminating chunk */
lwsl_debug("LWS_CALLBACK_CGI_TERMINATED: ending\n");
wsi->reason_bf |= LWS_CB_REASON_AUX_BF__CGI_CHUNK_END;
lws_callback_on_writable(wsi);
lws_set_timeout(wsi, PENDING_TIMEOUT_CGI, 3);
break;
}
return -1;
case LWS_CALLBACK_CGI_STDIN_DATA: /* POST body for stdin */
args = (struct lws_cgi_args *)in;
args->data[args->len] = '\0';
if (!args->stdwsi[LWS_STDIN])
return -1;
n = lws_get_socket_fd(args->stdwsi[LWS_STDIN]);
if (n < 0)
return -1;
#if defined(LWS_WITH_ZLIB)
if (wsi->http.cgi->gzip_inflate) {
/* gzip handling */
if (!wsi->http.cgi->gzip_init) {
lwsl_err("inflating gzip\n");
memset(&wsi->http.cgi->inflate, 0, sizeof(wsi->http.cgi->inflate));
if (inflateInit2(&wsi->http.cgi->inflate, 16 + 15) != Z_OK) {
lwsl_err("%s: iniflateInit failed\n", __func__);
return -1;
}
wsi->http.cgi->gzip_init = 1;
}
wsi->http.cgi->inflate.next_in = args->data;
wsi->http.cgi->inflate.avail_in = args->len;
do {
wsi->http.cgi->inflate.next_out = wsi->http.cgi->inflate_buf;
wsi->http.cgi->inflate.avail_out = sizeof(wsi->http.cgi->inflate_buf);
n = inflate(&wsi->http.cgi->inflate, Z_SYNC_FLUSH);
lwsl_err("inflate: %d\n", n);
switch (n) {
case Z_NEED_DICT:
case Z_STREAM_ERROR:
case Z_DATA_ERROR:
case Z_MEM_ERROR:
inflateEnd(&wsi->http.cgi->inflate);
wsi->http.cgi->gzip_init = 0;
lwsl_err("zlib error inflate %d\n", n);
return -1;
}
if (wsi->http.cgi->inflate.avail_out != sizeof(wsi->http.cgi->inflate_buf)) {
int written;
// lwsl_hexdump_notice(wsi->http.cgi->inflate_buf,
// sizeof(wsi->http.cgi->inflate_buf) - wsi->http.cgi->inflate.avail_out);
written = write(args->stdwsi[LWS_STDIN]->desc.filefd, wsi->http.cgi->inflate_buf,
sizeof(wsi->http.cgi->inflate_buf) - wsi->http.cgi->inflate.avail_out);
if (written != (int)(sizeof(wsi->http.cgi->inflate_buf) - wsi->http.cgi->inflate.avail_out)) {
lwsl_notice("LWS_CALLBACK_CGI_STDIN_DATA: "
"sent %d only %d went", n, args->len);
}
lwsl_err("send inflated on fd %d says %d\n", args->stdwsi[LWS_STDIN]->desc.filefd, written);
if (n == Z_STREAM_END) {
lwsl_err("gzip inflate end\n");
inflateEnd(&wsi->http.cgi->inflate);
wsi->http.cgi->gzip_init = 0;
//compatible_close(args->stdwsi[LWS_STDIN]->desc.filefd);
//args->stdwsi[LWS_STDIN]->desc.filefd = -1;
break;
}
} else
break;
if (wsi->http.cgi->inflate.avail_out)
break;
} while (1);
return args->len;
}
#endif /* WITH_ZLIB */
n = write(n, args->data, args->len);
// lwsl_hexdump_notice(args->data, args->len);
if (n < args->len)
lwsl_notice("LWS_CALLBACK_CGI_STDIN_DATA: "
"sent %d only %d went", n, args->len);
if (wsi->http.cgi->post_in_expected && args->stdwsi[LWS_STDIN] &&
args->stdwsi[LWS_STDIN]->desc.filefd > 0) {
wsi->http.cgi->post_in_expected -= n;
if (!wsi->http.cgi->post_in_expected) {
struct lws *siwsi = args->stdwsi[LWS_STDIN];
lwsl_debug("%s: expected POST in end: "
"closing stdin wsi %p, fd %d\n",
__func__, siwsi, siwsi->desc.sockfd);
__remove_wsi_socket_from_fds(siwsi);
lwsi_set_state(siwsi, LRS_DEAD_SOCKET);
siwsi->socket_is_permanently_unusable = 1;
lws_remove_child_from_any_parent(siwsi);
if (wsi->context->event_loop_ops->
close_handle_manually) {
wsi->context->event_loop_ops->
close_handle_manually(siwsi);
siwsi->told_event_loop_closed = 1;
} else {
compatible_close(siwsi->desc.sockfd);
__lws_free_wsi(siwsi);
}
wsi->http.cgi->pipe_fds[LWS_STDIN][1] = -1;
args->stdwsi[LWS_STDIN] = NULL;
}
}
return n;
#endif /* WITH_CGI */
#endif /* ROLE_ H1 / H2 */
case LWS_CALLBACK_SSL_INFO:
si = in;
(void)si;
lwsl_notice("LWS_CALLBACK_SSL_INFO: where: 0x%x, ret: 0x%x\n",
si->where, si->ret);
break;
#if LWS_MAX_SMP > 1
case LWS_CALLBACK_GET_THREAD_ID:
return (int)(unsigned long long)pthread_self();
#endif
default:
break;
}
return 0;
}
/* list of supported protocols and callbacks */
@ -1142,6 +801,7 @@ lws_destroy_event_pipe(struct lws *wsi)
if (wsi->context->event_loop_ops->wsi_logical_close) {
wsi->context->event_loop_ops->wsi_logical_close(wsi);
lws_plat_pipe_close(wsi);
wsi->context->count_wsi_allocated--;
return;
}
@ -1668,13 +1328,14 @@ lws_vhost_destroy1(struct lws_vhost *vh)
assert(v->lserv_wsi == NULL);
v->lserv_wsi = vh->lserv_wsi;
lwsl_notice("%s: listen skt from %s to %s\n",
__func__, vh->name, v->name);
if (v->lserv_wsi) {
lws_vhost_unbind_wsi(vh->lserv_wsi);
lws_vhost_bind_wsi(v, v->lserv_wsi);
}
lwsl_notice("%s: listen skt from %s to %s\n",
__func__, vh->name, v->name);
break;
}
} lws_end_foreach_ll(v, vhost_next);

605
lib/core/dummy-callback.c Normal file
View file

@ -0,0 +1,605 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010-2017 Andy Green <andy@warmcat.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation:
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
#include "core/private.h"
#if defined(LWS_WITH_HTTP_PROXY)
static int
proxy_header(struct lws *wsi, struct lws *par, unsigned char *temp,
int temp_len, int index, unsigned char **p, unsigned char *end)
{
int n = lws_hdr_total_length(par, index);
if (n < 1) {
lwsl_debug("%s: no index %d:\n", __func__, index);
return 0;
}
if (lws_hdr_copy(par, (char *)temp, temp_len, index) < 0)
return -1;
lwsl_debug("%s: index %d: %s\n", __func__, index, (char *)temp);
if (lws_add_http_header_by_token(wsi, index, temp, n, p, end))
return -1;
return 0;
}
static int
stream_close(struct lws *wsi)
{
char buf[LWS_PRE + 6], *out = buf + LWS_PRE;
if (wsi->http.did_stream_close)
return 0;
wsi->http.did_stream_close = 1;
if (wsi->http2_substream) {
if (lws_write(wsi, (unsigned char *)buf + LWS_PRE, 0,
LWS_WRITE_HTTP_FINAL) < 0) {
lwsl_info("%s: COMPL_CLIENT_HTTP: h2 fin wr failed\n",
__func__);
return -1;
}
} else {
*out++ = '0';
*out++ = '\x0d';
*out++ = '\x0a';
*out++ = '\x0d';
*out++ = '\x0a';
if (lws_write(wsi, (unsigned char *)buf + LWS_PRE, 5,
LWS_WRITE_HTTP_FINAL) < 0) {
lwsl_err("%s: COMPL_CLIENT_HTTP: "
"h2 final write failed\n", __func__);
return -1;
}
}
return 0;
}
#endif
LWS_VISIBLE int
lws_callback_http_dummy(struct lws *wsi, enum lws_callback_reasons reason,
void *user, void *in, size_t len)
{
struct lws_ssl_info *si;
#ifdef LWS_WITH_CGI
struct lws_cgi_args *args;
#endif
#if defined(LWS_WITH_CGI) || defined(LWS_WITH_HTTP_PROXY)
char buf[8192];
int n;
#endif
#if defined(LWS_WITH_HTTP_PROXY)
unsigned char **p, *end;
struct lws *parent;
#endif
switch (reason) {
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
case LWS_CALLBACK_HTTP:
#ifndef LWS_NO_SERVER
if (lws_return_http_status(wsi, HTTP_STATUS_NOT_FOUND, NULL))
return -1;
if (lws_http_transaction_completed(wsi))
#endif
return -1;
break;
#if !defined(LWS_NO_SERVER)
case LWS_CALLBACK_HTTP_BODY_COMPLETION:
case LWS_CALLBACK_HTTP_FILE_COMPLETION:
if (lws_http_transaction_completed(wsi))
return -1;
break;
#endif
case LWS_CALLBACK_HTTP_WRITEABLE:
#ifdef LWS_WITH_CGI
if (wsi->reason_bf & (LWS_CB_REASON_AUX_BF__CGI_HEADERS |
LWS_CB_REASON_AUX_BF__CGI)) {
n = lws_cgi_write_split_stdout_headers(wsi);
if (n < 0) {
lwsl_debug("AUX_BF__CGI forcing close\n");
return -1;
}
if (!n)
lws_rx_flow_control(
wsi->http.cgi->stdwsi[LWS_STDOUT], 1);
if (wsi->reason_bf & LWS_CB_REASON_AUX_BF__CGI_HEADERS)
wsi->reason_bf &=
~LWS_CB_REASON_AUX_BF__CGI_HEADERS;
else
wsi->reason_bf &= ~LWS_CB_REASON_AUX_BF__CGI;
if (wsi->http.cgi && wsi->http.cgi->cgi_transaction_over)
return -1;
break;
}
if (wsi->reason_bf & LWS_CB_REASON_AUX_BF__CGI_CHUNK_END) {
if (!wsi->http2_substream) {
memcpy(buf + LWS_PRE, "0\x0d\x0a\x0d\x0a", 5);
lwsl_debug("writing chunk term and exiting\n");
n = lws_write(wsi, (unsigned char *)buf +
LWS_PRE, 5, LWS_WRITE_HTTP);
} else
n = lws_write(wsi, (unsigned char *)buf +
LWS_PRE, 0,
LWS_WRITE_HTTP_FINAL);
/* always close after sending it */
return -1;
}
#endif
#if defined(LWS_WITH_HTTP_PROXY)
if (wsi->reason_bf & LWS_CB_REASON_AUX_BF__PROXY_HEADERS) {
wsi->reason_bf &= ~LWS_CB_REASON_AUX_BF__PROXY_HEADERS;
lwsl_notice("%s: %p: issuing proxy headers\n",
__func__, wsi);
n = lws_write(wsi, wsi->http.pending_return_headers + LWS_PRE,
wsi->http.pending_return_headers_len,
LWS_WRITE_HTTP_HEADERS);
lws_free_set_NULL(wsi->http.pending_return_headers);
if (n < 0) {
lwsl_err("%s: EST_CLIENT_HTTP: write failed\n",
__func__);
return -1;
}
lws_callback_on_writable(wsi);
break;
}
if (wsi->reason_bf & LWS_CB_REASON_AUX_BF__PROXY) {
char *px = buf + LWS_PRE;
int lenx = sizeof(buf) - LWS_PRE - 32;
/*
* 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.
*/
wsi->reason_bf &= ~LWS_CB_REASON_AUX_BF__PROXY;
if (!lws_get_child(wsi))
break;
/* this causes LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ */
if (lws_http_client_read(lws_get_child(wsi), &px,
&lenx) < 0) {
lwsl_info("%s: LWS_CB_REASON_AUX_BF__PROXY: "
"client closed\n", __func__);
stream_close(wsi);
return -1;
}
break;
}
if (wsi->reason_bf & LWS_CB_REASON_AUX_BF__PROXY_TRANS_END) {
lwsl_info("%s: LWS_CB_REASON_AUX_BF__PROXY_TRANS_END\n",
__func__);
wsi->reason_bf &= ~LWS_CB_REASON_AUX_BF__PROXY_TRANS_END;
if (stream_close(wsi))
return -1;
if (lws_http_transaction_completed(wsi))
return -1;
}
#endif
break;
#if defined(LWS_WITH_HTTP_PROXY)
case LWS_CALLBACK_RECEIVE_CLIENT_HTTP:
assert(lws_get_parent(wsi));
if (!lws_get_parent(wsi))
break;
lws_get_parent(wsi)->reason_bf |= LWS_CB_REASON_AUX_BF__PROXY;
lws_callback_on_writable(lws_get_parent(wsi));
break;
case LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ: {
char *out = buf + LWS_PRE;
assert(lws_get_parent(wsi));
if (wsi->http.proxy_parent_chunked) {
if (len > sizeof(buf) - LWS_PRE - 16) {
lwsl_err("oversize buf %d %d\n", (int)len,
(int)sizeof(buf) - LWS_PRE - 16);
return -1;
}
/*
* this only needs dealing with on http/1.1 to allow
* pipelining
*/
n = lws_snprintf(out, 14, "%X\x0d\x0a", (int)len);
out += n;
memcpy(out, in, len);
out += len;
*out++ = '\x0d';
*out++ = '\x0a';
n = lws_write(lws_get_parent(wsi),
(unsigned char *)buf + LWS_PRE,
len + n + 2, LWS_WRITE_HTTP);
} else
n = lws_write(lws_get_parent(wsi), (unsigned char *)in,
len, LWS_WRITE_HTTP);
if (n < 0)
return -1;
break; }
/* this handles the proxy case... */
case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP: {
unsigned char *start, *p, *end;
/*
* We want to proxy these headers, but we are being called
* at the point the onward client was established, which is
* unrelated to the state or writability of our proxy
* connection.
*
* Therefore produce the headers using the onward client ah
* while we have it, and stick them on the output buflist to be
* written on the proxy connection as soon as convenient.
*/
parent = lws_get_parent(wsi);
if (!parent)
return 0;
start = p = (unsigned char *)buf + LWS_PRE;
end = p + sizeof(buf) - LWS_PRE - 256;
if (lws_add_http_header_status(lws_get_parent(wsi),
lws_http_client_http_response(wsi), &p, end))
return 1;
/*
* copy these headers from the client connection to the parent
*/
proxy_header(parent, wsi, end, 256,
WSI_TOKEN_HTTP_CONTENT_LENGTH, &p, end);
proxy_header(parent, wsi, end, 256,
WSI_TOKEN_HTTP_CONTENT_TYPE, &p, end);
proxy_header(parent, wsi, end, 256,
WSI_TOKEN_HTTP_ETAG, &p, end);
proxy_header(parent, wsi, end, 256,
WSI_TOKEN_HTTP_ACCEPT_LANGUAGE, &p, end);
proxy_header(parent, wsi, end, 256,
WSI_TOKEN_HTTP_CONTENT_ENCODING, &p, end);
proxy_header(parent, wsi, end, 256,
WSI_TOKEN_HTTP_CACHE_CONTROL, &p, end);
if (!parent->http2_substream)
if (lws_add_http_header_by_token(parent,
WSI_TOKEN_CONNECTION, (unsigned char *)"close",
5, &p, end))
return -1;
/*
* We proxy using h1 only atm, and strip any chunking so it
* can go back out on h2 just fine.
*
* However if we are actually going out on h1, we need to add
* our own chunking since we still don't know the size.
*/
if (!parent->http2_substream &&
!lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH)) {
lwsl_debug("downstream parent chunked\n");
if (lws_add_http_header_by_token(parent,
WSI_TOKEN_HTTP_TRANSFER_ENCODING,
(unsigned char *)"chunked", 7, &p, end))
return -1;
wsi->http.proxy_parent_chunked = 1;
}
if (lws_finalize_http_header(parent, &p, end))
return 1;
parent->http.pending_return_headers_len = lws_ptr_diff(p, start);
parent->http.pending_return_headers =
lws_malloc(parent->http.pending_return_headers_len + LWS_PRE,
"return proxy headers");
if (!parent->http.pending_return_headers)
return -1;
memcpy(parent->http.pending_return_headers + LWS_PRE, start,
parent->http.pending_return_headers_len);
parent->reason_bf |= LWS_CB_REASON_AUX_BF__PROXY_HEADERS;
lwsl_notice("%s: LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP: prepared headers\n", __func__);
lws_callback_on_writable(parent);
break; }
case LWS_CALLBACK_COMPLETED_CLIENT_HTTP:
lwsl_info("%s: COMPLETED_CLIENT_HTTP: %p (parent %p)\n",
__func__, wsi, lws_get_parent(wsi));
if (!lws_get_parent(wsi))
break;
lws_get_parent(wsi)->reason_bf |=
LWS_CB_REASON_AUX_BF__PROXY_TRANS_END;
lws_callback_on_writable(lws_get_parent(wsi));
break;
case LWS_CALLBACK_CLOSED_CLIENT_HTTP:
if (!lws_get_parent(wsi))
break;
lwsl_err("%s: LWS_CALLBACK_CLOSED_CLIENT_HTTP\n", __func__);
lws_set_timeout(lws_get_parent(wsi), LWS_TO_KILL_ASYNC,
PENDING_TIMEOUT_KILLED_BY_PROXY_CLIENT_CLOSE);
break;
case LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER:
parent = lws_get_parent(wsi);
if (!parent)
break;
p = (unsigned char **)in;
end = (*p) + len;
/*
* copy these headers from the parent request to the client
* connection's request
*/
proxy_header(wsi, parent, (unsigned char *)buf, sizeof(buf),
WSI_TOKEN_HOST, p, end);
proxy_header(wsi, parent, (unsigned char *)buf, sizeof(buf),
WSI_TOKEN_HTTP_ETAG, p, end);
proxy_header(wsi, parent, (unsigned char *)buf, sizeof(buf),
WSI_TOKEN_HTTP_IF_MODIFIED_SINCE, p, end);
proxy_header(wsi, parent, (unsigned char *)buf, sizeof(buf),
WSI_TOKEN_HTTP_ACCEPT_LANGUAGE, p, end);
proxy_header(wsi, parent, (unsigned char *)buf, sizeof(buf),
WSI_TOKEN_HTTP_ACCEPT_ENCODING, p, end);
proxy_header(wsi, parent, (unsigned char *)buf, sizeof(buf),
WSI_TOKEN_HTTP_CACHE_CONTROL, p, end);
buf[0] = '\0';
lws_get_peer_simple(parent, buf, sizeof(buf));
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_X_FORWARDED_FOR,
(unsigned char *)buf, strlen(buf), p, end))
return -1;
break;
#endif
#ifdef LWS_WITH_CGI
/* CGI IO events (POLLIN/OUT) appear here, our default policy is:
*
* - POST data goes on subprocess stdin
* - subprocess stdout goes on http via writeable callback
* - subprocess stderr goes to the logs
*/
case LWS_CALLBACK_CGI:
args = (struct lws_cgi_args *)in;
switch (args->ch) { /* which of stdin/out/err ? */
case LWS_STDIN:
/* TBD stdin rx flow control */
break;
case LWS_STDOUT:
/* quench POLLIN on STDOUT until MASTER got writeable */
lws_rx_flow_control(args->stdwsi[LWS_STDOUT], 0);
wsi->reason_bf |= LWS_CB_REASON_AUX_BF__CGI;
/* when writing to MASTER would not block */
lws_callback_on_writable(wsi);
break;
case LWS_STDERR:
n = lws_get_socket_fd(args->stdwsi[LWS_STDERR]);
if (n < 0)
break;
n = read(n, buf, sizeof(buf) - 2);
if (n > 0) {
if (buf[n - 1] != '\n')
buf[n++] = '\n';
buf[n] = '\0';
lwsl_notice("CGI-stderr: %s\n", buf);
}
break;
}
break;
case LWS_CALLBACK_CGI_TERMINATED:
lwsl_debug("LWS_CALLBACK_CGI_TERMINATED: %d %" PRIu64 "\n",
wsi->http.cgi->explicitly_chunked,
(uint64_t)wsi->http.cgi->content_length);
if (!wsi->http.cgi->explicitly_chunked &&
!wsi->http.cgi->content_length) {
/* send terminating chunk */
lwsl_debug("LWS_CALLBACK_CGI_TERMINATED: ending\n");
wsi->reason_bf |= LWS_CB_REASON_AUX_BF__CGI_CHUNK_END;
lws_callback_on_writable(wsi);
lws_set_timeout(wsi, PENDING_TIMEOUT_CGI, 3);
break;
}
return -1;
case LWS_CALLBACK_CGI_STDIN_DATA: /* POST body for stdin */
args = (struct lws_cgi_args *)in;
args->data[args->len] = '\0';
if (!args->stdwsi[LWS_STDIN])
return -1;
n = lws_get_socket_fd(args->stdwsi[LWS_STDIN]);
if (n < 0)
return -1;
#if defined(LWS_WITH_ZLIB)
if (wsi->http.cgi->gzip_inflate) {
/* gzip handling */
if (!wsi->http.cgi->gzip_init) {
lwsl_err("inflating gzip\n");
memset(&wsi->http.cgi->inflate, 0, sizeof(wsi->http.cgi->inflate));
if (inflateInit2(&wsi->http.cgi->inflate, 16 + 15) != Z_OK) {
lwsl_err("%s: iniflateInit failed\n", __func__);
return -1;
}
wsi->http.cgi->gzip_init = 1;
}
wsi->http.cgi->inflate.next_in = args->data;
wsi->http.cgi->inflate.avail_in = args->len;
do {
wsi->http.cgi->inflate.next_out =
wsi->http.cgi->inflate_buf;
wsi->http.cgi->inflate.avail_out =
sizeof(wsi->http.cgi->inflate_buf);
n = inflate(&wsi->http.cgi->inflate,
Z_SYNC_FLUSH);
switch (n) {
case Z_NEED_DICT:
case Z_STREAM_ERROR:
case Z_DATA_ERROR:
case Z_MEM_ERROR:
inflateEnd(&wsi->http.cgi->inflate);
wsi->http.cgi->gzip_init = 0;
lwsl_err("zlib error inflate %d\n", n);
return -1;
}
if (wsi->http.cgi->inflate.avail_out !=
sizeof(wsi->http.cgi->inflate_buf)) {
int written;
written = write(args->stdwsi[LWS_STDIN]->desc.filefd,
wsi->http.cgi->inflate_buf,
sizeof(wsi->http.cgi->inflate_buf) -
wsi->http.cgi->inflate.avail_out);
if (written != (int)(
sizeof(wsi->http.cgi->inflate_buf) -
wsi->http.cgi->inflate.avail_out)) {
lwsl_notice("LWS_CALLBACK_CGI_STDIN_DATA: "
"sent %d only %d went", n, args->len);
}
if (n == Z_STREAM_END) {
lwsl_err("gzip inflate end\n");
inflateEnd(&wsi->http.cgi->inflate);
wsi->http.cgi->gzip_init = 0;
break;
}
} else
break;
if (wsi->http.cgi->inflate.avail_out)
break;
} while (1);
return args->len;
}
#endif /* WITH_ZLIB */
n = write(n, args->data, args->len);
// lwsl_hexdump_notice(args->data, args->len);
if (n < args->len)
lwsl_notice("LWS_CALLBACK_CGI_STDIN_DATA: "
"sent %d only %d went", n, args->len);
if (wsi->http.cgi->post_in_expected && args->stdwsi[LWS_STDIN] &&
args->stdwsi[LWS_STDIN]->desc.filefd > 0) {
wsi->http.cgi->post_in_expected -= n;
if (!wsi->http.cgi->post_in_expected) {
struct lws *siwsi = args->stdwsi[LWS_STDIN];
lwsl_debug("%s: expected POST in end: "
"closing stdin wsi %p, fd %d\n",
__func__, siwsi, siwsi->desc.sockfd);
__remove_wsi_socket_from_fds(siwsi);
lwsi_set_state(siwsi, LRS_DEAD_SOCKET);
siwsi->socket_is_permanently_unusable = 1;
lws_remove_child_from_any_parent(siwsi);
if (wsi->context->event_loop_ops->
close_handle_manually) {
wsi->context->event_loop_ops->
close_handle_manually(siwsi);
siwsi->told_event_loop_closed = 1;
} else {
compatible_close(siwsi->desc.sockfd);
__lws_free_wsi(siwsi);
}
wsi->http.cgi->pipe_fds[LWS_STDIN][1] = -1;
args->stdwsi[LWS_STDIN] = NULL;
}
}
return n;
#endif /* WITH_CGI */
#endif /* ROLE_ H1 / H2 */
case LWS_CALLBACK_SSL_INFO:
si = in;
(void)si;
lwsl_notice("LWS_CALLBACK_SSL_INFO: where: 0x%x, ret: 0x%x\n",
si->where, si->ret);
break;
#if LWS_MAX_SMP > 1
case LWS_CALLBACK_GET_THREAD_ID:
return (int)(unsigned long long)pthread_self();
#endif
default:
break;
}
return 0;
}

View file

@ -53,6 +53,7 @@ static const char * const log_level_names[] = {
"CLIENT",
"LATENCY",
"USER",
"THREAD",
"?",
"?"
};
@ -374,6 +375,15 @@ __lws_set_timer_usecs(struct lws *wsi, lws_usec_t usecs)
// lws_dll_dump(&pt->dll_head_hrtimer, "after set_timer_usec");
}
LWS_VISIBLE lws_usec_t
lws_now_usecs(void)
{
struct timeval now;
gettimeofday(&now, NULL);
return (now.tv_sec * 1000000ll) + now.tv_usec;
}
LWS_VISIBLE void
lws_set_timer_usecs(struct lws *wsi, lws_usec_t usecs)
{
@ -867,12 +877,16 @@ just_kill_connection:
lwsl_debug("%s: real just_kill_connection: %p (sockfd %d)\n", __func__,
wsi, wsi->desc.sockfd);
#ifdef LWS_WITH_HTTP_PROXY
#ifdef LWS_WITH_HUBBUB
if (wsi->http.rw) {
lws_rewrite_destroy(wsi->http.rw);
wsi->http.rw = NULL;
}
#endif
if (wsi->http.pending_return_headers)
lws_free_set_NULL(wsi->http.pending_return_headers);
/*
* we won't be servicing or receiving anything further from this guy
* delete socket from the internal poll list if still present
@ -2029,6 +2043,7 @@ static const char * const colours[] = {
"[33m", /* LLL_CLIENT */
"[33;1m", /* LLL_LATENCY */
"[30;1m", /* LLL_USER */
"[31m", /* LLL_THREAD */
};
LWS_VISIBLE void lwsl_emit_stderr(int level, const char *line)
@ -2394,7 +2409,7 @@ lws_parse_uri(char *p, const char **prot, const char **ads, int *port,
const char **path)
{
const char *end;
static const char *slash = "/";
char unix_skt = 0;
/* cut up the location into address, port and path */
*prot = p;
@ -2408,32 +2423,32 @@ lws_parse_uri(char *p, const char **prot, const char **ads, int *port,
*p = '\0';
p += 3;
}
if (*p == '+') /* unix skt */
unix_skt = 1;
*ads = p;
if (!strcmp(*prot, "http") || !strcmp(*prot, "ws"))
*port = 80;
else if (!strcmp(*prot, "https") || !strcmp(*prot, "wss"))
*port = 443;
if (*p == '[')
{
++(*ads);
while (*p && *p != ']')
p++;
if (*p)
*p++ = '\0';
}
else
{
while (*p && *p != ':' && *p != '/')
p++;
}
if (*p == '[') {
++(*ads);
while (*p && *p != ']')
p++;
if (*p)
*p++ = '\0';
} else
while (*p && *p != ':' && (unix_skt || *p != '/'))
p++;
if (*p == ':') {
*p++ = '\0';
*port = atoi(p);
while (*p && *p != '/')
p++;
}
*path = slash;
*path = "/";
if (*p) {
*p++ = '\0';
if (*p)

View file

@ -251,6 +251,10 @@ lws_ssl_capable_read_no_ssl(struct lws *wsi, unsigned char *buf, int len)
n = recv(wsi->desc.sockfd, (char *)buf, len, 0);
if (n >= 0) {
if (!n && wsi->unix_skt)
return LWS_SSL_CAPABLE_ERROR;
if (wsi->vhost)
wsi->vhost->conn_stats.rx += n;
lws_stats_atomic_bump(context, pt, LWSSTATS_B_READ, n);

View file

@ -959,6 +959,7 @@ struct lws {
unsigned int on_same_vh_list:1;
unsigned int handling_404:1;
unsigned int protocol_bind_balance:1;
unsigned int unix_skt:1;
unsigned int could_have_pending:1; /* detect back-to-back writes */
unsigned int outer_will_close:1;
@ -1156,7 +1157,8 @@ user_callback_handle_rxflow(lws_callback_function, struct lws *wsi,
void *in, size_t len);
LWS_EXTERN int
lws_plat_set_socket_options(struct lws_vhost *vhost, lws_sockfd_type fd);
lws_plat_set_socket_options(struct lws_vhost *vhost, lws_sockfd_type fd,
int unix_skt);
LWS_EXTERN int
lws_plat_check_connection_error(struct lws *wsi);
@ -1493,7 +1495,7 @@ void
lws_peer_dump_from_wsi(struct lws *wsi);
#endif
#ifdef LWS_WITH_HTTP_PROXY
#ifdef LWS_WITH_HUBBUB
hubbub_error
html_parser_cb(const hubbub_token *token, void *pw);
#endif

View file

@ -974,6 +974,26 @@ cleanup:
}
#endif
static int
rops_close_kill_connection_h1(struct lws *wsi, enum lws_close_status reason)
{
#if defined(LWS_WITH_HTTP_PROXY)
struct lws *wsi_eff = lws_client_wsi_effective(wsi);
if (!wsi_eff->http.proxy_clientside)
return 0;
wsi_eff->http.proxy_clientside = 0;
if (user_callback_handle_rxflow(wsi_eff->protocol->callback, wsi_eff,
LWS_CALLBACK_COMPLETED_CLIENT_HTTP,
wsi_eff->user_space, NULL, 0))
return 0;
#endif
return 0;
}
struct lws_role_ops role_ops_h1 = {
/* role name */ "h1",
/* alpn id */ "http/1.1",
@ -993,7 +1013,7 @@ struct lws_role_ops role_ops_h1 = {
/* alpn_negotiated */ rops_alpn_negotiated_h1,
/* close_via_role_protocol */ NULL,
/* close_role */ NULL,
/* close_kill_connection */ NULL,
/* close_kill_connection */ rops_close_kill_connection_h1,
/* destroy_role */ rops_destroy_role_h1,
#if !defined(LWS_NO_SERVER)
/* adoption_bind */ rops_adoption_bind_h1,

View file

@ -219,6 +219,8 @@ bail1:
parent_wsi->h2.child_list = wsi->h2.sibling_list;
parent_wsi->h2.child_count--;
vh->context->count_wsi_allocated--;
if (wsi->user_space)
lws_free_set_NULL(wsi->user_space);
vh->protocols[0].callback(wsi, LWS_CALLBACK_WSI_DESTROY, NULL, NULL, 0);
@ -381,6 +383,9 @@ lws_h2_rst_stream(struct lws *wsi, uint32_t err, const char *reason)
struct lws_h2_netconn *h2n = nwsi->h2.h2n;
struct lws_h2_protocol_send *pps;
if (!h2n)
return 0;
if (h2n->type == LWS_H2_FRAME_TYPE_COUNT)
return 0;

View file

@ -578,6 +578,19 @@ rops_close_kill_connection_h2(struct lws *wsi, enum lws_close_status reason)
{
struct lws *wsi2;
#if defined(LWS_WITH_HTTP_PROXY)
if (wsi->http.proxy_clientside) {
struct lws *wsi_eff = lws_client_wsi_effective(wsi);
wsi->http.proxy_clientside = 0;
if (user_callback_handle_rxflow(wsi_eff->protocol->callback, wsi_eff,
LWS_CALLBACK_COMPLETED_CLIENT_HTTP,
wsi_eff->user_space, NULL, 0))
wsi->http.proxy_clientside = 0;
}
#endif
if (wsi->http2_substream && wsi->h2_stream_carries_ws)
lws_h2_rst_stream(wsi, 0, "none");
@ -960,7 +973,8 @@ rops_perform_user_POLLOUT_h2(struct lws *wsi)
* states. In those cases we will hear about
* END_STREAM going out in the POLLOUT handler.
*/
if (n || w->h2.send_END_STREAM) {
if (!w->h2.pending_status_body &&
(n || w->h2.send_END_STREAM)) {
lwsl_info("closing stream after h2 action\n");
lws_close_free_wsi(w, LWS_CLOSE_STATUS_NOSTATUS,
"h2 end stream");

View file

@ -37,8 +37,13 @@ lws_client_connect_2(struct lws *wsi)
ssize_t plen = 0;
#endif
struct addrinfo *result;
#if defined(LWS_WITH_UNIX_SOCK)
struct sockaddr_un sau;
char unix_skt = 0;
#endif
const char *ads;
sockaddr46 sa46;
const struct sockaddr *psa;
int n, port;
const char *cce = "", *iface;
const char *meth = NULL;
@ -183,6 +188,29 @@ create_new_conn:
lws_vhost_unlock(wsi->vhost);
}
/*
* unix socket destination?
*/
ads = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS);
#if defined(LWS_WITH_UNIX_SOCK)
if (*ads == '+') {
ads++;
memset(&sau, 0, sizeof(sau));
sau.sun_family = AF_UNIX;
strncpy(sau.sun_path, ads, sizeof(sau.sun_path));
sau.sun_path[sizeof(sau.sun_path) - 1] = '\0';
lwsl_info("%s: Unix skt: %s\n", __func__, ads);
if (sau.sun_path[0] == '@')
sau.sun_path[0] = '\0';
unix_skt = 1;
goto ads_known;
}
#endif
/*
* start off allowing ipv6 on connection if vhost allows it
*/
@ -341,6 +369,10 @@ create_new_conn:
if (result)
freeaddrinfo(result);
#if defined(LWS_WITH_UNIX_SOCK)
ads_known:
#endif
/* now we decided on ipv4 or ipv6, set the port */
if (!lws_socket_is_valid(wsi->desc.sockfd)) {
@ -351,12 +383,21 @@ create_new_conn:
goto oom4;
}
#if defined(LWS_WITH_UNIX_SOCK)
if (unix_skt) {
wsi->unix_skt = 1;
wsi->desc.sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
} else
#endif
{
#ifdef LWS_WITH_IPV6
if (wsi->ipv6)
wsi->desc.sockfd = socket(AF_INET6, SOCK_STREAM, 0);
else
#endif
wsi->desc.sockfd = socket(AF_INET, SOCK_STREAM, 0);
}
if (!lws_socket_is_valid(wsi->desc.sockfd)) {
lwsl_warn("Unable to open socket\n");
@ -364,7 +405,12 @@ create_new_conn:
goto oom4;
}
if (lws_plat_set_socket_options(wsi->vhost, wsi->desc.sockfd)) {
if (lws_plat_set_socket_options(wsi->vhost, wsi->desc.sockfd,
#if defined(LWS_WITH_UNIX_SOCK)
unix_skt)) {
#else
0)) {
#endif
lwsl_err("Failed to set wsi socket options\n");
compatible_close(wsi->desc.sockfd);
cce = "set socket opts failed";
@ -409,18 +455,29 @@ create_new_conn:
}
}
#ifdef LWS_WITH_IPV6
if (wsi->ipv6) {
sa46.sa6.sin6_port = htons(port);
n = sizeof(struct sockaddr_in6);
#if defined(LWS_WITH_UNIX_SOCK)
if (unix_skt) {
psa = (const struct sockaddr *)&sau;
n = sizeof(sau);
} else
#endif
{
sa46.sa4.sin_port = htons(port);
n = sizeof(struct sockaddr);
#ifdef LWS_WITH_IPV6
if (wsi->ipv6) {
sa46.sa6.sin6_port = htons(port);
n = sizeof(struct sockaddr_in6);
psa = (const struct sockaddr *)&sa46;
} else
#endif
{
sa46.sa4.sin_port = htons(port);
n = sizeof(struct sockaddr);
psa = (const struct sockaddr *)&sa46;
}
}
if (connect(wsi->desc.sockfd, (const struct sockaddr *)&sa46, n) == -1 ||
if (connect(wsi->desc.sockfd, (const struct sockaddr *)psa, n) == -1 ||
LWS_ERRNO == LWS_EISCONN) {
if (LWS_ERRNO == LWS_EALREADY ||
LWS_ERRNO == LWS_EINPROGRESS ||
@ -729,7 +786,7 @@ lws_client_reset(struct lws **pwsi, int ssl, const char *address, int port,
return *pwsi;
}
#ifdef LWS_WITH_HTTP_PROXY
#if defined(LWS_WITH_HTTP_PROXY) && defined(LWS_WITH_HUBBUB)
hubbub_error
html_parser_cb(const hubbub_token *token, void *pw)
{

View file

@ -882,7 +882,7 @@ lws_client_interpret_server_handshake(struct lws *wsi)
if (!strncmp(lws_hdr_simple_ptr(wsi,
WSI_TOKEN_HTTP_CONTENT_TYPE),
"text/html", 9))
wsi->http.perform_rewrite = 1;
wsi->http.perform_rewrite = 0;
}
#endif
@ -993,7 +993,9 @@ bail2:
}
wsi->already_did_cce = 1;
lwsl_info("closing connection due to bail2 connection error\n");
lwsl_info("closing connection (prot %s) "
"due to bail2 connection error: %s\n", wsi->protocol ?
wsi->protocol->name : "unknown", cce);
/* closing will free up his parsing allocations */
lws_close_free_wsi(wsi, close_reason, "c hs interp");
@ -1065,6 +1067,9 @@ lws_generate_client_handshake(struct lws *wsi, char *pkt)
p += sprintf(p, "Pragma: no-cache\x0d\x0a"
"Cache-Control: no-cache\x0d\x0a");
if (!wsi->client_pipeline)
p += sprintf(p, "connection: close\x0d\x0a");
p += sprintf(p, "Host: %s\x0d\x0a",
lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_HOST));
@ -1196,7 +1201,7 @@ spin_chunks:
wsi->chunk_remaining < n)
n = wsi->chunk_remaining;
#ifdef LWS_WITH_HTTP_PROXY
#if defined(LWS_WITH_HTTP_PROXY) && defined(LWS_WITH_HUBBUB)
/* hubbub */
if (wsi->http.perform_rewrite)
lws_rewrite_parse(wsi->http.rw, (unsigned char *)*buf, n);

View file

@ -384,7 +384,7 @@ lws_return_http_status(struct lws *wsi, unsigned int code,
#if defined(LWS_WITH_HTTP2)
if (wsi->http2_substream) {
unsigned char *body = p + 512;
char *body = (char *)start + context->pt_serv_buf_size - 512;
/*
* for HTTP/2, the headers must be sent separately, since they
@ -398,7 +398,8 @@ lws_return_http_status(struct lws *wsi, unsigned int code,
*
* Solve it by writing the headers now...
*/
m = lws_write(wsi, start, p - start, LWS_WRITE_HTTP_HEADERS);
m = lws_write(wsi, start, lws_ptr_diff(p, start),
LWS_WRITE_HTTP_HEADERS);
if (m != lws_ptr_diff(p, start))
return 1;
@ -407,8 +408,7 @@ lws_return_http_status(struct lws *wsi, unsigned int code,
* handle_POLLOUT
*/
len = sprintf((char *)body,
"<html><body><h1>%u</h1>%s</body></html>",
len = sprintf(body, "<html><body><h1>%u</h1>%s</body></html>",
code, html_body);
wsi->http.tx_content_length = len;
wsi->http.tx_content_remain = len;
@ -418,8 +418,7 @@ lws_return_http_status(struct lws *wsi, unsigned int code,
if (!wsi->h2.pending_status_body)
return -1;
strcpy(wsi->h2.pending_status_body + LWS_PRE,
(const char *)body);
strcpy(wsi->h2.pending_status_body + LWS_PRE, body);
lws_callback_on_writable(wsi);
return 0;

View file

@ -22,7 +22,7 @@
* enabled
*/
#if defined(LWS_WITH_HTTP_PROXY)
#if defined(LWS_WITH_HUBBUB)
#include <hubbub/hubbub.h>
#include <hubbub/parser.h>
#endif
@ -137,7 +137,7 @@ struct allocated_headers {
#if defined(LWS_WITH_HTTP_PROXY)
#if defined(LWS_WITH_HUBBUB)
struct lws_rewrite {
hubbub_parser *parser;
hubbub_parser_optparams params;
@ -202,6 +202,9 @@ struct lws_access_log {
struct _lws_http_mode_related {
struct lws *new_wsi_list;
unsigned char *pending_return_headers;
size_t pending_return_headers_len;
#if defined(LWS_WITH_HTTP_PROXY)
struct lws_rewrite *rw;
#endif
@ -238,9 +241,12 @@ struct _lws_http_mode_related {
#if defined(LWS_WITH_HTTP_PROXY)
unsigned int perform_rewrite:1;
unsigned int proxy_clientside:1;
unsigned int proxy_parent_chunked:1;
#endif
unsigned int deferred_transaction_completed:1;
unsigned int content_length_explicitly_zero:1;
unsigned int did_stream_close:1;
};

View file

@ -93,7 +93,7 @@ lws_prepare_access_log_info(struct lws *wsi, char *uri_ptr, int uri_len, int met
pa, da, me, uri,
hver[wsi->http.request_version]);
lwsl_notice("%s\n", wsi->http.access_log.header_log);
//lwsl_notice("%s\n", wsi->http.access_log.header_log);
l = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_USER_AGENT);
if (l) {

View file

@ -1,5 +1,6 @@
#include "core/private.h"
#if defined(LWS_WITH_HUBBUB)
LWS_EXTERN struct lws_rewrite *
lws_rewrite_create(struct lws *wsi, hubbub_callback_t cb, const char *from, const char *to)
@ -50,3 +51,4 @@ lws_rewrite_destroy(struct lws_rewrite *r)
lws_free(r);
}
#endif

View file

@ -223,7 +223,7 @@ done_list:
}
#endif
#endif
lws_plat_set_socket_options(vhost, sockfd);
lws_plat_set_socket_options(vhost, sockfd, 0);
is = lws_socket_bind(vhost, sockfd, vhost->listen_port, vhost->iface);
/*
@ -236,15 +236,24 @@ done_list:
compatible_close(sockfd);
goto deal;
}
vhost->listen_port = is;
lwsl_debug("%s: lws_socket_bind says %d\n", __func__, is);
wsi = lws_zalloc(sizeof(struct lws), "listen wsi");
if (wsi == NULL) {
lwsl_err("Out of mem\n");
goto bail;
}
#ifdef LWS_WITH_UNIX_SOCK
if (!LWS_UNIX_SOCK_ENABLED(vhost))
#endif
{
wsi->unix_skt = 1;
vhost->listen_port = is;
lwsl_debug("%s: lws_socket_bind says %d\n", __func__,
is);
}
wsi->context = vhost->context;
wsi->desc.sockfd = sockfd;
lws_role_transition(wsi, 0, LRS_UNCONNECTED, &role_ops_listen);
@ -327,7 +336,7 @@ lws_select_vhost(struct lws_context *context, int port, const char *servername)
vhost = context->vhost_list;
while (vhost) {
m = (int)strlen(vhost->name);
if (port == vhost->listen_port &&
if (port && port == vhost->listen_port &&
m <= (colon - 2) &&
servername[colon - m - 1] == '.' &&
!strncmp(vhost->name, servername + colon - m, m)) {
@ -342,7 +351,7 @@ lws_select_vhost(struct lws_context *context, int port, const char *servername)
vhost = context->vhost_list;
while (vhost) {
if (port == vhost->listen_port) {
if (port && port == vhost->listen_port) {
lwsl_info("%s: vhost match to %s based on port %d\n",
__func__, vhost->name, port);
return vhost;
@ -784,7 +793,7 @@ lws_unauthorised_basic_auth(struct lws *wsi)
{
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
unsigned char *start = pt->serv_buf + LWS_PRE,
*p = start, *end = p + 512;
*p = start, *end = p + 2048;
char buf[64];
int n;
@ -1011,7 +1020,7 @@ lws_http_action(struct lws *wsi)
* URI from the host: header and ignore the path part
*/
unsigned char *start = pt->serv_buf + LWS_PRE, *p = start,
*end = p + 512;
*end = p + wsi->context->pt_serv_buf_size - LWS_PRE;
if (!lws_hdr_total_length(wsi, WSI_TOKEN_HOST))
goto bail_nuke_ah;
@ -1077,9 +1086,10 @@ lws_http_action(struct lws *wsi)
(hit->origin_protocol != LWSMPRO_CGI &&
hit->origin_protocol != LWSMPRO_CALLBACK)) {
unsigned char *start = pt->serv_buf + LWS_PRE,
*p = start, *end = p + 512;
*p = start, *end = p + wsi->context->pt_serv_buf_size -
LWS_PRE - 512;
lwsl_debug("Doing 301 '%s' org %s\n", s, hit->origin);
lwsl_info("Doing 301 '%s' org %s\n", s, hit->origin);
/* > at start indicates deal with by redirect */
if (hit->origin_protocol == LWSMPRO_REDIR_HTTP ||
@ -1162,15 +1172,21 @@ lws_http_action(struct lws *wsi)
* The mount is a reverse proxy?
*/
// lwsl_notice("%s: origin_protocol: %d\n", __func__, hit->origin_protocol);
if (hit->origin_protocol == LWSMPRO_HTTPS ||
hit->origin_protocol == LWSMPRO_HTTP) {
struct lws_client_connect_info i;
char ads[96], rpath[256], *pcolon, *pslash, *p;
struct lws *cwsi;
char ads[96], rpath[256], *pcolon, *pslash, *p, unix_skt = 0;
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) {
@ -1178,16 +1194,28 @@ lws_http_action(struct lws *wsi)
hit->origin);
return -1;
}
if (pcolon > pslash)
pcolon = NULL;
if (pcolon)
n = pcolon - hit->origin;
else
n = pslash - hit->origin;
if (unix_skt) {
if (!pcolon) {
lwsl_err("Proxy mount origin for unix skt must "
"have address delimited by :\n");
if (n >= (int)sizeof(ads) - 2)
n = sizeof(ads) - 2;
return -1;
}
n = lws_ptr_diff(pcolon, hit->origin);
pslash = pcolon;
} else {
if (pcolon > pslash)
pcolon = NULL;
if (pcolon)
n = pcolon - hit->origin;
else
n = pslash - hit->origin;
if (n >= (int)sizeof(ads) - 2)
n = sizeof(ads) - 2;
}
memcpy(ads, hit->origin, n);
ads[n] = '\0';
@ -1217,25 +1245,48 @@ lws_http_action(struct lws *wsi)
}
}
i.path = rpath;
i.host = i.address;
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.uri_replace_from = hit->origin;
i.uri_replace_to = hit->mountpoint;
i.pwsi = &cwsi;
lwsl_notice("proxying to %s port %d url %s, ssl %d, "
// 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;
}
#endif
@ -1531,7 +1582,8 @@ raw_transition:
/* select vhost */
if (lws_hdr_total_length(wsi, WSI_TOKEN_HOST)) {
if (wsi->vhost->listen_port &&
lws_hdr_total_length(wsi, WSI_TOKEN_HOST)) {
struct lws_vhost *vhost = lws_select_vhost(
context, wsi->vhost->listen_port,
lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST));
@ -1744,7 +1796,7 @@ lws_http_transaction_completed(struct lws *wsi)
return 0;
}
lwsl_debug("%s: wsi %p\n", __func__, wsi);
lwsl_info("%s: wsi %p\n", __func__, wsi);
#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)
lws_http_compression_destroy(wsi);
@ -2056,20 +2108,24 @@ lws_serve_http_file(struct lws *wsi, const char *file, const char *content_type,
total_content_length, &p, end))
return -1;
} else {
/* ...otherwise, for http 1 it must go chunked. For
* the compression case, the reason is we compress on
* the fly and do not know the compressed content-length
* until it has all been sent. Http/1.1 pipelining must
* be able to know where the transaction boundaries are
* ... so chunking...
*/
if (lws_add_http_header_by_token(wsi,
WSI_TOKEN_HTTP_TRANSFER_ENCODING,
(unsigned char *)"chunked", 7, &p, end))
return -1;
#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)
if (wsi->http.lcs) {
/* ...otherwise, for http 1 it must go chunked.
* For the compression case, the reason is we
* compress on the fly and do not know the
* compressed content-length until it has all
* been sent. Http/1.1 pipelining must be able
* to know where the transaction boundaries are
* ... so chunking...
*/
if (lws_add_http_header_by_token(wsi,
WSI_TOKEN_HTTP_TRANSFER_ENCODING,
(unsigned char *)"chunked", 7,
&p, end))
return -1;
/*
* ...this is fun, isn't it :-) For h1 that is
* using an http compression translation, the
@ -2093,7 +2149,8 @@ lws_serve_http_file(struct lws *wsi, const char *file, const char *content_type,
wsi->cache_secs);
} else {
cc = cache_control;
cclen = sprintf(cache_control, "must-revalidate, %s, max-age=%u",
cclen = sprintf(cache_control,
"must-revalidate, %s, max-age=%u",
intermediates[wsi->cache_intermediaries],
wsi->cache_secs);

View file

@ -93,7 +93,7 @@ rops_handle_POLLIN_listen(struct lws_context_per_thread *pt, struct lws *wsi,
break;
}
lws_plat_set_socket_options(wsi->vhost, accept_fd);
lws_plat_set_socket_options(wsi->vhost, accept_fd, 0);
#if defined(LWS_WITH_IPV6)
lwsl_debug("accepted new conn port %u on fd=%d\n",

View file

@ -1495,7 +1495,11 @@ rops_close_via_role_protocol_ws(struct lws *wsi, enum lws_close_status reason)
static int
rops_close_role_ws(struct lws_context_per_thread *pt, struct lws *wsi)
{
if (!wsi->ws)
return 0;
#if !defined(LWS_WITHOUT_EXTENSIONS)
if (wsi->ws->rx_draining_ext) {
struct lws **w = &pt->ws.rx_draining_ext_list;

View file

@ -672,9 +672,12 @@ int main(int argc, char **argv)
goto usage;
/* add back the leading / on path */
path[0] = '/';
lws_strncpy(path + 1, p, sizeof(path) - 1);
i.path = path;
if (p[0] != '/') {
path[0] = '/';
lws_strncpy(path + 1, p, sizeof(path) - 1);
i.path = path;
} else
i.path = p;
if (!strcmp(prot, "http") || !strcmp(prot, "ws"))
use_ssl = 0;
@ -682,6 +685,8 @@ int main(int argc, char **argv)
if (!use_ssl)
use_ssl = LCCSCF_USE_SSL;
lwsl_debug("'%s' %p '%s' %p\n", i.address, i.address, i.path, i.path);
/*
* create the websockets context. This tracks open connections and
* knows how to route any traffic and which protocol version to use,