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:
Andy Green 2016-03-20 11:59:53 +08:00
parent 5c8906e931
commit 1e5a9ad2dc
13 changed files with 515 additions and 172 deletions

View file

@ -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()

View file

@ -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

View file

@ -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:

View file

@ -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");

View file

@ -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);

View file

@ -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
*

View file

@ -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;

View 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
View 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);
}

View file

@ -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;

View file

@ -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;

View file

@ -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}

View file

@ -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;