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

refactor: subdirs for source in lib

Split out some optional code into own sources to
shrink down libwebsockets.c and server.c a bit
This commit is contained in:
Andy Green 2017-10-16 12:52:32 +08:00
parent 904a9c0920
commit c83afc66e6
53 changed files with 2818 additions and 2509 deletions

View file

@ -250,7 +250,7 @@ set(LWS_OPENSSL_LIBRARIES CACHE PATH "Path to the OpenSSL library")
set(LWS_OPENSSL_INCLUDE_DIRS CACHE PATH "Path to the OpenSSL include directory")
set(LWS_WOLFSSL_LIBRARIES CACHE PATH "Path to the wolfSSL library")
set(LWS_WOLFSSL_INCLUDE_DIRS CACHE PATH "Path to the wolfSSL include directory")
set( CACHE PATH "Path to the libev library")
set(LWS_LIBEV_LIBRARIES CACHE PATH "Path to the libev library")
set(LWS_LIBEV_INCLUDE_DIRS CACHE PATH "Path to the libev include directory")
set(LWS_LIBUV_LIBRARIES CACHE PATH "Path to the libuv library")
set(LWS_LIBUV_INCLUDE_DIRS CACHE PATH "Path to the libuv include directory")
@ -610,22 +610,38 @@ set(HDR_PUBLIC
)
set(SOURCES
lib/base64-decode.c
lib/misc/base64-decode.c
lib/handshake.c
lib/libwebsockets.c
lib/service.c
lib/pollfd.c
lib/output.c
lib/parsers.c
lib/server/parsers.c
lib/context.c
lib/alloc.c
lib/header.c)
lib/header.c
lib/misc/lws-ring.c)
if (LWS_WITH_CGI)
list(APPEND SOURCES
lib/server/cgi.c)
endif()
if (LWS_WITH_ACCESS_LOG)
list(APPEND SOURCES
lib/server/access-log.c)
endif()
if (LWS_WITH_PEER_LIMITS)
list(APPEND SOURCES
lib/server/peer-limits.c)
endif()
if (NOT LWS_WITHOUT_CLIENT)
list(APPEND SOURCES
lib/client.c
lib/client-handshake.c
lib/client-parser.c)
lib/client/client.c
lib/client/client-handshake.c
lib/client/client-parser.c)
endif()
if (LWS_WITH_MBEDTLS)
@ -673,51 +689,51 @@ endif()
if (LWS_WITH_SSL)
list(APPEND SOURCES
lib/ssl.c
lib/lws-genhash.c)
lib/misc/lws-genhash.c)
if (NOT LWS_WITHOUT_SERVER)
list(APPEND SOURCES
lib/ssl-server.c)
lib/server/ssl-server.c)
endif()
if (NOT LWS_WITHOUT_CLIENT)
list(APPEND SOURCES
lib/ssl-client.c)
lib/client/ssl-client.c)
endif()
endif()
if (NOT LWS_WITHOUT_BUILTIN_SHA1)
list(APPEND SOURCES
lib/sha-1.c)
lib/misc/sha-1.c)
endif()
if (LWS_WITH_HTTP2)
list(APPEND SOURCES
lib/http2.c
lib/hpack.c
lib/ssl-http2.c)
lib/http2/http2.c
lib/http2/hpack.c
lib/http2/ssl-http2.c)
endif()
# select the active platform files
if (WIN32)
list(APPEND SOURCES
lib/lws-plat-win.c)
lib/plat/lws-plat-win.c)
else()
if (LWS_WITH_ESP8266)
list(APPEND SOURCES
lib/lws-plat-esp8266.c)
lib/plat/lws-plat-esp8266.c)
else()
if (LWS_PLAT_OPTEE)
list(APPEND SOURCES
lib/lws-plat-optee.c)
lib/plat/lws-plat-optee.c)
else()
if (LWS_WITH_ESP32)
list(APPEND SOURCES
lib/lws-plat-esp32.c
lib/romfs.c)
lib/plat/lws-plat-esp32.c
lib/misc/romfs.c)
else()
list(APPEND SOURCES
lib/lws-plat-unix.c)
lib/plat/lws-plat-unix.c)
endif()
endif()
endif()
@ -725,64 +741,65 @@ endif()
if (NOT LWS_WITHOUT_SERVER)
list(APPEND SOURCES
lib/server.c
lib/server-handshake.c)
lib/server/server.c
lib/server/lws-spa.c
lib/server/server-handshake.c)
endif()
if (NOT LWS_WITHOUT_EXTENSIONS)
list(APPEND HDR_PRIVATE
lib/extension-permessage-deflate.h)
lib/ext/extension-permessage-deflate.h)
list(APPEND SOURCES
lib/extension.c
lib/extension-permessage-deflate.c)
lib/ext/extension.c
lib/ext/extension-permessage-deflate.c)
endif()
if (LWS_WITH_HTTP_PROXY)
list(APPEND SOURCES
lib/rewrite.c)
lib/server/rewrite.c)
endif()
if (LWS_WITH_LIBEV)
list(APPEND SOURCES
lib/libev.c)
lib/event-libs/libev.c)
endif()
if (LWS_WITH_LIBUV)
list(APPEND SOURCES
lib/libuv.c)
lib/event-libs/libuv.c)
endif()
if (LWS_WITH_LIBEVENT)
list(APPEND SOURCES
lib/libevent.c)
lib/event-libs/libevent.c)
endif()
if (LWS_WITH_LEJP)
list(APPEND SOURCES
lib/lejp.c)
lib/misc/lejp.c)
list(APPEND HDR_PUBLIC
lib/lejp.h)
lib/misc/lejp.h)
endif()
if (LWS_WITH_LEJP_CONF)
list(APPEND SOURCES
"lib/lejp-conf.c"
"lib/server/lejp-conf.c"
)
endif()
if (LWS_WITH_SMTP)
list(APPEND SOURCES
lib/smtp.c)
lib/misc/smtp.c)
endif()
if (LWS_WITH_RANGES)
list(APPEND SOURCES
lib/ranges.c)
lib/server/ranges.c)
endif()
if (LWS_WITH_ZIP_FOPS)
if (LWS_WITH_ZLIB)
list(APPEND SOURCES
lib/fops-zip.c)
lib/server/fops-zip.c)
else()
message(FATAL_ERROR "Pre-zipped file support (LWS_WITH_ZIP_FOPS) requires ZLIB (LWS_WITH_ZLIB)")
endif()
@ -807,14 +824,14 @@ else()
# Unix.
if (NOT LWS_WITHOUT_DAEMONIZE)
list(APPEND SOURCES
lib/daemonize.c)
lib/server/daemonize.c)
endif()
endif()
if (UNIX)
if (NOT LWS_HAVE_GETIFADDRS)
list(APPEND HDR_PRIVATE lib/getifaddrs.h)
list(APPEND SOURCES lib/getifaddrs.c)
list(APPEND HDR_PRIVATE lib/misc/getifaddrs.h)
list(APPEND SOURCES lib/misc/getifaddrs.c)
endif()
endif()

View file

