Commit ab650ec5 authored by Deomid Ryabkov's avatar Deomid Ryabkov

Merge branch 'dev'

parents 95afa323 8f365970
......@@ -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 }
......
---
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.
---
title: "mbuf_clear()"
decl_name: "mbuf_clear"
symbol_kind: "func"
signature: |
void mbuf_clear(struct mbuf *);
---
Removes all the data from mbuf (if any).
---
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. */
......
/*
* 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);
#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)
#define LOG(l, x) \
if (cs_log_threshold >= l) { \
cs_log_print_prefix(__func__); \
cs_log_printf x; \
}
#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;
}
......
This diff is collapsed.
......@@ -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.
*
* For the pattern syntax, refer to `mg_match_prefix()` in `str_util.h`.
*
* Example:
* ```c
* void foo(void) {
* LOG(LL_INFO, ("hello from foo"));
* }
*
* void bar(void) {
* LOG(LL_INFO, ("hello from bar"));
* }
* 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.
*
* void test(void) {
* cs_log_set_filter(NULL);
* foo();
* bar();
* 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_*
*
* 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
......
/*
* 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))
......
#!/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
}
uint32_t *token = NULL;
if (foi != NULL) {
am |= foi->flags;
token = foi->token;
}
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));
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
......
......@@ -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 (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) {
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;
}
return;
}
#endif /* MG_ENABLE_HTTP_STREAMING_MULTIPART */
if (ev == MG_EV_RECV) {
struct mg_str *s;
pd->rcvd += *(int *) ev_data;
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) {
......
......@@ -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
......
......@@ -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_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("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(!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);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment