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)
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")
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")
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)
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.
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()
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
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
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:
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");
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
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,
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);
}
#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
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);
/* 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
*/
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);
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 ---^ ******/
* @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 {
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
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
*
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;
#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
#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
};
#endif
+struct lws_rewrite;
+
struct lws {
/* structs */
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;
#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;
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,
--- /dev/null
+#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);
+}
+
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;
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
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 {
-
- 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;
- }
-
- /*
- * give any active extensions a chance to munge the buffer
- * before parse. We pass in a pointer to an lws_tokens struct
- * prepared with the default buffer and content length that's in
- * there. Rather than rewrite the default buffer, extensions
- * that expect to grow the buffer can adapt .token to
- * point to their own per-connection buffer in the extension
- * user allocation. By default with no extensions or no
- * 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;
+ 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 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;
- }
+ case LWS_SSL_CAPABLE_ERROR:
+ lwsl_info("Closing when error\n");
goto close_and_handled;
}
- eff_buf.token++;
- eff_buf.token_len--;
- }
- if (wsi->chunked && !wsi->chunk_remaining) {
- n = 0;
- goto handled;
+ eff_buf.token = (char *)pt->serv_buf;
}
+ }
- 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;
+drain:
+ if (wsi->mode == LWSCM_HTTP_CLIENT_ACCEPTED) {
- if (wsi->chunked && wsi->chunk_remaining &&
- wsi->chunk_remaining < n)
- n = wsi->chunk_remaining;
+ /*
+ * 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, (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;
+
}
+ /*
+ * give any active extensions a chance to munge the buffer
+ * before parse. We pass in a pointer to an lws_tokens struct
+ * prepared with the default buffer and content length that's in
+ * there. Rather than rewrite the default buffer, extensions
+ * that expect to grow the buffer can adapt .token to
+ * point to their own per-connection buffer in the extension
+ * user allocation. By default with no extensions or no
+ * extension callback handling, just the normal input buffer is
+ * used then so it is efficient.
+ */
do {
more = 0;
/* 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}
unsigned char *end;
struct timeval tv;
unsigned char *p;
+ struct lws *wsi1;
char buf[256];
char b64[64];
int n, m;
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;
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[] = {
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
* 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:
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))
(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))
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
/* 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;