@ -4,20 +4,81 @@ Notes about coding with lws
@section era Old lws and lws v2.0
Originally lws only supported the "manual" method of handling everything in the
user callback found in test-server.c.
user callback found in test-server.c / test-server-http.c.
Since v2.0, the need for most or all of this manual boilerplate has been eliminated:
the protocols[0] http stuff is provided by a lib export `lws_callback_http_dummy()`.
You can serve parts of your filesystem at part of the URL space using mounts.
Since v2.0, the need for most or all of this manual boilerplate has been
eliminated: the protocols[0] http stuff is provided by a generic lib export
`lws_callback_http_dummy()`. You can serve parts of your filesystem at part of
the URL space using mounts, the dummy http callback will do the right thing.
It's much preferred to use the "automated" v2.0 type scheme, because it's less
code and it's easier to support.
You can see an example of the new way in test-server-v2.0.c.
If you just need generic serving capability, consider not writing any server code
and instead use lwsws and writing your user code in a standalone plugin. The
server is configured for mounts etc using JSON, see README.lwsws.md.
If you just need generic serving capability, without the need to integrate lws
to some other app, consider not writing any server code at all, and instead use
the generic server `lwsws`, and writing your special user code in a standalone
"plugin". The server is configured for mounts etc using JSON, see
./READMEs/README.lwsws.md.
Although the "plugins" are dynamically loaded if you use lwsws or lws built
with libuv, actually they may perfectly well be statically included if that
suits your situation better, eg, ESP32 test server, where the platform does
not support processes or dynamic loading, just #includes the plugins
one after the other and gets the same benefit from the same code.
Isolating and collating the protocol code in one place also makes it very easy
to maintain and understand.
So it if highly recommended you put your protocol-specific code into the
form of a "plugin" at the source level, even if you have no immediate plan to
use it dynamically-loaded.
@section writeable Only send data when socket writeable
You should only send data on a websocket connection from the user callback
`LWS_CALLBACK_SERVER_WRITEABLE` (or `LWS_CALLBACK_CLIENT_WRITEABLE` for
clients).
If you want to send something, do NOT just send it but request a callback
when the socket is writeable using
- `lws_callback_on_writable(context, wsi)` for a specific `wsi`, or
- `lws_callback_on_writable_all_protocol(protocol)` for all connections
using that protocol to get a callback when next writeable.
Usually you will get called back immediately next time around the service
loop, but if your peer is slow or temporarily inactive the callback will be
delayed accordingly. Generating what to write and sending it should be done
in the ...WRITEABLE callback.
See the test server code for an example of how to do this.
Otherwise evolved libs like libuv get this wrong, they will allow you to "send"
anything you want but it only uses up your local memory (and costs you
memcpys) until the socket can actually accept it. It is much better to regulate
your send action by the downstream peer readiness to take new data in the first
place, avoiding all the wasted buffering.
Libwebsockets' concept is that the downstream peer is truly the boss, if he,
or our connection to him, cannot handle anything new, we should not generate
anything new for him. This is how unix shell piping works, you may have
`cat a.txt | grep xyz > remote", but actually that does not cat anything from
a.txt while remote cannot accept anything new.
@section otherwr Do not rely on only your own WRITEABLE requests appearing
Libwebsockets may generate additional `LWS_CALLBACK_CLIENT_WRITEABLE` events
if it met network conditions where it had to buffer your send data internally.
So your code for `LWS_CALLBACK_CLIENT_WRITEABLE` needs to own the decision
about what to send, it can't assume that just because the writeable callback
came it really is time to send something.
It's quite possible you get an 'extra' writeable callback at any time and
just need to `return 0` and wait for the expected callback later.
@section dae Daemonization
@ -28,8 +89,7 @@ headless background process and exit the starting process.
Notice stdout, stderr, stdin are all redirected to /dev/null to enforce your
daemon is headless, so you'll need to sort out alternative logging, by, eg,
syslog.
syslog via `lws_set_log_level(..., lwsl_emit_syslog)`.
@section conns Maximum number of connections
@ -81,7 +141,9 @@ repeat that in other words:
There is another network-programming truism that surprises some people which
is if the sink for the data cannot accept more:
***YOU MUST PERFORM RX FLOW CONTROL***
***YOU MUST PERFORM RX FLOW CONTROL*** to stop taking new input. TCP will make
this situation known to the upstream sender by making it impossible for him to
send anything more on the connection until we start accepting things again.
See the mirror protocol implementations for example code.
@ -99,42 +161,6 @@ you might simultaneously create more than one context from different threads.
SSL_library_init() is called from the context create api and it also is not
reentrant. So at least create the contexts sequentially.
@section writeable Only send data when socket writeable
You should only send data on a websocket connection from the user callback
`LWS_CALLBACK_SERVER_WRITEABLE` (or `LWS_CALLBACK_CLIENT_WRITEABLE` for
clients).
If you want to send something, do not just send it but request a callback
when the socket is writeable using
- `lws_callback_on_writable(context, wsi)` for a specific `wsi`, or
- `lws_callback_on_writable_all_protocol(protocol)` for all connections
using that protocol to get a callback when next writeable.
Usually you will get called back immediately next time around the service
loop, but if your peer is slow or temporarily inactive the callback will be
delayed accordingly. Generating what to write and sending it should be done
in the ...WRITEABLE callback.
See the test server code for an example of how to do this.
@section otherwr Do not rely on only your own WRITEABLE requests appearing
Libwebsockets may generate additional `LWS_CALLBACK_CLIENT_WRITEABLE` events
if it met network conditions where it had to buffer your send data internally.
So your code for `LWS_CALLBACK_CLIENT_WRITEABLE` needs to own the decision
about what to send, it can't assume that just because the writeable callback
came it really is time to send something.
It's quite possible you get an 'extra' writeable callback at any time and
just need to `return 0` and wait for the expected callback later.
@section closing Closing connections from the user side
When you want to close a connection, you do it by returning `-1` from a

8
lib/.gitignore vendored
View file

@ -1,8 +0,0 @@
#Ignore build files
Makefile
*.o
*.lo
*.la
.libs
.deps

0
lib/client.c → lib/client/client.c Executable file → Normal file
View file

View file

@ -84,7 +84,7 @@ const struct http2_settings lws_h2_defaults = { {
const struct http2_settings lws_h2_stock_settings = { {
1,
/* H2SET_HEADER_TABLE_SIZE */ 512,
/* H2SET_HEADER_TABLE_SIZE */ 4096,
/* *** This controls how many entries in the dynamic table ***
* Allows the sender to inform the remote endpoint of the maximum
* size of the header compression table used to decode header
@ -92,6 +92,8 @@ const struct http2_settings lws_h2_stock_settings = { {
* less than this value by using signaling specific to the header
* compression format inside a header block (see [COMPRESSION]).
* The initial value is 4,096 octets.
*
* Can't pass h2spec with less than 4096 here...
*/
/* H2SET_ENABLE_PUSH */ 1,
/* H2SET_MAX_CONCURRENT_STREAMS */ 24,

View file

@ -384,7 +384,8 @@ lws_token_from_index(struct lws *wsi, int index, const char **arg, int *len,
if (index < 0)
index += dyn->num_entries;
lwsl_header("%s: dyn index %d, tok %d\n", __func__, index, dyn->entries[index].lws_hdr_idx);
lwsl_header("%s: dyn index %d, tok %d\n", __func__, index,
dyn->entries[index].lws_hdr_idx);
if (arg && len) {
*arg = dyn->entries[index].value;
@ -410,9 +411,11 @@ lws_h2_dynamic_table_dump(struct lws *wsi)
return 1;
dyn = &nwsi->u.h2.h2n->hpack_dyn_table;
lwsl_header("Dump dyn table for nwsi %p (%d / %d members, pos = %d, start index %d, virt used %d / %d)\n", nwsi,
dyn->used_entries, dyn->num_entries, dyn->pos, (uint32_t)ARRAY_SIZE(static_token),
dyn->virtual_payload_usage, dyn->virtual_payload_max);
lwsl_header("Dump dyn table for nwsi %p (%d / %d members, pos = %d, "
"start index %d, virt used %d / %d)\n", nwsi,
dyn->used_entries, dyn->num_entries, dyn->pos,
(uint32_t)ARRAY_SIZE(static_token),
dyn->virtual_payload_usage, dyn->virtual_payload_max);
for (n = 0; n < dyn->used_entries; n++) {
m = (dyn->pos - 1 - n) % dyn->num_entries;
@ -424,8 +427,9 @@ lws_h2_dynamic_table_dump(struct lws *wsi)
else
p = "(ignored)";
lwsl_header(" %3d: tok %s: (len %d) val '%s'\n",
(int)(n + ARRAY_SIZE(static_token)), p, dyn->entries[m].hdr_len,
dyn->entries[m].value ? dyn->entries[m].value : "null");
(int)(n + ARRAY_SIZE(static_token)), p,
dyn->entries[m].hdr_len, dyn->entries[m].value ?
dyn->entries[m].value : "null");
}
#endif
return 0;
@ -509,6 +513,8 @@ lws_dynamic_token_insert(struct lws *wsi, int hdr_len,
dyn->entries[new_index].value_len = 0;
if (lws_hdr_index != LWS_HPACK_IGNORE_ENTRY) {
if (dyn->entries[new_index].value)
lws_free_set_NULL(dyn->entries[new_index].value);
dyn->entries[new_index].value = lws_malloc(len + 1, "hpack dyn");
if (!dyn->entries[new_index].value)
return 1;
@ -565,7 +571,9 @@ lws_hpack_dynamic_size(struct lws *wsi, int size)
goto bail;
dyn = &nwsi->u.h2.h2n->hpack_dyn_table;
lwsl_info("%s: from %d to %d\n", __func__, (int)dyn->num_entries, size);
lwsl_info("%s: from %d to %d, lim %d\n", __func__,
(int)dyn->num_entries, size,
nwsi->u.h2.h2n->set.s[H2SET_HEADER_TABLE_SIZE]);
if (size > nwsi->u.h2.h2n->set.s[H2SET_HEADER_TABLE_SIZE]) {
lws_h2_goaway(nwsi, H2_ERR_COMPRESSION_ERROR,
@ -615,7 +623,10 @@ lws_hpack_dynamic_size(struct lws *wsi, int size)
dyn->entries = dte;
dyn->num_entries = size;
dyn->used_entries = min;
dyn->pos = min % size;
if (size)
dyn->pos = min % size;
else
dyn->pos = 0;
lws_h2_dynamic_table_dump(wsi);
@ -732,7 +743,6 @@ int lws_hpack_interpret(struct lws *wsi, unsigned char c)
h2n->ext_count = 0;
h2n->hpack_hdr_len = 0;
h2n->unknown_header = 0;
h2n->seen_nonpseudoheader = 0;
if (c & 0x80) { /* 1.... indexed header field only */
/* just a possibly-extended integer */
@ -930,7 +940,8 @@ pre_data:
} else
h2n->hdr_idx = 1;
} else {
n = lws_token_from_index(wsi, h2n->hdr_idx, NULL, NULL, NULL);
n = lws_token_from_index(wsi, h2n->hdr_idx, NULL,
NULL, NULL);
lwsl_header(" lws_tok_from_idx(%d) says %d\n",
h2n->hdr_idx, n);
}
@ -1010,9 +1021,11 @@ pre_data:
if (h2n->hdr_idx &&
h2n->hdr_idx != LWS_HPACK_IGNORE_ENTRY) {
if (ah->hdr_token_idx == WSI_TOKEN_HTTP_COLON_PATH) {
if (ah->hdr_token_idx ==
WSI_TOKEN_HTTP_COLON_PATH) {
switch (lws_parse_urldecode(wsi, &c1)) {
switch (lws_parse_urldecode(
wsi, &c1)) {
case LPUR_CONTINUE:
break;
case LPUR_SWALLOW:
@ -1020,8 +1033,8 @@ pre_data:
case LPUR_EXCESSIVE:
case LPUR_FORBID:
lws_h2_goaway(nwsi,
H2_ERR_PROTOCOL_ERROR,
"Evil URI");
H2_ERR_PROTOCOL_ERROR,
"Evil URI");
return 1;
default:
@ -1030,11 +1043,11 @@ pre_data:
}
if (lws_frag_append(wsi, c1)) {
lwsl_notice("%s: frag app fail\n",
__func__);
__func__);
return 1;
}
} else
lwsl_header("ignoring %c\n", c1);
} //else
//lwsl_header("ignoring %c\n", c1);
} else {
/*
* Convert name using existing parser,
@ -1140,7 +1153,8 @@ swallow:
wsi->u.hdr.parser_state == WSI_TOKEN_NAME_PART ||
wsi->u.hdr.parser_state == WSI_TOKEN_SKIPPING) {
if (h2n->first_hdr_char == ':') {
lwsl_info("HPKT_LITERAL_HDR_VALUE_INCR: end parser state %d unk hdr %d\n",
lwsl_info("HPKT_LITERAL_HDR_VALUE_INCR:"
" end state %d unk hdr %d\n",
wsi->u.hdr.parser_state,
h2n->unknown_header);
/* unknown pseudoheaders are illegal */
@ -1180,7 +1194,8 @@ add_it:
if (m == 255)
m = -1;
} else
m = lws_token_from_index(wsi, h2n->hdr_idx, NULL, NULL, NULL);
m = lws_token_from_index(wsi, h2n->hdr_idx, NULL, NULL,
NULL);
if (m != -1 && m != LWS_HPACK_IGNORE_ENTRY)
lws_dump_header(wsi, m);

View file

@ -189,7 +189,8 @@ lws_wsi_server_new(struct lws_vhost *vh, struct lws *parent_wsi,
wsi->vhost->conn_stats.h2_subs++;
lwsl_info("%s: %p new ch %p, sid %d, usersp=%p, tx cr %d, peer_credit %d (nwsi tx_cr %d)\n",
lwsl_info("%s: %p new ch %p, sid %d, usersp=%p, tx cr %d, "
"peer_credit %d (nwsi tx_cr %d)\n",
__func__, parent_wsi, wsi, sid, wsi->user_space,
wsi->u.h2.tx_cr, wsi->u.h2.peer_tx_cr_est, nwsi->u.h2.tx_cr);
@ -274,6 +275,7 @@ lws_h2_goaway(struct lws *wsi, uint32_t err, const char *reason)
lwsl_info("%s: %p: ERR 0x%x, '%s'\n", __func__, wsi, err, reason);
pps->u.ga.err = err;
pps->u.ga.highest_sid = h2n->highest_sid;
strncpy(pps->u.ga.str, reason, sizeof(pps->u.ga.str) - 1);
pps->u.ga.str[sizeof(pps->u.ga.str) - 1] = '\0';
lws_pps_schedule(wsi, pps);
@ -334,7 +336,7 @@ lws_h2_settings(struct lws *wsi, struct http2_settings *settings,
case H2SET_ENABLE_PUSH:
if (b > 1) {
lws_h2_goaway(nwsi, H2_ERR_PROTOCOL_ERROR,
"ENABLE_PUSH invalid arg");
"ENABLE_PUSH invalid arg");
return 1;
}
break;
@ -343,7 +345,7 @@ lws_h2_settings(struct lws *wsi, struct http2_settings *settings,
case H2SET_INITIAL_WINDOW_SIZE:
if (b > 0x7fffffff) {
lws_h2_goaway(nwsi, H2_ERR_FLOW_CONTROL_ERROR,
"Inital Window beyond max");
"Inital Window beyond max");
return 1;
}
/*
@ -621,9 +623,9 @@ int lws_h2_do_pps_send(struct lws *wsi)
*p++ = pps->u.ga.err >> 16;
*p++ = pps->u.ga.err >> 8;
*p++ = pps->u.ga.err;
q = (unsigned char *)h2n->goaway_str;
q = (unsigned char *)pps->u.ga.str;
n = 0;
while (*q && n++ < sizeof(h2n->goaway_str))
while (*q && n++ < sizeof(pps->u.ga.str))
*p++ = *q++;
h2n->we_told_goaway = 1;
n = lws_h2_frame_write(wsi, LWS_H2_FRAME_TYPE_GOAWAY, 0,
@ -653,13 +655,16 @@ int lws_h2_do_pps_send(struct lws *wsi)
break;
case LWS_H2_PPS_UPDATE_WINDOW:
lwsl_notice("LWS_H2_PPS_UPDATE_WINDOW: sid %d: add %d\n", pps->u.update_window.sid, pps->u.update_window.credit);
lwsl_notice("LWS_H2_PPS_UPDATE_WINDOW: sid %d: add %d\n",
pps->u.update_window.sid,
pps->u.update_window.credit);
*p++ = pps->u.update_window.credit >> 24;
*p++ = pps->u.update_window.credit >> 16;
*p++ = pps->u.update_window.credit >> 8;
*p++ = pps->u.update_window.credit;
n = lws_h2_frame_write(wsi, LWS_H2_FRAME_TYPE_WINDOW_UPDATE,
0, pps->u.update_window.sid, 4, &set[LWS_PRE]);
0, pps->u.update_window.sid, 4,
&set[LWS_PRE]);
if (n != 4) {
lwsl_info("send %d %d\n", n, m);
goto bail;
@ -802,7 +807,8 @@ lws_h2_parse_frame_header(struct lws *wsi)
break;
h2n->swsi->u.h2.peer_tx_cr_est -= h2n->length;
lwsl_debug(" peer_tx_cr_est %d\n", h2n->swsi->u.h2.peer_tx_cr_est);
lwsl_debug(" peer_tx_cr_est %d\n",
h2n->swsi->u.h2.peer_tx_cr_est);
if (h2n->swsi->u.h2.peer_tx_cr_est < 32768) {
h2n->swsi->u.h2.peer_tx_cr_est += 65536;
pps = lws_h2_new_pps(LWS_H2_PPS_UPDATE_WINDOW);
@ -819,9 +825,9 @@ lws_h2_parse_frame_header(struct lws *wsi)
lws_pps_schedule(wsi, pps);
}
if ((
if (
h2n->swsi->u.h2.h2_state == LWS_H2_STATE_HALF_CLOSED_REMOTE ||
h2n->swsi->u.h2.h2_state == LWS_H2_STATE_CLOSED)) {
h2n->swsi->u.h2.h2_state == LWS_H2_STATE_CLOSED) {
lws_h2_goaway(wsi, H2_ERR_STREAM_CLOSED, "conn closed");
break;
}
@ -950,7 +956,8 @@ lws_h2_parse_frame_header(struct lws *wsi)
return 1;
}
h2n->swsi = lws_wsi_server_new(wsi->vhost, wsi, h2n->sid);
h2n->swsi = lws_wsi_server_new(wsi->vhost, wsi,
h2n->sid);
if (!h2n->swsi) {
lws_h2_goaway(wsi, H2_ERR_PROTOCOL_ERROR, "OOM");
@ -986,7 +993,8 @@ lws_h2_parse_frame_header(struct lws *wsi)
/* END_STREAM means after servicing this, close the stream */
h2n->swsi->u.h2.END_STREAM = !!(h2n->flags & LWS_H2_FLAG_END_STREAM);
h2n->swsi->u.h2.END_STREAM =
!!(h2n->flags & LWS_H2_FLAG_END_STREAM);
lwsl_info("%s: hdr END_STREAM = %d\n",__func__,
h2n->swsi->u.h2.END_STREAM);
@ -1187,7 +1195,9 @@ lws_h2_parse_end_of_frame(struct lws *wsi)
lwsl_info(" action start...\n");
n = lws_http_action(h2n->swsi);
lwsl_info(" action result %d (wsi->u.http.rx_content_remain %lld)\n", n, h2n->swsi->u.http.rx_content_remain);
lwsl_info(" action result %d "
"(wsi->u.http.rx_content_remain %lld)\n",
n, h2n->swsi->u.http.rx_content_remain);
/*
* Commonly we only managed to start a larger transfer that will
@ -1206,7 +1216,8 @@ lws_h2_parse_end_of_frame(struct lws *wsi)
break;
if (lws_hdr_total_length(h2n->swsi, WSI_TOKEN_HTTP_CONTENT_LENGTH) &&
h2n->swsi->u.h2.END_STREAM && h2n->swsi->u.http.rx_content_length &&
h2n->swsi->u.h2.END_STREAM &&
h2n->swsi->u.http.rx_content_length &&
h2n->swsi->u.http.rx_content_remain) {
lws_h2_rst_stream(h2n->swsi, H2_ERR_PROTOCOL_ERROR,
"Not enough rx content");
@ -1291,7 +1302,8 @@ lws_h2_parse_end_of_frame(struct lws *wsi)
case LWS_H2_FRAME_TYPE_GOAWAY:
lwsl_info("GOAWAY: last sid %d, error 0x%08X, string '%s'\n",
h2n->goaway_last_sid, h2n->goaway_err, h2n->goaway_str);
h2n->goaway_last_sid, h2n->goaway_err,
h2n->goaway_str);
wsi->u.h2.GOING_AWAY = 1;
return 1;
@ -1446,17 +1458,11 @@ lws_h2_parser(struct lws *wsi, unsigned char c)
}
h2n->swsi->state = LWSS_HTTP_BODY;
h2n->inside++;
/* because the HTTP_BODY stuff will handle it */
//h2n->swsi->u.http.rx_content_remain--;
//lwsl_info("remain %lld, %d / %d",
// (long long)h2n->swsi->u.http.rx_content_remain,
// h2n->inside, h2n->length);
if (lws_hdr_total_length(h2n->swsi,
WSI_TOKEN_HTTP_CONTENT_LENGTH) &&
h2n->swsi->u.http.rx_content_length &&
h2n->swsi->u.http.rx_content_remain == 1 && /* last byte */
h2n->swsi->u.http.rx_content_remain == 1 && /* last */
h2n->inside < h2n->length) { /* unread data in frame */
lws_h2_goaway(wsi, H2_ERR_PROTOCOL_ERROR,
"More rx than content_length told");

View file

@ -52,7 +52,8 @@
#if !defined(LWS_NO_SERVER)
#if defined(LWS_OPENSSL_SUPPORT)
#if defined(LWS_WITH_MBEDTLS) || (defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x10002000L)
#if defined(LWS_WITH_MBEDTLS) || (defined(OPENSSL_VERSION_NUMBER) && \
OPENSSL_VERSION_NUMBER >= 0x10002000L)
struct alpn_ctx {
unsigned char *data;
@ -79,7 +80,8 @@ alpn_cb(SSL *s, const unsigned char **out, unsigned char *outlen,
LWS_VISIBLE void
lws_context_init_http2_ssl(struct lws_vhost *vhost)
{
#if defined(LWS_WITH_MBEDTLS) || (defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x10002000L)
#if defined(LWS_WITH_MBEDTLS) || (defined(OPENSSL_VERSION_NUMBER) && \
OPENSSL_VERSION_NUMBER >= 0x10002000L)
static struct alpn_ctx protos = { (unsigned char *)"\x02h2"
"\x08http/1.1", 6 + 9 };
@ -94,7 +96,8 @@ lws_context_init_http2_ssl(struct lws_vhost *vhost)
int lws_h2_configure_if_upgraded(struct lws *wsi)
{
#if defined(LWS_WITH_MBEDTLS) || (defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x10002000L)
#if defined(LWS_WITH_MBEDTLS) || (defined(OPENSSL_VERSION_NUMBER) && \
OPENSSL_VERSION_NUMBER >= 0x10002000L)
struct allocated_headers *ah;
const unsigned char *name = NULL;
char cstr[10];

1400
lib/libwebsockets.c Executable file → Normal file

File diff suppressed because it is too large Load diff

View file

@ -313,14 +313,35 @@ lwsl_timestamp(int level, char *p, int len);
#endif
/**
* lwsl_hexdump() - helper to hexdump a buffer
*
* \param level: one of LLL_ constants
* \param buf: buffer start to dump
* \param len: length of buffer to dump
*
* If \p level is visible, does a nice hexdump -C style dump of \p buf for
* \p len bytes. This can be extremely convenient while debugging.
*/
LWS_VISIBLE LWS_EXTERN void
lwsl_hexdump_level(int level, const void *vbuf, size_t len);
/**
* lwsl_hexdump() - helper to hexdump a buffer (DEBUG builds only)
*
* \param buf: buffer start to dump
* \param len: length of buffer to dump
*
* Calls through to lwsl_hexdump_level(LLL_DEBUG, ... for compatability.
* It's better to use lwsl_hexdump_level(level, ... directly so you can control
* the visibility.
*/
LWS_VISIBLE LWS_EXTERN void lwsl_hexdump(const void *buf, size_t len);
LWS_VISIBLE LWS_EXTERN void
lwsl_hexdump(const void *buf, size_t len);
/**
* lws_is_be() - returns nonzero if the platform is Big Endian
*/
static LWS_INLINE int lws_is_be(void) {
const int probe = ~0xff;
@ -335,7 +356,8 @@ static LWS_INLINE int lws_is_be(void) {
* the default stderr one.
*
* log level defaults to "err", "warn" and "notice" contexts enabled and
* emission on stderr.
* emission on stderr. If stderr is a tty (according to isatty()) then
* the output is coloured according to the log level using ANSI escapes.
*/
LWS_VISIBLE LWS_EXTERN void
lws_set_log_level(int level,

280
lib/misc/lws-ring.c Normal file
View file

@ -0,0 +1,280 @@
/*
* libwebsockets - lws-ring multi-tail abstract ringbuffer api
*
* Copyright (C) 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 "private-libwebsockets.h"
LWS_VISIBLE LWS_EXTERN struct lws_ring *
lws_ring_create(size_t element_len, size_t count,
void (*destroy_element)(void *))
{
struct lws_ring *ring = lws_malloc(sizeof(*ring), "ring create");
if (!ring)
return NULL;
ring->buflen = count * element_len;
ring->element_len = element_len;
ring->head = 0;
ring->oldest_tail = 0;
ring->destroy_element = destroy_element;
ring->buf = lws_malloc(ring->buflen, "ring buf");
if (!ring->buf) {
lws_free(ring);
return NULL;
}
return ring;
}
LWS_VISIBLE LWS_EXTERN void
lws_ring_destroy(struct lws_ring *ring)
{
if (ring->destroy_element)
while (ring->oldest_tail != ring->head) {
ring->destroy_element((uint8_t *)ring->buf +
ring->oldest_tail);
ring->oldest_tail =
(ring->oldest_tail + ring->element_len) %
ring->buflen;
}
if (ring->buf)
lws_free_set_NULL(ring->buf);
lws_free(ring);
}
LWS_VISIBLE LWS_EXTERN size_t
lws_ring_get_count_free_elements(struct lws_ring *ring)
{
int f;
/*
* possible ringbuf patterns
*
* h == t
* |--------t***h---|
* |**h-----------t*|
* |t**************h|
* |*****ht*********|
*/
if (ring->head == ring->oldest_tail)
f = ring->buflen - ring->element_len;
else
if (ring->head < ring->oldest_tail)
f = (ring->oldest_tail - ring->head) -
ring->element_len;
else
f = (ring->buflen - ring->head) + ring->oldest_tail -
ring->element_len;
if (f < 2)
return 0;
return f / ring->element_len;
}
LWS_VISIBLE LWS_EXTERN size_t
lws_ring_get_count_waiting_elements(struct lws_ring *ring, uint32_t *tail)
{ int f;
if (!tail)
tail = &ring->oldest_tail;
/*
* possible ringbuf patterns
*
* h == t
* |--------t***h---|
* |**h-----------t*|
* |t**************h|
* |*****ht*********|
*/
if (ring->head == *tail)
f = 0;
else
if (ring->head > *tail)
f = (ring->head - *tail);
else
f = (ring->buflen - *tail) + ring->head;
return f / ring->element_len;
}
LWS_VISIBLE LWS_EXTERN int
lws_ring_next_linear_insert_range(struct lws_ring *ring, void **start,
size_t *bytes)
{
int n;
/* n is how many bytes the whole fifo can take */
n = lws_ring_get_count_free_elements(ring) * ring->element_len;
if (!n)
return 1;
if (ring->head + n > ring->buflen) {
*start = (void *)(((uint8_t *)ring->buf) + ring->head);
*bytes = ring->buflen - ring->head;
return 0;
}
*start = (void *)(((uint8_t *)ring->buf) + ring->head);
*bytes = n;
return 0;
}
LWS_VISIBLE LWS_EXTERN void
lws_ring_bump_head(struct lws_ring *ring, size_t bytes)
{
ring->head = (ring->head + bytes) % ring->buflen;
}
LWS_VISIBLE LWS_EXTERN size_t
lws_ring_insert(struct lws_ring *ring, const void *src, size_t max_count)
{
const uint8_t *osrc = src;
int m, n;
/* n is how many bytes the whole fifo can take */
n = lws_ring_get_count_free_elements(ring) * ring->element_len;
/* restrict n to how much we want to insert */
if ((size_t)n > max_count * ring->element_len)
n = max_count * ring->element_len;
/*
* n is legal to insert, but as an optimization we can cut the
* insert into one or two memcpys, depending on if it wraps
*/
if (ring->head + n > ring->buflen) {
/*
* He does wrap. The first memcpy should take us up to
* the end of the buffer
*/
m = ring->buflen - ring->head;
memcpy(((uint8_t *)ring->buf) + ring->head, src, m);
/* we know it will wrap exactly back to zero */
ring->head = 0;
/* adapt the second memcpy for what we already did */
src = ((uint8_t *)src) + m;
n -= m;
}
memcpy(((uint8_t *)ring->buf) + ring->head, src, n);
ring->head = (ring->head + n) % ring->buflen;
return (((uint8_t *)src + n) - osrc) / ring->element_len;
}
LWS_VISIBLE LWS_EXTERN size_t
lws_ring_consume(struct lws_ring *ring, uint32_t *tail, void *dest,
size_t max_count)
{
uint8_t *odest = dest;
void *orig_tail = tail;
uint32_t fake_tail;
int m, n;
if (!tail) {
fake_tail = ring->oldest_tail;
tail = &fake_tail;
}
/* n is how many bytes the whole fifo has for us */
n = lws_ring_get_count_waiting_elements(ring, tail) * ring->element_len;
/* restrict n to how much we want to insert */
if ((size_t)n > max_count * ring->element_len)
n = max_count * ring->element_len;
if (!dest) {
*tail = ((*tail) + n) % ring->buflen;
if (!orig_tail) /* single tail */
lws_ring_update_oldest_tail(ring, *tail);
return n / ring->element_len;
}
if (*tail + n > ring->buflen) {
/*
* He does wrap. The first memcpy should take us up to
* the end of the buffer
*/
m = ring->buflen - *tail;
memcpy(dest, ((uint8_t *)ring->buf) + *tail, m);
/* we know it will wrap exactly back to zero */
*tail = 0;
/* adapt the second memcpy for what we already did */
dest = ((uint8_t *)dest) + m;
n -= m;
}
memcpy(dest, ((uint8_t *)ring->buf) + *tail, n);
*tail = ((*tail) + n) % ring->buflen;
if (!orig_tail) /* single tail */
lws_ring_update_oldest_tail(ring, *tail);
return (((uint8_t *)dest + n) - odest) / ring->element_len;
}
LWS_VISIBLE LWS_EXTERN const void *
lws_ring_get_element(struct lws_ring *ring, uint32_t *tail)
{
if (!tail)
tail = &ring->oldest_tail;
if (*tail == ring->head)
return NULL;
return ((uint8_t *)ring->buf) + *tail;
}
LWS_VISIBLE LWS_EXTERN void
lws_ring_update_oldest_tail(struct lws_ring *ring, uint32_t tail)
{
if (!ring->destroy_element) {
ring->oldest_tail = tail;
return;
}
while (ring->oldest_tail != tail) {
ring->destroy_element((uint8_t *)ring->buf + ring->oldest_tail);
ring->oldest_tail = (ring->oldest_tail + ring->element_len) %
ring->buflen;
}
}
LWS_VISIBLE LWS_EXTERN uint32_t
lws_ring_get_oldest_tail(struct lws_ring *ring)
{
return ring->oldest_tail;
}

View file

@ -1,7 +1,7 @@
/*
* SMTP support for libwebsockets
*
* Copyright (C) 2016 Andy Green <andy@warmcat.com>
* Copyright (C) 2016-2017 Andy Green <andy@warmcat.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -93,7 +93,8 @@ lwsgs_email_read(struct uv_stream_s *s, ssize_t nread, const uv_buf_t *buf)
email->estate = LGSSMTP_SENT_HELO;
break;
case LGSSMTP_SENT_HELO:
n = sprintf(email->content, "MAIL FROM: <%s>\n", email->email_from);
n = sprintf(email->content, "MAIL FROM: <%s>\n",
email->email_from);
email->estate = LGSSMTP_SENT_FROM;
break;
case LGSSMTP_SENT_FROM:
@ -105,7 +106,8 @@ lwsgs_email_read(struct uv_stream_s *s, ssize_t nread, const uv_buf_t *buf)
email->estate = LGSSMTP_SENT_DATA;
break;
case LGSSMTP_SENT_DATA:
if (email->on_get_body(email, email->content, email->max_content_size))
if (email->on_get_body(email, email->content,
email->max_content_size))
return;
n = strlen(email->content);
email->estate = LGSSMTP_SENT_BODY;
@ -238,4 +240,3 @@ lws_email_destroy(struct lws_email *email)
uv_timer_stop(&email->timeout_email);
uv_close((uv_handle_t *)&email->timeout_email, NULL);
}

View file

@ -46,49 +46,9 @@ lws_0405_frame_mask_generate(struct lws *wsi)
return 0;
}
LWS_VISIBLE void lwsl_hexdump(const void *vbuf, size_t len)
{
unsigned char *buf = (unsigned char *)vbuf;
unsigned int n, m, start;
char line[80];
char *p;
lwsl_debug("\n");
for (n = 0; n < len;) {
start = n;
p = line;
p += sprintf(p, "%04X: ", start);
for (m = 0; m < 16 && n < len; m++)
p += sprintf(p, "%02X ", buf[n++]);
while (m++ < 16)
p += sprintf(p, " ");
p += sprintf(p, " ");
for (m = 0; m < 16 && (start + m) < len; m++) {
if (buf[start + m] >= ' ' && buf[start + m] < 127)
*p++ = buf[start + m];
else
*p++ = '.';
}
while (m++ < 16)
*p++ = ' ';
*p++ = '\n';
*p = '\0';
lwsl_debug("%s", line);
(void)line;
}
lwsl_debug("\n");
}
/*
* notice this returns number of bytes consumed, or -1
*/
int lws_issue_raw(struct lws *wsi, unsigned char *buf, size_t len)
{
struct lws_context *context = lws_get_context(wsi);

View file

@ -1094,17 +1094,7 @@ struct lws_context {
int uid, gid;
int fd_random;
#ifdef LWS_OPENSSL_SUPPORT
#define lws_ssl_anybody_has_buffered_read(w) \
(w->vhost->use_ssl && \
w->context->pt[(int)w->tsi].pending_read_list)
#define lws_ssl_anybody_has_buffered_read_tsi(c, t) \
(/*c->use_ssl && */ \
c->pt[(int)t].pending_read_list)
#else
#define lws_ssl_anybody_has_buffered_read(ctx) (0)
#define lws_ssl_anybody_has_buffered_read_tsi(ctx, t) (0)
#endif
int count_wsi_allocated;
int count_cgi_spawned;
unsigned int options;
@ -2023,6 +2013,9 @@ lws_get_addr_scope(const char *ipaddr);
LWS_EXTERN void
lws_close_free_wsi(struct lws *wsi, enum lws_close_status);
LWS_EXTERN void
lws_free_wsi(struct lws *wsi);
LWS_EXTERN int
remove_wsi_socket_from_fds(struct lws *wsi);
LWS_EXTERN int
@ -2297,6 +2290,7 @@ enum lws_ssl_capable_status {
#define lws_ssl_SSL_CTX_destroy(_a)
#define lws_ssl_remove_wsi_from_buffered_list(_a)
#define lws_context_init_ssl_library(_a)
#define lws_ssl_anybody_has_buffered_read_tsi(_a, _b) (0)
#else
#define LWS_SSL_ENABLED(context) (context->use_ssl)
LWS_EXTERN int openssl_websocket_private_data_index;
@ -2326,6 +2320,8 @@ LWS_EXTERN int
lws_ssl_client_connect2(struct lws *wsi);
LWS_EXTERN void
lws_ssl_elaborate_error(void);
LWS_EXTERN int
lws_ssl_anybody_has_buffered_read_tsi(struct lws_context *context, int tsi);
#ifndef LWS_NO_SERVER
LWS_EXTERN int
lws_context_init_server_ssl(struct lws_context_creation_info *info,
@ -2474,6 +2470,8 @@ lws_handshake_server(struct lws *wsi, unsigned char **buf, size_t len);
#ifdef LWS_WITH_ACCESS_LOG
LWS_EXTERN int
lws_access_log(struct lws *wsi);
LWS_EXTERN void
lws_prepare_access_log_info(struct lws *wsi, char *uri_ptr, int meth);
#else
#define lws_access_log(_a)
#endif
@ -2481,6 +2479,9 @@ lws_access_log(struct lws *wsi);
LWS_EXTERN int
lws_cgi_kill_terminated(struct lws_context_per_thread *pt);
LWS_EXTERN void
lws_cgi_remove_and_kill(struct lws *wsi);
int
lws_protocol_init(struct lws_context *context);
@ -2595,6 +2596,11 @@ void
lws_peer_track_ah_detach(struct lws_context *context, struct lws_peer *peer);
void
lws_peer_cull_peer_wait_list(struct lws_context *context);
struct lws_peer *
lws_get_or_create_peer(struct lws_vhost *vhost, lws_sockfd_type sockfd);
void
lws_peer_add_wsi(struct lws_context *context, struct lws_peer *peer,
struct lws *wsi);
#endif
#ifdef __cplusplus

172
lib/server/access-log.c Normal file
View file

@ -0,0 +1,172 @@
/*
* libwebsockets - server access log handling
*
* 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 "private-libwebsockets.h"
/*
* Produce Apache-compatible log string for wsi, like this:
*
* 2.31.234.19 - - [27/Mar/2016:03:22:44 +0800]
* "GET /aep-screen.png HTTP/1.1"
* 200 152987 "https://libwebsockets.org/index.html"
* "Mozilla/5.0 (Macint... Chrome/49.0.2623.87 Safari/537.36"
*
*/
extern const char * const method_names[];
static const char * const hver[] = {
"HTTP/1.0", "HTTP/1.1", "HTTP/2"
};
void
lws_prepare_access_log_info(struct lws *wsi, char *uri_ptr, int meth)
{
#ifdef LWS_WITH_IPV6
char ads[INET6_ADDRSTRLEN];
#else
char ads[INET_ADDRSTRLEN];
#endif
char da[64];
const char *pa, *me;
struct tm *tmp;
time_t t = time(NULL);
int l = 256, m;
if (wsi->access_log_pending)
lws_access_log(wsi);
wsi->access_log.header_log = lws_malloc(l, "access log");
if (wsi->access_log.header_log) {
tmp = localtime(&t);
if (tmp)
strftime(da, sizeof(da), "%d/%b/%Y:%H:%M:%S %z", tmp);
else
strcpy(da, "01/Jan/1970:00:00:00 +0000");
pa = lws_get_peer_simple(wsi, ads, sizeof(ads));
if (!pa)
pa = "(unknown)";
if (wsi->http2_substream)
me = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_COLON_METHOD);
else
me = method_names[meth];
if (!me)
me = "(null)";
lws_snprintf(wsi->access_log.header_log, l,
"%s - - [%s] \"%s %s %s\"",
pa, da, me, uri_ptr,
hver[wsi->u.http.request_version]);
l = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_USER_AGENT);
if (l) {
wsi->access_log.user_agent = lws_malloc(l + 2, "access log");
if (!wsi->access_log.user_agent) {
lwsl_err("OOM getting user agent\n");
lws_free_set_NULL(wsi->access_log.header_log);
return;
}
lws_hdr_copy(wsi, wsi->access_log.user_agent,
l + 1, WSI_TOKEN_HTTP_USER_AGENT);
for (m = 0; m < l; m++)
if (wsi->access_log.user_agent[m] == '\"')
wsi->access_log.user_agent[m] = '\'';
}
l = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_REFERER);
if (l) {
wsi->access_log.referrer = lws_malloc(l + 2, "referrer");
if (!wsi->access_log.referrer) {
lwsl_err("OOM getting user agent\n");
lws_free_set_NULL(wsi->access_log.user_agent);
lws_free_set_NULL(wsi->access_log.header_log);
return;
}
lws_hdr_copy(wsi, wsi->access_log.referrer,
l + 1, WSI_TOKEN_HTTP_REFERER);
for (m = 0; m < l; m++)
if (wsi->access_log.referrer[m] == '\"')
wsi->access_log.referrer[m] = '\'';
}
wsi->access_log_pending = 1;
}
}
int
lws_access_log(struct lws *wsi)
{
char *p = wsi->access_log.user_agent, ass[512],
*p1 = wsi->access_log.referrer;
int l;
if (!wsi->access_log_pending)
return 0;
if (!wsi->access_log.header_log)
return 0;
if (!p)
p = "";
if (!p1)
p1 = "";
/*
* We do this in two parts to restrict an oversize referrer such that
* we will always have space left to append an empty useragent, while
* maintaining the structure of the log text
*/
l = lws_snprintf(ass, sizeof(ass) - 7, "%s %d %lu \"%s",
wsi->access_log.header_log,
wsi->access_log.response, wsi->access_log.sent, p1);
if (strlen(p) > sizeof(ass) - 6 - l)
p[sizeof(ass) - 6 - l] = '\0';
l += lws_snprintf(ass + l, sizeof(ass) - 1 - l, "\" \"%s\"\n", p);
if (wsi->vhost->log_fd != (int)LWS_INVALID_FILE) {
if (write(wsi->vhost->log_fd, ass, l) != l)
lwsl_err("Failed to write log\n");
} else
lwsl_err("%s", ass);
if (wsi->access_log.header_log) {
lws_free(wsi->access_log.header_log);
wsi->access_log.header_log = NULL;
}
if (wsi->access_log.user_agent) {
lws_free(wsi->access_log.user_agent);
wsi->access_log.user_agent = NULL;
}
if (wsi->access_log.referrer) {
lws_free(wsi->access_log.referrer);
wsi->access_log.referrer = NULL;
}
wsi->access_log_pending = 0;
return 0;
}

1103
lib/server/cgi.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -20,7 +20,7 @@
*/
#include "private-libwebsockets.h"
#include "lejp.h"
#include "../misc/lejp.h"
#ifndef _WIN32
/* this is needed for Travis CI */

586
lib/server/lws-spa.c Normal file
View file

@ -0,0 +1,586 @@
/*
* libwebsockets - Stateful urldecode for POST
*
* 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 "private-libwebsockets.h"
#define LWS_MAX_ELEM_NAME 32
enum urldecode_stateful {
US_NAME,
US_IDLE,
US_PC1,
US_PC2,
MT_LOOK_BOUND_IN,
MT_HNAME,
MT_DISP,
MT_TYPE,
MT_IGNORE1,
MT_IGNORE2,
};
static const char * const mp_hdr[] = {
"content-disposition: ",
"content-type: ",
"\x0d\x0a"
};
typedef int (*lws_urldecode_stateful_cb)(void *data,
const char *name, char **buf, int len, int final);
struct lws_urldecode_stateful {
char *out;
void *data;
char name[LWS_MAX_ELEM_NAME];
char temp[LWS_MAX_ELEM_NAME];
char content_type[32];
char content_disp[32];
char content_disp_filename[256];
char mime_boundary[128];
int out_len;
int pos;
int hdr_idx;
int mp;
int sum;
unsigned int multipart_form_data:1;
unsigned int inside_quote:1;
unsigned int subname:1;
unsigned int boundary_real_crlf:1;
enum urldecode_stateful state;
lws_urldecode_stateful_cb output;
};
static struct lws_urldecode_stateful *
lws_urldecode_s_create(struct lws *wsi, char *out, int out_len, void *data,
lws_urldecode_stateful_cb output)
{
struct lws_urldecode_stateful *s = lws_zalloc(sizeof(*s),
"stateful urldecode");
char buf[200], *p;
int m = 0;
if (!s)
return NULL;
s->out = out;
s->out_len = out_len;
s->output = output;
s->pos = 0;
s->sum = 0;
s->mp = 0;
s->state = US_NAME;
s->name[0] = '\0';
s->data = data;
if (lws_hdr_copy(wsi, buf, sizeof(buf),
WSI_TOKEN_HTTP_CONTENT_TYPE) > 0) {
/* multipart/form-data; boundary=----WebKitFormBoundarycc7YgAPEIHvgE9Bf */
if (!strncmp(buf, "multipart/form-data", 19)) {
s->multipart_form_data = 1;
s->state = MT_LOOK_BOUND_IN;
s->mp = 2;
p = strstr(buf, "boundary=");
if (p) {
p += 9;
s->mime_boundary[m++] = '\x0d';
s->mime_boundary[m++] = '\x0a';
s->mime_boundary[m++] = '-';
s->mime_boundary[m++] = '-';
while (m < sizeof(s->mime_boundary) - 1 &&
*p && *p != ' ')
s->mime_boundary[m++] = *p++;
s->mime_boundary[m] = '\0';
lwsl_notice("boundary '%s'\n", s->mime_boundary);
}
}
}
return s;
}
static int
lws_urldecode_s_process(struct lws_urldecode_stateful *s, const char *in,
int len)
{
int n, m, hit = 0;
char c, was_end = 0;
while (len--) {
if (s->pos == s->out_len - s->mp - 1) {
if (s->output(s->data, s->name, &s->out, s->pos, 0))
return -1;
was_end = s->pos;
s->pos = 0;
}
switch (s->state) {
/* states for url arg style */
case US_NAME:
s->inside_quote = 0;
if (*in == '=') {
s->name[s->pos] = '\0';
s->pos = 0;
s->state = US_IDLE;
in++;
continue;
}
if (*in == '&') {
s->name[s->pos] = '\0';
if (s->output(s->data, s->name, &s->out,
s->pos, 1))
return -1;
s->pos = 0;
s->state = US_IDLE;
in++;
continue;
}
if (s->pos >= sizeof(s->name) - 1) {
lwsl_notice("Name too long\n");
return -1;
}
s->name[s->pos++] = *in++;
break;
case US_IDLE:
if (*in == '%') {
s->state++;
in++;
continue;
}
if (*in == '&') {
s->out[s->pos] = '\0';
if (s->output(s->data, s->name, &s->out,
s->pos, 1))
return -1;
s->pos = 0;
s->state = US_NAME;
in++;
continue;
}
if (*in == '+') {
in++;
s->out[s->pos++] = ' ';
continue;
}
s->out[s->pos++] = *in++;
break;
case US_PC1:
n = char_to_hex(*in);
if (n < 0)
return -1;
in++;
s->sum = n << 4;
s->state++;
break;
case US_PC2:
n = char_to_hex(*in);
if (n < 0)
return -1;
in++;
s->out[s->pos++] = s->sum | n;
s->state = US_IDLE;
break;
/* states for multipart / mime style */
case MT_LOOK_BOUND_IN:
retry_as_first:
if (*in == s->mime_boundary[s->mp] &&
s->mime_boundary[s->mp]) {
in++;
s->mp++;
if (!s->mime_boundary[s->mp]) {
s->mp = 0;
s->state = MT_IGNORE1;
if (s->pos || was_end)
if (s->output(s->data, s->name,
&s->out, s->pos, 1))
return -1;
s->pos = 0;
s->content_disp[0] = '\0';
s->name[0] = '\0';
s->content_disp_filename[0] = '\0';
s->boundary_real_crlf = 1;
}
continue;
}
if (s->mp) {
n = 0;
if (!s->boundary_real_crlf)
n = 2;
memcpy(s->out + s->pos, s->mime_boundary + n,
s->mp - n);
s->pos += s->mp;
s->mp = 0;
goto retry_as_first;
}
s->out[s->pos++] = *in;
in++;
s->mp = 0;
break;
case MT_HNAME:
m = 0;
c =*in;
if (c >= 'A' && c <= 'Z')
c += 'a' - 'A';
for (n = 0; n < ARRAY_SIZE(mp_hdr); n++)
if (c == mp_hdr[n][s->mp]) {
m++;
hit = n;
}
in++;
if (!m) {
s->mp = 0;
continue;
}
s->mp++;
if (m != 1)
continue;
if (mp_hdr[hit][s->mp])
continue;
s->mp = 0;
s->temp[0] = '\0';
s->subname = 0;
if (hit == 2)
s->state = MT_LOOK_BOUND_IN;
else
s->state += hit + 1;
break;
case MT_DISP:
/* form-data; name="file"; filename="t.txt" */
if (*in == '\x0d') {
if (s->content_disp_filename[0])
if (s->output(s->data, s->name,
&s->out, s->pos,
LWS_UFS_OPEN))
return -1;
s->state = MT_IGNORE2;
goto done;
}
if (*in == ';') {
s->subname = 1;
s->temp[0] = '\0';
s->mp = 0;
goto done;
}
if (*in == '\"') {
s->inside_quote ^= 1;
goto done;
}
if (s->subname) {
if (*in == '=') {
s->temp[s->mp] = '\0';
s->subname = 0;
s->mp = 0;
goto done;
}
if (s->mp < sizeof(s->temp) - 1 &&
(*in != ' ' || s->inside_quote))
s->temp[s->mp++] = *in;
goto done;
}
if (!s->temp[0]) {
if (s->mp < sizeof(s->content_disp) - 1)
s->content_disp[s->mp++] = *in;
s->content_disp[s->mp] = '\0';
goto done;
}
if (!strcmp(s->temp, "name")) {
if (s->mp < sizeof(s->name) - 1)
s->name[s->mp++] = *in;
s->name[s->mp] = '\0';
goto done;
}
if (!strcmp(s->temp, "filename")) {
if (s->mp < sizeof(s->content_disp_filename) - 1)
s->content_disp_filename[s->mp++] = *in;
s->content_disp_filename[s->mp] = '\0';
goto done;
}
done:
in++;
break;
case MT_TYPE:
if (*in == '\x0d')
s->state = MT_IGNORE2;
else {
if (s->mp < sizeof(s->content_type) - 1)
s->content_type[s->mp++] = *in;
s->content_type[s->mp] = '\0';
}
in++;
break;
case MT_IGNORE1:
if (*in == '\x0d')
s->state = MT_IGNORE2;
in++;
break;
case MT_IGNORE2:
s->mp = 0;
if (*in == '\x0a')
s->state = MT_HNAME;
in++;
break;
}
}
return 0;
}
static int
lws_urldecode_s_destroy(struct lws_urldecode_stateful *s)
{
int ret = 0;
if (s->state != US_IDLE)
ret = -1;
if (!ret)
if (s->output(s->data, s->name, &s->out, s->pos, 1))
ret = -1;
lws_free(s);
return ret;
}
struct lws_spa {
struct lws_urldecode_stateful *s;
lws_spa_fileupload_cb opt_cb;
const char * const *param_names;
int count_params;
char **params;
int *param_length;
void *opt_data;
char *storage;
char *end;
int max_storage;
char finalized;
};
static int
lws_urldecode_spa_lookup(struct lws_spa *spa,
const char *name)
{
int n;
for (n = 0; n < spa->count_params; n++)
if (!strcmp(spa->param_names[n], name))
return n;
return -1;
}
static int
lws_urldecode_spa_cb(void *data, const char *name, char **buf, int len,
int final)
{
struct lws_spa *spa =
(struct lws_spa *)data;
int n;
if (spa->s->content_disp_filename[0]) {
if (spa->opt_cb) {
n = spa->opt_cb(spa->opt_data, name,
spa->s->content_disp_filename,
*buf, len, final);
if (n < 0)
return -1;
}
return 0;
}
n = lws_urldecode_spa_lookup(spa, name);
if (n == -1 || !len) /* unrecognized */
return 0;
if (!spa->params[n])
spa->params[n] = *buf;
if ((*buf) + len >= spa->end) {
lwsl_notice("%s: exceeded storage\n", __func__);
return -1;
}
spa->param_length[n] += len;
/* move it on inside storage */
(*buf) += len;
*((*buf)++) = '\0';
spa->s->out_len -= len + 1;
return 0;
}
LWS_VISIBLE LWS_EXTERN struct lws_spa *
lws_spa_create(struct lws *wsi, const char * const *param_names,
int count_params, int max_storage,
lws_spa_fileupload_cb opt_cb, void *opt_data)
{
struct lws_spa *spa = lws_zalloc(sizeof(*spa), "spa");
if (!spa)
return NULL;
spa->param_names = param_names;
spa->count_params = count_params;
spa->max_storage = max_storage;
spa->opt_cb = opt_cb;
spa->opt_data = opt_data;
spa->storage = lws_malloc(max_storage, "spa");
if (!spa->storage)
goto bail2;
spa->end = spa->storage + max_storage - 1;
spa->params = lws_zalloc(sizeof(char *) * count_params, "spa params");
if (!spa->params)
goto bail3;
spa->s = lws_urldecode_s_create(wsi, spa->storage, max_storage, spa,
lws_urldecode_spa_cb);
if (!spa->s)
goto bail4;
spa->param_length = lws_zalloc(sizeof(int) * count_params,
"spa param len");
if (!spa->param_length)
goto bail5;
lwsl_info("%s: Created SPA %p\n", __func__, spa);
return spa;
bail5:
lws_urldecode_s_destroy(spa->s);
bail4:
lws_free(spa->params);
bail3:
lws_free(spa->storage);
bail2:
lws_free(spa);
return NULL;
}
LWS_VISIBLE LWS_EXTERN int
lws_spa_process(struct lws_spa *ludspa, const char *in, int len)
{
if (!ludspa) {
lwsl_err("%s: NULL spa\n", __func__);
return -1;
}
/* we reject any junk after the last part arrived and we finalized */
if (ludspa->finalized)
return 0;
return lws_urldecode_s_process(ludspa->s, in, len);
}
LWS_VISIBLE LWS_EXTERN int
lws_spa_get_length(struct lws_spa *ludspa, int n)
{
if (n >= ludspa->count_params)
return 0;
return ludspa->param_length[n];
}
LWS_VISIBLE LWS_EXTERN const char *
lws_spa_get_string(struct lws_spa *ludspa, int n)
{
if (n >= ludspa->count_params)
return NULL;
return ludspa->params[n];
}
LWS_VISIBLE LWS_EXTERN int
lws_spa_finalize(struct lws_spa *spa)
{
if (spa->s) {
lws_urldecode_s_destroy(spa->s);
spa->s = NULL;
}
spa->finalized = 1;
return 0;
}
LWS_VISIBLE LWS_EXTERN int
lws_spa_destroy(struct lws_spa *spa)
{
int n = 0;
lwsl_notice("%s: destroy spa %p\n", __func__, spa);
if (spa->s)
lws_urldecode_s_destroy(spa->s);
lwsl_debug("%s %p %p %p %p\n", __func__,
spa->param_length,
spa->params,
spa->storage,
spa);
lws_free(spa->param_length);
lws_free(spa->params);
lws_free(spa->storage);
lws_free(spa);
return n;
}

252
lib/server/peer-limits.c Normal file
View file

@ -0,0 +1,252 @@
/*
* libwebsockets - peer limits tracking
*
* 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 "private-libwebsockets.h"
/* requires context->lock */
static void
__lws_peer_remove_from_peer_wait_list(struct lws_context *context,
struct lws_peer *peer)
{
struct lws_peer *df;
lws_start_foreach_llp(struct lws_peer **, p, context->peer_wait_list) {
if (*p == peer) {
df = *p;
*p = df->peer_wait_list;
df->peer_wait_list = NULL;
return;
}
} lws_end_foreach_llp(p, peer_wait_list);
}
/* requires context->lock */
static void
__lws_peer_add_to_peer_wait_list(struct lws_context *context,
struct lws_peer *peer)
{
__lws_peer_remove_from_peer_wait_list(context, peer);
peer->peer_wait_list = context->peer_wait_list;
context->peer_wait_list = peer;
}
struct lws_peer *
lws_get_or_create_peer(struct lws_vhost *vhost, lws_sockfd_type sockfd)
{
struct lws_context *context = vhost->context;
socklen_t rlen = 0;
void *q;
uint8_t *q8;
struct lws_peer *peer;
uint32_t hash = 0;
int n, af = AF_INET;
struct sockaddr_storage addr;
#ifdef LWS_WITH_IPV6
if (LWS_IPV6_ENABLED(vhost)) {
af = AF_INET6;
}
#endif
rlen = sizeof(addr);
if (getpeername(sockfd, (struct sockaddr*)&addr, &rlen))
return NULL;
if (af == AF_INET) {
struct sockaddr_in *s = (struct sockaddr_in *)&addr;
q = &s->sin_addr;
rlen = sizeof(s->sin_addr);
} else
#ifdef LWS_WITH_IPV6
{
struct sockaddr_in6 *s = (struct sockaddr_in6 *)&addr;
q = &s->sin6_addr;
rlen = sizeof(s->sin6_addr);
}
#else
return NULL;
#endif
q8 = q;
for (n = 0; n < rlen; n++)
hash = (((hash << 4) | (hash >> 28)) * n) ^ q8[n];
hash = hash % context->pl_hash_elements;
lws_context_lock(context); /* <====================================== */
lws_start_foreach_ll(struct lws_peer *, peerx,
context->pl_hash_table[hash]) {
if (peerx->af == af && !memcmp(q, peerx->addr, rlen)) {
lws_context_unlock(context); /* === */
return peerx;
}
} lws_end_foreach_ll(peerx, next);
lwsl_info("%s: creating new peer\n", __func__);
peer = lws_zalloc(sizeof(*peer), "peer");
if (!peer) {
lws_context_unlock(context); /* === */
return NULL;
}
context->count_peers++;
peer->next = context->pl_hash_table[hash];
peer->hash = hash;
peer->af = af;
context->pl_hash_table[hash] = peer;
memcpy(peer->addr, q, rlen);
time(&peer->time_created);
/*
* On creation, the peer has no wsi attached, so is created on the
* wait list. When a wsi is added it is removed from the wait list.
*/
time(&peer->time_closed_all);
__lws_peer_add_to_peer_wait_list(context, peer);
lws_context_unlock(context); /* ====================================> */
return peer;
}
/* requires context->lock */
static int
__lws_peer_destroy(struct lws_context *context, struct lws_peer *peer)
{
lws_start_foreach_llp(struct lws_peer **, p,
context->pl_hash_table[peer->hash]) {
if (*p == peer) {
struct lws_peer *df = *p;
*p = df->next;
lws_free(df);
context->count_peers--;
return 0;
}
} lws_end_foreach_llp(p, next);
return 1;
}
void
lws_peer_cull_peer_wait_list(struct lws_context *context)
{
struct lws_peer *df;
time_t t;
time(&t);
if (context->next_cull && t < context->next_cull)
return;
lws_context_lock(context); /* <====================================== */
context->next_cull = t + 5;
lws_start_foreach_llp(struct lws_peer **, p, context->peer_wait_list) {
if (t - (*p)->time_closed_all > 10) {
df = *p;
/* remove us from the peer wait list */
*p = df->peer_wait_list;
df->peer_wait_list = NULL;
__lws_peer_destroy(context, df);
continue; /* we already point to next, if any */
}
} lws_end_foreach_llp(p, peer_wait_list);
lws_context_unlock(context); /* ====================================> */
}
void
lws_peer_add_wsi(struct lws_context *context, struct lws_peer *peer,
struct lws *wsi)
{
if (!peer)
return;
lws_context_lock(context); /* <====================================== */
peer->count_wsi++;
wsi->peer = peer;
__lws_peer_remove_from_peer_wait_list(context, peer);
lws_context_unlock(context); /* ====================================> */
}
void
lws_peer_track_wsi_close(struct lws_context *context, struct lws_peer *peer)
{
if (!peer)
return;
lws_context_lock(context); /* <====================================== */
assert(peer->count_wsi);
peer->count_wsi--;
if (!peer->count_wsi && !peer->count_ah) {
/*
* in order that we can accumulate peer activity correctly
* allowing for periods when the peer has no connections,
* we don't synchronously destroy the peer when his last
* wsi closes. Instead we mark the time his last wsi
* closed and add him to a peer_wait_list to be reaped
* later if no further activity is coming.
*/
time(&peer->time_closed_all);
__lws_peer_add_to_peer_wait_list(context, peer);
}
lws_context_unlock(context); /* ====================================> */
}
int
lws_peer_confirm_ah_attach_ok(struct lws_context *context, struct lws_peer *peer)
{
if (!peer)
return 0;
if (context->ip_limit_ah && peer->count_ah >= context->ip_limit_ah) {
lwsl_info("peer reached ah limit %d, deferring\n",
context->ip_limit_ah);
return 1;
}
return 0;
}
void
lws_peer_track_ah_detach(struct lws_context *context, struct lws_peer *peer)
{
if (!peer)
return;
assert(peer->count_ah);
peer->count_ah--;
}

File diff suppressed because it is too large Load diff

View file

@ -383,6 +383,26 @@ lws_ssl_destroy(struct lws_vhost *vhost)
#endif
}
int
lws_ssl_anybody_has_buffered_read_tsi(struct lws_context *context, int tsi)
{
struct lws_context_per_thread *pt = &context->pt[tsi];
struct lws *wsi, *wsi_next;
wsi = pt->pending_read_list;
while (wsi) {
wsi_next = wsi->pending_read_list_next;
pt->fds[wsi->position_in_fds_table].revents |=
pt->fds[wsi->position_in_fds_table].events & LWS_POLLIN;
if (pt->fds[wsi->position_in_fds_table].revents & LWS_POLLIN)
return 1;
wsi = wsi_next;
}
return 0;
}
LWS_VISIBLE void
lws_ssl_remove_wsi_from_buffered_list(struct lws *wsi)
{