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

HTTP_PROXY: make usable

This commit is contained in:
Andy Green 2017-06-12 13:36:24 +08:00
parent 449eec9b54
commit 581b86efd0
11 changed files with 290 additions and 49 deletions

View file

@ -98,7 +98,7 @@ option(LWS_UNIX_SOCK "Compile with support for UNIX domain socket" OFF)
option(LWS_WITH_HTTP2 "Compile with support for http2" 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)
option(LWS_WITH_HTTP_PROXY "Support for rewriting HTTP proxying (requires libhubbub)" OFF)
option(LWS_WITH_LWSWS "Libwebsockets Webserver" OFF)
option(LWS_WITH_PLUGINS "Support plugins for protocols and extensions" OFF)
option(LWS_WITH_ACCESS_LOG "Support generating Apache-compatible access logs" OFF)
@ -392,7 +392,7 @@ if (LWS_WITH_HTTP2)
endif()
if ("${LWS_MAX_SMP}" STREQUAL "")
set(LWS_MAX_SMP 32)
set(LWS_MAX_SMP 1)
endif()
@ -735,7 +735,7 @@ if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX OR (CMAKE_C_COMPILER_ID
endif ()
if ((CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) AND NOT LWS_WITHOUT_TESTAPPS)
if (UNIX)
if (UNIX AND LWS_MAX_SMP GREATER 1)
# jeez clang understands -pthread but dies if he sees it at link time!
# http://stackoverflow.com/questions/2391194/what-is-gs-pthread-equiv-in-clang
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pthread" )
@ -1251,7 +1251,7 @@ if (NOT LWS_WITHOUT_TESTAPPS)
""
"")
endif()
if (UNIX AND NOT ((CMAKE_C_COMPILER_ID MATCHES "Clang") OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang")))
if (UNIX AND NOT ((CMAKE_C_COMPILER_ID MATCHES "Clang") OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang")) AND LWS_MAX_SMP GREATER 1)
create_test_app(test-server-pthreads
"test-server/test-server-pthreads.c"
"test-server/test-server-http.c"

View file

@ -292,6 +292,20 @@ Mount protocols are used to control what kind of translation happens
```
would cause the url /git/myrepo to pass "myrepo" to the cgi /var/www/cgi-bin/cgit and send the results to the client.
- http:// or https:// these perform reverse proxying, serving the remote origin content from the mountpoint. Eg
```
{
"mountpoint": "/proxytest",
"origin": "https://libwebsockets.org"
}
```
This will cause your local url `/proxytest` to serve content fetched from libwebsockets.org over ssl; whether it's served from your server using ssl is unrelated and depends how you configured your local server. Notice if you will use the proxying feature, `LWS_WITH_HTTP_PROXY` is required to be enabled at cmake, and for `https` proxy origins, your lwsws configuration must include `"init-ssl": "1"` and the vhost with the proxy mount must have `"enable-client-ssl": "1"`, even if you are not using ssl to serve.
`/proxytest/abc`, or `/proxytest/abc?def=ghi` etc map to the origin + the part past `/proxytest`, so links and img src urls etc work as do all urls under the origin path.
In addition link and src urls in the document are rewritten so / or the origin url part are rewritten to the mountpoint part.
@section lwswsomo Lwsws Other mount options

View file

@ -262,7 +262,8 @@ lws_client_connect_2(struct lws *wsi)
n = sizeof(struct sockaddr);
}
if (connect(wsi->desc.sockfd, v, n) == -1 || LWS_ERRNO == LWS_EISCONN) {
if (connect(wsi->desc.sockfd, v, n) == -1 ||
LWS_ERRNO == LWS_EISCONN) {
if (LWS_ERRNO == LWS_EALREADY ||
LWS_ERRNO == LWS_EINPROGRESS ||
LWS_ERRNO == LWS_EWOULDBLOCK
@ -527,14 +528,14 @@ html_parser_cb(const hubbub_token *token, void *pw)
"(force-quirks) " : "");
if (token->data.doctype.public_missing)
printf("\tpublic: missing\n");
lwsl_debug("\tpublic: missing\n");
else
p += lws_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");
lwsl_debug("\tsystem: missing\n");
else
p += lws_snprintf(p, end - p, " \"%.*s\">\n",
(int) token->data.doctype.system_id.len,
@ -557,25 +558,28 @@ html_parser_cb(const hubbub_token *token, void *pw)
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;
if (strncmp(pp, "http:", 5) && strncmp(pp, "https:", 6)) {
if (!hstrcmp(&token->data.tag.attributes[i].value,
r->from, r->from_len)) {
pp += r->from_len;
plen -= r->from_len;
}
p += lws_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);
continue;
}
p += lws_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 += lws_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 += lws_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 += lws_snprintf(p, end - p, ">\n");
p += lws_snprintf(p, end - p, ">");
break;
case HUBBUB_TOKEN_END_TAG:
p += lws_snprintf(p, end - p, "</%.*s", (int) token->data.tag.name.len,
@ -593,7 +597,7 @@ html_parser_cb(const hubbub_token *token, void *pw)
(int) token->data.tag.attributes[i].value.len,
token->data.tag.attributes[i].value.ptr);
}
p += lws_snprintf(p, end - p, ">\n");
p += lws_snprintf(p, end - p, ">");
break;
case HUBBUB_TOKEN_COMMENT:
p += lws_snprintf(p, end - p, "<!-- %.*s -->\n",
@ -601,6 +605,21 @@ html_parser_cb(const hubbub_token *token, void *pw)
token->data.comment.ptr);
break;
case HUBBUB_TOKEN_CHARACTER:
if (token->data.character.len == 1) {
if (*token->data.character.ptr == '<') {
p += lws_snprintf(p, end - p, "&lt;");
break;
}
if (*token->data.character.ptr == '>') {
p += lws_snprintf(p, end - p, "&gt;");
break;
}
if (*token->data.character.ptr == '&') {
p += lws_snprintf(p, end - p, "&amp;");
break;
}
}
p += lws_snprintf(p, end - p, "%.*s", (int) token->data.character.len,
token->data.character.ptr);
break;

View file

@ -222,10 +222,13 @@ lws_callback_http_dummy(struct lws *wsi, enum lws_callback_reasons reason,
{
#ifdef LWS_WITH_CGI
struct lws_cgi_args *args;
char buf[128];
#endif
#if defined(LWS_WITH_CGI) || defined(LWS_WITH_HTTP_PROXY)
char buf[512];
int n;
#endif
switch (reason) {
case LWS_CALLBACK_HTTP:
#ifndef LWS_NO_SERVER
@ -250,9 +253,92 @@ lws_callback_http_dummy(struct lws *wsi, enum lws_callback_reasons reason,
break;
}
#endif
#if defined(LWS_WITH_HTTP_PROXY)
if (wsi->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.
*/
wsi->reason_bf &= ~2;
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:
//lwsl_err("LWS_CALLBACK_RECEIVE_CLIENT_HTTP: wsi %p\n", wsi);
assert(lws_get_parent(wsi));
if (!lws_get_parent(wsi))
break;
lws_get_parent(wsi)->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", (int)len);
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;
//lwsl_err("LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP\n");
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 0
if (lws_add_http_header_content_length(lws_get_parent(wsi),
file_len, &p, end))
return 1;
#endif
if (lws_finalize_http_header(lws_get_parent(wsi), &p, end))
return 1;
*p = '\0';
// lwsl_info("%s\n", buf + LWS_PRE);
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:
*

View file

@ -2289,8 +2289,8 @@ struct lws_protocol_vhost_options {
* served from a filesystem, or it is a cgi etc.
*/
enum lws_mount_protocols {
LWSMPRO_HTTP = 0, /**< not supported yet */
LWSMPRO_HTTPS = 1, /**< not supported yet */
LWSMPRO_HTTP = 0, /**< http reverse proxy */
LWSMPRO_HTTPS = 1, /**< https reverse proxy */
LWSMPRO_FILE = 2, /**< serve from filesystem directory */
LWSMPRO_CGI = 3, /**< pass to CGI to handle */
LWSMPRO_REDIR_HTTP = 4, /**< redirect to http:// url */

View file

@ -321,15 +321,18 @@ int lws_header_table_detach(struct lws *wsi, int autoservice)
pt->ah_wait_list_length--;
#ifndef LWS_NO_CLIENT
if (wsi->state == LWSS_CLIENT_UNCONNECTED)
if (wsi->state == LWSS_CLIENT_UNCONNECTED) {
lws_pt_unlock(pt);
if (!lws_client_connect_via_info2(wsi)) {
/* our client connect has failed, the wsi
* has been closed
*/
lws_pt_unlock(pt);
return -1;
}
return 0;
}
#endif
assert(!!pt->ah_wait_list_length == !!(int)(long)pt->ah_wait_list);

View file

@ -662,6 +662,23 @@ lws_unauthorised_basic_auth(struct lws *wsi)
#endif
int lws_clean_url(char *p)
{
while (*p) {
if (p[0] == '/' && p[1] == '/') {
char *p1 = p;
while (*p1) {
*p1 = p1[1];
p1++;
}
continue;
}
p++;
}
return 0;
}
int
lws_http_action(struct lws *wsi)
{
@ -958,6 +975,7 @@ lws_http_action(struct lws *wsi)
"%s%s%s/", oprot[lws_is_ssl(wsi)],
lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST),
uri_ptr);
lws_clean_url((char *)end);
n = lws_http_redirect(wsi, HTTP_STATUS_MOVED_PERMANENTLY,
end, n, &p, end);
@ -1013,6 +1031,84 @@ lws_http_action(struct lws *wsi)
}
#endif
#if defined(LWS_WITH_HTTP_PROXY)
/*
* The mount is a reverse proxy?
*/
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;
int n, na;
memset(&i, 0, sizeof(i));
i.context = lws_get_context(wsi);
pcolon = strchr(hit->origin, ':');
pslash = strchr(hit->origin, '/');
if (!pslash) {
lwsl_err("Proxy mount origin '%s' must have /\n", hit->origin);
return -1;
}
if (pcolon > pslash)
pcolon = NULL;
if (pcolon)
n = pcolon - hit->origin;
else
n = pslash - hit->origin;
if (n >= sizeof(ads) - 2)
n = sizeof(ads) - 2;
memcpy(ads, hit->origin, n);
ads[n] = '\0';
i.address = ads;
i.port = 80;
if (hit->origin_protocol == LWSMPRO_HTTPS) {
i.port = 443;
i.ssl_connection = 1;
}
if (pcolon)
i.port = atoi(pcolon + 1);
lws_snprintf(rpath, sizeof(rpath) - 1, "/%s/%s", pslash + 1, uri_ptr + hit->mountpoint_len);
lws_clean_url(rpath);
na = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_URI_ARGS);
if (na) {
p = rpath + strlen(rpath);
*p++ = '?';
lws_hdr_copy(wsi, p, &rpath[sizeof(rpath) - 1] - p, WSI_TOKEN_HTTP_URI_ARGS);
while (--na) {
if (*p == '\0')
*p = '&';
p++;
}
}
i.path = rpath;
i.host = i.address;
i.origin = NULL;
i.method = "GET";
i.parent_wsi = wsi;
i.uri_replace_from = hit->origin;
i.uri_replace_to = hit->mountpoint;
lwsl_notice("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");
return 1;
}
return 0;
}
#endif
/*
* A particular protocol callback is mounted here?
*

View file

@ -1203,6 +1203,9 @@ drain:
lwsl_notice("LWS_CALLBACK_RECEIVE_CLIENT_HTTP closed it\n");
goto close_and_handled;
}
n = 0;
goto handled;
}
#endif
/*

View file

@ -318,7 +318,7 @@ lws_ssl_client_connect2(struct lws *wsi)
if (wsi->mode == LWSCM_WSCL_WAITING_SSL) {
lws_latency_pre(context, wsi);
n = SSL_connect(wsi->ssl);
lwsl_notice("%s: SSL_connect says %d\n", __func__, n);
lwsl_debug("%s: SSL_connect says %d\n", __func__, n);
lws_latency(context, wsi,
"SSL_connect LWSCM_WSCL_WAITING_SSL", n, n > 0);
@ -395,7 +395,7 @@ lws_ssl_client_connect2(struct lws *wsi)
lws_latency(context, wsi,
"SSL_get_verify_result LWS_CONNMODE..HANDSHAKE", n, n > 0);
lwsl_notice("get_verify says %d\n", n);
lwsl_debug("get_verify says %d\n", n);
if (n != X509_V_OK) {
if ((n == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT ||

View file

@ -230,38 +230,37 @@ int callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user,
goto try_to_reuse;
}
#ifndef LWS_NO_CLIENT
#if !defined(LWS_NO_CLIENT) && defined(LWS_OPENSSL_SUPPORT)
if (!strncmp(in, "/proxytest", 10)) {
struct lws_client_connect_info i;
char *rootpath = "/";
char *rootpath = "/git/";
const char *p = (const char *)in;
if (lws_get_child(wsi))
break;
pss->client_finished = 0;
memset(&i,0, sizeof(i));
memset(&i, 0, sizeof(i));
i.context = lws_get_context(wsi);
i.address = "git.libwebsockets.org";
i.port = 80;
i.ssl_connection = 0;
i.address = "libwebsockets.org";
i.port = 443;
i.ssl_connection = 1;
if (p[10])
i.path = (char *)in + 10;
else
i.path = rootpath;
i.host = "git.libwebsockets.org";
i.host = i.address;
i.origin = NULL;
i.method = "GET";
i.parent_wsi = wsi;
i.uri_replace_from = "git.libwebsockets.org/";
i.uri_replace_from = "libwebsockets.org/git/";
i.uri_replace_to = "/proxytest/";
if (!lws_client_connect_via_info(&i)) {
lwsl_err("proxy connect fail\n");
break;
}
break;
}
#endif
@ -421,6 +420,11 @@ int callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user,
*/
break;
case LWS_CALLBACK_CLIENT_RECEIVE:
((char *)in)[len] = '\0';
lwsl_info("rx %d '%s'\n", (int)len, (char *)in);
break;
case LWS_CALLBACK_HTTP_BODY:
/* create the POST argument parser if not already existing */
if (!pss->spa) {
@ -511,8 +515,10 @@ int callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user,
if (pss->client_finished)
return -1;
if (!pss->fop_fd)
if (!lws_get_child(wsi) && !pss->fop_fd) {
lwsl_notice("fop_fd NULL\n");
goto try_to_reuse;
}
#ifndef LWS_NO_CLIENT
if (pss->reason_bf & 2) {
@ -524,17 +530,24 @@ int callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user,
* 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;
return -1;
if (pss->client_finished)
return -1;
break;
}
if (lws_get_child(wsi))
break;
#endif
/*
* we can send more of whatever it is we were sending
@ -657,16 +670,12 @@ bail:
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);
//lwsl_err("LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ len %d\n", (int)len);
assert(lws_get_parent(wsi));
m = lws_write(lws_get_parent(wsi), (unsigned char *)in,
len, LWS_WRITE_HTTP);

View file

@ -191,6 +191,7 @@ static struct option options[] = {
int main(int argc, char **argv)
{
struct lws_context_creation_info info;
struct lws_vhost *vhost;
char interface_name[128] = "";
unsigned int ms, oldms = 0;
const char *iface = NULL;
@ -382,7 +383,7 @@ int main(int argc, char **argv)
info.gid = gid;
info.uid = uid;
info.max_http_header_pool = 16;
info.options = opts | LWS_SERVER_OPTION_VALIDATE_UTF8;
info.options = opts | LWS_SERVER_OPTION_VALIDATE_UTF8 | LWS_SERVER_OPTION_EXPLICIT_VHOSTS | LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
info.extensions = exts;
info.timeout_secs = 5;
info.ssl_cipher_list = "ECDHE-ECDSA-AES256-GCM-SHA384:"
@ -409,6 +410,16 @@ int main(int argc, char **argv)
return -1;
}
vhost = lws_create_vhost(context, &info);
if (!vhost) {
lwsl_err("vhost creation failed\n");
return -1;
}
#if !defined(LWS_NO_CLIENT) && defined(LWS_OPENSSL_SUPPORT)
lws_init_vhost_client_ssl(&info, vhost);
#endif
/* this shows how to override the lws file operations. You don't need
* to do any of this unless you have a reason (eg, want to serve
* compressed files without decompressing the whole archive)