mirror of
https://github.com/cesanta/mongoose.git
synced 2025-08-06 13:37:34 +08:00
Merge branch 'dev'
This commit is contained in:
commit
ab650ec5c9
@ -4,9 +4,12 @@ symbol_kind: "intro"
|
||||
decl_name: "mbuf.h"
|
||||
items:
|
||||
- { name: mbuf_append.md }
|
||||
- { name: mbuf_append_and_free.md }
|
||||
- { name: mbuf_clear.md }
|
||||
- { name: mbuf_free.md }
|
||||
- { name: mbuf_init.md }
|
||||
- { name: mbuf_insert.md }
|
||||
- { name: mbuf_move.md }
|
||||
- { name: mbuf_remove.md }
|
||||
- { name: mbuf_resize.md }
|
||||
- { name: mbuf_trim.md }
|
||||
|
13
docs/c-api/mbuf.h/mbuf_append_and_free.md
Normal file
13
docs/c-api/mbuf.h/mbuf_append_and_free.md
Normal file
@ -0,0 +1,13 @@
|
||||
---
|
||||
title: "mbuf_append_and_free()"
|
||||
decl_name: "mbuf_append_and_free"
|
||||
symbol_kind: "func"
|
||||
signature: |
|
||||
size_t mbuf_append_and_free(struct mbuf *, void *data, size_t data_size);
|
||||
---
|
||||
|
||||
Appends data to the Mbuf and frees it (data must be heap-allocated).
|
||||
|
||||
Returns the number of bytes appended or 0 if out of memory.
|
||||
data is freed irrespective of return value.
|
||||
|
10
docs/c-api/mbuf.h/mbuf_clear.md
Normal file
10
docs/c-api/mbuf.h/mbuf_clear.md
Normal file
@ -0,0 +1,10 @@
|
||||
---
|
||||
title: "mbuf_clear()"
|
||||
decl_name: "mbuf_clear"
|
||||
symbol_kind: "func"
|
||||
signature: |
|
||||
void mbuf_clear(struct mbuf *);
|
||||
---
|
||||
|
||||
Removes all the data from mbuf (if any).
|
||||
|
10
docs/c-api/mbuf.h/mbuf_move.md
Normal file
10
docs/c-api/mbuf.h/mbuf_move.md
Normal file
@ -0,0 +1,10 @@
|
||||
---
|
||||
title: "mbuf_move()"
|
||||
decl_name: "mbuf_move"
|
||||
symbol_kind: "func"
|
||||
signature: |
|
||||
void mbuf_move(struct mbuf *from, struct mbuf *to);
|
||||
---
|
||||
|
||||
Moves the state from one mbuf to the other.
|
||||
|
@ -30,7 +30,9 @@ The user-defined event handler will receive following extra events:
|
||||
- MG_EV_WEBSOCKET_HANDSHAKE_REQUEST: server has received the WebSocket
|
||||
handshake request. `ev_data` contains parsed HTTP request.
|
||||
- MG_EV_WEBSOCKET_HANDSHAKE_DONE: server has completed the WebSocket
|
||||
handshake. `ev_data` is `NULL`.
|
||||
handshake. `ev_data` is a `struct http_message` containing the
|
||||
client's request (server mode) or server's response (client).
|
||||
In client mode handler can examine `resp_code`, which should be 101.
|
||||
- MG_EV_WEBSOCKET_FRAME: new WebSocket frame has arrived. `ev_data` is
|
||||
`struct websocket_message *`
|
||||
|
||||
|
@ -9,6 +9,14 @@ signature: |
|
||||
struct mg_str data;
|
||||
int status; /* <0 on error */
|
||||
void *user_data;
|
||||
/*
|
||||
* User handler can indicate how much of the data was consumed
|
||||
* by setting this variable. By default, it is assumed that all
|
||||
* data has been consumed by the handler.
|
||||
* If not all data was consumed, user's handler will be invoked again later
|
||||
* with the remainder.
|
||||
*/
|
||||
size_t num_data_consumed;
|
||||
};
|
||||
---
|
||||
|
||||
|
@ -16,5 +16,5 @@ that can be, and must be, called from a different (non-IO) thread.
|
||||
`func` callback function will be called by the IO thread for each
|
||||
connection. When called, the event will be `MG_EV_POLL`, and a message will
|
||||
be passed as the `ev_data` pointer. Maximum message size is capped
|
||||
by `MG_CTL_MSG_MESSAGE_SIZE` which is set to 8192 bytes.
|
||||
by `MG_CTL_MSG_MESSAGE_SIZE` which is set to 8192 bytes by default.
|
||||
|
||||
|
@ -46,6 +46,7 @@ signature: |
|
||||
#define MG_F_WANT_READ (1 << 6) /* SSL specific */
|
||||
#define MG_F_WANT_WRITE (1 << 7) /* SSL specific */
|
||||
#define MG_F_IS_WEBSOCKET (1 << 8) /* Websocket specific */
|
||||
#define MG_F_RECV_AND_CLOSE (1 << 9) /* Drain rx and close the connection. */
|
||||
|
||||
/* Flags that are settable by user */
|
||||
#define MG_F_SEND_AND_CLOSE (1 << 10) /* Push remaining data and close */
|
||||
|
@ -98,7 +98,8 @@ bool bm222_get_data(struct bm222_ctx *ctx) {
|
||||
s->y = v[3];
|
||||
s->z = v[5];
|
||||
if (d > max_d) max_d = d;
|
||||
LOG(LL_VERBOSE_DEBUG, ("dx %d dy %d dz %d d %d", dx, dy, dz, d));
|
||||
LOG(LL_VERBOSE_DEBUG,
|
||||
("dx %d dy %d dz %d d %d", (int) dx, (int) dy, (int) dz, (int) d));
|
||||
}
|
||||
}
|
||||
return (overflow ? bm222_fifo_init(ctx) : true); /* Clear the ovf flag. */
|
||||
|
@ -1,11 +1,25 @@
|
||||
/*
|
||||
* Copyright (c) 2014-2016 Cesanta Software Limited
|
||||
* Copyright (c) 2014-2018 Cesanta Software Limited
|
||||
* All rights reserved
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the ""License"");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an ""AS IS"" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef CS_COMMON_CS_DBG_H_
|
||||
#define CS_COMMON_CS_DBG_H_
|
||||
|
||||
#include "common/platform.h"
|
||||
|
||||
#if CS_ENABLE_STDIO
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
@ -14,6 +28,10 @@
|
||||
#define CS_ENABLE_DEBUG 0
|
||||
#endif
|
||||
|
||||
#ifndef CS_LOG_PREFIX_LEN
|
||||
#define CS_LOG_PREFIX_LEN 24
|
||||
#endif
|
||||
|
||||
#ifndef CS_LOG_ENABLE_TS_DIFF
|
||||
#define CS_LOG_ENABLE_TS_DIFF 0
|
||||
#endif
|
||||
@ -22,6 +40,9 @@
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/*
|
||||
* Log level; `LL_INFO` is the default. Use `cs_log_set_level()` to change it.
|
||||
*/
|
||||
enum cs_log_level {
|
||||
LL_NONE = -1,
|
||||
LL_ERROR = 0,
|
||||
@ -34,29 +55,78 @@ enum cs_log_level {
|
||||
_LL_MAX = 5,
|
||||
};
|
||||
|
||||
/*
|
||||
* Set max log level to print; messages with the level above the given one will
|
||||
* not be printed.
|
||||
*/
|
||||
void cs_log_set_level(enum cs_log_level level);
|
||||
|
||||
/*
|
||||
* A comma-separated set of prefix=level.
|
||||
* prefix is matched against the log prefix exactly as printed, including line
|
||||
* number, but partial match is ok. Check stops on first matching entry.
|
||||
* If nothing matches, default level is used.
|
||||
*
|
||||
* Examples:
|
||||
* main.c:=4 - everything from main C at verbose debug level.
|
||||
* mongoose.c=1,mjs.c=1,=4 - everything at verbose debug except mg_* and mjs_*
|
||||
*
|
||||
*/
|
||||
void cs_log_set_file_level(const char *file_level);
|
||||
|
||||
/*
|
||||
* Helper function which prints message prefix with the given `level`.
|
||||
* If message should be printed (according to the current log level
|
||||
* and filter), prints the prefix and returns 1, otherwise returns 0.
|
||||
*
|
||||
* Clients should typically just use `LOG()` macro.
|
||||
*/
|
||||
int cs_log_print_prefix(enum cs_log_level level, const char *fname, int line);
|
||||
|
||||
extern enum cs_log_level cs_log_level;
|
||||
|
||||
#if CS_ENABLE_STDIO
|
||||
|
||||
/*
|
||||
* Set file to write logs into. If `NULL`, logs go to `stderr`.
|
||||
*/
|
||||
void cs_log_set_file(FILE *file);
|
||||
|
||||
extern enum cs_log_level cs_log_threshold;
|
||||
void cs_log_print_prefix(const char *func);
|
||||
void cs_log_printf(const char *fmt, ...);
|
||||
/*
|
||||
* Prints log to the current log file, appends "\n" in the end and flushes the
|
||||
* stream.
|
||||
*/
|
||||
void cs_log_printf(const char *fmt, ...) PRINTF_LIKE(1, 2);
|
||||
|
||||
#define LOG(l, x) \
|
||||
if (cs_log_threshold >= l) { \
|
||||
cs_log_print_prefix(__func__); \
|
||||
cs_log_printf x; \
|
||||
}
|
||||
#if CS_ENABLE_STDIO
|
||||
|
||||
/*
|
||||
* Format and print message `x` with the given level `l`. Example:
|
||||
*
|
||||
* ```c
|
||||
* LOG(LL_INFO, ("my info message: %d", 123));
|
||||
* LOG(LL_DEBUG, ("my debug message: %d", 123));
|
||||
* ```
|
||||
*/
|
||||
#define LOG(l, x) \
|
||||
do { \
|
||||
if (cs_log_print_prefix(l, __FILE__, __LINE__)) { \
|
||||
cs_log_printf x; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#else
|
||||
|
||||
#define LOG(l, x) ((void) l)
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef CS_NDEBUG
|
||||
|
||||
#define DBG(x) \
|
||||
if (cs_log_threshold >= LL_VERBOSE_DEBUG) { \
|
||||
cs_log_print_prefix(__func__); \
|
||||
cs_log_printf x; \
|
||||
}
|
||||
/*
|
||||
* Shortcut for `LOG(LL_VERBOSE_DEBUG, (...))`
|
||||
*/
|
||||
#define DBG(x) LOG(LL_VERBOSE_DEBUG, x)
|
||||
|
||||
#else /* NDEBUG */
|
||||
|
||||
|
@ -50,7 +50,7 @@
|
||||
#define BM222_ADDR 0x18
|
||||
#define TMP006_ADDR 0x41
|
||||
|
||||
void fs_slfs_set_new_file_size(const char *name, size_t size);
|
||||
void fs_slfs_set_file_size(const char *name, size_t size);
|
||||
|
||||
static const char *upload_form =
|
||||
"\
|
||||
@ -70,7 +70,7 @@ static struct mg_str upload_fname(struct mg_connection *nc,
|
||||
if (nc->user_data != NULL) {
|
||||
intptr_t cl = (intptr_t) nc->user_data;
|
||||
if (cl >= 0) {
|
||||
fs_slfs_set_new_file_size(fn + 3, cl);
|
||||
fs_slfs_set_file_size(fn + 3, cl);
|
||||
}
|
||||
}
|
||||
lfn.len = fname.len + 4;
|
||||
|
@ -25,7 +25,7 @@ void SimpleLinkWlanEventHandler(SlWlanEvent_t *e) {
|
||||
LOG(LL_INFO, ("WiFi: station disconnected"));
|
||||
break;
|
||||
default:
|
||||
LOG(LL_INFO, ("WiFi: event %d", e->Event));
|
||||
LOG(LL_INFO, ("WiFi: event %d", (int) e->Event));
|
||||
}
|
||||
}
|
||||
|
||||
@ -39,7 +39,7 @@ void SimpleLinkNetAppEventHandler(SlNetAppEvent_t *e) {
|
||||
} else if (e->Event == SL_NETAPP_IP_LEASED_EVENT) {
|
||||
LOG(LL_INFO, ("IP leased"));
|
||||
} else {
|
||||
LOG(LL_INFO, ("NetApp event %d", e->Event));
|
||||
LOG(LL_INFO, ("NetApp event %d", (int) e->Event));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14,7 +14,6 @@ static int s_done = 0;
|
||||
static int s_is_connected = 0;
|
||||
|
||||
static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
|
||||
struct websocket_message *wm = (struct websocket_message *) ev_data;
|
||||
(void) nc;
|
||||
|
||||
switch (ev) {
|
||||
@ -26,8 +25,14 @@ static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
|
||||
break;
|
||||
}
|
||||
case MG_EV_WEBSOCKET_HANDSHAKE_DONE: {
|
||||
printf("-- Connected\n");
|
||||
s_is_connected = 1;
|
||||
struct http_message *hm = (struct http_message *) ev_data;
|
||||
if (hm->resp_code == 101) {
|
||||
printf("-- Connected\n");
|
||||
s_is_connected = 1;
|
||||
} else {
|
||||
printf("-- Connection failed! HTTP code %d\n", hm->resp_code);
|
||||
/* Connection will be closed after this. */
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MG_EV_POLL: {
|
||||
@ -66,6 +71,7 @@ static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
|
||||
break;
|
||||
}
|
||||
case MG_EV_WEBSOCKET_FRAME: {
|
||||
struct websocket_message *wm = (struct websocket_message *) ev_data;
|
||||
printf("%.*s\n", (int) wm->size, wm->data);
|
||||
break;
|
||||
}
|
||||
|
506
mongoose.c
506
mongoose.c
@ -38,7 +38,9 @@
|
||||
/* Amalgamated: #include "mg_http.h" */
|
||||
/* Amalgamated: #include "mg_net.h" */
|
||||
|
||||
#ifndef MG_CTL_MSG_MESSAGE_SIZE
|
||||
#define MG_CTL_MSG_MESSAGE_SIZE 8192
|
||||
#endif
|
||||
|
||||
/* internals that need to be accessible in unit tests */
|
||||
MG_INTERNAL struct mg_connection *mg_do_connect(struct mg_connection *nc,
|
||||
@ -460,6 +462,10 @@ int cs_base64_decode(const unsigned char *s, int len, char *dst, int *dec_len) {
|
||||
#define CS_ENABLE_DEBUG 0
|
||||
#endif
|
||||
|
||||
#ifndef CS_LOG_PREFIX_LEN
|
||||
#define CS_LOG_PREFIX_LEN 24
|
||||
#endif
|
||||
|
||||
#ifndef CS_LOG_ENABLE_TS_DIFF
|
||||
#define CS_LOG_ENABLE_TS_DIFF 0
|
||||
#endif
|
||||
@ -490,51 +496,28 @@ enum cs_log_level {
|
||||
void cs_log_set_level(enum cs_log_level level);
|
||||
|
||||
/*
|
||||
* Set log filter. NULL (a default) logs everything.
|
||||
* Otherwise, function name and file name will be tested against the given
|
||||
* pattern, and only matching messages will be printed.
|
||||
* A comma-separated set of prefix=level.
|
||||
* prefix is matched against the log prefix exactly as printed, including line
|
||||
* number, but partial match is ok. Check stops on first matching entry.
|
||||
* If nothing matches, default level is used.
|
||||
*
|
||||
* For the pattern syntax, refer to `mg_match_prefix()` in `str_util.h`.
|
||||
* Examples:
|
||||
* main.c:=4 - everything from main C at verbose debug level.
|
||||
* mongoose.c=1,mjs.c=1,=4 - everything at verbose debug except mg_* and mjs_*
|
||||
*
|
||||
* Example:
|
||||
* ```c
|
||||
* void foo(void) {
|
||||
* LOG(LL_INFO, ("hello from foo"));
|
||||
* }
|
||||
*
|
||||
* void bar(void) {
|
||||
* LOG(LL_INFO, ("hello from bar"));
|
||||
* }
|
||||
*
|
||||
* void test(void) {
|
||||
* cs_log_set_filter(NULL);
|
||||
* foo();
|
||||
* bar();
|
||||
*
|
||||
* cs_log_set_filter("f*");
|
||||
* foo();
|
||||
* bar(); // Will NOT print anything
|
||||
*
|
||||
* cs_log_set_filter("bar");
|
||||
* foo(); // Will NOT print anything
|
||||
* bar();
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
void cs_log_set_filter(const char *pattern);
|
||||
void cs_log_set_file_level(const char *file_level);
|
||||
|
||||
/*
|
||||
* Helper function which prints message prefix with the given `level`, function
|
||||
* name `func` and `filename`. If message should be printed (accordingly to the
|
||||
* current log level and filter), prints the prefix and returns 1, otherwise
|
||||
* returns 0.
|
||||
* Helper function which prints message prefix with the given `level`.
|
||||
* If message should be printed (according to the current log level
|
||||
* and filter), prints the prefix and returns 1, otherwise returns 0.
|
||||
*
|
||||
* Clients should typically just use `LOG()` macro.
|
||||
*/
|
||||
int cs_log_print_prefix(enum cs_log_level level, const char *func,
|
||||
const char *filename);
|
||||
int cs_log_print_prefix(enum cs_log_level level, const char *fname, int line);
|
||||
|
||||
extern enum cs_log_level cs_log_threshold;
|
||||
extern enum cs_log_level cs_log_level;
|
||||
|
||||
#if CS_ENABLE_STDIO
|
||||
|
||||
@ -559,9 +542,11 @@ void cs_log_printf(const char *fmt, ...) PRINTF_LIKE(1, 2);
|
||||
* LOG(LL_DEBUG, ("my debug message: %d", 123));
|
||||
* ```
|
||||
*/
|
||||
#define LOG(l, x) \
|
||||
do { \
|
||||
if (cs_log_print_prefix(l, __func__, __FILE__)) cs_log_printf x; \
|
||||
#define LOG(l, x) \
|
||||
do { \
|
||||
if (cs_log_print_prefix(l, __FILE__, __LINE__)) { \
|
||||
cs_log_printf x; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#else
|
||||
@ -624,7 +609,7 @@ void cs_log_printf(const char *fmt, ...) PRINTF_LIKE(1, 2);
|
||||
/* Amalgamated: #include "common/cs_time.h" */
|
||||
/* Amalgamated: #include "common/str_util.h" */
|
||||
|
||||
enum cs_log_level cs_log_threshold WEAK =
|
||||
enum cs_log_level cs_log_level WEAK =
|
||||
#if CS_ENABLE_DEBUG
|
||||
LL_VERBOSE_DEBUG;
|
||||
#else
|
||||
@ -632,10 +617,9 @@ enum cs_log_level cs_log_threshold WEAK =
|
||||
#endif
|
||||
|
||||
#if CS_ENABLE_STDIO
|
||||
static char *s_filter_pattern = NULL;
|
||||
static size_t s_filter_pattern_len;
|
||||
static char *s_file_level = NULL;
|
||||
|
||||
void cs_log_set_filter(const char *pattern) WEAK;
|
||||
void cs_log_set_file_level(const char *file_level) WEAK;
|
||||
|
||||
FILE *cs_log_file WEAK = NULL;
|
||||
|
||||
@ -645,34 +629,62 @@ double cs_log_ts WEAK;
|
||||
|
||||
enum cs_log_level cs_log_cur_msg_level WEAK = LL_NONE;
|
||||
|
||||
void cs_log_set_filter(const char *pattern) {
|
||||
free(s_filter_pattern);
|
||||
if (pattern != NULL) {
|
||||
s_filter_pattern = strdup(pattern);
|
||||
s_filter_pattern_len = strlen(pattern);
|
||||
void cs_log_set_file_level(const char *file_level) {
|
||||
char *fl = s_file_level;
|
||||
if (file_level != NULL) {
|
||||
s_file_level = strdup(file_level);
|
||||
} else {
|
||||
s_filter_pattern = NULL;
|
||||
s_filter_pattern_len = 0;
|
||||
s_file_level = NULL;
|
||||
}
|
||||
free(fl);
|
||||
}
|
||||
|
||||
int cs_log_print_prefix(enum cs_log_level, const char *, const char *) WEAK;
|
||||
int cs_log_print_prefix(enum cs_log_level level, const char *func,
|
||||
const char *filename) {
|
||||
char prefix[21];
|
||||
int cs_log_print_prefix(enum cs_log_level level, const char *file, int ln) WEAK;
|
||||
int cs_log_print_prefix(enum cs_log_level level, const char *file, int ln) {
|
||||
char prefix[CS_LOG_PREFIX_LEN], *q;
|
||||
const char *p;
|
||||
size_t fl = 0, ll = 0, pl = 0;
|
||||
|
||||
if (level > cs_log_threshold) return 0;
|
||||
if (s_filter_pattern != NULL &&
|
||||
mg_match_prefix(s_filter_pattern, s_filter_pattern_len, func) == 0 &&
|
||||
mg_match_prefix(s_filter_pattern, s_filter_pattern_len, filename) == 0) {
|
||||
return 0;
|
||||
if (level > cs_log_level && s_file_level == NULL) return 0;
|
||||
|
||||
p = file + strlen(file);
|
||||
|
||||
while (p != file) {
|
||||
const char c = *(p - 1);
|
||||
if (c == '/' || c == '\\') break;
|
||||
p--;
|
||||
fl++;
|
||||
}
|
||||
|
||||
ll = (ln < 10000 ? (ln < 1000 ? (ln < 100 ? (ln < 10 ? 1 : 2) : 3) : 4) : 5);
|
||||
if (fl > (sizeof(prefix) - ll - 2)) fl = (sizeof(prefix) - ll - 2);
|
||||
|
||||
pl = fl + 1 + ll;
|
||||
memcpy(prefix, p, fl);
|
||||
q = prefix + pl;
|
||||
memset(q, ' ', sizeof(prefix) - pl);
|
||||
do {
|
||||
*(--q) = '0' + (ln % 10);
|
||||
ln /= 10;
|
||||
} while (ln > 0);
|
||||
*(--q) = ':';
|
||||
|
||||
if (s_file_level != NULL) {
|
||||
enum cs_log_level pll = cs_log_level;
|
||||
struct mg_str fl = mg_mk_str(s_file_level), ps = MG_MK_STR_N(prefix, pl);
|
||||
struct mg_str k, v;
|
||||
while ((fl = mg_next_comma_list_entry_n(fl, &k, &v)).p != NULL) {
|
||||
bool yes = !(!mg_str_starts_with(ps, k) || v.len == 0);
|
||||
if (!yes) continue;
|
||||
pll = (enum cs_log_level)(*v.p - '0');
|
||||
break;
|
||||
}
|
||||
if (level > pll) return 0;
|
||||
}
|
||||
|
||||
strncpy(prefix, func, 20);
|
||||
prefix[20] = '\0';
|
||||
if (cs_log_file == NULL) cs_log_file = stderr;
|
||||
cs_log_cur_msg_level = level;
|
||||
fprintf(cs_log_file, "%-20s ", prefix);
|
||||
fwrite(prefix, 1, sizeof(prefix), cs_log_file);
|
||||
#if CS_LOG_ENABLE_TS_DIFF
|
||||
{
|
||||
double now = cs_time();
|
||||
@ -701,15 +713,15 @@ void cs_log_set_file(FILE *file) {
|
||||
|
||||
#else
|
||||
|
||||
void cs_log_set_filter(const char *pattern) {
|
||||
(void) pattern;
|
||||
void cs_log_set_file_level(const char *file_level) {
|
||||
(void) file_level;
|
||||
}
|
||||
|
||||
#endif /* CS_ENABLE_STDIO */
|
||||
|
||||
void cs_log_set_level(enum cs_log_level level) WEAK;
|
||||
void cs_log_set_level(enum cs_log_level level) {
|
||||
cs_log_threshold = level;
|
||||
cs_log_level = level;
|
||||
#if CS_LOG_ENABLE_TS_DIFF && CS_ENABLE_STDIO
|
||||
cs_log_ts = cs_time();
|
||||
#endif
|
||||
@ -1612,6 +1624,22 @@ size_t mbuf_append(struct mbuf *a, const void *buf, size_t len) {
|
||||
return mbuf_insert(a, a->len, buf, len);
|
||||
}
|
||||
|
||||
size_t mbuf_append_and_free(struct mbuf *a, void *buf, size_t len) WEAK;
|
||||
size_t mbuf_append_and_free(struct mbuf *a, void *data, size_t len) {
|
||||
size_t ret;
|
||||
/* Optimization: if the buffer is currently empty,
|
||||
* take over the user-provided buffer. */
|
||||
if (a->len == 0) {
|
||||
if (a->buf != NULL) free(a->buf);
|
||||
a->buf = (char *) data;
|
||||
a->len = a->size = len;
|
||||
return len;
|
||||
}
|
||||
ret = mbuf_insert(a, a->len, data, len);
|
||||
free(data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void mbuf_remove(struct mbuf *mb, size_t n) WEAK;
|
||||
void mbuf_remove(struct mbuf *mb, size_t n) {
|
||||
if (n > 0 && n <= mb->len) {
|
||||
@ -1620,6 +1648,17 @@ void mbuf_remove(struct mbuf *mb, size_t n) {
|
||||
}
|
||||
}
|
||||
|
||||
void mbuf_clear(struct mbuf *mb) WEAK;
|
||||
void mbuf_clear(struct mbuf *mb) {
|
||||
mb->len = 0;
|
||||
}
|
||||
|
||||
void mbuf_move(struct mbuf *from, struct mbuf *to) WEAK;
|
||||
void mbuf_move(struct mbuf *from, struct mbuf *to) {
|
||||
memcpy(to, from, sizeof(*to));
|
||||
memset(from, 0, sizeof(*from));
|
||||
}
|
||||
|
||||
#endif /* EXCLUDE_COMMON */
|
||||
#ifdef MG_MODULE_LINES
|
||||
#line 1 "common/mg_str.c"
|
||||
@ -1645,6 +1684,7 @@ void mbuf_remove(struct mbuf *mb, size_t n) {
|
||||
/* Amalgamated: #include "common/mg_str.h" */
|
||||
/* Amalgamated: #include "common/platform.h" */
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
@ -1744,6 +1784,14 @@ int mg_strncmp(const struct mg_str str1, const struct mg_str str2, size_t n) {
|
||||
return mg_strcmp(s1, s2);
|
||||
}
|
||||
|
||||
void mg_strfree(struct mg_str *s) WEAK;
|
||||
void mg_strfree(struct mg_str *s) {
|
||||
char *sp = (char *) s->p;
|
||||
s->p = NULL;
|
||||
s->len = 0;
|
||||
if (sp != NULL) free(sp);
|
||||
}
|
||||
|
||||
const char *mg_strstr(const struct mg_str haystack,
|
||||
const struct mg_str needle) WEAK;
|
||||
const char *mg_strstr(const struct mg_str haystack,
|
||||
@ -1769,6 +1817,13 @@ struct mg_str mg_strstrip(struct mg_str s) {
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
int mg_str_starts_with(struct mg_str s, struct mg_str prefix) WEAK;
|
||||
int mg_str_starts_with(struct mg_str s, struct mg_str prefix) {
|
||||
const struct mg_str sp = MG_MK_STR_N(s.p, prefix.len);
|
||||
if (s.len < prefix.len) return 0;
|
||||
return (mg_strcmp(sp, prefix) == 0);
|
||||
}
|
||||
#ifdef MG_MODULE_LINES
|
||||
#line 1 "common/str_util.c"
|
||||
#endif
|
||||
@ -2437,8 +2492,16 @@ MG_INTERNAL size_t recv_avail_size(struct mg_connection *conn, size_t max) {
|
||||
static int mg_do_recv(struct mg_connection *nc);
|
||||
|
||||
int mg_if_poll(struct mg_connection *nc, double now) {
|
||||
if ((nc->flags & MG_F_CLOSE_IMMEDIATELY) ||
|
||||
(nc->send_mbuf.len == 0 && (nc->flags & MG_F_SEND_AND_CLOSE))) {
|
||||
if (nc->flags & MG_F_CLOSE_IMMEDIATELY) {
|
||||
mg_close_conn(nc);
|
||||
return 0;
|
||||
} else if (nc->flags & MG_F_SEND_AND_CLOSE) {
|
||||
if (nc->send_mbuf.len == 0) {
|
||||
nc->flags |= MG_F_CLOSE_IMMEDIATELY;
|
||||
mg_close_conn(nc);
|
||||
return 0;
|
||||
}
|
||||
} else if (nc->flags & MG_F_RECV_AND_CLOSE) {
|
||||
mg_close_conn(nc);
|
||||
return 0;
|
||||
}
|
||||
@ -2481,6 +2544,13 @@ void mg_destroy_conn(struct mg_connection *conn, int destroy_if) {
|
||||
}
|
||||
|
||||
void mg_close_conn(struct mg_connection *conn) {
|
||||
/* See if there's any remaining data to deliver. Skip if user completely
|
||||
* throttled the connection there will be no progress anyway. */
|
||||
if (conn->sock != INVALID_SOCKET && mg_do_recv(conn) == -2) {
|
||||
/* Receive is throttled, wait. */
|
||||
conn->flags |= MG_F_RECV_AND_CLOSE;
|
||||
return;
|
||||
}
|
||||
#if MG_ENABLE_SSL
|
||||
if (conn->flags & MG_F_SSL_HANDSHAKE_DONE) {
|
||||
mg_ssl_if_conn_close_notify(conn);
|
||||
@ -2571,6 +2641,7 @@ void mg_mgr_free(struct mg_mgr *m) {
|
||||
|
||||
for (conn = m->active_connections; conn != NULL; conn = tmp_conn) {
|
||||
tmp_conn = conn->next;
|
||||
conn->flags |= MG_F_CLOSE_IMMEDIATELY;
|
||||
mg_close_conn(conn);
|
||||
}
|
||||
|
||||
@ -2874,18 +2945,23 @@ static int mg_do_recv(struct mg_connection *nc) {
|
||||
((nc->flags & MG_F_LISTENING) && !(nc->flags & MG_F_UDP))) {
|
||||
return -1;
|
||||
}
|
||||
len = recv_avail_size(nc, len);
|
||||
if (len == 0) return -2;
|
||||
if (nc->recv_mbuf.size < nc->recv_mbuf.len + len) {
|
||||
mbuf_resize(&nc->recv_mbuf, nc->recv_mbuf.len + len);
|
||||
}
|
||||
buf = nc->recv_mbuf.buf + nc->recv_mbuf.len;
|
||||
len = nc->recv_mbuf.size - nc->recv_mbuf.len;
|
||||
if (nc->flags & MG_F_UDP) {
|
||||
res = mg_recv_udp(nc, buf, len);
|
||||
} else {
|
||||
res = mg_recv_tcp(nc, buf, len);
|
||||
}
|
||||
do {
|
||||
len = recv_avail_size(nc, len);
|
||||
if (len == 0) {
|
||||
res = -2;
|
||||
break;
|
||||
}
|
||||
if (nc->recv_mbuf.size < nc->recv_mbuf.len + len) {
|
||||
mbuf_resize(&nc->recv_mbuf, nc->recv_mbuf.len + len);
|
||||
}
|
||||
buf = nc->recv_mbuf.buf + nc->recv_mbuf.len;
|
||||
len = nc->recv_mbuf.size - nc->recv_mbuf.len;
|
||||
if (nc->flags & MG_F_UDP) {
|
||||
res = mg_recv_udp(nc, buf, len);
|
||||
} else {
|
||||
res = mg_recv_tcp(nc, buf, len);
|
||||
}
|
||||
} while (res > 0 && !(nc->flags & (MG_F_CLOSE_IMMEDIATELY | MG_F_UDP)));
|
||||
return res;
|
||||
}
|
||||
|
||||
@ -3002,7 +3078,9 @@ static int mg_recv_udp(struct mg_connection *nc, char *buf, size_t len) {
|
||||
mg_hexdump_connection(nc, nc->mgr->hexdump_file, buf, n, MG_EV_RECV);
|
||||
}
|
||||
#endif
|
||||
mg_call(nc, NULL, nc->user_data, MG_EV_RECV, &n);
|
||||
if (n != 0) {
|
||||
mg_call(nc, NULL, nc->user_data, MG_EV_RECV, &n);
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
@ -5884,6 +5962,7 @@ struct mg_http_multipart_stream {
|
||||
void *user_data;
|
||||
enum mg_http_multipart_stream_state state;
|
||||
int processing_part;
|
||||
int data_avail;
|
||||
};
|
||||
|
||||
struct mg_reverse_proxy_data {
|
||||
@ -6502,9 +6581,9 @@ void mg_http_handler(struct mg_connection *nc, int ev,
|
||||
if (io->len > 0 &&
|
||||
(req_len = mg_parse_http(io->buf, io->len, hm, is_req)) > 0) {
|
||||
/*
|
||||
* For HTTP messages without Content-Length, always send HTTP message
|
||||
* before MG_EV_CLOSE message.
|
||||
*/
|
||||
* For HTTP messages without Content-Length, always send HTTP message
|
||||
* before MG_EV_CLOSE message.
|
||||
*/
|
||||
int ev2 = is_req ? MG_EV_HTTP_REQUEST : MG_EV_HTTP_REPLY;
|
||||
hm->message.len = io->len;
|
||||
hm->body.len = io->buf + io->len - hm->body.p;
|
||||
@ -6512,6 +6591,9 @@ void mg_http_handler(struct mg_connection *nc, int ev,
|
||||
mg_http_call_endpoint_handler(nc, ev2, hm);
|
||||
}
|
||||
pd->rcvd = 0;
|
||||
if (pd->endpoint_handler != NULL && pd->endpoint_handler != nc->handler) {
|
||||
mg_call(nc, pd->endpoint_handler, nc->user_data, ev, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
#if MG_ENABLE_FILESYSTEM
|
||||
@ -6522,17 +6604,24 @@ void mg_http_handler(struct mg_connection *nc, int ev,
|
||||
|
||||
mg_call(nc, nc->handler, nc->user_data, ev, ev_data);
|
||||
|
||||
#if MG_ENABLE_HTTP_STREAMING_MULTIPART
|
||||
if (pd->mp_stream.boundary != NULL &&
|
||||
(ev == MG_EV_RECV || ev == MG_EV_POLL)) {
|
||||
if (ev == MG_EV_RECV) {
|
||||
pd->rcvd += *(int *) ev_data;
|
||||
mg_http_multipart_continue(nc);
|
||||
} else if (pd->mp_stream.data_avail) {
|
||||
/* Try re-delivering the data. */
|
||||
mg_http_multipart_continue(nc);
|
||||
}
|
||||
return;
|
||||
}
|
||||
#endif /* MG_ENABLE_HTTP_STREAMING_MULTIPART */
|
||||
|
||||
if (ev == MG_EV_RECV) {
|
||||
struct mg_str *s;
|
||||
pd->rcvd += *(int *) ev_data;
|
||||
|
||||
#if MG_ENABLE_HTTP_STREAMING_MULTIPART
|
||||
if (pd->mp_stream.boundary != NULL) {
|
||||
mg_http_multipart_continue(nc);
|
||||
return;
|
||||
}
|
||||
#endif /* MG_ENABLE_HTTP_STREAMING_MULTIPART */
|
||||
|
||||
again:
|
||||
req_len = mg_parse_http(io->buf, io->len, hm, is_req);
|
||||
|
||||
@ -6560,16 +6649,23 @@ void mg_http_handler(struct mg_connection *nc, int ev,
|
||||
/* Do nothing, request is not yet fully buffered */
|
||||
}
|
||||
#if MG_ENABLE_HTTP_WEBSOCKET
|
||||
else if (nc->listener == NULL &&
|
||||
mg_get_http_header(hm, "Sec-WebSocket-Accept")) {
|
||||
else if (nc->listener == NULL && (nc->flags & MG_F_IS_WEBSOCKET)) {
|
||||
/* We're websocket client, got handshake response from server. */
|
||||
/* TODO(lsm): check the validity of accept Sec-WebSocket-Accept */
|
||||
mbuf_remove(io, req_len);
|
||||
nc->proto_handler = mg_ws_handler;
|
||||
nc->flags |= MG_F_IS_WEBSOCKET;
|
||||
mg_call(nc, nc->handler, nc->user_data, MG_EV_WEBSOCKET_HANDSHAKE_DONE,
|
||||
NULL);
|
||||
mg_ws_handler(nc, MG_EV_RECV, ev_data MG_UD_ARG(user_data));
|
||||
DBG(("%p WebSocket upgrade code %d", nc, hm->resp_code));
|
||||
if (hm->resp_code == 101 &&
|
||||
mg_get_http_header(hm, "Sec-WebSocket-Accept")) {
|
||||
/* TODO(lsm): check the validity of accept Sec-WebSocket-Accept */
|
||||
mg_call(nc, nc->handler, nc->user_data, MG_EV_WEBSOCKET_HANDSHAKE_DONE,
|
||||
hm);
|
||||
mbuf_remove(io, req_len);
|
||||
nc->proto_handler = mg_ws_handler;
|
||||
mg_ws_handler(nc, MG_EV_RECV, ev_data MG_UD_ARG(user_data));
|
||||
} else {
|
||||
mg_call(nc, nc->handler, nc->user_data, MG_EV_WEBSOCKET_HANDSHAKE_DONE,
|
||||
hm);
|
||||
nc->flags |= MG_F_CLOSE_IMMEDIATELY;
|
||||
mbuf_remove(io, req_len);
|
||||
}
|
||||
} else if (nc->listener != NULL &&
|
||||
(vec = mg_get_http_header(hm, "Sec-WebSocket-Key")) != NULL) {
|
||||
struct mg_http_endpoint *ep;
|
||||
@ -6600,7 +6696,7 @@ void mg_http_handler(struct mg_connection *nc, int ev,
|
||||
mg_ws_handshake(nc, vec, hm);
|
||||
}
|
||||
mg_call(nc, nc->handler, nc->user_data, MG_EV_WEBSOCKET_HANDSHAKE_DONE,
|
||||
NULL);
|
||||
hm);
|
||||
mg_ws_handler(nc, MG_EV_RECV, ev_data MG_UD_ARG(user_data));
|
||||
}
|
||||
}
|
||||
@ -6727,8 +6823,9 @@ exit_mp:
|
||||
|
||||
#define CONTENT_DISPOSITION "Content-Disposition: "
|
||||
|
||||
static void mg_http_multipart_call_handler(struct mg_connection *c, int ev,
|
||||
const char *data, size_t data_len) {
|
||||
static size_t mg_http_multipart_call_handler(struct mg_connection *c, int ev,
|
||||
const char *data,
|
||||
size_t data_len) {
|
||||
struct mg_http_multipart_part mp;
|
||||
struct mg_http_proto_data *pd = mg_http_get_proto_data(c);
|
||||
memset(&mp, 0, sizeof(mp));
|
||||
@ -6738,8 +6835,11 @@ static void mg_http_multipart_call_handler(struct mg_connection *c, int ev,
|
||||
mp.user_data = pd->mp_stream.user_data;
|
||||
mp.data.p = data;
|
||||
mp.data.len = data_len;
|
||||
mp.num_data_consumed = data_len;
|
||||
mg_call(c, pd->endpoint_handler, c->user_data, ev, &mp);
|
||||
pd->mp_stream.user_data = mp.user_data;
|
||||
pd->mp_stream.data_avail = (mp.num_data_consumed != data_len);
|
||||
return mp.num_data_consumed;
|
||||
}
|
||||
|
||||
static int mg_http_multipart_finalize(struct mg_connection *c) {
|
||||
@ -6877,19 +6977,25 @@ static int mg_http_multipart_continue_wait_for_chunk(struct mg_connection *c) {
|
||||
|
||||
boundary = c_strnstr(io->buf, pd->mp_stream.boundary, io->len);
|
||||
if (boundary == NULL) {
|
||||
int data_size = (io->len - (pd->mp_stream.boundary_len + 6));
|
||||
if (data_size > 0) {
|
||||
mg_http_multipart_call_handler(c, MG_EV_HTTP_PART_DATA, io->buf,
|
||||
data_size);
|
||||
mbuf_remove(io, data_size);
|
||||
int data_len = (io->len - (pd->mp_stream.boundary_len + 6));
|
||||
if (data_len > 0) {
|
||||
size_t consumed = mg_http_multipart_call_handler(
|
||||
c, MG_EV_HTTP_PART_DATA, io->buf, (size_t) data_len);
|
||||
mbuf_remove(io, consumed);
|
||||
}
|
||||
return 0;
|
||||
} else if (boundary != NULL) {
|
||||
int data_size = (boundary - io->buf - 4);
|
||||
mg_http_multipart_call_handler(c, MG_EV_HTTP_PART_DATA, io->buf, data_size);
|
||||
mbuf_remove(io, (boundary - io->buf));
|
||||
pd->mp_stream.state = MPS_WAITING_FOR_BOUNDARY;
|
||||
return 1;
|
||||
size_t data_len = ((size_t)(boundary - io->buf) - 4);
|
||||
size_t consumed = mg_http_multipart_call_handler(c, MG_EV_HTTP_PART_DATA,
|
||||
io->buf, data_len);
|
||||
mbuf_remove(io, consumed);
|
||||
if (consumed == data_len) {
|
||||
mbuf_remove(io, 4);
|
||||
pd->mp_stream.state = MPS_WAITING_FOR_BOUNDARY;
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
@ -7667,7 +7773,7 @@ int mg_check_digest_auth(struct mg_str method, struct mg_str uri,
|
||||
struct mg_str nc, struct mg_str nonce,
|
||||
struct mg_str auth_domain, FILE *fp) {
|
||||
char buf[128], f_user[sizeof(buf)], f_ha1[sizeof(buf)], f_domain[sizeof(buf)];
|
||||
char expected_response[33];
|
||||
char exp_resp[33];
|
||||
|
||||
/*
|
||||
* Read passwords file line by line. If should have htdigest format,
|
||||
@ -7681,11 +7787,10 @@ int mg_check_digest_auth(struct mg_str method, struct mg_str uri,
|
||||
/* Username and domain matched, check the password */
|
||||
mg_mkmd5resp(method.p, method.len, uri.p, uri.len, f_ha1, strlen(f_ha1),
|
||||
nonce.p, nonce.len, nc.p, nc.len, cnonce.p, cnonce.len,
|
||||
qop.p, qop.len, expected_response);
|
||||
LOG(LL_DEBUG,
|
||||
("%.*s %s %.*s %s", (int) username.len, username.p, f_domain,
|
||||
(int) response.len, response.p, expected_response));
|
||||
return mg_ncasecmp(response.p, expected_response, response.len) == 0;
|
||||
qop.p, qop.len, exp_resp);
|
||||
LOG(LL_DEBUG, ("%.*s %s %.*s %s", (int) username.len, username.p,
|
||||
f_domain, (int) response.len, response.p, exp_resp));
|
||||
return mg_ncasecmp(response.p, exp_resp, strlen(exp_resp)) == 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -8280,9 +8385,11 @@ void mg_http_send_digest_auth_request(struct mg_connection *c,
|
||||
domain, (unsigned long) mg_time());
|
||||
}
|
||||
|
||||
static void mg_http_send_options(struct mg_connection *nc) {
|
||||
static void mg_http_send_options(struct mg_connection *nc,
|
||||
struct mg_serve_http_opts *opts) {
|
||||
mg_send_response_line(nc, 200, opts->extra_headers);
|
||||
mg_printf(nc, "%s",
|
||||
"HTTP/1.1 200 OK\r\nAllow: GET, POST, HEAD, CONNECT, OPTIONS"
|
||||
"Allow: GET, POST, HEAD, CONNECT, OPTIONS"
|
||||
#if MG_ENABLE_HTTP_WEBDAV
|
||||
", MKCOL, PUT, DELETE, PROPFIND, MOVE\r\nDAV: 1,2"
|
||||
#endif
|
||||
@ -8389,7 +8496,7 @@ MG_INTERNAL void mg_send_http_file(struct mg_connection *nc, char *path,
|
||||
#endif
|
||||
#endif /* MG_ENABLE_HTTP_WEBDAV */
|
||||
} else if (!mg_vcmp(&hm->method, "OPTIONS")) {
|
||||
mg_http_send_options(nc);
|
||||
mg_http_send_options(nc, opts);
|
||||
} else if (is_directory && index_file == NULL) {
|
||||
#if MG_ENABLE_DIRECTORY_LISTING
|
||||
if (strcmp(opts->enable_directory_listing, "yes") == 0) {
|
||||
@ -10302,6 +10409,8 @@ void mg_send_websocket_handshake3v(struct mg_connection *nc,
|
||||
}
|
||||
mg_printf(nc, "\r\n");
|
||||
|
||||
nc->flags |= MG_F_IS_WEBSOCKET;
|
||||
|
||||
mbuf_free(&auth);
|
||||
}
|
||||
|
||||
@ -10887,26 +10996,44 @@ static void mg_mqtt_proto_data_destructor(void *proto_data) {
|
||||
MG_FREE(proto_data);
|
||||
}
|
||||
|
||||
static struct mg_str mg_mqtt_next_topic_component(struct mg_str *topic) {
|
||||
struct mg_str res = *topic;
|
||||
const char *c = mg_strchr(*topic, '/');
|
||||
if (c != NULL) {
|
||||
res.len = (c - topic->p);
|
||||
topic->len -= (res.len + 1);
|
||||
topic->p += (res.len + 1);
|
||||
} else {
|
||||
topic->len = 0;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Refernce: https://mosquitto.org/man/mqtt-7.html */
|
||||
int mg_mqtt_match_topic_expression(struct mg_str exp, struct mg_str topic) {
|
||||
/* TODO(mkm): implement real matching */
|
||||
if (memchr(exp.p, '#', exp.len)) {
|
||||
/* exp `foo/#` will become `foo/` */
|
||||
exp.len -= 1;
|
||||
/*
|
||||
* topic should be longer than the expression: e.g. topic `foo/bar` does
|
||||
* match `foo/#`, but neither `foo` nor `foo/` do.
|
||||
*/
|
||||
if (topic.len <= exp.len) {
|
||||
struct mg_str ec, tc;
|
||||
if (exp.len == 0) return 0;
|
||||
while (1) {
|
||||
ec = mg_mqtt_next_topic_component(&exp);
|
||||
tc = mg_mqtt_next_topic_component(&topic);
|
||||
if (ec.len == 0) {
|
||||
if (tc.len != 0) return 0;
|
||||
if (exp.len == 0) break;
|
||||
continue;
|
||||
}
|
||||
if (mg_vcmp(&ec, "+") == 0) {
|
||||
if (tc.len == 0 && topic.len == 0) return 0;
|
||||
continue;
|
||||
}
|
||||
if (mg_vcmp(&ec, "#") == 0) {
|
||||
/* Must be the last component in the expression or it's invalid. */
|
||||
return (exp.len == 0);
|
||||
}
|
||||
if (mg_strcmp(ec, tc) != 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Truncate topic so that it'll pass the next length check */
|
||||
topic.len = exp.len;
|
||||
}
|
||||
if (topic.len != exp.len) {
|
||||
return 0;
|
||||
}
|
||||
return strncmp(topic.p, exp.p, exp.len) == 0;
|
||||
return (tc.len == 0 && topic.len == 0);
|
||||
}
|
||||
|
||||
int mg_mqtt_vmatch_topic_expression(const char *exp, struct mg_str topic) {
|
||||
@ -13405,7 +13532,9 @@ off_t fs_slfs_lseek(int fd, off_t offset, int whence);
|
||||
int fs_slfs_unlink(const char *filename);
|
||||
int fs_slfs_rename(const char *from, const char *to);
|
||||
|
||||
void fs_slfs_set_new_file_size(const char *name, size_t size);
|
||||
void fs_slfs_set_file_size(const char *name, size_t size);
|
||||
void fs_slfs_set_file_flags(const char *name, uint32_t flags, uint32_t *token);
|
||||
void fs_slfs_unset_file_flags(const char *name);
|
||||
|
||||
#endif /* defined(MG_FS_SLFS) */
|
||||
|
||||
@ -13446,14 +13575,14 @@ void fs_slfs_set_new_file_size(const char *name, size_t size);
|
||||
/* Amalgamated: #include "common/mg_mem.h" */
|
||||
|
||||
#if SL_MAJOR_VERSION_NUM < 2
|
||||
int slfs_open(const unsigned char *fname, uint32_t flags) {
|
||||
int slfs_open(const unsigned char *fname, uint32_t flags, uint32_t *token) {
|
||||
_i32 fh;
|
||||
_i32 r = sl_FsOpen(fname, flags, NULL /* token */, &fh);
|
||||
_i32 r = sl_FsOpen(fname, flags, (unsigned long *) token, &fh);
|
||||
return (r < 0 ? r : fh);
|
||||
}
|
||||
#else /* SL_MAJOR_VERSION_NUM >= 2 */
|
||||
int slfs_open(const unsigned char *fname, uint32_t flags) {
|
||||
return sl_FsOpen(fname, flags, NULL /* token */);
|
||||
int slfs_open(const unsigned char *fname, uint32_t flags, uint32_t *token) {
|
||||
return sl_FsOpen(fname, flags, (unsigned long *) token);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -13469,9 +13598,11 @@ const char *drop_dir(const char *fname, bool *is_slfs);
|
||||
#define FS_SLFS_MAX_FILE_SIZE (64 * 1024)
|
||||
#endif
|
||||
|
||||
struct sl_file_size_hint {
|
||||
struct sl_file_open_info {
|
||||
char *name;
|
||||
size_t size;
|
||||
uint32_t flags;
|
||||
uint32_t *token;
|
||||
};
|
||||
|
||||
struct sl_fd_info {
|
||||
@ -13481,7 +13612,10 @@ struct sl_fd_info {
|
||||
};
|
||||
|
||||
static struct sl_fd_info s_sl_fds[MAX_OPEN_SLFS_FILES];
|
||||
static struct sl_file_size_hint s_sl_file_size_hints[MAX_OPEN_SLFS_FILES];
|
||||
static struct sl_file_open_info s_sl_file_open_infos[MAX_OPEN_SLFS_FILES];
|
||||
|
||||
static struct sl_file_open_info *fs_slfs_find_foi(const char *name,
|
||||
bool create);
|
||||
|
||||
static int sl_fs_to_errno(_i32 r) {
|
||||
DBG(("SL error: %d", (int) r));
|
||||
@ -13522,7 +13656,13 @@ int fs_slfs_open(const char *pathname, int flags, mode_t mode) {
|
||||
_u32 am = 0;
|
||||
fi->size = (size_t) -1;
|
||||
int rw = (flags & 3);
|
||||
size_t new_size = FS_SLFS_MAX_FILE_SIZE;
|
||||
size_t new_size = 0;
|
||||
struct sl_file_open_info *foi =
|
||||
fs_slfs_find_foi(pathname, false /* create */);
|
||||
if (foi != NULL) {
|
||||
LOG(LL_DEBUG, ("FOI for %s: %d 0x%x %p", pathname, (int) foi->size,
|
||||
(unsigned int) foi->flags, foi->token));
|
||||
}
|
||||
if (rw == O_RDONLY) {
|
||||
SlFsFileInfo_t sl_fi;
|
||||
_i32 r = sl_FsGetInfo((const _u8 *) pathname, 0, &sl_fi);
|
||||
@ -13537,24 +13677,27 @@ int fs_slfs_open(const char *pathname, int flags, mode_t mode) {
|
||||
return set_errno(ENOTSUP);
|
||||
}
|
||||
if (flags & O_CREAT) {
|
||||
size_t i;
|
||||
for (i = 0; i < MAX_OPEN_SLFS_FILES; i++) {
|
||||
if (s_sl_file_size_hints[i].name != NULL &&
|
||||
strcmp(s_sl_file_size_hints[i].name, pathname) == 0) {
|
||||
new_size = s_sl_file_size_hints[i].size;
|
||||
MG_FREE(s_sl_file_size_hints[i].name);
|
||||
s_sl_file_size_hints[i].name = NULL;
|
||||
break;
|
||||
}
|
||||
if (foi->size > 0) {
|
||||
new_size = foi->size;
|
||||
} else {
|
||||
new_size = FS_SLFS_MAX_FILE_SIZE;
|
||||
}
|
||||
am = FS_MODE_OPEN_CREATE(new_size, 0);
|
||||
} else {
|
||||
am = SL_FS_WRITE;
|
||||
}
|
||||
#if SL_MAJOR_VERSION_NUM >= 2
|
||||
am |= SL_FS_OVERWRITE;
|
||||
#endif
|
||||
}
|
||||
fi->fh = slfs_open((_u8 *) pathname, am);
|
||||
LOG(LL_DEBUG, ("sl_FsOpen(%s, 0x%x) sz %u = %d", pathname, (int) am,
|
||||
(unsigned int) new_size, (int) fi->fh));
|
||||
uint32_t *token = NULL;
|
||||
if (foi != NULL) {
|
||||
am |= foi->flags;
|
||||
token = foi->token;
|
||||
}
|
||||
fi->fh = slfs_open((_u8 *) pathname, am, token);
|
||||
LOG(LL_DEBUG, ("sl_FsOpen(%s, 0x%x, %p) sz %u = %d", pathname, (int) am,
|
||||
token, (unsigned int) new_size, (int) fi->fh));
|
||||
int r;
|
||||
if (fi->fh >= 0) {
|
||||
fi->pos = 0;
|
||||
@ -13658,16 +13801,46 @@ int fs_slfs_rename(const char *from, const char *to) {
|
||||
return set_errno(ENOTSUP);
|
||||
}
|
||||
|
||||
void fs_slfs_set_new_file_size(const char *name, size_t size) {
|
||||
int i;
|
||||
static struct sl_file_open_info *fs_slfs_find_foi(const char *name,
|
||||
bool create) {
|
||||
int i = 0;
|
||||
for (i = 0; i < MAX_OPEN_SLFS_FILES; i++) {
|
||||
if (s_sl_file_size_hints[i].name == NULL) {
|
||||
DBG(("File size hint: %s %d", name, (int) size));
|
||||
s_sl_file_size_hints[i].name = strdup(name);
|
||||
s_sl_file_size_hints[i].size = size;
|
||||
if (s_sl_file_open_infos[i].name != NULL &&
|
||||
strcmp(drop_dir(s_sl_file_open_infos[i].name, NULL), name) == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i != MAX_OPEN_SLFS_FILES) return &s_sl_file_open_infos[i];
|
||||
if (!create) return NULL;
|
||||
for (i = 0; i < MAX_OPEN_SLFS_FILES; i++) {
|
||||
if (s_sl_file_open_infos[i].name == NULL) break;
|
||||
}
|
||||
if (i == MAX_OPEN_SLFS_FILES) {
|
||||
i = 0; /* Evict a random slot. */
|
||||
}
|
||||
if (s_sl_file_open_infos[i].name != NULL) {
|
||||
free(s_sl_file_open_infos[i].name);
|
||||
}
|
||||
s_sl_file_open_infos[i].name = strdup(name);
|
||||
return &s_sl_file_open_infos[i];
|
||||
}
|
||||
|
||||
void fs_slfs_set_file_size(const char *name, size_t size) {
|
||||
struct sl_file_open_info *foi = fs_slfs_find_foi(name, true /* create */);
|
||||
foi->size = size;
|
||||
}
|
||||
|
||||
void fs_slfs_set_file_flags(const char *name, uint32_t flags, uint32_t *token) {
|
||||
struct sl_file_open_info *foi = fs_slfs_find_foi(name, true /* create */);
|
||||
foi->flags = flags;
|
||||
foi->token = token;
|
||||
}
|
||||
|
||||
void fs_slfs_unset_file_flags(const char *name) {
|
||||
struct sl_file_open_info *foi = fs_slfs_find_foi(name, false /* create */);
|
||||
if (foi == NULL) return;
|
||||
free(foi->name);
|
||||
memset(foi, 0, sizeof(*foi));
|
||||
}
|
||||
|
||||
#endif /* defined(MG_FS_SLFS) || defined(CC3200_FS_SLFS) */
|
||||
@ -14810,9 +14983,9 @@ bool pem_to_der(const char *pem_file, const char *der_file) {
|
||||
pf = fopen(pem_file, "r");
|
||||
if (pf == NULL) goto clean;
|
||||
remove(der_file);
|
||||
fs_slfs_set_new_file_size(der_file + MG_SSL_IF_SIMPLELINK_SLFS_PREFIX_LEN,
|
||||
2048);
|
||||
fs_slfs_set_file_size(der_file + MG_SSL_IF_SIMPLELINK_SLFS_PREFIX_LEN, 2048);
|
||||
df = fopen(der_file, "w");
|
||||
fs_slfs_unset_file_flags(der_file + MG_SSL_IF_SIMPLELINK_SLFS_PREFIX_LEN);
|
||||
if (df == NULL) goto clean;
|
||||
while (1) {
|
||||
char pem_buf[70];
|
||||
@ -15598,7 +15771,7 @@ struct tcp_recved_ctx {
|
||||
|
||||
void tcp_recved_tcpip(void *arg) {
|
||||
struct tcp_recved_ctx *ctx = (struct tcp_recved_ctx *) arg;
|
||||
tcp_recved(ctx->tpcb, ctx->len);
|
||||
if (ctx->tpcb != NULL) tcp_recved(ctx->tpcb, ctx->len);
|
||||
}
|
||||
|
||||
static int mg_lwip_if_tcp_recv(struct mg_connection *nc, void *buf,
|
||||
@ -15805,7 +15978,6 @@ void mg_ev_mgr_lwip_process_signals(struct mg_mgr *mgr) {
|
||||
break;
|
||||
}
|
||||
case MG_SIG_CLOSE_CONN: {
|
||||
nc->flags |= MG_F_SEND_AND_CLOSE;
|
||||
mg_close_conn(nc);
|
||||
break;
|
||||
}
|
||||
|
47
mongoose.h
47
mongoose.h
@ -23,7 +23,7 @@
|
||||
#ifndef CS_MONGOOSE_SRC_COMMON_H_
|
||||
#define CS_MONGOOSE_SRC_COMMON_H_
|
||||
|
||||
#define MG_VERSION "6.13"
|
||||
#define MG_VERSION "6.14"
|
||||
|
||||
/* Local tweaks, applied before any of Mongoose's own headers. */
|
||||
#ifdef MG_LOCALS
|
||||
@ -1531,7 +1531,7 @@ int sl_set_ssl_opts(int sock, struct mg_connection *nc);
|
||||
|
||||
#endif /* SL_MAJOR_VERSION_NUM < 2 */
|
||||
|
||||
int slfs_open(const unsigned char *fname, uint32_t flags);
|
||||
int slfs_open(const unsigned char *fname, uint32_t flags, uint32_t *token);
|
||||
|
||||
#endif /* MG_NET_IF == MG_NET_IF_SIMPLELINK */
|
||||
|
||||
@ -2239,6 +2239,8 @@ struct mg_str mg_mk_str_n(const char *s, size_t len);
|
||||
/* Macro for initializing mg_str. */
|
||||
#define MG_MK_STR(str_literal) \
|
||||
{ str_literal, sizeof(str_literal) - 1 }
|
||||
#define MG_MK_STR_N(str_literal, len) \
|
||||
{ str_literal, len }
|
||||
#define MG_NULL_STR \
|
||||
{ NULL, 0 }
|
||||
|
||||
@ -2278,6 +2280,11 @@ int mg_strcmp(const struct mg_str str1, const struct mg_str str2);
|
||||
*/
|
||||
int mg_strncmp(const struct mg_str str1, const struct mg_str str2, size_t n);
|
||||
|
||||
/*
|
||||
* Free the string (assuming it was heap allocated).
|
||||
*/
|
||||
void mg_strfree(struct mg_str *s);
|
||||
|
||||
/*
|
||||
* Finds the first occurrence of a substring `needle` in the `haystack`.
|
||||
*/
|
||||
@ -2286,6 +2293,9 @@ const char *mg_strstr(const struct mg_str haystack, const struct mg_str needle);
|
||||
/* Strip whitespace at the start and the end of s */
|
||||
struct mg_str mg_strstrip(struct mg_str s);
|
||||
|
||||
/* Returns 1 if s starts with the given prefix. */
|
||||
int mg_str_starts_with(struct mg_str s, struct mg_str prefix);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@ -2363,6 +2373,14 @@ void mbuf_free(struct mbuf *);
|
||||
*/
|
||||
size_t mbuf_append(struct mbuf *, const void *data, size_t data_size);
|
||||
|
||||
/*
|
||||
* Appends data to the Mbuf and frees it (data must be heap-allocated).
|
||||
*
|
||||
* Returns the number of bytes appended or 0 if out of memory.
|
||||
* data is freed irrespective of return value.
|
||||
*/
|
||||
size_t mbuf_append_and_free(struct mbuf *, void *data, size_t data_size);
|
||||
|
||||
/*
|
||||
* Inserts data at a specified offset in the Mbuf.
|
||||
*
|
||||
@ -2383,6 +2401,12 @@ void mbuf_remove(struct mbuf *, size_t data_size);
|
||||
*/
|
||||
void mbuf_resize(struct mbuf *, size_t new_size);
|
||||
|
||||
/* Moves the state from one mbuf to the other. */
|
||||
void mbuf_move(struct mbuf *from, struct mbuf *to);
|
||||
|
||||
/* Removes all the data from mbuf (if any). */
|
||||
void mbuf_clear(struct mbuf *);
|
||||
|
||||
/* Shrinks an Mbuf by resizing its `size` to `len`. */
|
||||
void mbuf_trim(struct mbuf *);
|
||||
|
||||
@ -2623,6 +2647,8 @@ const char *mg_next_comma_list_entry(const char *list, struct mg_str *val,
|
||||
|
||||
/*
|
||||
* Like `mg_next_comma_list_entry()`, but takes `list` as `struct mg_str`.
|
||||
* NB: Test return value's .p, not .len. On last itreation that yields result
|
||||
* .len will be 0 but .p will not. When finished, .p will be NULL.
|
||||
*/
|
||||
struct mg_str mg_next_comma_list_entry_n(struct mg_str list, struct mg_str *val,
|
||||
struct mg_str *eq_val);
|
||||
@ -3930,6 +3956,7 @@ struct mg_connection {
|
||||
#define MG_F_WANT_READ (1 << 6) /* SSL specific */
|
||||
#define MG_F_WANT_WRITE (1 << 7) /* SSL specific */
|
||||
#define MG_F_IS_WEBSOCKET (1 << 8) /* Websocket specific */
|
||||
#define MG_F_RECV_AND_CLOSE (1 << 9) /* Drain rx and close the connection. */
|
||||
|
||||
/* Flags that are settable by user */
|
||||
#define MG_F_SEND_AND_CLOSE (1 << 10) /* Push remaining data and close */
|
||||
@ -4015,7 +4042,7 @@ int mg_mgr_poll(struct mg_mgr *mgr, int milli);
|
||||
* `func` callback function will be called by the IO thread for each
|
||||
* connection. When called, the event will be `MG_EV_POLL`, and a message will
|
||||
* be passed as the `ev_data` pointer. Maximum message size is capped
|
||||
* by `MG_CTL_MSG_MESSAGE_SIZE` which is set to 8192 bytes.
|
||||
* by `MG_CTL_MSG_MESSAGE_SIZE` which is set to 8192 bytes by default.
|
||||
*/
|
||||
void mg_broadcast(struct mg_mgr *mgr, mg_event_handler_t cb, void *data,
|
||||
size_t len);
|
||||
@ -4753,6 +4780,14 @@ struct mg_http_multipart_part {
|
||||
struct mg_str data;
|
||||
int status; /* <0 on error */
|
||||
void *user_data;
|
||||
/*
|
||||
* User handler can indicate how much of the data was consumed
|
||||
* by setting this variable. By default, it is assumed that all
|
||||
* data has been consumed by the handler.
|
||||
* If not all data was consumed, user's handler will be invoked again later
|
||||
* with the remainder.
|
||||
*/
|
||||
size_t num_data_consumed;
|
||||
};
|
||||
|
||||
/* SSI call context */
|
||||
@ -4771,7 +4806,7 @@ struct mg_ssi_call_ctx {
|
||||
|
||||
#if MG_ENABLE_HTTP_WEBSOCKET
|
||||
#define MG_EV_WEBSOCKET_HANDSHAKE_REQUEST 111 /* struct http_message * */
|
||||
#define MG_EV_WEBSOCKET_HANDSHAKE_DONE 112 /* NULL */
|
||||
#define MG_EV_WEBSOCKET_HANDSHAKE_DONE 112 /* struct http_message * */
|
||||
#define MG_EV_WEBSOCKET_FRAME 113 /* struct websocket_message * */
|
||||
#define MG_EV_WEBSOCKET_CONTROL_FRAME 114 /* struct websocket_message * */
|
||||
#endif
|
||||
@ -4810,7 +4845,9 @@ struct mg_ssi_call_ctx {
|
||||
* - MG_EV_WEBSOCKET_HANDSHAKE_REQUEST: server has received the WebSocket
|
||||
* handshake request. `ev_data` contains parsed HTTP request.
|
||||
* - MG_EV_WEBSOCKET_HANDSHAKE_DONE: server has completed the WebSocket
|
||||
* handshake. `ev_data` is `NULL`.
|
||||
* handshake. `ev_data` is a `struct http_message` containing the
|
||||
* client's request (server mode) or server's response (client).
|
||||
* In client mode handler can examine `resp_code`, which should be 101.
|
||||
* - MG_EV_WEBSOCKET_FRAME: new WebSocket frame has arrived. `ev_data` is
|
||||
* `struct websocket_message *`
|
||||
*
|
||||
|
@ -24,7 +24,7 @@
|
||||
#include "common/cs_time.h"
|
||||
#include "common/str_util.h"
|
||||
|
||||
enum cs_log_level cs_log_threshold WEAK =
|
||||
enum cs_log_level cs_log_level WEAK =
|
||||
#if CS_ENABLE_DEBUG
|
||||
LL_VERBOSE_DEBUG;
|
||||
#else
|
||||
@ -32,10 +32,9 @@ enum cs_log_level cs_log_threshold WEAK =
|
||||
#endif
|
||||
|
||||
#if CS_ENABLE_STDIO
|
||||
static char *s_filter_pattern = NULL;
|
||||
static size_t s_filter_pattern_len;
|
||||
static char *s_file_level = NULL;
|
||||
|
||||
void cs_log_set_filter(const char *pattern) WEAK;
|
||||
void cs_log_set_file_level(const char *file_level) WEAK;
|
||||
|
||||
FILE *cs_log_file WEAK = NULL;
|
||||
|
||||
@ -45,34 +44,62 @@ double cs_log_ts WEAK;
|
||||
|
||||
enum cs_log_level cs_log_cur_msg_level WEAK = LL_NONE;
|
||||
|
||||
void cs_log_set_filter(const char *pattern) {
|
||||
free(s_filter_pattern);
|
||||
if (pattern != NULL) {
|
||||
s_filter_pattern = strdup(pattern);
|
||||
s_filter_pattern_len = strlen(pattern);
|
||||
void cs_log_set_file_level(const char *file_level) {
|
||||
char *fl = s_file_level;
|
||||
if (file_level != NULL) {
|
||||
s_file_level = strdup(file_level);
|
||||
} else {
|
||||
s_filter_pattern = NULL;
|
||||
s_filter_pattern_len = 0;
|
||||
s_file_level = NULL;
|
||||
}
|
||||
free(fl);
|
||||
}
|
||||
|
||||
int cs_log_print_prefix(enum cs_log_level, const char *, const char *) WEAK;
|
||||
int cs_log_print_prefix(enum cs_log_level level, const char *func,
|
||||
const char *filename) {
|
||||
char prefix[21];
|
||||
int cs_log_print_prefix(enum cs_log_level level, const char *file, int ln) WEAK;
|
||||
int cs_log_print_prefix(enum cs_log_level level, const char *file, int ln) {
|
||||
char prefix[CS_LOG_PREFIX_LEN], *q;
|
||||
const char *p;
|
||||
size_t fl = 0, ll = 0, pl = 0;
|
||||
|
||||
if (level > cs_log_threshold) return 0;
|
||||
if (s_filter_pattern != NULL &&
|
||||
mg_match_prefix(s_filter_pattern, s_filter_pattern_len, func) == 0 &&
|
||||
mg_match_prefix(s_filter_pattern, s_filter_pattern_len, filename) == 0) {
|
||||
return 0;
|
||||
if (level > cs_log_level && s_file_level == NULL) return 0;
|
||||
|
||||
p = file + strlen(file);
|
||||
|
||||
while (p != file) {
|
||||
const char c = *(p - 1);
|
||||
if (c == '/' || c == '\\') break;
|
||||
p--;
|
||||
fl++;
|
||||
}
|
||||
|
||||
ll = (ln < 10000 ? (ln < 1000 ? (ln < 100 ? (ln < 10 ? 1 : 2) : 3) : 4) : 5);
|
||||
if (fl > (sizeof(prefix) - ll - 2)) fl = (sizeof(prefix) - ll - 2);
|
||||
|
||||
pl = fl + 1 + ll;
|
||||
memcpy(prefix, p, fl);
|
||||
q = prefix + pl;
|
||||
memset(q, ' ', sizeof(prefix) - pl);
|
||||
do {
|
||||
*(--q) = '0' + (ln % 10);
|
||||
ln /= 10;
|
||||
} while (ln > 0);
|
||||
*(--q) = ':';
|
||||
|
||||
if (s_file_level != NULL) {
|
||||
enum cs_log_level pll = cs_log_level;
|
||||
struct mg_str fl = mg_mk_str(s_file_level), ps = MG_MK_STR_N(prefix, pl);
|
||||
struct mg_str k, v;
|
||||
while ((fl = mg_next_comma_list_entry_n(fl, &k, &v)).p != NULL) {
|
||||
bool yes = !(!mg_str_starts_with(ps, k) || v.len == 0);
|
||||
if (!yes) continue;
|
||||
pll = (enum cs_log_level)(*v.p - '0');
|
||||
break;
|
||||
}
|
||||
if (level > pll) return 0;
|
||||
}
|
||||
|
||||
strncpy(prefix, func, 20);
|
||||
prefix[20] = '\0';
|
||||
if (cs_log_file == NULL) cs_log_file = stderr;
|
||||
cs_log_cur_msg_level = level;
|
||||
fprintf(cs_log_file, "%-20s ", prefix);
|
||||
fwrite(prefix, 1, sizeof(prefix), cs_log_file);
|
||||
#if CS_LOG_ENABLE_TS_DIFF
|
||||
{
|
||||
double now = cs_time();
|
||||
@ -101,15 +128,15 @@ void cs_log_set_file(FILE *file) {
|
||||
|
||||
#else
|
||||
|
||||
void cs_log_set_filter(const char *pattern) {
|
||||
(void) pattern;
|
||||
void cs_log_set_file_level(const char *file_level) {
|
||||
(void) file_level;
|
||||
}
|
||||
|
||||
#endif /* CS_ENABLE_STDIO */
|
||||
|
||||
void cs_log_set_level(enum cs_log_level level) WEAK;
|
||||
void cs_log_set_level(enum cs_log_level level) {
|
||||
cs_log_threshold = level;
|
||||
cs_log_level = level;
|
||||
#if CS_LOG_ENABLE_TS_DIFF && CS_ENABLE_STDIO
|
||||
cs_log_ts = cs_time();
|
||||
#endif
|
||||
|
@ -28,6 +28,10 @@
|
||||
#define CS_ENABLE_DEBUG 0
|
||||
#endif
|
||||
|
||||
#ifndef CS_LOG_PREFIX_LEN
|
||||
#define CS_LOG_PREFIX_LEN 24
|
||||
#endif
|
||||
|
||||
#ifndef CS_LOG_ENABLE_TS_DIFF
|
||||
#define CS_LOG_ENABLE_TS_DIFF 0
|
||||
#endif
|
||||
@ -58,51 +62,28 @@ enum cs_log_level {
|
||||
void cs_log_set_level(enum cs_log_level level);
|
||||
|
||||
/*
|
||||
* Set log filter. NULL (a default) logs everything.
|
||||
* Otherwise, function name and file name will be tested against the given
|
||||
* pattern, and only matching messages will be printed.
|
||||
* A comma-separated set of prefix=level.
|
||||
* prefix is matched against the log prefix exactly as printed, including line
|
||||
* number, but partial match is ok. Check stops on first matching entry.
|
||||
* If nothing matches, default level is used.
|
||||
*
|
||||
* For the pattern syntax, refer to `mg_match_prefix()` in `str_util.h`.
|
||||
* Examples:
|
||||
* main.c:=4 - everything from main C at verbose debug level.
|
||||
* mongoose.c=1,mjs.c=1,=4 - everything at verbose debug except mg_* and mjs_*
|
||||
*
|
||||
* Example:
|
||||
* ```c
|
||||
* void foo(void) {
|
||||
* LOG(LL_INFO, ("hello from foo"));
|
||||
* }
|
||||
*
|
||||
* void bar(void) {
|
||||
* LOG(LL_INFO, ("hello from bar"));
|
||||
* }
|
||||
*
|
||||
* void test(void) {
|
||||
* cs_log_set_filter(NULL);
|
||||
* foo();
|
||||
* bar();
|
||||
*
|
||||
* cs_log_set_filter("f*");
|
||||
* foo();
|
||||
* bar(); // Will NOT print anything
|
||||
*
|
||||
* cs_log_set_filter("bar");
|
||||
* foo(); // Will NOT print anything
|
||||
* bar();
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
void cs_log_set_filter(const char *pattern);
|
||||
void cs_log_set_file_level(const char *file_level);
|
||||
|
||||
/*
|
||||
* Helper function which prints message prefix with the given `level`, function
|
||||
* name `func` and `filename`. If message should be printed (accordingly to the
|
||||
* current log level and filter), prints the prefix and returns 1, otherwise
|
||||
* returns 0.
|
||||
* Helper function which prints message prefix with the given `level`.
|
||||
* If message should be printed (according to the current log level
|
||||
* and filter), prints the prefix and returns 1, otherwise returns 0.
|
||||
*
|
||||
* Clients should typically just use `LOG()` macro.
|
||||
*/
|
||||
int cs_log_print_prefix(enum cs_log_level level, const char *func,
|
||||
const char *filename);
|
||||
int cs_log_print_prefix(enum cs_log_level level, const char *fname, int line);
|
||||
|
||||
extern enum cs_log_level cs_log_threshold;
|
||||
extern enum cs_log_level cs_log_level;
|
||||
|
||||
#if CS_ENABLE_STDIO
|
||||
|
||||
@ -127,9 +108,11 @@ void cs_log_printf(const char *fmt, ...) PRINTF_LIKE(1, 2);
|
||||
* LOG(LL_DEBUG, ("my debug message: %d", 123));
|
||||
* ```
|
||||
*/
|
||||
#define LOG(l, x) \
|
||||
do { \
|
||||
if (cs_log_print_prefix(l, __func__, __FILE__)) cs_log_printf x; \
|
||||
#define LOG(l, x) \
|
||||
do { \
|
||||
if (cs_log_print_prefix(l, __FILE__, __LINE__)) { \
|
||||
cs_log_printf x; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#else
|
||||
|
50
src/common/platforms/arm/arm_nsleep100_m3.S
Normal file
50
src/common/platforms/arm/arm_nsleep100_m3.S
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (c) 2014-2019 Cesanta Software Limited
|
||||
* All rights reserved
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the ""License"");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an ""AS IS"" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
.arch armv7e-m
|
||||
.syntax unified
|
||||
.thumb
|
||||
|
||||
/* These are required to satisfy TI linker. */
|
||||
.eabi_attribute Tag_ABI_align_needed, 1
|
||||
.eabi_attribute Tag_ABI_align_preserved, 1
|
||||
|
||||
.global mgos_nsleep100_impl
|
||||
.global mgos_nsleep100_loop_count
|
||||
|
||||
#ifdef STM32
|
||||
.section .RamFunc.mgos_nsleep100_impl
|
||||
#else
|
||||
.section .iram.mgos_nsleep100_impl
|
||||
#endif
|
||||
.type mgos_nsleep100_impl, %function
|
||||
.align 4
|
||||
|
||||
mgos_nsleep100_impl:
|
||||
ldr r3, =mgos_nsleep100_loop_count
|
||||
ldr r3, [r3]
|
||||
mul r0, r3
|
||||
mov r1, #6
|
||||
udiv r0, r0, r1
|
||||
cbz r0, xxx
|
||||
lxx:
|
||||
subs r0, #1
|
||||
bne lxx
|
||||
xxx:
|
||||
bx lr
|
||||
.align 4
|
||||
.size mgos_nsleep100_impl, . - mgos_nsleep100_impl
|
@ -26,7 +26,11 @@
|
||||
.global mgos_nsleep100_impl
|
||||
.global mgos_nsleep100_loop_count
|
||||
|
||||
#ifdef STM32
|
||||
.section .RamFunc.mgos_nsleep100_impl
|
||||
#else
|
||||
.section .iram.mgos_nsleep100_impl
|
||||
#endif
|
||||
.type mgos_nsleep100_impl, %function
|
||||
.align 4
|
||||
|
||||
@ -34,7 +38,11 @@ mgos_nsleep100_impl:
|
||||
ldr r3, =mgos_nsleep100_loop_count
|
||||
ldr r3, [r3]
|
||||
mul r0, r3
|
||||
#ifdef STM32L4
|
||||
mov r1, #3
|
||||
#else
|
||||
mov r1, #6
|
||||
#endif
|
||||
udiv r0, r0, r1
|
||||
cbz r0, xxx
|
||||
lxx:
|
||||
|
@ -26,7 +26,11 @@
|
||||
.global mgos_nsleep100_impl
|
||||
.global mgos_nsleep100_loop_count
|
||||
|
||||
#ifdef STM32
|
||||
.section .RamFunc.mgos_nsleep100_impl
|
||||
#else
|
||||
.section .iram.mgos_nsleep100_impl
|
||||
#endif
|
||||
.type mgos_nsleep100_impl, %function
|
||||
.align 4
|
||||
|
||||
|
@ -25,7 +25,7 @@
|
||||
#if defined(ESP8266)
|
||||
#include "eagle_soc.h"
|
||||
#include "ets_sys.h"
|
||||
#include "../../../miniz.c"
|
||||
#include "miniz.c"
|
||||
#elif defined(ESP32)
|
||||
#include "rom/efuse.h"
|
||||
#include "rom/miniz.h"
|
||||
@ -38,7 +38,7 @@
|
||||
#include "uart.h"
|
||||
|
||||
/* Param: baud rate. */
|
||||
uint32_t params[1] __attribute__((section(".params")));
|
||||
uint32_t params[2] __attribute__((section(".params")));
|
||||
|
||||
#define FLASH_BLOCK_SIZE 65536
|
||||
#define FLASH_SECTOR_SIZE 4096
|
||||
@ -470,6 +470,27 @@ uint8_t cmd_loop(void) {
|
||||
resp = 0;
|
||||
break;
|
||||
}
|
||||
case CMD_READ_REG: {
|
||||
len = SLIP_recv(args, sizeof(args));
|
||||
if (len == 4) {
|
||||
uint32_t value = READ_PERI_REG((uint32_t *) args[0]);
|
||||
SLIP_send(&value, sizeof(value));
|
||||
resp = 0;
|
||||
} else {
|
||||
resp = 0x91;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CMD_WRITE_REG: {
|
||||
len = SLIP_recv(args, sizeof(args));
|
||||
if (len == 8) {
|
||||
WRITE_PERI_REG((uint32_t *) args[0], args[1]);
|
||||
resp = 0;
|
||||
} else {
|
||||
resp = 0xa1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
SLIP_send(&resp, 1);
|
||||
} while (cmd != CMD_BOOT_FW && cmd != CMD_REBOOT);
|
||||
@ -477,8 +498,7 @@ uint8_t cmd_loop(void) {
|
||||
}
|
||||
|
||||
void stub_main1(void) {
|
||||
uint32_t baud_rate = params[0];
|
||||
uint32_t greeting = 0x4941484f; /* OHAI */
|
||||
uint32_t old_baud_rate = params[0], new_baud_rate = params[1];
|
||||
uint8_t last_cmd;
|
||||
|
||||
/* This points at us right now, reset for next boot. */
|
||||
@ -487,6 +507,7 @@ void stub_main1(void) {
|
||||
/* Selects SPI functions for flash pins. */
|
||||
#if defined(ESP8266)
|
||||
SelectSpiFunction();
|
||||
spi_flash_attach();
|
||||
SET_PERI_REG_MASK(0x3FF00014, 1); /* Switch to 160 MHz */
|
||||
#elif defined(ESP32)
|
||||
esp_rom_spiflash_attach(ets_efuse_get_spiconfig(), 0 /* legacy */);
|
||||
@ -496,9 +517,10 @@ void stub_main1(void) {
|
||||
0 /* deviceId */, 16 * 1024 * 1024 /* chip_size */, FLASH_BLOCK_SIZE,
|
||||
FLASH_SECTOR_SIZE, FLASH_PAGE_SIZE, 0xffff /* status_mask */);
|
||||
|
||||
if (baud_rate > 0) {
|
||||
uint32_t old_div = 0;
|
||||
if (new_baud_rate > 0) {
|
||||
ets_delay_us(10000);
|
||||
set_baud_rate(0, baud_rate);
|
||||
old_div = set_baud_rate(0, old_baud_rate, new_baud_rate);
|
||||
}
|
||||
|
||||
/* Give host time to get ready too. */
|
||||
@ -509,7 +531,7 @@ void stub_main1(void) {
|
||||
WRITE_PERI_REG(UART_FIFO_REG(0), 0x55);
|
||||
}
|
||||
#else
|
||||
SLIP_send(&greeting, 4);
|
||||
SLIP_send(&old_div, 4);
|
||||
#endif
|
||||
|
||||
last_cmd = cmd_loop();
|
||||
@ -525,7 +547,7 @@ void stub_main1(void) {
|
||||
* then jumps to 0x4000108a, then checks strapping bits again (which will
|
||||
* not have changed), and then proceeds to 0x400010a8.
|
||||
*/
|
||||
volatile uint32_t *sp = &baud_rate;
|
||||
volatile uint32_t *sp = &old_baud_rate;
|
||||
while (*sp != (uint32_t) 0x40001100) sp++;
|
||||
*sp = 0x400010a8;
|
||||
/*
|
||||
|
@ -91,6 +91,24 @@ enum stub_cmd {
|
||||
* Output: arguments.
|
||||
*/
|
||||
CMD_ECHO = 8,
|
||||
|
||||
/*
|
||||
* Read register value.
|
||||
*
|
||||
* Args: register address.
|
||||
* Input: None.
|
||||
* Output: register value.
|
||||
*/
|
||||
CMD_READ_REG = 9,
|
||||
|
||||
/*
|
||||
* Write register value.
|
||||
*
|
||||
* Args: register address, value.
|
||||
* Input: None.
|
||||
* Output: None.
|
||||
*/
|
||||
CMD_WRITE_REG = 10,
|
||||
};
|
||||
|
||||
#endif /* CS_COMMON_PLATFORMS_ESP8266_STUBS_STUB_FLASHER_H_ */
|
||||
|
@ -42,5 +42,10 @@ SECTIONS {
|
||||
INCLUDE "components/esp32/ld/esp32.rom.ld"
|
||||
INCLUDE "components/esp32/ld/esp32.rom.spiram_incompatible_fns.ld"
|
||||
|
||||
PROVIDE(ets_isr_mask = 0x400067fc);
|
||||
PROVIDE(ets_isr_unmask = 0x40006808);
|
||||
PROVIDE(MD5Init = 0x4005da7c);
|
||||
PROVIDE(MD5Update = 0x4005da9c);
|
||||
PROVIDE(MD5Final = 0x4005db1c);
|
||||
PROVIDE(esp_rom_spiflash_attach = 0x40062a6c);
|
||||
PROVIDE(esp_rom_spiflash_config_clk = 0x40062bc8);
|
||||
|
@ -19,9 +19,13 @@
|
||||
|
||||
#include "rom_functions.h"
|
||||
|
||||
void set_baud_rate(uint32_t uart_no, uint32_t baud_rate) {
|
||||
uint32_t master_freq = ets_get_detected_xtal_freq() << 4;
|
||||
master_freq += (baud_rate / 2);
|
||||
uint32_t div = master_freq / baud_rate;
|
||||
uart_div_modify(uart_no, div);
|
||||
uint32_t set_baud_rate(uint32_t uart_no, uint32_t old_baud_rate,
|
||||
uint32_t new_baud_rate) {
|
||||
uint32_t uart_reg = REG_READ(UART_CLKDIV_REG(uart_no));
|
||||
uint32_t uart_div = uart_reg & UART_CLKDIV_M;
|
||||
uint32_t fraction = (uart_reg >> UART_CLKDIV_FRAG_S) & UART_CLKDIV_FRAG_V;
|
||||
uart_div = (uart_div << 4) + fraction;
|
||||
uint32_t master_freq = uart_div * old_baud_rate;
|
||||
uart_div_modify(uart_no, master_freq / new_baud_rate);
|
||||
return uart_div;
|
||||
}
|
||||
|
@ -20,6 +20,7 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
void set_baud_rate(uint32_t uart_no, uint32_t baud_rate);
|
||||
uint32_t set_baud_rate(uint32_t uart_no, uint32_t old_baud_rate,
|
||||
uint32_t new_baud_rate);
|
||||
|
||||
#endif /* CS_COMMON_PLATFORMS_ESP32_STUBS_UART_H_ */
|
||||
|
@ -23,13 +23,13 @@ all: $(STUB_ELF)
|
||||
$(STUB_ELF): $(STUB) $(LIBS)
|
||||
@echo " CC $^ -> $@"
|
||||
@[ -d $(BUILD_DIR) ] || mkdir $(BUILD_DIR)
|
||||
@docker run --rm -i -v $(CURDIR)/../../../..:/src $(SDK) //bin/bash -c \
|
||||
docker run --rm -i -v $(CURDIR)/../../../..:/src $(SDK) //bin/bash -c \
|
||||
"cd /src/common/platforms/esp8266/stubs && \
|
||||
$(XT_CC) -std=c99 -Wall -Werror -Os -DESP8266 \
|
||||
-mtext-section-literals -mlongcalls -nostdlib -fno-builtin \
|
||||
-I. -I/src/common/platforms/esp \
|
||||
-I. -I/src/common/platforms/esp -I/src/common/platforms/esp8266 \
|
||||
-I/opt/Espressif/ESP8266_SDK \
|
||||
-Wl,-static -ffunction-sections -Wl,--gc-sections \
|
||||
-Wl,-static -ffunction-sections -lgcc -Wl,--gc-sections \
|
||||
-Tstub.ld $(CFLAGS) -o $@ $^"
|
||||
|
||||
wrap: $(STUB_JSON)
|
||||
|
@ -41,3 +41,4 @@ INCLUDE "eagle.rom.addr.v6.ld"
|
||||
PROVIDE(SPIFlashModeConfig = 0x40004568);
|
||||
PROVIDE(SPI_erase_sector = 0x400040c0);
|
||||
PROVIDE(SPI_erase_block = 0x40004120);
|
||||
PROVIDE(uart_div_modify = 0x400039d8);
|
||||
|
@ -18,9 +18,15 @@
|
||||
#include "uart.h"
|
||||
#include "ets_sys.h"
|
||||
|
||||
#define UART_CLKDIV_26MHZ(B) (52000000 + B / 2) / B
|
||||
#define UART_CLKDIV_M (UART_CLKDIV_CNT << UART_CLKDIV_S)
|
||||
|
||||
void set_baud_rate(uint32_t uart_no, uint32_t baud_rate) {
|
||||
uint32_t div = UART_CLKDIV_26MHZ(baud_rate);
|
||||
WRITE_PERI_REG(UART_CLKDIV_REG(uart_no), div & 0xfffff);
|
||||
void uart_div_modify(uint8_t uart_no, uint32_t div);
|
||||
|
||||
uint32_t set_baud_rate(uint32_t uart_no, uint32_t old_baud_rate,
|
||||
uint32_t new_baud_rate) {
|
||||
uint32_t uart_reg = READ_PERI_REG(UART_CLKDIV_REG(uart_no));
|
||||
uint32_t uart_div = uart_reg & UART_CLKDIV_M;
|
||||
uint32_t master_freq = uart_div * old_baud_rate;
|
||||
uart_div_modify(uart_no, master_freq / new_baud_rate);
|
||||
return uart_div;
|
||||
}
|
||||
|
@ -20,9 +20,11 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
void set_baud_rate(uint32_t uart_no, uint32_t baud_rate);
|
||||
#include "uart_register.h"
|
||||
|
||||
uint32_t set_baud_rate(uint32_t uart_no, uint32_t old_baud_rate,
|
||||
uint32_t new_baud_rate);
|
||||
|
||||
#define REG_UART_BASE(i) (0x60000000 + (i) *0xf00)
|
||||
#define UART_FIFO_REG(i) (REG_UART_BASE(i) + 0x0)
|
||||
#define UART_CONF1_REG(i) (REG_UART_BASE(i) + 0x24)
|
||||
#define UART_RX_TOUT_EN (BIT(31))
|
||||
|
@ -1,23 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
# Analyzers output of firmware compiler with -DESP_ENABLE_MALLOC_TRACES
|
||||
|
||||
import re
|
||||
import sys
|
||||
|
||||
allocs = {}
|
||||
|
||||
for l in sys.stdin:
|
||||
m = re.match(r'(ca|ma|re|fr) (\S+)\s*(\S*)\s*(\S*)', l)
|
||||
if not m: continue
|
||||
op = m.group(1)
|
||||
if op in ('ca', 'ma'):
|
||||
allocs[m.group(2)] = long(m.group(3))
|
||||
else:
|
||||
if m.group(2) in allocs:
|
||||
del allocs[m.group(2)]
|
||||
if op == 're':
|
||||
allocs[m.group(3)] = long(m.group(4))
|
||||
|
||||
for k, v in sorted(allocs.iteritems()):
|
||||
print k, v
|
@ -67,7 +67,6 @@ void mg_ev_mgr_lwip_process_signals(struct mg_mgr *mgr) {
|
||||
break;
|
||||
}
|
||||
case MG_SIG_CLOSE_CONN: {
|
||||
nc->flags |= MG_F_SEND_AND_CLOSE;
|
||||
mg_close_conn(nc);
|
||||
break;
|
||||
}
|
||||
|
@ -581,7 +581,7 @@ struct tcp_recved_ctx {
|
||||
|
||||
void tcp_recved_tcpip(void *arg) {
|
||||
struct tcp_recved_ctx *ctx = (struct tcp_recved_ctx *) arg;
|
||||
tcp_recved(ctx->tpcb, ctx->len);
|
||||
if (ctx->tpcb != NULL) tcp_recved(ctx->tpcb, ctx->len);
|
||||
}
|
||||
|
||||
static int mg_lwip_if_tcp_recv(struct mg_connection *nc, void *buf,
|
||||
|
@ -169,7 +169,7 @@ int sl_set_ssl_opts(int sock, struct mg_connection *nc);
|
||||
|
||||
#endif /* SL_MAJOR_VERSION_NUM < 2 */
|
||||
|
||||
int slfs_open(const unsigned char *fname, uint32_t flags);
|
||||
int slfs_open(const unsigned char *fname, uint32_t flags, uint32_t *token);
|
||||
|
||||
#endif /* MG_NET_IF == MG_NET_IF_SIMPLELINK */
|
||||
|
||||
|
@ -31,14 +31,14 @@
|
||||
#include "common/mg_mem.h"
|
||||
|
||||
#if SL_MAJOR_VERSION_NUM < 2
|
||||
int slfs_open(const unsigned char *fname, uint32_t flags) {
|
||||
int slfs_open(const unsigned char *fname, uint32_t flags, uint32_t *token) {
|
||||
_i32 fh;
|
||||
_i32 r = sl_FsOpen(fname, flags, NULL /* token */, &fh);
|
||||
_i32 r = sl_FsOpen(fname, flags, (unsigned long *) token, &fh);
|
||||
return (r < 0 ? r : fh);
|
||||
}
|
||||
#else /* SL_MAJOR_VERSION_NUM >= 2 */
|
||||
int slfs_open(const unsigned char *fname, uint32_t flags) {
|
||||
return sl_FsOpen(fname, flags, NULL /* token */);
|
||||
int slfs_open(const unsigned char *fname, uint32_t flags, uint32_t *token) {
|
||||
return sl_FsOpen(fname, flags, (unsigned long *) token);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -54,9 +54,11 @@ const char *drop_dir(const char *fname, bool *is_slfs);
|
||||
#define FS_SLFS_MAX_FILE_SIZE (64 * 1024)
|
||||
#endif
|
||||
|
||||
struct sl_file_size_hint {
|
||||
struct sl_file_open_info {
|
||||
char *name;
|
||||
size_t size;
|
||||
uint32_t flags;
|
||||
uint32_t *token;
|
||||
};
|
||||
|
||||
struct sl_fd_info {
|
||||
@ -66,7 +68,10 @@ struct sl_fd_info {
|
||||
};
|
||||
|
||||
static struct sl_fd_info s_sl_fds[MAX_OPEN_SLFS_FILES];
|
||||
static struct sl_file_size_hint s_sl_file_size_hints[MAX_OPEN_SLFS_FILES];
|
||||
static struct sl_file_open_info s_sl_file_open_infos[MAX_OPEN_SLFS_FILES];
|
||||
|
||||
static struct sl_file_open_info *fs_slfs_find_foi(const char *name,
|
||||
bool create);
|
||||
|
||||
static int sl_fs_to_errno(_i32 r) {
|
||||
DBG(("SL error: %d", (int) r));
|
||||
@ -107,7 +112,13 @@ int fs_slfs_open(const char *pathname, int flags, mode_t mode) {
|
||||
_u32 am = 0;
|
||||
fi->size = (size_t) -1;
|
||||
int rw = (flags & 3);
|
||||
size_t new_size = FS_SLFS_MAX_FILE_SIZE;
|
||||
size_t new_size = 0;
|
||||
struct sl_file_open_info *foi =
|
||||
fs_slfs_find_foi(pathname, false /* create */);
|
||||
if (foi != NULL) {
|
||||
LOG(LL_DEBUG, ("FOI for %s: %d 0x%x %p", pathname, (int) foi->size,
|
||||
(unsigned int) foi->flags, foi->token));
|
||||
}
|
||||
if (rw == O_RDONLY) {
|
||||
SlFsFileInfo_t sl_fi;
|
||||
_i32 r = sl_FsGetInfo((const _u8 *) pathname, 0, &sl_fi);
|
||||
@ -122,24 +133,27 @@ int fs_slfs_open(const char *pathname, int flags, mode_t mode) {
|
||||
return set_errno(ENOTSUP);
|
||||
}
|
||||
if (flags & O_CREAT) {
|
||||
size_t i;
|
||||
for (i = 0; i < MAX_OPEN_SLFS_FILES; i++) {
|
||||
if (s_sl_file_size_hints[i].name != NULL &&
|
||||
strcmp(s_sl_file_size_hints[i].name, pathname) == 0) {
|
||||
new_size = s_sl_file_size_hints[i].size;
|
||||
MG_FREE(s_sl_file_size_hints[i].name);
|
||||
s_sl_file_size_hints[i].name = NULL;
|
||||
break;
|
||||
}
|
||||
if (foi->size > 0) {
|
||||
new_size = foi->size;
|
||||
} else {
|
||||
new_size = FS_SLFS_MAX_FILE_SIZE;
|
||||
}
|
||||
am = FS_MODE_OPEN_CREATE(new_size, 0);
|
||||
} else {
|
||||
am = SL_FS_WRITE;
|
||||
}
|
||||
#if SL_MAJOR_VERSION_NUM >= 2
|
||||
am |= SL_FS_OVERWRITE;
|
||||
#endif
|
||||
}
|
||||
fi->fh = slfs_open((_u8 *) pathname, am);
|
||||
LOG(LL_DEBUG, ("sl_FsOpen(%s, 0x%x) sz %u = %d", pathname, (int) am,
|
||||
(unsigned int) new_size, (int) fi->fh));
|
||||
uint32_t *token = NULL;
|
||||
if (foi != NULL) {
|
||||
am |= foi->flags;
|
||||
token = foi->token;
|
||||
}
|
||||
fi->fh = slfs_open((_u8 *) pathname, am, token);
|
||||
LOG(LL_DEBUG, ("sl_FsOpen(%s, 0x%x, %p) sz %u = %d", pathname, (int) am,
|
||||
token, (unsigned int) new_size, (int) fi->fh));
|
||||
int r;
|
||||
if (fi->fh >= 0) {
|
||||
fi->pos = 0;
|
||||
@ -243,16 +257,46 @@ int fs_slfs_rename(const char *from, const char *to) {
|
||||
return set_errno(ENOTSUP);
|
||||
}
|
||||
|
||||
void fs_slfs_set_new_file_size(const char *name, size_t size) {
|
||||
int i;
|
||||
static struct sl_file_open_info *fs_slfs_find_foi(const char *name,
|
||||
bool create) {
|
||||
int i = 0;
|
||||
for (i = 0; i < MAX_OPEN_SLFS_FILES; i++) {
|
||||
if (s_sl_file_size_hints[i].name == NULL) {
|
||||
DBG(("File size hint: %s %d", name, (int) size));
|
||||
s_sl_file_size_hints[i].name = strdup(name);
|
||||
s_sl_file_size_hints[i].size = size;
|
||||
if (s_sl_file_open_infos[i].name != NULL &&
|
||||
strcmp(drop_dir(s_sl_file_open_infos[i].name, NULL), name) == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i != MAX_OPEN_SLFS_FILES) return &s_sl_file_open_infos[i];
|
||||
if (!create) return NULL;
|
||||
for (i = 0; i < MAX_OPEN_SLFS_FILES; i++) {
|
||||
if (s_sl_file_open_infos[i].name == NULL) break;
|
||||
}
|
||||
if (i == MAX_OPEN_SLFS_FILES) {
|
||||
i = 0; /* Evict a random slot. */
|
||||
}
|
||||
if (s_sl_file_open_infos[i].name != NULL) {
|
||||
free(s_sl_file_open_infos[i].name);
|
||||
}
|
||||
s_sl_file_open_infos[i].name = strdup(name);
|
||||
return &s_sl_file_open_infos[i];
|
||||
}
|
||||
|
||||
void fs_slfs_set_file_size(const char *name, size_t size) {
|
||||
struct sl_file_open_info *foi = fs_slfs_find_foi(name, true /* create */);
|
||||
foi->size = size;
|
||||
}
|
||||
|
||||
void fs_slfs_set_file_flags(const char *name, uint32_t flags, uint32_t *token) {
|
||||
struct sl_file_open_info *foi = fs_slfs_find_foi(name, true /* create */);
|
||||
foi->flags = flags;
|
||||
foi->token = token;
|
||||
}
|
||||
|
||||
void fs_slfs_unset_file_flags(const char *name) {
|
||||
struct sl_file_open_info *foi = fs_slfs_find_foi(name, false /* create */);
|
||||
if (foi == NULL) return;
|
||||
free(foi->name);
|
||||
memset(foi, 0, sizeof(*foi));
|
||||
}
|
||||
|
||||
#endif /* defined(MG_FS_SLFS) || defined(CC3200_FS_SLFS) */
|
||||
|
@ -39,7 +39,9 @@ off_t fs_slfs_lseek(int fd, off_t offset, int whence);
|
||||
int fs_slfs_unlink(const char *filename);
|
||||
int fs_slfs_rename(const char *from, const char *to);
|
||||
|
||||
void fs_slfs_set_new_file_size(const char *name, size_t size);
|
||||
void fs_slfs_set_file_size(const char *name, size_t size);
|
||||
void fs_slfs_set_file_flags(const char *name, uint32_t flags, uint32_t *token);
|
||||
void fs_slfs_unset_file_flags(const char *name);
|
||||
|
||||
#endif /* defined(MG_FS_SLFS) */
|
||||
|
||||
|
@ -115,9 +115,9 @@ bool pem_to_der(const char *pem_file, const char *der_file) {
|
||||
pf = fopen(pem_file, "r");
|
||||
if (pf == NULL) goto clean;
|
||||
remove(der_file);
|
||||
fs_slfs_set_new_file_size(der_file + MG_SSL_IF_SIMPLELINK_SLFS_PREFIX_LEN,
|
||||
2048);
|
||||
fs_slfs_set_file_size(der_file + MG_SSL_IF_SIMPLELINK_SLFS_PREFIX_LEN, 2048);
|
||||
df = fopen(der_file, "w");
|
||||
fs_slfs_unset_file_flags(der_file + MG_SSL_IF_SIMPLELINK_SLFS_PREFIX_LEN);
|
||||
if (df == NULL) goto clean;
|
||||
while (1) {
|
||||
char pem_buf[70];
|
||||
|
@ -11,7 +11,7 @@ import (
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"cesanta.com/mos/flash/common"
|
||||
"cesanta.com/common/go/fwbundle"
|
||||
"github.com/cesanta/errors"
|
||||
)
|
||||
|
||||
@ -20,7 +20,7 @@ type FlashOpts struct {
|
||||
Timeout time.Duration
|
||||
}
|
||||
|
||||
func Flash(fw *common.FirmwareBundle, opts *FlashOpts) error {
|
||||
func Flash(fw *fwbundle.FirmwareBundle, opts *FlashOpts) error {
|
||||
data, err := fw.GetPartData("boot")
|
||||
if err != nil {
|
||||
return errors.Annotatef(err, "invalid manifest")
|
||||
|
@ -20,7 +20,7 @@
|
||||
#ifndef CS_MONGOOSE_SRC_COMMON_H_
|
||||
#define CS_MONGOOSE_SRC_COMMON_H_
|
||||
|
||||
#define MG_VERSION "6.13"
|
||||
#define MG_VERSION "6.14"
|
||||
|
||||
/* Local tweaks, applied before any of Mongoose's own headers. */
|
||||
#ifdef MG_LOCALS
|
||||
|
111
src/mg_http.c
111
src/mg_http.c
@ -140,6 +140,7 @@ struct mg_http_multipart_stream {
|
||||
void *user_data;
|
||||
enum mg_http_multipart_stream_state state;
|
||||
int processing_part;
|
||||
int data_avail;
|
||||
};
|
||||
|
||||
struct mg_reverse_proxy_data {
|
||||
@ -758,9 +759,9 @@ void mg_http_handler(struct mg_connection *nc, int ev,
|
||||
if (io->len > 0 &&
|
||||
(req_len = mg_parse_http(io->buf, io->len, hm, is_req)) > 0) {
|
||||
/*
|
||||
* For HTTP messages without Content-Length, always send HTTP message
|
||||
* before MG_EV_CLOSE message.
|
||||
*/
|
||||
* For HTTP messages without Content-Length, always send HTTP message
|
||||
* before MG_EV_CLOSE message.
|
||||
*/
|
||||
int ev2 = is_req ? MG_EV_HTTP_REQUEST : MG_EV_HTTP_REPLY;
|
||||
hm->message.len = io->len;
|
||||
hm->body.len = io->buf + io->len - hm->body.p;
|
||||
@ -768,6 +769,9 @@ void mg_http_handler(struct mg_connection *nc, int ev,
|
||||
mg_http_call_endpoint_handler(nc, ev2, hm);
|
||||
}
|
||||
pd->rcvd = 0;
|
||||
if (pd->endpoint_handler != NULL && pd->endpoint_handler != nc->handler) {
|
||||
mg_call(nc, pd->endpoint_handler, nc->user_data, ev, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
#if MG_ENABLE_FILESYSTEM
|
||||
@ -778,17 +782,24 @@ void mg_http_handler(struct mg_connection *nc, int ev,
|
||||
|
||||
mg_call(nc, nc->handler, nc->user_data, ev, ev_data);
|
||||
|
||||
#if MG_ENABLE_HTTP_STREAMING_MULTIPART
|
||||
if (pd->mp_stream.boundary != NULL &&
|
||||
(ev == MG_EV_RECV || ev == MG_EV_POLL)) {
|
||||
if (ev == MG_EV_RECV) {
|
||||
pd->rcvd += *(int *) ev_data;
|
||||
mg_http_multipart_continue(nc);
|
||||
} else if (pd->mp_stream.data_avail) {
|
||||
/* Try re-delivering the data. */
|
||||
mg_http_multipart_continue(nc);
|
||||
}
|
||||
return;
|
||||
}
|
||||
#endif /* MG_ENABLE_HTTP_STREAMING_MULTIPART */
|
||||
|
||||
if (ev == MG_EV_RECV) {
|
||||
struct mg_str *s;
|
||||
pd->rcvd += *(int *) ev_data;
|
||||
|
||||
#if MG_ENABLE_HTTP_STREAMING_MULTIPART
|
||||
if (pd->mp_stream.boundary != NULL) {
|
||||
mg_http_multipart_continue(nc);
|
||||
return;
|
||||
}
|
||||
#endif /* MG_ENABLE_HTTP_STREAMING_MULTIPART */
|
||||
|
||||
again:
|
||||
req_len = mg_parse_http(io->buf, io->len, hm, is_req);
|
||||
|
||||
@ -816,16 +827,23 @@ void mg_http_handler(struct mg_connection *nc, int ev,
|
||||
/* Do nothing, request is not yet fully buffered */
|
||||
}
|
||||
#if MG_ENABLE_HTTP_WEBSOCKET
|
||||
else if (nc->listener == NULL &&
|
||||
mg_get_http_header(hm, "Sec-WebSocket-Accept")) {
|
||||
else if (nc->listener == NULL && (nc->flags & MG_F_IS_WEBSOCKET)) {
|
||||
/* We're websocket client, got handshake response from server. */
|
||||
/* TODO(lsm): check the validity of accept Sec-WebSocket-Accept */
|
||||
mbuf_remove(io, req_len);
|
||||
nc->proto_handler = mg_ws_handler;
|
||||
nc->flags |= MG_F_IS_WEBSOCKET;
|
||||
mg_call(nc, nc->handler, nc->user_data, MG_EV_WEBSOCKET_HANDSHAKE_DONE,
|
||||
NULL);
|
||||
mg_ws_handler(nc, MG_EV_RECV, ev_data MG_UD_ARG(user_data));
|
||||
DBG(("%p WebSocket upgrade code %d", nc, hm->resp_code));
|
||||
if (hm->resp_code == 101 &&
|
||||
mg_get_http_header(hm, "Sec-WebSocket-Accept")) {
|
||||
/* TODO(lsm): check the validity of accept Sec-WebSocket-Accept */
|
||||
mg_call(nc, nc->handler, nc->user_data, MG_EV_WEBSOCKET_HANDSHAKE_DONE,
|
||||
hm);
|
||||
mbuf_remove(io, req_len);
|
||||
nc->proto_handler = mg_ws_handler;
|
||||
mg_ws_handler(nc, MG_EV_RECV, ev_data MG_UD_ARG(user_data));
|
||||
} else {
|
||||
mg_call(nc, nc->handler, nc->user_data, MG_EV_WEBSOCKET_HANDSHAKE_DONE,
|
||||
hm);
|
||||
nc->flags |= MG_F_CLOSE_IMMEDIATELY;
|
||||
mbuf_remove(io, req_len);
|
||||
}
|
||||
} else if (nc->listener != NULL &&
|
||||
(vec = mg_get_http_header(hm, "Sec-WebSocket-Key")) != NULL) {
|
||||
struct mg_http_endpoint *ep;
|
||||
@ -856,7 +874,7 @@ void mg_http_handler(struct mg_connection *nc, int ev,
|
||||
mg_ws_handshake(nc, vec, hm);
|
||||
}
|
||||
mg_call(nc, nc->handler, nc->user_data, MG_EV_WEBSOCKET_HANDSHAKE_DONE,
|
||||
NULL);
|
||||
hm);
|
||||
mg_ws_handler(nc, MG_EV_RECV, ev_data MG_UD_ARG(user_data));
|
||||
}
|
||||
}
|
||||
@ -983,8 +1001,9 @@ exit_mp:
|
||||
|
||||
#define CONTENT_DISPOSITION "Content-Disposition: "
|
||||
|
||||
static void mg_http_multipart_call_handler(struct mg_connection *c, int ev,
|
||||
const char *data, size_t data_len) {
|
||||
static size_t mg_http_multipart_call_handler(struct mg_connection *c, int ev,
|
||||
const char *data,
|
||||
size_t data_len) {
|
||||
struct mg_http_multipart_part mp;
|
||||
struct mg_http_proto_data *pd = mg_http_get_proto_data(c);
|
||||
memset(&mp, 0, sizeof(mp));
|
||||
@ -994,8 +1013,11 @@ static void mg_http_multipart_call_handler(struct mg_connection *c, int ev,
|
||||
mp.user_data = pd->mp_stream.user_data;
|
||||
mp.data.p = data;
|
||||
mp.data.len = data_len;
|
||||
mp.num_data_consumed = data_len;
|
||||
mg_call(c, pd->endpoint_handler, c->user_data, ev, &mp);
|
||||
pd->mp_stream.user_data = mp.user_data;
|
||||
pd->mp_stream.data_avail = (mp.num_data_consumed != data_len);
|
||||
return mp.num_data_consumed;
|
||||
}
|
||||
|
||||
static int mg_http_multipart_finalize(struct mg_connection *c) {
|
||||
@ -1133,19 +1155,25 @@ static int mg_http_multipart_continue_wait_for_chunk(struct mg_connection *c) {
|
||||
|
||||
boundary = c_strnstr(io->buf, pd->mp_stream.boundary, io->len);
|
||||
if (boundary == NULL) {
|
||||
int data_size = (io->len - (pd->mp_stream.boundary_len + 6));
|
||||
if (data_size > 0) {
|
||||
mg_http_multipart_call_handler(c, MG_EV_HTTP_PART_DATA, io->buf,
|
||||
data_size);
|
||||
mbuf_remove(io, data_size);
|
||||
int data_len = (io->len - (pd->mp_stream.boundary_len + 6));
|
||||
if (data_len > 0) {
|
||||
size_t consumed = mg_http_multipart_call_handler(
|
||||
c, MG_EV_HTTP_PART_DATA, io->buf, (size_t) data_len);
|
||||
mbuf_remove(io, consumed);
|
||||
}
|
||||
return 0;
|
||||
} else if (boundary != NULL) {
|
||||
int data_size = (boundary - io->buf - 4);
|
||||
mg_http_multipart_call_handler(c, MG_EV_HTTP_PART_DATA, io->buf, data_size);
|
||||
mbuf_remove(io, (boundary - io->buf));
|
||||
pd->mp_stream.state = MPS_WAITING_FOR_BOUNDARY;
|
||||
return 1;
|
||||
size_t data_len = ((size_t)(boundary - io->buf) - 4);
|
||||
size_t consumed = mg_http_multipart_call_handler(c, MG_EV_HTTP_PART_DATA,
|
||||
io->buf, data_len);
|
||||
mbuf_remove(io, consumed);
|
||||
if (consumed == data_len) {
|
||||
mbuf_remove(io, 4);
|
||||
pd->mp_stream.state = MPS_WAITING_FOR_BOUNDARY;
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
@ -1923,7 +1951,7 @@ int mg_check_digest_auth(struct mg_str method, struct mg_str uri,
|
||||
struct mg_str nc, struct mg_str nonce,
|
||||
struct mg_str auth_domain, FILE *fp) {
|
||||
char buf[128], f_user[sizeof(buf)], f_ha1[sizeof(buf)], f_domain[sizeof(buf)];
|
||||
char expected_response[33];
|
||||
char exp_resp[33];
|
||||
|
||||
/*
|
||||
* Read passwords file line by line. If should have htdigest format,
|
||||
@ -1937,11 +1965,10 @@ int mg_check_digest_auth(struct mg_str method, struct mg_str uri,
|
||||
/* Username and domain matched, check the password */
|
||||
mg_mkmd5resp(method.p, method.len, uri.p, uri.len, f_ha1, strlen(f_ha1),
|
||||
nonce.p, nonce.len, nc.p, nc.len, cnonce.p, cnonce.len,
|
||||
qop.p, qop.len, expected_response);
|
||||
LOG(LL_DEBUG,
|
||||
("%.*s %s %.*s %s", (int) username.len, username.p, f_domain,
|
||||
(int) response.len, response.p, expected_response));
|
||||
return mg_ncasecmp(response.p, expected_response, response.len) == 0;
|
||||
qop.p, qop.len, exp_resp);
|
||||
LOG(LL_DEBUG, ("%.*s %s %.*s %s", (int) username.len, username.p,
|
||||
f_domain, (int) response.len, response.p, exp_resp));
|
||||
return mg_ncasecmp(response.p, exp_resp, strlen(exp_resp)) == 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2536,9 +2563,11 @@ void mg_http_send_digest_auth_request(struct mg_connection *c,
|
||||
domain, (unsigned long) mg_time());
|
||||
}
|
||||
|
||||
static void mg_http_send_options(struct mg_connection *nc) {
|
||||
static void mg_http_send_options(struct mg_connection *nc,
|
||||
struct mg_serve_http_opts *opts) {
|
||||
mg_send_response_line(nc, 200, opts->extra_headers);
|
||||
mg_printf(nc, "%s",
|
||||
"HTTP/1.1 200 OK\r\nAllow: GET, POST, HEAD, CONNECT, OPTIONS"
|
||||
"Allow: GET, POST, HEAD, CONNECT, OPTIONS"
|
||||
#if MG_ENABLE_HTTP_WEBDAV
|
||||
", MKCOL, PUT, DELETE, PROPFIND, MOVE\r\nDAV: 1,2"
|
||||
#endif
|
||||
@ -2645,7 +2674,7 @@ MG_INTERNAL void mg_send_http_file(struct mg_connection *nc, char *path,
|
||||
#endif
|
||||
#endif /* MG_ENABLE_HTTP_WEBDAV */
|
||||
} else if (!mg_vcmp(&hm->method, "OPTIONS")) {
|
||||
mg_http_send_options(nc);
|
||||
mg_http_send_options(nc, opts);
|
||||
} else if (is_directory && index_file == NULL) {
|
||||
#if MG_ENABLE_DIRECTORY_LISTING
|
||||
if (strcmp(opts->enable_directory_listing, "yes") == 0) {
|
||||
|
@ -80,6 +80,14 @@ struct mg_http_multipart_part {
|
||||
struct mg_str data;
|
||||
int status; /* <0 on error */
|
||||
void *user_data;
|
||||
/*
|
||||
* User handler can indicate how much of the data was consumed
|
||||
* by setting this variable. By default, it is assumed that all
|
||||
* data has been consumed by the handler.
|
||||
* If not all data was consumed, user's handler will be invoked again later
|
||||
* with the remainder.
|
||||
*/
|
||||
size_t num_data_consumed;
|
||||
};
|
||||
|
||||
/* SSI call context */
|
||||
@ -98,7 +106,7 @@ struct mg_ssi_call_ctx {
|
||||
|
||||
#if MG_ENABLE_HTTP_WEBSOCKET
|
||||
#define MG_EV_WEBSOCKET_HANDSHAKE_REQUEST 111 /* struct http_message * */
|
||||
#define MG_EV_WEBSOCKET_HANDSHAKE_DONE 112 /* NULL */
|
||||
#define MG_EV_WEBSOCKET_HANDSHAKE_DONE 112 /* struct http_message * */
|
||||
#define MG_EV_WEBSOCKET_FRAME 113 /* struct websocket_message * */
|
||||
#define MG_EV_WEBSOCKET_CONTROL_FRAME 114 /* struct websocket_message * */
|
||||
#endif
|
||||
@ -137,7 +145,9 @@ struct mg_ssi_call_ctx {
|
||||
* - MG_EV_WEBSOCKET_HANDSHAKE_REQUEST: server has received the WebSocket
|
||||
* handshake request. `ev_data` contains parsed HTTP request.
|
||||
* - MG_EV_WEBSOCKET_HANDSHAKE_DONE: server has completed the WebSocket
|
||||
* handshake. `ev_data` is `NULL`.
|
||||
* handshake. `ev_data` is a `struct http_message` containing the
|
||||
* client's request (server mode) or server's response (client).
|
||||
* In client mode handler can examine `resp_code`, which should be 101.
|
||||
* - MG_EV_WEBSOCKET_FRAME: new WebSocket frame has arrived. `ev_data` is
|
||||
* `struct websocket_message *`
|
||||
*
|
||||
|
@ -478,6 +478,8 @@ void mg_send_websocket_handshake3v(struct mg_connection *nc,
|
||||
}
|
||||
mg_printf(nc, "\r\n");
|
||||
|
||||
nc->flags |= MG_F_IS_WEBSOCKET;
|
||||
|
||||
mbuf_free(&auth);
|
||||
}
|
||||
|
||||
|
@ -34,7 +34,9 @@
|
||||
#include "mg_http.h"
|
||||
#include "mg_net.h"
|
||||
|
||||
#ifndef MG_CTL_MSG_MESSAGE_SIZE
|
||||
#define MG_CTL_MSG_MESSAGE_SIZE 8192
|
||||
#endif
|
||||
|
||||
/* internals that need to be accessible in unit tests */
|
||||
MG_INTERNAL struct mg_connection *mg_do_connect(struct mg_connection *nc,
|
||||
|
@ -196,26 +196,44 @@ static void mg_mqtt_proto_data_destructor(void *proto_data) {
|
||||
MG_FREE(proto_data);
|
||||
}
|
||||
|
||||
static struct mg_str mg_mqtt_next_topic_component(struct mg_str *topic) {
|
||||
struct mg_str res = *topic;
|
||||
const char *c = mg_strchr(*topic, '/');
|
||||
if (c != NULL) {
|
||||
res.len = (c - topic->p);
|
||||
topic->len -= (res.len + 1);
|
||||
topic->p += (res.len + 1);
|
||||
} else {
|
||||
topic->len = 0;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Refernce: https://mosquitto.org/man/mqtt-7.html */
|
||||
int mg_mqtt_match_topic_expression(struct mg_str exp, struct mg_str topic) {
|
||||
/* TODO(mkm): implement real matching */
|
||||
if (memchr(exp.p, '#', exp.len)) {
|
||||
/* exp `foo/#` will become `foo/` */
|
||||
exp.len -= 1;
|
||||
/*
|
||||
* topic should be longer than the expression: e.g. topic `foo/bar` does
|
||||
* match `foo/#`, but neither `foo` nor `foo/` do.
|
||||
*/
|
||||
if (topic.len <= exp.len) {
|
||||
struct mg_str ec, tc;
|
||||
if (exp.len == 0) return 0;
|
||||
while (1) {
|
||||
ec = mg_mqtt_next_topic_component(&exp);
|
||||
tc = mg_mqtt_next_topic_component(&topic);
|
||||
if (ec.len == 0) {
|
||||
if (tc.len != 0) return 0;
|
||||
if (exp.len == 0) break;
|
||||
continue;
|
||||
}
|
||||
if (mg_vcmp(&ec, "+") == 0) {
|
||||
if (tc.len == 0 && topic.len == 0) return 0;
|
||||
continue;
|
||||
}
|
||||
if (mg_vcmp(&ec, "#") == 0) {
|
||||
/* Must be the last component in the expression or it's invalid. */
|
||||
return (exp.len == 0);
|
||||
}
|
||||
if (mg_strcmp(ec, tc) != 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Truncate topic so that it'll pass the next length check */
|
||||
topic.len = exp.len;
|
||||
}
|
||||
if (topic.len != exp.len) {
|
||||
return 0;
|
||||
}
|
||||
return strncmp(topic.p, exp.p, exp.len) == 0;
|
||||
return (tc.len == 0 && topic.len == 0);
|
||||
}
|
||||
|
||||
int mg_mqtt_vmatch_topic_expression(const char *exp, struct mg_str topic) {
|
||||
|
53
src/mg_net.c
53
src/mg_net.c
@ -128,8 +128,16 @@ MG_INTERNAL size_t recv_avail_size(struct mg_connection *conn, size_t max) {
|
||||
static int mg_do_recv(struct mg_connection *nc);
|
||||
|
||||
int mg_if_poll(struct mg_connection *nc, double now) {
|
||||
if ((nc->flags & MG_F_CLOSE_IMMEDIATELY) ||
|
||||
(nc->send_mbuf.len == 0 && (nc->flags & MG_F_SEND_AND_CLOSE))) {
|
||||
if (nc->flags & MG_F_CLOSE_IMMEDIATELY) {
|
||||
mg_close_conn(nc);
|
||||
return 0;
|
||||
} else if (nc->flags & MG_F_SEND_AND_CLOSE) {
|
||||
if (nc->send_mbuf.len == 0) {
|
||||
nc->flags |= MG_F_CLOSE_IMMEDIATELY;
|
||||
mg_close_conn(nc);
|
||||
return 0;
|
||||
}
|
||||
} else if (nc->flags & MG_F_RECV_AND_CLOSE) {
|
||||
mg_close_conn(nc);
|
||||
return 0;
|
||||
}
|
||||
@ -172,6 +180,13 @@ void mg_destroy_conn(struct mg_connection *conn, int destroy_if) {
|
||||
}
|
||||
|
||||
void mg_close_conn(struct mg_connection *conn) {
|
||||
/* See if there's any remaining data to deliver. Skip if user completely
|
||||
* throttled the connection there will be no progress anyway. */
|
||||
if (conn->sock != INVALID_SOCKET && mg_do_recv(conn) == -2) {
|
||||
/* Receive is throttled, wait. */
|
||||
conn->flags |= MG_F_RECV_AND_CLOSE;
|
||||
return;
|
||||
}
|
||||
#if MG_ENABLE_SSL
|
||||
if (conn->flags & MG_F_SSL_HANDSHAKE_DONE) {
|
||||
mg_ssl_if_conn_close_notify(conn);
|
||||
@ -262,6 +277,7 @@ void mg_mgr_free(struct mg_mgr *m) {
|
||||
|
||||
for (conn = m->active_connections; conn != NULL; conn = tmp_conn) {
|
||||
tmp_conn = conn->next;
|
||||
conn->flags |= MG_F_CLOSE_IMMEDIATELY;
|
||||
mg_close_conn(conn);
|
||||
}
|
||||
|
||||
@ -565,18 +581,23 @@ static int mg_do_recv(struct mg_connection *nc) {
|
||||
((nc->flags & MG_F_LISTENING) && !(nc->flags & MG_F_UDP))) {
|
||||
return -1;
|
||||
}
|
||||
len = recv_avail_size(nc, len);
|
||||
if (len == 0) return -2;
|
||||
if (nc->recv_mbuf.size < nc->recv_mbuf.len + len) {
|
||||
mbuf_resize(&nc->recv_mbuf, nc->recv_mbuf.len + len);
|
||||
}
|
||||
buf = nc->recv_mbuf.buf + nc->recv_mbuf.len;
|
||||
len = nc->recv_mbuf.size - nc->recv_mbuf.len;
|
||||
if (nc->flags & MG_F_UDP) {
|
||||
res = mg_recv_udp(nc, buf, len);
|
||||
} else {
|
||||
res = mg_recv_tcp(nc, buf, len);
|
||||
}
|
||||
do {
|
||||
len = recv_avail_size(nc, len);
|
||||
if (len == 0) {
|
||||
res = -2;
|
||||
break;
|
||||
}
|
||||
if (nc->recv_mbuf.size < nc->recv_mbuf.len + len) {
|
||||
mbuf_resize(&nc->recv_mbuf, nc->recv_mbuf.len + len);
|
||||
}
|
||||
buf = nc->recv_mbuf.buf + nc->recv_mbuf.len;
|
||||
len = nc->recv_mbuf.size - nc->recv_mbuf.len;
|
||||
if (nc->flags & MG_F_UDP) {
|
||||
res = mg_recv_udp(nc, buf, len);
|
||||
} else {
|
||||
res = mg_recv_tcp(nc, buf, len);
|
||||
}
|
||||
} while (res > 0 && !(nc->flags & (MG_F_CLOSE_IMMEDIATELY | MG_F_UDP)));
|
||||
return res;
|
||||
}
|
||||
|
||||
@ -693,7 +714,9 @@ static int mg_recv_udp(struct mg_connection *nc, char *buf, size_t len) {
|
||||
mg_hexdump_connection(nc, nc->mgr->hexdump_file, buf, n, MG_EV_RECV);
|
||||
}
|
||||
#endif
|
||||
mg_call(nc, NULL, nc->user_data, MG_EV_RECV, &n);
|
||||
if (n != 0) {
|
||||
mg_call(nc, NULL, nc->user_data, MG_EV_RECV, &n);
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
|
@ -140,6 +140,7 @@ struct mg_connection {
|
||||
#define MG_F_WANT_READ (1 << 6) /* SSL specific */
|
||||
#define MG_F_WANT_WRITE (1 << 7) /* SSL specific */
|
||||
#define MG_F_IS_WEBSOCKET (1 << 8) /* Websocket specific */
|
||||
#define MG_F_RECV_AND_CLOSE (1 << 9) /* Drain rx and close the connection. */
|
||||
|
||||
/* Flags that are settable by user */
|
||||
#define MG_F_SEND_AND_CLOSE (1 << 10) /* Push remaining data and close */
|
||||
@ -225,7 +226,7 @@ int mg_mgr_poll(struct mg_mgr *mgr, int milli);
|
||||
* `func` callback function will be called by the IO thread for each
|
||||
* connection. When called, the event will be `MG_EV_POLL`, and a message will
|
||||
* be passed as the `ev_data` pointer. Maximum message size is capped
|
||||
* by `MG_CTL_MSG_MESSAGE_SIZE` which is set to 8192 bytes.
|
||||
* by `MG_CTL_MSG_MESSAGE_SIZE` which is set to 8192 bytes by default.
|
||||
*/
|
||||
void mg_broadcast(struct mg_mgr *mgr, mg_event_handler_t cb, void *data,
|
||||
size_t len);
|
||||
|
@ -9,7 +9,9 @@ SRC_DIR = ../src
|
||||
COMMON_DIR_PUB = ../src/common
|
||||
COMMON_DIR_DEV = ../../common
|
||||
|
||||
ifneq ("$(wildcard ../../fw/defs)","")
|
||||
# Our dev repo and public mongoose repo have different layouts, so here
|
||||
# we make it work on both
|
||||
ifneq ("$(wildcard ../../fw)","")
|
||||
COMMON_PARENT = ../..
|
||||
else
|
||||
COMMON_PARENT = $(SRC_DIR)
|
||||
@ -19,8 +21,6 @@ TEST_SOURCES = unit_test.c test_util.c test_main.c
|
||||
AMALGAMATED_SOURCES = ../mongoose.c
|
||||
KRYPTON_PATH = $(REPO_ROOT)/krypton
|
||||
|
||||
# Our dev repo and public mongoose repo have different layouts, so here
|
||||
# we make it work on both
|
||||
VPATH = $(MONGOOSE_DIR) $(SRC_DIR) $(COMMON_DIR_PUB) $(COMMON_DIR_DEV) $(COMMON_PARENT)
|
||||
|
||||
# or Krypton, or mbedTLS
|
||||
|
128
test/unit_test.c
128
test/unit_test.c
@ -15,12 +15,12 @@
|
||||
* license, as set out in <https://www.cesanta.com/license>.
|
||||
*/
|
||||
|
||||
#include "mongoose.h"
|
||||
#include "src/mg_internal.h"
|
||||
#include "unit_test.h"
|
||||
#include "common/cs_md5.h"
|
||||
#include "common/test_main.h"
|
||||
#include "common/test_util.h"
|
||||
#include "common/cs_md5.h"
|
||||
#include "mongoose.h"
|
||||
#include "src/mg_internal.h"
|
||||
|
||||
#if defined(__STDC_VERSION__) && __STDC_VERSION__ < 199901L && !defined(WIN32)
|
||||
#define __func__ ""
|
||||
@ -2011,7 +2011,8 @@ static const char *test_http(void) {
|
||||
char buf[50] = "", status[100] = "", mime1[20] = "", mime2[100] = "";
|
||||
char opt_buf[1024] = "";
|
||||
const char *opt_answer =
|
||||
"HTTP/1.1 200 OK\r\nAllow: GET, POST, HEAD, CONNECT, OPTIONS, MKCOL, "
|
||||
"HTTP/1.1 200 OK\r\nServer: Mongoose/" MG_VERSION
|
||||
"\r\nAllow: GET, POST, HEAD, CONNECT, OPTIONS, MKCOL, "
|
||||
"PUT, DELETE, PROPFIND, MOVE";
|
||||
char url[1000];
|
||||
|
||||
@ -2539,32 +2540,6 @@ static const char *test_http_range(void) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void cb3(struct mg_connection *nc, int ev, void *ev_data) {
|
||||
struct websocket_message *wm = (struct websocket_message *) ev_data;
|
||||
|
||||
if (ev == MG_EV_WEBSOCKET_FRAME) {
|
||||
const char *reply = wm->size == 2 && !memcmp(wm->data, "hi", 2) ? "A" : "B";
|
||||
mg_printf_websocket_frame(nc, WEBSOCKET_OP_TEXT, "%s", reply);
|
||||
}
|
||||
}
|
||||
|
||||
static void cb4(struct mg_connection *nc, int ev, void *ev_data) {
|
||||
struct websocket_message *wm = (struct websocket_message *) ev_data;
|
||||
|
||||
if (ev == MG_EV_WEBSOCKET_FRAME) {
|
||||
memcpy(nc->user_data, wm->data, wm->size);
|
||||
mg_send_websocket_frame(nc, WEBSOCKET_OP_CLOSE, NULL, 0);
|
||||
} else if (ev == MG_EV_WEBSOCKET_HANDSHAKE_DONE) {
|
||||
/* Send "hi" to server. server must reply "A". */
|
||||
struct mg_str h[2];
|
||||
h[0].p = "h";
|
||||
h[0].len = 1;
|
||||
h[1].p = "i";
|
||||
h[1].len = 1;
|
||||
mg_send_websocket_framev(nc, WEBSOCKET_OP_TEXT, h, 2);
|
||||
}
|
||||
}
|
||||
|
||||
static void cb_ws_server(struct mg_connection *nc, int ev, void *ev_data) {
|
||||
struct websocket_message *wm = (struct websocket_message *) ev_data;
|
||||
|
||||
@ -2779,6 +2754,38 @@ static const char *test_websocket(void) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void cb3(struct mg_connection *nc, int ev, void *ev_data) {
|
||||
struct websocket_message *wm = (struct websocket_message *) ev_data;
|
||||
if (ev != MG_EV_WEBSOCKET_FRAME) return;
|
||||
DBG(("server data '%.*s'", (int) wm->size, wm->data));
|
||||
const char *reply = wm->size == 2 && !memcmp(wm->data, "hi", 2) ? "A" : "B";
|
||||
mg_printf_websocket_frame(nc, WEBSOCKET_OP_TEXT, "%s", reply);
|
||||
}
|
||||
|
||||
static void cb4(struct mg_connection *nc, int ev, void *ev_data) {
|
||||
char *buf = (char *) nc->user_data;
|
||||
if (ev == MG_EV_WEBSOCKET_FRAME) {
|
||||
struct websocket_message *wm = (struct websocket_message *) ev_data;
|
||||
DBG(("client data '%.*s'", (int) wm->size, wm->data));
|
||||
memcpy(buf, wm->data, wm->size);
|
||||
mg_send_websocket_frame(nc, WEBSOCKET_OP_CLOSE, NULL, 0);
|
||||
} else if (ev == MG_EV_WEBSOCKET_HANDSHAKE_DONE) {
|
||||
struct http_message *hm = (struct http_message *) ev_data;
|
||||
DBG(("code %d", hm->resp_code));
|
||||
if (hm->resp_code == 101) {
|
||||
/* Send "hi" to server. server must reply "A". */
|
||||
struct mg_str h[2];
|
||||
h[0].p = "h";
|
||||
h[0].len = 1;
|
||||
h[1].p = "i";
|
||||
h[1].len = 1;
|
||||
mg_send_websocket_framev(nc, WEBSOCKET_OP_TEXT, h, 2);
|
||||
} else {
|
||||
snprintf(buf, 20, "code %d", hm->resp_code);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void cbwep(struct mg_connection *c, int ev, void *ev_data) {
|
||||
struct websocket_message *wm = (struct websocket_message *) ev_data;
|
||||
char *buf = (char *) c->user_data;
|
||||
@ -2786,6 +2793,9 @@ static void cbwep(struct mg_connection *c, int ev, void *ev_data) {
|
||||
switch (ev) {
|
||||
case MG_EV_WEBSOCKET_HANDSHAKE_REQUEST:
|
||||
strcat(buf, "R");
|
||||
if (buf[0] != '0') {
|
||||
mg_http_send_error(c, 403, "I don't like you");
|
||||
}
|
||||
break;
|
||||
case MG_EV_WEBSOCKET_HANDSHAKE_DONE:
|
||||
strcat(buf, "D");
|
||||
@ -2802,7 +2812,7 @@ static const char *test_websocket_endpoint(void) {
|
||||
struct mg_mgr mgr;
|
||||
struct mg_connection *nc;
|
||||
const char *local_addr = "127.0.0.1:7798";
|
||||
char buf[20] = "", buf2[20] = "";
|
||||
char buf[20] = "", buf2[20] = "0";
|
||||
|
||||
mg_mgr_init(&mgr, NULL);
|
||||
/* mgr.hexdump_file = "-"; */
|
||||
@ -2817,10 +2827,21 @@ static const char *test_websocket_endpoint(void) {
|
||||
nc->user_data = buf;
|
||||
mg_send_websocket_handshake(nc, "/boo", NULL);
|
||||
poll_until(&mgr, 1, c_str_ne, buf, (void *) "");
|
||||
mg_mgr_free(&mgr);
|
||||
|
||||
/* Check that test buffer has been filled by the callback properly. */
|
||||
ASSERT_STREQ(buf, "RDF|hi");
|
||||
ASSERT_STREQ(buf, "0RDF|hi");
|
||||
|
||||
/* Test handshake failure */
|
||||
ASSERT((nc = mg_connect(&mgr, local_addr, cb4)) != NULL);
|
||||
mg_set_protocol_http_websocket(nc);
|
||||
buf[0] = '\0';
|
||||
buf2[0] = '1';
|
||||
buf2[1] = '\0';
|
||||
nc->user_data = buf;
|
||||
mg_send_websocket_handshake(nc, "/boo", NULL);
|
||||
poll_until(&mgr, 1, c_str_ne, buf, (void *) "");
|
||||
ASSERT_STREQ(buf, "code 403");
|
||||
|
||||
mg_mgr_free(&mgr);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@ -3379,15 +3400,36 @@ static const char *test_mqtt_parse_mqtt_qos1(void) {
|
||||
}
|
||||
|
||||
static const char *test_mqtt_match_topic_expression(void) {
|
||||
ASSERT_EQ(mg_mqtt_vmatch_topic_expression("foo", mg_mk_str("foo")), 1);
|
||||
ASSERT_EQ(mg_mqtt_vmatch_topic_expression("foo", mg_mk_str("foo/")), 0);
|
||||
ASSERT_EQ(mg_mqtt_vmatch_topic_expression("foo", mg_mk_str("foo/bar")), 0);
|
||||
ASSERT(mg_mqtt_vmatch_topic_expression("foo", mg_mk_str("foo")));
|
||||
ASSERT(mg_mqtt_vmatch_topic_expression("/foo", mg_mk_str("/foo")));
|
||||
ASSERT(mg_mqtt_vmatch_topic_expression("+/foo", mg_mk_str("/foo")));
|
||||
ASSERT(!mg_mqtt_vmatch_topic_expression("foo", mg_mk_str("foobar")));
|
||||
ASSERT(mg_mqtt_vmatch_topic_expression("foo", mg_mk_str("foo/")));
|
||||
ASSERT(mg_mqtt_vmatch_topic_expression("foo", mg_mk_str("foo//")));
|
||||
ASSERT(!mg_mqtt_vmatch_topic_expression("foo", mg_mk_str("foo/bar")));
|
||||
ASSERT(!mg_mqtt_vmatch_topic_expression("foo", mg_mk_str("foo/+")));
|
||||
ASSERT(mg_mqtt_vmatch_topic_expression("foo/bar", mg_mk_str("foo/bar")));
|
||||
ASSERT(mg_mqtt_vmatch_topic_expression("foo/+", mg_mk_str("foo/bar")));
|
||||
ASSERT(mg_mqtt_vmatch_topic_expression("+/bar", mg_mk_str("foo/bar")));
|
||||
ASSERT(mg_mqtt_vmatch_topic_expression("+/+", mg_mk_str("foo/bar")));
|
||||
ASSERT(mg_mqtt_vmatch_topic_expression("foo/+/bar", mg_mk_str("foo//bar")));
|
||||
ASSERT(!mg_mqtt_vmatch_topic_expression("foo/+/+", mg_mk_str("foo/bar")));
|
||||
ASSERT(mg_mqtt_vmatch_topic_expression("foo/+/#", mg_mk_str("foo/bar")));
|
||||
ASSERT(mg_mqtt_vmatch_topic_expression("+/foo/bar", mg_mk_str("/foo/bar")));
|
||||
|
||||
ASSERT_EQ(mg_mqtt_vmatch_topic_expression("foo/#", mg_mk_str("foo")), 0);
|
||||
ASSERT_EQ(mg_mqtt_vmatch_topic_expression("foo/#", mg_mk_str("foo/")), 0);
|
||||
ASSERT_EQ(mg_mqtt_vmatch_topic_expression("foo/#", mg_mk_str("foo/bar")), 1);
|
||||
ASSERT_EQ(mg_mqtt_vmatch_topic_expression("foo/#", mg_mk_str("foo/bar/baz")),
|
||||
1);
|
||||
ASSERT(!mg_mqtt_vmatch_topic_expression("", mg_mk_str("")));
|
||||
ASSERT(mg_mqtt_vmatch_topic_expression("/", mg_mk_str("")));
|
||||
ASSERT(mg_mqtt_vmatch_topic_expression("/", mg_mk_str("/")));
|
||||
|
||||
ASSERT(mg_mqtt_vmatch_topic_expression("#", mg_mk_str("")));
|
||||
ASSERT(mg_mqtt_vmatch_topic_expression("#", mg_mk_str("foo")));
|
||||
ASSERT(mg_mqtt_vmatch_topic_expression("#", mg_mk_str("foo/bar")));
|
||||
ASSERT(mg_mqtt_vmatch_topic_expression("foo/#", mg_mk_str("foo")));
|
||||
ASSERT(mg_mqtt_vmatch_topic_expression("foo/#", mg_mk_str("foo/")));
|
||||
ASSERT(mg_mqtt_vmatch_topic_expression("foo/#", mg_mk_str("foo/bar")));
|
||||
ASSERT(mg_mqtt_vmatch_topic_expression("foo/#", mg_mk_str("foo/bar/baz")));
|
||||
ASSERT(!mg_mqtt_vmatch_topic_expression("#/foo", mg_mk_str("foo")));
|
||||
ASSERT(!mg_mqtt_vmatch_topic_expression("#/foo", mg_mk_str("bar/foo")));
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@ -5665,7 +5707,7 @@ static const char *test_socks(void) {
|
||||
mbuf_resize(&c->recv_mbuf, 10000000);
|
||||
|
||||
/* Run event loop. Use more cycles to let file download complete. */
|
||||
poll_until(&mgr, 10, c_str_ne, status, (void *) "");
|
||||
poll_until(&mgr, 15, c_str_ne, status, (void *) "");
|
||||
ASSERT_STREQ(status, "success");
|
||||
|
||||
mg_mgr_free(&mgr);
|
||||
|
Loading…
Reference in New Issue
Block a user