cleanup 'str' API

This commit is contained in:
Sergio R. Caprile 2024-04-30 18:19:42 -03:00
parent 6c7672f4af
commit 3c531897b6
20 changed files with 350 additions and 400 deletions

View File

@ -2227,6 +2227,17 @@ struct mg_fs mg_fs_posix = {p_stat, p_list, p_open, p_close, p_read,
static int mg_ncasecmp(const char *s1, const char *s2, size_t len) {
int diff = 0;
if (len > 0) do {
char c = *s1++, d = *s2++;
if (c >= 'A' && c <= 'Z') c += 'a' - 'A';
if (d >= 'A' && d <= 'Z') d += 'a' - 'A';
diff = c - d;
} while (diff == 0 && s1[-1] != '\0' && --len > 0);
return diff;
}
bool mg_to_size_t(struct mg_str str, size_t *val);
bool mg_to_size_t(struct mg_str str, size_t *val) {
size_t i = 0, max = (size_t) -1, max2 = max / 10, result = 0, ndigits = 0;
@ -2517,15 +2528,15 @@ int mg_http_parse(const char *s, size_t len, struct mg_http_message *hm) {
// and method is not (PUT or POST) then reset body length to zero.
is_response = mg_ncasecmp(hm->method.buf, "HTTP/", 5) == 0;
if (hm->body.len == (size_t) ~0 && !is_response &&
mg_vcasecmp(&hm->method, "PUT") != 0 &&
mg_vcasecmp(&hm->method, "POST") != 0) {
mg_strcasecmp(hm->method, mg_str("PUT")) != 0 &&
mg_strcasecmp(hm->method, mg_str("POST")) != 0) {
hm->body.len = 0;
hm->message.len = (size_t) req_len;
}
// The 204 (No content) responses also have 0 body length
if (hm->body.len == (size_t) ~0 && is_response &&
mg_vcasecmp(&hm->uri, "204") == 0) {
mg_strcasecmp(hm->uri, mg_str("204")) == 0) {
hm->body.len = 0;
hm->message.len = (size_t) req_len;
}
@ -2776,10 +2787,14 @@ void mg_http_serve_file(struct mg_connection *c, struct mg_http_message *hm,
if (path != NULL) {
// If a browser sends us "Accept-Encoding: gzip", try to open .gz first
struct mg_str *ae = mg_http_get_header(hm, "Accept-Encoding");
if (ae != NULL && mg_strstr(*ae, mg_str("gzip")) != NULL) {
mg_snprintf(tmp, sizeof(tmp), "%s.gz", path);
fd = mg_fs_open(fs, tmp, MG_FS_READ);
if (fd != NULL) gzip = true, path = tmp;
if (ae != NULL) {
char *ae_ = mg_mprintf("%.*s", ae->len, ae->buf);
if (strstr(ae_, "gzip") != NULL) {
mg_snprintf(tmp, sizeof(tmp), "%s.gz", path);
fd = mg_fs_open(fs, tmp, MG_FS_READ);
if (fd != NULL) gzip = true, path = tmp;
}
free(ae_);
}
// No luck opening .gz? Open what we've told to open
if (fd == NULL) fd = mg_fs_open(fs, path, MG_FS_READ);
@ -2798,7 +2813,7 @@ void mg_http_serve_file(struct mg_connection *c, struct mg_http_message *hm,
// NOTE: mg_http_etag() call should go first!
} else if (mg_http_etag(etag, sizeof(etag), size, mtime) != NULL &&
(inm = mg_http_get_header(hm, "If-None-Match")) != NULL &&
mg_vcasecmp(inm, etag) == 0) {
mg_strcasecmp(*inm, mg_str(etag)) == 0) {
mg_fs_close(fd);
mg_http_reply(c, 304, opts->extra_headers, "");
} else {
@ -2835,7 +2850,7 @@ void mg_http_serve_file(struct mg_connection *c, struct mg_http_message *hm,
status, mg_http_status_code_str(status), (int) mime.len, mime.buf,
etag, (uint64_t) cl, gzip ? "Content-Encoding: gzip\r\n" : "",
range, opts->extra_headers ? opts->extra_headers : "");
if (mg_vcasecmp(&hm->method, "HEAD") == 0) {
if (mg_strcasecmp(hm->method, mg_str("HEAD")) == 0) {
c->is_draining = 1;
c->is_resp = 0;
mg_fs_close(fd);
@ -2991,7 +3006,8 @@ static int uri_to_path2(struct mg_connection *c, struct mg_http_message *hm,
}
n = strlen(path);
while (n > 1 && path[n - 1] == '/') path[--n] = 0; // Trim trailing slashes
flags = mg_vcmp(&hm->uri, "/") == 0 ? MG_FS_DIR : fs->st(path, NULL, NULL);
flags = mg_strcmp(hm->uri, mg_str("/")) == 0 ? MG_FS_DIR
: fs->st(path, NULL, NULL);
MG_VERBOSE(("%lu %.*s -> %s %d", c->id, (int) hm->uri.len, hm->uri.buf, path,
flags));
if (flags == 0) {
@ -3054,8 +3070,7 @@ void mg_http_serve_dir(struct mg_connection *c, struct mg_http_message *hm,
#else
mg_http_reply(c, 403, "", "Forbidden\n");
#endif
} else if (flags && sp != NULL &&
mg_globmatch(sp, strlen(sp), path, strlen(path))) {
} else if (flags && sp != NULL && mg_match(mg_str(path), mg_str(sp), NULL)) {
mg_http_serve_ssi(c, opts->root_dir, path);
} else {
mg_http_serve_file(c, hm, path, opts);
@ -3075,9 +3090,8 @@ size_t mg_url_encode(const char *s, size_t sl, char *buf, size_t len) {
if (mg_is_url_safe(c)) {
buf[n++] = s[i];
} else {
buf[n++] = '%';
mg_hex(&s[i], 1, &buf[n]);
n += 2;
mg_snprintf(&buf[n], 4, "%%%M", mg_print_hex, 1, &s[i]);
n += 3;
}
}
if (len > 0 && n < len - 1) buf[n] = '\0'; // Null-terminate the destination
@ -3223,7 +3237,7 @@ static void http_cb(struct mg_connection *c, int ev, void *ev_data) {
hm.body.len = hm.message.len - (size_t) (hm.body.buf - hm.message.buf);
}
if ((te = mg_http_get_header(&hm, "Transfer-Encoding")) != NULL) {
if (mg_vcasecmp(te, "chunked") == 0) {
if (mg_strcasecmp(*te, mg_str("chunked")) == 0) {
is_chunked = true;
} else {
mg_error(c, "Invalid Transfer-Encoding"); // See #2460
@ -3234,8 +3248,8 @@ static void http_cb(struct mg_connection *c, int ev, void *ev_data) {
// Content-length
bool is_response = mg_ncasecmp(hm.method.buf, "HTTP/", 5) == 0;
bool require_content_len = false;
if (!is_response && (mg_vcasecmp(&hm.method, "POST") == 0 ||
mg_vcasecmp(&hm.method, "PUT") == 0)) {
if (!is_response && (mg_strcasecmp(hm.method, mg_str("POST")) == 0 ||
mg_strcasecmp(hm.method, mg_str("PUT")) == 0)) {
// POST and PUT should include an entity body. Therefore, they should
// contain a Content-length header. Other requests can also contain a
// body, but their content has no defined semantics (RFC 7231)
@ -4307,14 +4321,13 @@ size_t mg_mqtt_next_prop(struct mg_mqtt_message *msg, struct mg_mqtt_prop *prop,
}
void mg_mqtt_login(struct mg_connection *c, const struct mg_mqtt_opts *opts) {
char rnd[10], client_id[21];
char client_id[21];
struct mg_str cid = opts->client_id;
size_t total_len = 7 + 1 + 2 + 2;
uint8_t hdr[8] = {0, 4, 'M', 'Q', 'T', 'T', opts->version, 0};
if (cid.len == 0) {
mg_random(rnd, sizeof(rnd));
mg_hex(rnd, sizeof(rnd), client_id);
mg_random_str(client_id, sizeof(client_id) - 1);
client_id[sizeof(client_id) - 1] = '\0';
cid = mg_str(client_id);
}
@ -4626,7 +4639,7 @@ size_t mg_printf(struct mg_connection *c, const char *fmt, ...) {
static bool mg_atonl(struct mg_str str, struct mg_addr *addr) {
uint32_t localhost = mg_htonl(0x7f000001);
if (mg_vcasecmp(&str, "localhost") != 0) return false;
if (mg_strcasecmp(str, mg_str("localhost")) != 0) return false;
memcpy(addr->ip, &localhost, sizeof(uint32_t));
addr->is_ip6 = false;
return true;
@ -6618,7 +6631,10 @@ void mg_rpc_add(struct mg_rpc **head, struct mg_str method,
void (*fn)(struct mg_rpc_req *), void *fn_data) {
struct mg_rpc *rpc = (struct mg_rpc *) calloc(1, sizeof(*rpc));
if (rpc != NULL) {
rpc->method = mg_strdup(method), rpc->fn = fn, rpc->fn_data = fn_data;
rpc->method.buf = mg_mprintf("%.*s", method.len, method.buf);
rpc->method.len = method.len;
rpc->fn = fn;
rpc->fn_data = fn_data;
rpc->next = *head, *head = rpc;
}
}
@ -8018,57 +8034,24 @@ struct mg_str mg_str_n(const char *s, size_t n) {
return str;
}
int mg_lower(const char *s) {
int c = *s;
if (c >= 'A' && c <= 'Z') c += 'a' - 'A';
return c;
}
int mg_ncasecmp(const char *s1, const char *s2, size_t len) {
int diff = 0;
if (len > 0) do {
diff = mg_lower(s1++) - mg_lower(s2++);
} while (diff == 0 && s1[-1] != '\0' && --len > 0);
return diff;
static char tolc(char c) {
return (c >= 'A' && c <= 'Z') ? c + 'a' - 'A' : c;
}
int mg_casecmp(const char *s1, const char *s2) {
return mg_ncasecmp(s1, s2, (size_t) ~0);
}
int mg_vcmp(const struct mg_str *s1, const char *s2) {
size_t n2 = strlen(s2), n1 = s1->len;
int r = strncmp(s1->buf, s2, (n1 < n2) ? n1 : n2);
if (r == 0) return (int) (n1 - n2);
return r;
}
int mg_vcasecmp(const struct mg_str *str1, const char *str2) {
size_t n2 = strlen(str2), n1 = str1->len;
int r = mg_ncasecmp(str1->buf, str2, (n1 < n2) ? n1 : n2);
if (r == 0) return (int) (n1 - n2);
return r;
}
struct mg_str mg_strdup(const struct mg_str s) {
struct mg_str r = {NULL, 0};
if (s.len > 0 && s.buf != NULL) {
char *sc = (char *) calloc(1, s.len + 1);
if (sc != NULL) {
memcpy(sc, s.buf, s.len);
sc[s.len] = '\0';
r.buf = sc;
r.len = s.len;
}
}
return r;
int diff = 0;
do {
char c = tolc(*s1++), d = tolc(*s2++);
diff = c - d;
} while (diff == 0 && s1[-1] != '\0');
return diff;
}
int mg_strcmp(const struct mg_str str1, const struct mg_str str2) {
size_t i = 0;
while (i < str1.len && i < str2.len) {
int c1 = str1.buf[i];
int c2 = str2.buf[i];
char c1 = str1.buf[i];
char c2 = str2.buf[i];
if (c1 < c2) return -1;
if (c1 > c2) return 1;
i++;
@ -8078,27 +8061,18 @@ int mg_strcmp(const struct mg_str str1, const struct mg_str str2) {
return 0;
}
const char *mg_strstr(const struct mg_str haystack,
const struct mg_str needle) {
size_t i;
if (needle.len > haystack.len) return NULL;
if (needle.len == 0) return haystack.buf;
for (i = 0; i <= haystack.len - needle.len; i++) {
if (memcmp(haystack.buf + i, needle.buf, needle.len) == 0) {
return haystack.buf + i;
}
int mg_strcasecmp(const struct mg_str str1, const struct mg_str str2) {
size_t i = 0;
while (i < str1.len && i < str2.len) {
char c1 = tolc(str1.buf[i]);
char c2 = tolc(str2.buf[i]);
if (c1 < c2) return -1;
if (c1 > c2) return 1;
i++;
}
return NULL;
}
static bool is_space(int c) {
return c == ' ' || c == '\r' || c == '\n' || c == '\t';
}
struct mg_str mg_strstrip(struct mg_str s) {
while (s.len > 0 && is_space((int) *s.buf)) s.buf++, s.len--;
while (s.len > 0 && is_space((int) *(s.buf + s.len - 1))) s.len--;
return s;
if (i < str1.len) return 1;
if (i < str2.len) return -1;
return 0;
}
bool mg_match(struct mg_str s, struct mg_str p, struct mg_str *caps) {
@ -8133,10 +8107,6 @@ bool mg_match(struct mg_str s, struct mg_str p, struct mg_str *caps) {
return true;
}
bool mg_globmatch(const char *s1, size_t n1, const char *s2, size_t n2) {
return mg_match(mg_str_n(s2, n2), mg_str_n(s1, n1), NULL);
}
bool mg_span(struct mg_str s, struct mg_str *a, struct mg_str *b, char sep) {
if (s.len == 0 || s.buf == NULL) {
return false; // Empty string, nothing to span - fail
@ -8150,27 +8120,17 @@ bool mg_span(struct mg_str s, struct mg_str *a, struct mg_str *b, char sep) {
}
}
char *mg_hex(const void *buf, size_t len, char *to) {
const unsigned char *p = (const unsigned char *) buf;
const char *hex = "0123456789abcdef";
size_t i = 0;
for (; len--; p++) {
to[i++] = hex[p[0] >> 4];
to[i++] = hex[p[0] & 0x0f];
}
to[i] = '\0';
return to;
}
static unsigned char mg_unhex_nimble(unsigned char c) {
return (c >= '0' && c <= '9') ? (unsigned char) (c - '0')
: (c >= 'A' && c <= 'F') ? (unsigned char) (c - '7')
: (unsigned char) (c - 'W');
uint8_t mg_toi(char c, int base) {
return (c >= '0' && c <= '9') ? (uint8_t) (c - '0')
: base == 16 ? (c >= 'A' && c <= 'F') ? (uint8_t) (c - '7')
: (c >= 'a' && c <= 'f') ? (uint8_t) (c - 'W')
: (uint8_t) ~0
: (uint8_t) ~0;
}
unsigned long mg_unhexn(const char *s, size_t len) {
unsigned long i = 0, v = 0;
for (i = 0; i < len; i++) v <<= 4, v |= mg_unhex_nimble(((uint8_t *) s)[i]);
for (i = 0; i < len; i++) v <<= 4, v |= mg_toi(((char *) s)[i], 16);
return v;
}
@ -8181,18 +8141,6 @@ void mg_unhex(const char *buf, size_t len, unsigned char *to) {
}
}
bool mg_path_is_sane(const struct mg_str path) {
const char *s = path.buf;
size_t n = path.len;
if (path.buf[0] == '.' && path.buf[1] == '.') return false; // Starts with ..
for (; s[0] != '\0' && n > 0; s++, n--) {
if ((s[0] == '/' || s[0] == '\\') && n >= 2) { // Subdir?
if (s[1] == '.' && s[2] == '.') return false; // Starts with ..
}
}
return true;
}
#ifdef MG_ENABLE_LINES
#line 1 "src/timer.c"
#endif
@ -10607,7 +10555,8 @@ static int mg_parse_pem(const struct mg_str pem, const struct mg_str label,
const char *c;
struct mg_str caps[5];
if (!mg_match(pem, mg_str("#-----BEGIN #-----#-----END #-----#"), caps)) {
*der = mg_strdup(pem);
der->buf = mg_mprintf("%.*s", pem.len, pem.buf);
der->len = pem.len;
return 0;
}
if (mg_strcmp(caps[1], label) != 0 || mg_strcmp(caps[3], label) != 0) {
@ -10923,7 +10872,7 @@ void mg_tls_init(struct mg_connection *c, const struct mg_tls_opts *opts) {
}
mbedtls_ssl_conf_rng(&tls->conf, mg_mbed_rng, c);
if (opts->ca.len == 0 || mg_vcmp(&opts->ca, "*") == 0) {
if (opts->ca.len == 0 || mg_strcmp(opts->ca, mg_str("*")) == 0) {
mbedtls_ssl_conf_authmode(&tls->conf, MBEDTLS_SSL_VERIFY_NONE);
} else {
if (mg_load_cert(opts->ca, &tls->ca) == false) goto fail;
@ -14948,6 +14897,18 @@ int mg_check_ip_acl(struct mg_str acl, struct mg_addr *remote_ip) {
return allowed == '+';
}
bool mg_path_is_sane(const struct mg_str path) {
const char *s = path.buf;
size_t n = path.len;
if (path.buf[0] == '.' && path.buf[1] == '.') return false; // Starts with ..
for (; s[0] != '\0' && n > 0; s++, n--) {
if ((s[0] == '/' || s[0] == '\\') && n >= 2) { // Subdir?
if (s[1] == '.' && s[2] == '.') return false; // Starts with ..
}
}
return true;
}
#if MG_ENABLE_CUSTOM_MILLIS
#else
uint64_t mg_millis(void) {

View File

@ -186,7 +186,7 @@ extern "C" {
#define calloc(a, b) mg_calloc(a, b)
#define free(a) vPortFree(a)
#define malloc(a) pvPortMalloc(a)
#define strdup(s) ((char *) mg_strdup(mg_str(s)).buf)
#define strdup(s) mg_mprintf("%s", s)
// Re-route calloc/free to the FreeRTOS's functions, don't use stdlib
static inline void *mg_calloc(size_t cnt, size_t size) {
@ -288,7 +288,7 @@ extern uint32_t rt_time_get(void);
#include "cmsis_os2.h" // keep this include
#endif
#define strdup(s) ((char *) mg_strdup(mg_str(s)).buf)
#define strdup(s) mg_mprintf("%s", s)
#if defined(__ARMCC_VERSION)
#define mode_t size_t
@ -858,22 +858,16 @@ struct mg_str {
struct mg_str mg_str(const char *s);
struct mg_str mg_str_n(const char *s, size_t n);
int mg_lower(const char *s);
int mg_ncasecmp(const char *s1, const char *s2, size_t len);
int mg_casecmp(const char *s1, const char *s2);
int mg_vcmp(const struct mg_str *s1, const char *s2);
int mg_vcasecmp(const struct mg_str *str1, const char *str2);
int mg_strcmp(const struct mg_str str1, const struct mg_str str2);
struct mg_str mg_strstrip(struct mg_str s);
struct mg_str mg_strdup(const struct mg_str s);
const char *mg_strstr(const struct mg_str haystack, const struct mg_str needle);
int mg_strcasecmp(const struct mg_str str1, const struct mg_str str2); // this one is new
bool mg_match(struct mg_str str, struct mg_str pattern, struct mg_str *caps);
bool mg_globmatch(const char *pattern, size_t plen, const char *s, size_t n);
bool mg_span(struct mg_str s, struct mg_str *a, struct mg_str *b, char delim);
char *mg_hex(const void *buf, size_t len, char *dst);
void mg_unhex(const char *buf, size_t len, unsigned char *to);
unsigned long mg_unhexn(const char *s, size_t len);
bool mg_path_is_sane(const struct mg_str path);
uint8_t mg_toi(char c, int base); // base: 16, 10
@ -1061,6 +1055,7 @@ uint32_t mg_ntohl(uint32_t net);
uint32_t mg_crc32(uint32_t crc, const char *buf, size_t len);
uint64_t mg_millis(void); // Return milliseconds since boot
uint64_t mg_now(void); // Return milliseconds since Epoch
bool mg_path_is_sane(const struct mg_str path);
#define mg_htons(x) mg_ntohs(x)
#define mg_htonl(x) mg_ntohl(x)
@ -2925,6 +2920,49 @@ struct mg_tcpip_driver_tm4c_data {
#endif
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_W5500) && MG_ENABLE_DRIVER_W5500
#undef MG_ENABLE_TCPIP_DRIVER_INIT
#define MG_ENABLE_TCPIP_DRIVER_INIT 0
#endif
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_XMC7) && MG_ENABLE_DRIVER_XMC7
struct mg_tcpip_driver_xmc7_data {
int mdc_cr; // Valid values: -1, 0, 1, 2, 3, 4, 5
uint8_t phy_addr;
};
#ifndef MG_TCPIP_PHY_ADDR
#define MG_TCPIP_PHY_ADDR 0
#endif
#ifndef MG_DRIVER_MDC_CR
#define MG_DRIVER_MDC_CR 3
#endif
#define MG_TCPIP_DRIVER_INIT(mgr) \
do { \
static struct mg_tcpip_driver_xmc7_data driver_data_; \
static struct mg_tcpip_if mif_; \
driver_data_.mdc_cr = MG_DRIVER_MDC_CR; \
driver_data_.phy_addr = MG_TCPIP_PHY_ADDR; \
mif_.ip = MG_TCPIP_IP; \
mif_.mask = MG_TCPIP_MASK; \
mif_.gw = MG_TCPIP_GW; \
mif_.driver = &mg_tcpip_driver_xmc7; \
mif_.driver_data = &driver_data_; \
MG_SET_MAC_ADDRESS(mif_.mac); \
mg_tcpip_init(mgr, &mif_); \
MG_INFO(("Driver: xmc7, MAC: %M", mg_print_mac, mif_.mac)); \
} while (0)
#endif
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_XMC) && MG_ENABLE_DRIVER_XMC
struct mg_tcpip_driver_xmc_data {
@ -2971,41 +3009,6 @@ struct mg_tcpip_driver_xmc_data {
#endif
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_XMC7) && MG_ENABLE_DRIVER_XMC7
struct mg_tcpip_driver_xmc7_data {
int mdc_cr; // Valid values: -1, 0, 1, 2, 3, 4, 5
uint8_t phy_addr;
};
#ifndef MG_TCPIP_PHY_ADDR
#define MG_TCPIP_PHY_ADDR 0
#endif
#ifndef MG_DRIVER_MDC_CR
#define MG_DRIVER_MDC_CR 3
#endif
#define MG_TCPIP_DRIVER_INIT(mgr) \
do { \
static struct mg_tcpip_driver_xmc7_data driver_data_; \
static struct mg_tcpip_if mif_; \
driver_data_.mdc_cr = MG_DRIVER_MDC_CR; \
driver_data_.phy_addr = MG_TCPIP_PHY_ADDR; \
mif_.ip = MG_TCPIP_IP; \
mif_.mask = MG_TCPIP_MASK; \
mif_.gw = MG_TCPIP_GW; \
mif_.driver = &mg_tcpip_driver_xmc7; \
mif_.driver_data = &driver_data_; \
MG_SET_MAC_ADDRESS(mif_.mac); \
mg_tcpip_init(mgr, &mif_); \
MG_INFO(("Driver: xmc7, MAC: %M", mg_print_mac, mif_.mac)); \
} while (0)
#endif
#ifdef __cplusplus
}
#endif

View File

@ -32,7 +32,7 @@
#define calloc(a, b) mg_calloc(a, b)
#define free(a) vPortFree(a)
#define malloc(a) pvPortMalloc(a)
#define strdup(s) ((char *) mg_strdup(mg_str(s)).buf)
#define strdup(s) mg_mprintf("%s", s)
// Re-route calloc/free to the FreeRTOS's functions, don't use stdlib
static inline void *mg_calloc(size_t cnt, size_t size) {

View File

@ -22,7 +22,7 @@ extern uint32_t rt_time_get(void);
#include "cmsis_os2.h" // keep this include
#endif
#define strdup(s) ((char *) mg_strdup(mg_str(s)).buf)
#define strdup(s) mg_mprintf("%s", s)
#if defined(__ARMCC_VERSION)
#define mode_t size_t

View File

@ -1,7 +1,7 @@
#include "http.h"
#include "arch.h"
#include "base64.h"
#include "fmt.h"
#include "http.h"
#include "json.h"
#include "log.h"
#include "net.h"
@ -11,6 +11,17 @@
#include "version.h"
#include "ws.h"
static int mg_ncasecmp(const char *s1, const char *s2, size_t len) {
int diff = 0;
if (len > 0) do {
char c = *s1++, d = *s2++;
if (c >= 'A' && c <= 'Z') c += 'a' - 'A';
if (d >= 'A' && d <= 'Z') d += 'a' - 'A';
diff = c - d;
} while (diff == 0 && s1[-1] != '\0' && --len > 0);
return diff;
}
bool mg_to_size_t(struct mg_str str, size_t *val);
bool mg_to_size_t(struct mg_str str, size_t *val) {
size_t i = 0, max = (size_t) -1, max2 = max / 10, result = 0, ndigits = 0;
@ -301,15 +312,15 @@ int mg_http_parse(const char *s, size_t len, struct mg_http_message *hm) {
// and method is not (PUT or POST) then reset body length to zero.
is_response = mg_ncasecmp(hm->method.buf, "HTTP/", 5) == 0;
if (hm->body.len == (size_t) ~0 && !is_response &&
mg_vcasecmp(&hm->method, "PUT") != 0 &&
mg_vcasecmp(&hm->method, "POST") != 0) {
mg_strcasecmp(hm->method, mg_str("PUT")) != 0 &&
mg_strcasecmp(hm->method, mg_str("POST")) != 0) {
hm->body.len = 0;
hm->message.len = (size_t) req_len;
}
// The 204 (No content) responses also have 0 body length
if (hm->body.len == (size_t) ~0 && is_response &&
mg_vcasecmp(&hm->uri, "204") == 0) {
mg_strcasecmp(hm->uri, mg_str("204")) == 0) {
hm->body.len = 0;
hm->message.len = (size_t) req_len;
}
@ -560,10 +571,14 @@ void mg_http_serve_file(struct mg_connection *c, struct mg_http_message *hm,
if (path != NULL) {
// If a browser sends us "Accept-Encoding: gzip", try to open .gz first
struct mg_str *ae = mg_http_get_header(hm, "Accept-Encoding");
if (ae != NULL && mg_strstr(*ae, mg_str("gzip")) != NULL) {
mg_snprintf(tmp, sizeof(tmp), "%s.gz", path);
fd = mg_fs_open(fs, tmp, MG_FS_READ);
if (fd != NULL) gzip = true, path = tmp;
if (ae != NULL) {
char *ae_ = mg_mprintf("%.*s", ae->len, ae->buf);
if (strstr(ae_, "gzip") != NULL) {
mg_snprintf(tmp, sizeof(tmp), "%s.gz", path);
fd = mg_fs_open(fs, tmp, MG_FS_READ);
if (fd != NULL) gzip = true, path = tmp;
}
free(ae_);
}
// No luck opening .gz? Open what we've told to open
if (fd == NULL) fd = mg_fs_open(fs, path, MG_FS_READ);
@ -582,7 +597,7 @@ void mg_http_serve_file(struct mg_connection *c, struct mg_http_message *hm,
// NOTE: mg_http_etag() call should go first!
} else if (mg_http_etag(etag, sizeof(etag), size, mtime) != NULL &&
(inm = mg_http_get_header(hm, "If-None-Match")) != NULL &&
mg_vcasecmp(inm, etag) == 0) {
mg_strcasecmp(*inm, mg_str(etag)) == 0) {
mg_fs_close(fd);
mg_http_reply(c, 304, opts->extra_headers, "");
} else {
@ -619,7 +634,7 @@ void mg_http_serve_file(struct mg_connection *c, struct mg_http_message *hm,
status, mg_http_status_code_str(status), (int) mime.len, mime.buf,
etag, (uint64_t) cl, gzip ? "Content-Encoding: gzip\r\n" : "",
range, opts->extra_headers ? opts->extra_headers : "");
if (mg_vcasecmp(&hm->method, "HEAD") == 0) {
if (mg_strcasecmp(hm->method, mg_str("HEAD")) == 0) {
c->is_draining = 1;
c->is_resp = 0;
mg_fs_close(fd);
@ -775,7 +790,8 @@ static int uri_to_path2(struct mg_connection *c, struct mg_http_message *hm,
}
n = strlen(path);
while (n > 1 && path[n - 1] == '/') path[--n] = 0; // Trim trailing slashes
flags = mg_vcmp(&hm->uri, "/") == 0 ? MG_FS_DIR : fs->st(path, NULL, NULL);
flags = mg_strcmp(hm->uri, mg_str("/")) == 0 ? MG_FS_DIR
: fs->st(path, NULL, NULL);
MG_VERBOSE(("%lu %.*s -> %s %d", c->id, (int) hm->uri.len, hm->uri.buf, path,
flags));
if (flags == 0) {
@ -838,8 +854,7 @@ void mg_http_serve_dir(struct mg_connection *c, struct mg_http_message *hm,
#else
mg_http_reply(c, 403, "", "Forbidden\n");
#endif
} else if (flags && sp != NULL &&
mg_globmatch(sp, strlen(sp), path, strlen(path))) {
} else if (flags && sp != NULL && mg_match(mg_str(path), mg_str(sp), NULL)) {
mg_http_serve_ssi(c, opts->root_dir, path);
} else {
mg_http_serve_file(c, hm, path, opts);
@ -859,9 +874,8 @@ size_t mg_url_encode(const char *s, size_t sl, char *buf, size_t len) {
if (mg_is_url_safe(c)) {
buf[n++] = s[i];
} else {
buf[n++] = '%';
mg_hex(&s[i], 1, &buf[n]);
n += 2;
mg_snprintf(&buf[n], 4, "%%%M", mg_print_hex, 1, &s[i]);
n += 3;
}
}
if (len > 0 && n < len - 1) buf[n] = '\0'; // Null-terminate the destination
@ -1007,7 +1021,7 @@ static void http_cb(struct mg_connection *c, int ev, void *ev_data) {
hm.body.len = hm.message.len - (size_t) (hm.body.buf - hm.message.buf);
}
if ((te = mg_http_get_header(&hm, "Transfer-Encoding")) != NULL) {
if (mg_vcasecmp(te, "chunked") == 0) {
if (mg_strcasecmp(*te, mg_str("chunked")) == 0) {
is_chunked = true;
} else {
mg_error(c, "Invalid Transfer-Encoding"); // See #2460
@ -1018,8 +1032,8 @@ static void http_cb(struct mg_connection *c, int ev, void *ev_data) {
// Content-length
bool is_response = mg_ncasecmp(hm.method.buf, "HTTP/", 5) == 0;
bool require_content_len = false;
if (!is_response && (mg_vcasecmp(&hm.method, "POST") == 0 ||
mg_vcasecmp(&hm.method, "PUT") == 0)) {
if (!is_response && (mg_strcasecmp(hm.method, mg_str("POST")) == 0 ||
mg_strcasecmp(hm.method, mg_str("PUT")) == 0)) {
// POST and PUT should include an entity body. Therefore, they should
// contain a Content-length header. Other requests can also contain a
// body, but their content has no defined semantics (RFC 7231)

View File

@ -254,14 +254,13 @@ size_t mg_mqtt_next_prop(struct mg_mqtt_message *msg, struct mg_mqtt_prop *prop,
}
void mg_mqtt_login(struct mg_connection *c, const struct mg_mqtt_opts *opts) {
char rnd[10], client_id[21];
char client_id[21];
struct mg_str cid = opts->client_id;
size_t total_len = 7 + 1 + 2 + 2;
uint8_t hdr[8] = {0, 4, 'M', 'Q', 'T', 'T', opts->version, 0};
if (cid.len == 0) {
mg_random(rnd, sizeof(rnd));
mg_hex(rnd, sizeof(rnd), client_id);
mg_random_str(client_id, sizeof(client_id) - 1);
client_id[sizeof(client_id) - 1] = '\0';
cid = mg_str(client_id);
}

View File

@ -24,7 +24,7 @@ size_t mg_printf(struct mg_connection *c, const char *fmt, ...) {
static bool mg_atonl(struct mg_str str, struct mg_addr *addr) {
uint32_t localhost = mg_htonl(0x7f000001);
if (mg_vcasecmp(&str, "localhost") != 0) return false;
if (mg_strcasecmp(str, mg_str("localhost")) != 0) return false;
memcpy(addr->ip, &localhost, sizeof(uint32_t));
addr->is_ip6 = false;
return true;

View File

@ -5,7 +5,10 @@ void mg_rpc_add(struct mg_rpc **head, struct mg_str method,
void (*fn)(struct mg_rpc_req *), void *fn_data) {
struct mg_rpc *rpc = (struct mg_rpc *) calloc(1, sizeof(*rpc));
if (rpc != NULL) {
rpc->method = mg_strdup(method), rpc->fn = fn, rpc->fn_data = fn_data;
rpc->method.buf = mg_mprintf("%.*s", method.len, method.buf);
rpc->method.len = method.len;
rpc->fn = fn;
rpc->fn_data = fn_data;
rpc->next = *head, *head = rpc;
}
}

124
src/str.c
View File

@ -10,57 +10,24 @@ struct mg_str mg_str_n(const char *s, size_t n) {
return str;
}
int mg_lower(const char *s) {
int c = *s;
if (c >= 'A' && c <= 'Z') c += 'a' - 'A';
return c;
}
int mg_ncasecmp(const char *s1, const char *s2, size_t len) {
int diff = 0;
if (len > 0) do {
diff = mg_lower(s1++) - mg_lower(s2++);
} while (diff == 0 && s1[-1] != '\0' && --len > 0);
return diff;
static char tolc(char c) {
return (c >= 'A' && c <= 'Z') ? c + 'a' - 'A' : c;
}
int mg_casecmp(const char *s1, const char *s2) {
return mg_ncasecmp(s1, s2, (size_t) ~0);
}
int mg_vcmp(const struct mg_str *s1, const char *s2) {
size_t n2 = strlen(s2), n1 = s1->len;
int r = strncmp(s1->buf, s2, (n1 < n2) ? n1 : n2);
if (r == 0) return (int) (n1 - n2);
return r;
}
int mg_vcasecmp(const struct mg_str *str1, const char *str2) {
size_t n2 = strlen(str2), n1 = str1->len;
int r = mg_ncasecmp(str1->buf, str2, (n1 < n2) ? n1 : n2);
if (r == 0) return (int) (n1 - n2);
return r;
}
struct mg_str mg_strdup(const struct mg_str s) {
struct mg_str r = {NULL, 0};
if (s.len > 0 && s.buf != NULL) {
char *sc = (char *) calloc(1, s.len + 1);
if (sc != NULL) {
memcpy(sc, s.buf, s.len);
sc[s.len] = '\0';
r.buf = sc;
r.len = s.len;
}
}
return r;
int diff = 0;
do {
char c = tolc(*s1++), d = tolc(*s2++);
diff = c - d;
} while (diff == 0 && s1[-1] != '\0');
return diff;
}
int mg_strcmp(const struct mg_str str1, const struct mg_str str2) {
size_t i = 0;
while (i < str1.len && i < str2.len) {
int c1 = str1.buf[i];
int c2 = str2.buf[i];
char c1 = str1.buf[i];
char c2 = str2.buf[i];
if (c1 < c2) return -1;
if (c1 > c2) return 1;
i++;
@ -70,27 +37,18 @@ int mg_strcmp(const struct mg_str str1, const struct mg_str str2) {
return 0;
}
const char *mg_strstr(const struct mg_str haystack,
const struct mg_str needle) {
size_t i;
if (needle.len > haystack.len) return NULL;
if (needle.len == 0) return haystack.buf;
for (i = 0; i <= haystack.len - needle.len; i++) {
if (memcmp(haystack.buf + i, needle.buf, needle.len) == 0) {
return haystack.buf + i;
}
int mg_strcasecmp(const struct mg_str str1, const struct mg_str str2) {
size_t i = 0;
while (i < str1.len && i < str2.len) {
char c1 = tolc(str1.buf[i]);
char c2 = tolc(str2.buf[i]);
if (c1 < c2) return -1;
if (c1 > c2) return 1;
i++;
}
return NULL;
}
static bool is_space(int c) {
return c == ' ' || c == '\r' || c == '\n' || c == '\t';
}
struct mg_str mg_strstrip(struct mg_str s) {
while (s.len > 0 && is_space((int) *s.buf)) s.buf++, s.len--;
while (s.len > 0 && is_space((int) *(s.buf + s.len - 1))) s.len--;
return s;
if (i < str1.len) return 1;
if (i < str2.len) return -1;
return 0;
}
bool mg_match(struct mg_str s, struct mg_str p, struct mg_str *caps) {
@ -125,10 +83,6 @@ bool mg_match(struct mg_str s, struct mg_str p, struct mg_str *caps) {
return true;
}
bool mg_globmatch(const char *s1, size_t n1, const char *s2, size_t n2) {
return mg_match(mg_str_n(s2, n2), mg_str_n(s1, n1), NULL);
}
bool mg_span(struct mg_str s, struct mg_str *a, struct mg_str *b, char sep) {
if (s.len == 0 || s.buf == NULL) {
return false; // Empty string, nothing to span - fail
@ -142,27 +96,17 @@ bool mg_span(struct mg_str s, struct mg_str *a, struct mg_str *b, char sep) {
}
}
char *mg_hex(const void *buf, size_t len, char *to) {
const unsigned char *p = (const unsigned char *) buf;
const char *hex = "0123456789abcdef";
size_t i = 0;
for (; len--; p++) {
to[i++] = hex[p[0] >> 4];
to[i++] = hex[p[0] & 0x0f];
}
to[i] = '\0';
return to;
}
static unsigned char mg_unhex_nimble(unsigned char c) {
return (c >= '0' && c <= '9') ? (unsigned char) (c - '0')
: (c >= 'A' && c <= 'F') ? (unsigned char) (c - '7')
: (unsigned char) (c - 'W');
uint8_t mg_toi(char c, int base) {
return (c >= '0' && c <= '9') ? (uint8_t) (c - '0')
: base == 16 ? (c >= 'A' && c <= 'F') ? (uint8_t) (c - '7')
: (c >= 'a' && c <= 'f') ? (uint8_t) (c - 'W')
: (uint8_t) ~0
: (uint8_t) ~0;
}
unsigned long mg_unhexn(const char *s, size_t len) {
unsigned long i = 0, v = 0;
for (i = 0; i < len; i++) v <<= 4, v |= mg_unhex_nimble(((uint8_t *) s)[i]);
for (i = 0; i < len; i++) v <<= 4, v |= mg_toi(((char *) s)[i], 16);
return v;
}
@ -172,15 +116,3 @@ void mg_unhex(const char *buf, size_t len, unsigned char *to) {
to[i >> 1] = (unsigned char) mg_unhexn(&buf[i], 2);
}
}
bool mg_path_is_sane(const struct mg_str path) {
const char *s = path.buf;
size_t n = path.len;
if (path.buf[0] == '.' && path.buf[1] == '.') return false; // Starts with ..
for (; s[0] != '\0' && n > 0; s++, n--) {
if ((s[0] == '/' || s[0] == '\\') && n >= 2) { // Subdir?
if (s[1] == '.' && s[2] == '.') return false; // Starts with ..
}
}
return true;
}

View File

@ -13,19 +13,13 @@ struct mg_str {
struct mg_str mg_str(const char *s);
struct mg_str mg_str_n(const char *s, size_t n);
int mg_lower(const char *s);
int mg_ncasecmp(const char *s1, const char *s2, size_t len);
int mg_casecmp(const char *s1, const char *s2);
int mg_vcmp(const struct mg_str *s1, const char *s2);
int mg_vcasecmp(const struct mg_str *str1, const char *str2);
int mg_strcmp(const struct mg_str str1, const struct mg_str str2);
struct mg_str mg_strstrip(struct mg_str s);
struct mg_str mg_strdup(const struct mg_str s);
const char *mg_strstr(const struct mg_str haystack, const struct mg_str needle);
int mg_strcasecmp(const struct mg_str str1, const struct mg_str str2); // this one is new
bool mg_match(struct mg_str str, struct mg_str pattern, struct mg_str *caps);
bool mg_globmatch(const char *pattern, size_t plen, const char *s, size_t n);
bool mg_span(struct mg_str s, struct mg_str *a, struct mg_str *b, char delim);
char *mg_hex(const void *buf, size_t len, char *dst);
void mg_unhex(const char *buf, size_t len, unsigned char *to);
unsigned long mg_unhexn(const char *s, size_t len);
bool mg_path_is_sane(const struct mg_str path);
uint8_t mg_toi(char c, int base); // base: 16, 10

View File

@ -1233,7 +1233,8 @@ static int mg_parse_pem(const struct mg_str pem, const struct mg_str label,
const char *c;
struct mg_str caps[5];
if (!mg_match(pem, mg_str("#-----BEGIN #-----#-----END #-----#"), caps)) {
*der = mg_strdup(pem);
der->buf = mg_mprintf("%.*s", pem.len, pem.buf);
der->len = pem.len;
return 0;
}
if (mg_strcmp(caps[1], label) != 0 || mg_strcmp(caps[3], label) != 0) {

View File

@ -123,7 +123,7 @@ void mg_tls_init(struct mg_connection *c, const struct mg_tls_opts *opts) {
}
mbedtls_ssl_conf_rng(&tls->conf, mg_mbed_rng, c);
if (opts->ca.len == 0 || mg_vcmp(&opts->ca, "*") == 0) {
if (opts->ca.len == 0 || mg_strcmp(opts->ca, mg_str("*")) == 0) {
mbedtls_ssl_conf_authmode(&tls->conf, MBEDTLS_SSL_VERIFY_NONE);
} else {
if (mg_load_cert(opts->ca, &tls->ca) == false) goto fail;

View File

@ -106,6 +106,18 @@ int mg_check_ip_acl(struct mg_str acl, struct mg_addr *remote_ip) {
return allowed == '+';
}
bool mg_path_is_sane(const struct mg_str path) {
const char *s = path.buf;
size_t n = path.len;
if (path.buf[0] == '.' && path.buf[1] == '.') return false; // Starts with ..
for (; s[0] != '\0' && n > 0; s++, n--) {
if ((s[0] == '/' || s[0] == '\\') && n >= 2) { // Subdir?
if (s[1] == '.' && s[2] == '.') return false; // Starts with ..
}
}
return true;
}
#if MG_ENABLE_CUSTOM_MILLIS
#else
uint64_t mg_millis(void) {

View File

@ -19,6 +19,7 @@ uint32_t mg_ntohl(uint32_t net);
uint32_t mg_crc32(uint32_t crc, const char *buf, size_t len);
uint64_t mg_millis(void); // Return milliseconds since boot
uint64_t mg_now(void); // Return milliseconds since Epoch
bool mg_path_is_sane(const struct mg_str path);
#define mg_htons(x) mg_ntohs(x)
#define mg_htonl(x) mg_ntohl(x)

View File

@ -72,7 +72,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
mg_base64_encode(data, size, buf, sizeof(buf));
mg_base64_encode(NULL, 0, buf, sizeof(buf));
mg_globmatch((char *) data, size, (char *) data, size);
mg_match(mg_str_n((char *) data, size), mg_str_n((char *) data, size), NULL);
struct mg_str entry, s = mg_str_n((char *) data, size);
while (mg_span(s, &entry, &s, ',')) entry.len = 0;

View File

@ -77,7 +77,7 @@ static void f_http_fetch_query(struct mg_connection *c, int ev, void *ev_data) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
http_responses_received++;
if (!http_response_allocated) {
http_response = (char *) mg_strdup(hm->message).buf;
http_response = mg_mprintf("%.*s", hm->message.len, hm->message.buf);
http_response_allocated = 1;
}
if (http_responses_received > 0) {

View File

@ -19,39 +19,39 @@ static int s_num_tests = 0;
// Important: we use different port numbers for the Windows bug workaround. See
// https://support.microsoft.com/en-ae/help/3039044/error-10013-wsaeacces-is-returned-when-a-second-bind-to-a-excluded-por
static void test_globmatch(void) {
ASSERT(mg_globmatch("", 0, "", 0) == 1);
ASSERT(mg_globmatch("*", 1, "a", 1) == 1);
ASSERT(mg_globmatch("*", 1, "ab", 2) == 1);
ASSERT(mg_globmatch("", 0, "a", 1) == 0);
ASSERT(mg_globmatch("/", 1, "/foo", 4) == 0);
ASSERT(mg_globmatch("/*/foo", 6, "/x/bar", 6) == 0);
ASSERT(mg_globmatch("/*/foo", 6, "/x/foo", 6) == 1);
ASSERT(mg_globmatch("/*/foo", 6, "/x/foox", 7) == 0);
ASSERT(mg_globmatch("/*/foo*", 7, "/x/foox", 7) == 1);
ASSERT(mg_globmatch("/*", 2, "/abc", 4) == 1);
ASSERT(mg_globmatch("/*", 2, "/ab/", 4) == 0);
ASSERT(mg_globmatch("/*", 2, "/", 1) == 1);
ASSERT(mg_globmatch("/x/*", 4, "/x/2", 4) == 1);
ASSERT(mg_globmatch("/x/*", 4, "/x/2/foo", 8) == 0);
ASSERT(mg_globmatch("/x/*/*", 6, "/x/2/foo", 8) == 1);
ASSERT(mg_globmatch("#", 1, "///", 3) == 1);
ASSERT(mg_globmatch("/api/*", 6, "/api/foo", 8) == 1);
ASSERT(mg_globmatch("/api/*", 6, "/api/log/static", 15) == 0);
ASSERT(mg_globmatch("/api/#", 6, "/api/log/static", 15) == 1);
ASSERT(mg_globmatch("#.shtml", 7, "/ssi/index.shtml", 16) == 1);
ASSERT(mg_globmatch("#.c", 3, ".c", 2) == 1);
ASSERT(mg_globmatch("abc", 3, "ab", 2) == 0);
ASSERT(mg_globmatch("#.c", 3, "a.c", 3) == 1);
ASSERT(mg_globmatch("#.c", 3, "..c", 3) == 1);
ASSERT(mg_globmatch("#.c", 3, "/.c", 3) == 1);
ASSERT(mg_globmatch("#.c", 3, "//a.c", 5) == 1);
ASSERT(mg_globmatch("#.c", 3, "x/a.c", 5) == 1);
ASSERT(mg_globmatch("#.c", 3, "./a.c", 5) == 1);
ASSERT(mg_globmatch("#.shtml", 7, "./ssi/index.shtml", 17) == 1);
ASSERT(mg_globmatch("#aa#bb#", 7, "caabba", 6) == 1);
ASSERT(mg_globmatch("#aa#bb#", 7, "caabxa", 6) == 0);
ASSERT(mg_globmatch("a*b*c", 5, "a__b_c", 6) == 1);
static void test_match(void) {
ASSERT(mg_match(mg_str_n("", 0), mg_str_n("", 0), NULL) == 1);
ASSERT(mg_match(mg_str_n("a", 1), mg_str_n("*", 1), NULL) == 1);
ASSERT(mg_match(mg_str_n("ab", 2), mg_str_n("*", 1), NULL) == 1);
ASSERT(mg_match(mg_str_n("a", 1), mg_str_n("", 0), NULL) == 0);
ASSERT(mg_match(mg_str_n("/foo", 4), mg_str_n("/", 1), NULL) == 0);
ASSERT(mg_match(mg_str_n("/x/bar", 6), mg_str_n("/*/foo", 6), NULL) == 0);
ASSERT(mg_match(mg_str_n("/x/foo", 6), mg_str_n("/*/foo", 6), NULL) == 1);
ASSERT(mg_match(mg_str_n("/x/foox", 7), mg_str_n("/*/foo", 6), NULL) == 0);
ASSERT(mg_match(mg_str_n("/x/foox", 7), mg_str_n("/*/foo*", 7), NULL) == 1);
ASSERT(mg_match(mg_str_n("/abc", 4), mg_str_n("/*", 2), NULL) == 1);
ASSERT(mg_match(mg_str_n("/ab/", 4), mg_str_n("/*", 2), NULL) == 0);
ASSERT(mg_match(mg_str_n("/", 1), mg_str_n("/*", 2), NULL) == 1);
ASSERT(mg_match(mg_str_n("/x/2", 4), mg_str_n("/x/*", 4), NULL) == 1);
ASSERT(mg_match(mg_str_n("/x/2/foo", 8), mg_str_n("/x/*", 4), NULL) == 0);
ASSERT(mg_match(mg_str_n("/x/2/foo", 8), mg_str_n("/x/*/*", 6), NULL) == 1);
ASSERT(mg_match(mg_str_n("///", 3), mg_str_n("#", 1), NULL) == 1);
ASSERT(mg_match(mg_str_n("/api/foo", 8), mg_str_n("/api/*", 6), NULL) == 1);
ASSERT(mg_match(mg_str_n("/api/log/static", 15), mg_str_n("/api/*", 6), NULL) == 0);
ASSERT(mg_match(mg_str_n("/api/log/static", 15), mg_str_n("/api/#", 6), NULL) == 1);
ASSERT(mg_match(mg_str_n("/ssi/index.shtml", 16), mg_str_n("#.shtml", 7), NULL) == 1);
ASSERT(mg_match(mg_str_n(".c", 2), mg_str_n("#.c", 3), NULL) == 1);
ASSERT(mg_match(mg_str_n("ab", 2), mg_str_n("abc", 3), NULL) == 0);
ASSERT(mg_match(mg_str_n("a.c", 3), mg_str_n("#.c", 3), NULL) == 1);
ASSERT(mg_match(mg_str_n("..c", 3), mg_str_n("#.c", 3), NULL) == 1);
ASSERT(mg_match(mg_str_n("/.c", 3), mg_str_n("#.c", 3), NULL) == 1);
ASSERT(mg_match(mg_str_n("//a.c", 5), mg_str_n("#.c", 3), NULL) == 1);
ASSERT(mg_match(mg_str_n("x/a.c", 5), mg_str_n("#.c", 3), NULL) == 1);
ASSERT(mg_match(mg_str_n("./a.c", 5), mg_str_n("#.c", 3), NULL) == 1);
ASSERT(mg_match(mg_str_n("./ssi/index.shtml", 17), mg_str_n("#.shtml", 7), NULL) == 1);
ASSERT(mg_match(mg_str_n("caabba", 6), mg_str_n("#aa#bb#", 7), NULL) == 1);
ASSERT(mg_match(mg_str_n("caabxa", 6), mg_str_n("#aa#bb#", 7), NULL) == 0);
ASSERT(mg_match(mg_str_n("a__b_c", 6), mg_str_n("a*b*c", 5), NULL) == 1);
{
struct mg_str caps[3];
@ -128,10 +128,30 @@ static void test_http_get_var(void) {
}
static int vcmp(struct mg_str s1, const char *s2) {
// MG_INFO(("->%.*s<->%s<- %d %d %d", (int) s1.len, s1.buf, s2,
//(int) s1.len, strncmp(s1.buf, s2, s1.len), mg_vcmp(&s1, s2)));
return mg_vcmp(&s1, s2) == 0;
return mg_strcmp(s1, mg_str(s2)) == 0;
}
static bool is_space(int c) {
return c == ' ' || c == '\r' || c == '\n' || c == '\t';
}
static struct mg_str strstrip(struct mg_str s) {
while (s.len > 0 && is_space((int) *s.buf)) s.buf++, s.len--;
while (s.len > 0 && is_space((int) *(s.buf + s.len - 1))) s.len--;
return s;
}
static const char *mgstrstr(const struct mg_str haystack,
const struct mg_str needle) {
size_t i;
if (needle.len > haystack.len) return NULL;
if (needle.len == 0) return haystack.buf;
for (i = 0; i <= haystack.len - needle.len; i++) {
if (memcmp(haystack.buf + i, needle.buf, needle.len) == 0) {
return haystack.buf + i;
}
}
return NULL;
}
static void test_url(void) {
// Host
@ -767,10 +787,10 @@ static void wcb(struct mg_connection *c, int ev, void *ev_data) {
p[0] += 100;
} else if (ev == MG_EV_WS_MSG) {
struct mg_ws_message *wm = (struct mg_ws_message *) ev_data;
if (mg_strstr(wm->data, mg_str("foobar")))
if (mgstrstr(wm->data, mg_str("foobar")))
mg_ws_send(c, "", 0, WEBSOCKET_OP_CLOSE);
if (mg_strstr(wm->data, mg_str("boo"))) p[0] += 2;
if (mg_strstr(wm->data, mg_str("foobar"))) p[0] += 3;
if (mgstrstr(wm->data, mg_str("boo"))) p[0] += 2;
if (mgstrstr(wm->data, mg_str("foobar"))) p[0] += 3;
} else if (ev == MG_EV_CLOSE) {
p[0] += 10;
}
@ -998,8 +1018,8 @@ static void test_http_server(void) {
// Directory listing
fetch(&mgr, buf, url, "GET /dirtest/ HTTP/1.0\n\n");
ASSERT(fetch(&mgr, buf, url, "GET /dirtest/ HTTP/1.0\n\n") == 200);
ASSERT(mg_strstr(mg_str(buf), mg_str(">Index of /dirtest/<")) != NULL);
ASSERT(mg_strstr(mg_str(buf), mg_str(">fuzz.c<")) != NULL);
ASSERT(mgstrstr(mg_str(buf), mg_str(">Index of /dirtest/<")) != NULL);
ASSERT(mgstrstr(mg_str(buf), mg_str(">fuzz.c<")) != NULL);
ASSERT(cmpheader(buf, "A", "B"));
ASSERT(!cmpheader(buf, "C", "D"));
ASSERT(cmpheader(buf, "E", "F"));
@ -1218,8 +1238,8 @@ static void f3(struct mg_connection *c, int ev, void *ev_data) {
} else if (ev == MG_EV_HTTP_MSG) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
// MG_INFO(("-->[%.*s]", (int) hm->message.len, hm->message.buf));
// ASSERT(mg_vcmp(&hm->method, "HTTP/1.1") == 0);
// ASSERT(mg_vcmp(&hm->uri, "301") == 0);
// ASSERT(vcmp(hm->method, "HTTP/1.1"));
// ASSERT(vcmp(hm->uri, "301"));
*ok = mg_http_status(hm);
} else if (ev == MG_EV_CLOSE) {
if (*ok == 0) *ok = 888;
@ -1440,16 +1460,16 @@ static void test_http_parse(void) {
const char *s = "GET /blah HTTP/1.0\r\nFoo: bar \r\n\r\n";
size_t idx, len = strlen(s);
ASSERT(mg_http_parse(s, strlen(s), &req) == (int) len);
ASSERT(mg_vcmp(&req.headers[0].name, "Foo") == 0);
ASSERT(mg_vcmp(&req.headers[0].value, "bar") == 0);
ASSERT(vcmp(req.headers[0].name, "Foo"));
ASSERT(vcmp(req.headers[0].value, "bar"));
ASSERT(req.headers[1].name.len == 0);
ASSERT(req.headers[1].name.buf == NULL);
ASSERT(req.query.len == 0);
ASSERT(req.message.len == len);
ASSERT(req.body.len == 0);
ASSERT(mg_vcmp(&req.method, "GET") == 0);
ASSERT(mg_vcmp(&req.uri, "/blah") == 0);
ASSERT(mg_vcmp(&req.proto, "HTTP/1.0") == 0);
ASSERT(vcmp(req.method, "GET"));
ASSERT(vcmp(req.uri, "/blah"));
ASSERT(vcmp(req.proto, "HTTP/1.0"));
for (idx = 0; idx < len; idx++) ASSERT(mg_http_parse(s, idx, &req) == 0);
}
@ -1477,12 +1497,12 @@ static void test_http_parse(void) {
const char *s = "get b c\nz: k \nb: t\nv:k\n\n xx";
ASSERT(mg_http_parse(s, strlen(s), &req) == (int) strlen(s) - 3);
ASSERT(req.headers[3].name.len == 0);
ASSERT(mg_vcmp(&req.headers[0].name, "z") == 0);
ASSERT(mg_vcmp(&req.headers[0].value, "k") == 0);
ASSERT(mg_vcmp(&req.headers[1].name, "b") == 0);
ASSERT(mg_vcmp(&req.headers[1].value, "t") == 0);
ASSERT(mg_vcmp(&req.headers[2].name, "v") == 0);
ASSERT(mg_vcmp(&req.headers[2].value, "k") == 0);
ASSERT(vcmp(req.headers[0].name, "z"));
ASSERT(vcmp(req.headers[0].value, "k"));
ASSERT(vcmp(req.headers[1].name, "b"));
ASSERT(vcmp(req.headers[1].value, "t"));
ASSERT(vcmp(req.headers[2].name, "v"));
ASSERT(vcmp(req.headers[2].value, "k"));
ASSERT(req.body.len == 0);
}
@ -1502,12 +1522,12 @@ static void test_http_parse(void) {
ASSERT(mg_http_parse(s, strlen(s), &req) == (int) strlen(s));
ASSERT(req.body.len == 0);
ASSERT(req.headers[1].name.len == 0);
ASSERT(mg_vcmp(&req.headers[0].name, "місто") == 0);
ASSERT(mg_vcmp(&req.headers[0].value, "кіїв") == 0);
ASSERT(vcmp(req.headers[0].name, "місто"));
ASSERT(vcmp(req.headers[0].value, "кіїв"));
ASSERT((v = mg_http_get_header(&req, "місто")) != NULL);
ASSERT(mg_vcmp(&req.method, "ґєт") == 0);
ASSERT(mg_vcmp(&req.uri, "/слеш") == 0);
ASSERT(mg_vcmp(&req.proto, "вах вах") == 0);
ASSERT(vcmp(req.method, "ґєт"));
ASSERT(vcmp(req.uri, "/слеш"));
ASSERT(vcmp(req.proto, "вах вах"));
}
{
@ -1517,16 +1537,16 @@ static void test_http_parse(void) {
ASSERT(req.message.len == 21 - 3 + strlen(s));
ASSERT(mg_http_get_header(&req, "foo") == NULL);
ASSERT((v = mg_http_get_header(&req, "contENT-Length")) != NULL);
ASSERT(mg_vcmp(v, "21") == 0);
ASSERT(vcmp(*v, "21"));
ASSERT((v = mg_http_get_header(&req, "B")) != NULL);
ASSERT(mg_vcmp(v, "t") == 0);
ASSERT(vcmp(*v, "t"));
}
{
const char *s = "GET /foo?a=b&c=d HTTP/1.0\n\n";
ASSERT(mg_http_parse(s, strlen(s), &req) == (int) strlen(s));
ASSERT(mg_vcmp(&req.uri, "/foo") == 0);
ASSERT(mg_vcmp(&req.query, "a=b&c=d") == 0);
ASSERT(vcmp(req.uri, "/foo"));
ASSERT(vcmp(req.query, "a=b&c=d"));
}
{
@ -1544,9 +1564,9 @@ static void test_http_parse(void) {
{
const char *s = "HTTP/1.0 200 OK\n\n";
ASSERT(mg_http_parse(s, strlen(s), &req) == (int) strlen(s));
ASSERT(mg_vcmp(&req.method, "HTTP/1.0") == 0);
ASSERT(mg_vcmp(&req.uri, "200") == 0);
ASSERT(mg_vcmp(&req.proto, "OK") == 0);
ASSERT(vcmp(req.method, "HTTP/1.0"));
ASSERT(vcmp(req.uri, "200"));
ASSERT(vcmp(req.proto, "OK"));
ASSERT(req.body.len == (size_t) ~0);
}
@ -1561,20 +1581,20 @@ static void test_http_parse(void) {
"45455\r\nRange: 0-1 \r\n\r\n";
ASSERT(mg_http_parse(s, strlen(s), &req) == (int) strlen(s));
ASSERT((v = mg_http_get_header(&req, "Host")) != NULL);
ASSERT(mg_vcmp(v, "127.0.0.1:18888") == 0);
ASSERT(vcmp(*v, "127.0.0.1:18888"));
ASSERT((v = mg_http_get_header(&req, "Cookie")) != NULL);
ASSERT(v->len == 0);
ASSERT((v = mg_http_get_header(&req, "X-PlayID")) != NULL);
ASSERT(mg_vcmp(v, "45455") == 0);
ASSERT(vcmp(*v, "45455"));
ASSERT((v = mg_http_get_header(&req, "Range")) != NULL);
ASSERT(mg_vcmp(v, "0-1") == 0);
ASSERT(vcmp(*v, "0-1"));
}
{
static const char *s = "a b c\na:1\nb:2\nc:3\nd:4\ne:5\nf:6\ng:7\nh:8\n\n";
ASSERT(mg_http_parse(s, strlen(s), &req) == (int) strlen(s));
ASSERT((v = mg_http_get_header(&req, "e")) != NULL);
ASSERT(mg_vcmp(v, "5") == 0);
ASSERT(vcmp(*v, "5"));
ASSERT((v = mg_http_get_header(&req, "h")) == NULL);
}
@ -1610,7 +1630,7 @@ static void test_http_parse(void) {
s = "a b\nc: \xc0\n\n"; // Invalid UTF in the header value: accept
ASSERT(mg_http_parse(s, strlen(s), &hm) == (int) strlen(s));
ASSERT((v = mg_http_get_header(&hm, "c")) != NULL);
ASSERT(mg_vcmp(v, "\xc0") == 0);
ASSERT(vcmp(*v, "\xc0"));
s = "a b\n\xc0: 2\n\n"; // Invalid UTF in the header name: do NOT accept
ASSERT(mg_http_parse(s, strlen(s), &hm) == -1);
}
@ -1855,26 +1875,18 @@ static bool chkdbl(struct mg_str s, double val) {
}
static void test_str(void) {
{
struct mg_str s = mg_strdup(mg_str("a"));
ASSERT(mg_strcmp(s, mg_str("a")) == 0);
free((void *) s.buf);
}
{
const char *s;
struct mg_str a = mg_str("hello"), b = mg_str("a"), c = mg_str(NULL);
ASSERT((s = mg_strstr(a, b)) == NULL);
ASSERT((s = mg_strstr(a, c)) != NULL);
ASSERT((s = mgstrstr(a, b)) == NULL);
ASSERT((s = mgstrstr(a, c)) != NULL);
ASSERT(s == a.buf);
}
ASSERT(mg_strcmp(mg_str(""), mg_str(NULL)) == 0);
ASSERT(mg_strcmp(mg_str("a"), mg_str("b")) < 0);
ASSERT(mg_strcmp(mg_str("b"), mg_str("a")) > 0);
ASSERT(mg_strstr(mg_str("abc"), mg_str("d")) == NULL);
ASSERT(mg_strstr(mg_str("abc"), mg_str("b")) != NULL);
ASSERT(mg_strcmp(mg_str("hi"), mg_strstrip(mg_str(" \thi\r\n"))) == 0);
ASSERT(mg_strcmp(mg_str("hi"), strstrip(mg_str(" \thi\r\n"))) == 0);
ASSERT(sccmp("", "", 0));
ASSERT(sccmp("", "1", -49));
@ -1882,6 +1894,22 @@ static void test_str(void) {
ASSERT(sccmp("a1", "A", 49));
ASSERT(sccmp("a", "A1", -49));
ASSERT(mg_strcasecmp(mg_str(""), mg_str(NULL)) == 0);
ASSERT(mg_strcasecmp(mg_str("a"), mg_str("B")) < 0);
ASSERT(mg_strcasecmp(mg_str("b"), mg_str("A")) > 0);
ASSERT(mg_strcasecmp(mg_str("hi"), mg_str("HI")) == 0);
ASSERT(mg_toi('0', 10) == 0);
ASSERT(mg_toi('9', 10) == 9);
ASSERT(mg_toi('A', 10) == (uint8_t) ~0);
ASSERT(mg_toi('0', 16) == 0);
ASSERT(mg_toi('9', 16) == 9);
ASSERT(mg_toi('A', 16) == 10);
ASSERT(mg_toi('a', 16) == 10);
ASSERT(mg_toi('f', 16) == 15);
ASSERT(mg_toi('F', 16) == 15);
ASSERT(mg_toi('G', 16) == (uint8_t) ~0);
{
ASSERT(chkdbl(mg_str_n("1.23", 3), 1.2));
ASSERT(chkdbl(mg_str("1.23 "), 1.23));
@ -2268,7 +2296,6 @@ static void test_util(void) {
e = "\x20\x01\x48\x60\x48\x60\x00\x00\x00\x00\x00\x00\x00\x00\x88\x88";
ASSERT(memcmp(a.ip, e, sizeof(a.ip)) == 0);
ASSERT(strcmp(mg_hex("abc", 3, buf), "616263") == 0);
ASSERT(mg_url_decode("a=%", 3, buf, sizeof(buf), 0) < 0);
ASSERT(mg_url_decode("&&&a=%", 6, buf, sizeof(buf), 0) < 0);
@ -3032,11 +3059,11 @@ static void test_json(void) {
struct mg_str k, v, sub = mg_str_n(json.buf + 8, json.len - 8);
const char *a = "\"a\"", *b = "\"b\"";
ASSERT(mg_json_next(sub, 0, &k, &v) == 9);
ASSERT(mg_vcmp(&k, a) == 0);
ASSERT(mg_vcmp(&v, "[3]") == 0);
ASSERT(vcmp(k, a));
ASSERT(vcmp(v, "[3]"));
ASSERT(mg_json_next(sub, 9, &k, &v) == 15);
ASSERT(mg_vcmp(&k, b) == 0);
ASSERT(mg_vcmp(&v, "42") == 0);
ASSERT(vcmp(k, b));
ASSERT(vcmp(v, "42"));
ASSERT(mg_json_next(sub, 15, &k, &v) == 0);
}
@ -3332,7 +3359,7 @@ int main(void) {
test_queue();
test_rpc();
test_str();
test_globmatch();
test_match();
test_get_header_var();
test_http_parse();
test_rewrites();

View File

@ -48,8 +48,10 @@ static void fn(struct mg_connection *c, int ev, void *ev_data) {
mg_http_reply(c, 200, "Host: foo.com\r\n", "hi\n");
} else {
// Multithreading code path
struct thread_data *data = calloc(1, sizeof(*data)); // Worker owns it
data->message = mg_strdup(hm->message); // Pass message
struct thread_data *data =
(struct thread_data *) calloc(1, sizeof(*data)); // Worker owns it
data->message.buf = mg_mprintf("%.*s", hm->message.len, hm->message.buf);
data->message.len = hm->message.len; // Pass message
data->conn_id = c->id;
data->mgr = c->mgr;
start_thread(thread_function, data); // Start thread and pass data

View File

@ -80,7 +80,8 @@ static void fn(struct mg_connection *c, int ev, void *ev_data) {
while ((pos = mg_mqtt_next_sub(mm, &topic, &qos, pos)) > 0) {
struct sub *sub = calloc(1, sizeof(*sub));
sub->c = c;
sub->topic = mg_strdup(topic);
sub->topic.buf = mg_mprintf("%.*s", topic.len, topic.buf);
sub->topic.len = topic.len;
sub->qos = qos;
LIST_ADD_HEAD(struct sub, &s_subs, sub);
MG_INFO(

View File

@ -19,11 +19,11 @@ static void fn(struct mg_connection *c, int ev, void *ev_data) {
size_t i, max = sizeof(hm.headers) / sizeof(hm.headers[0]);
// Iterate over request headers
for (i = 0; i < max && hm.headers[i].name.len > 0; i++) {
struct mg_str *k = &hm.headers[i].name, *v = &hm.headers[i].value;
if ((mg_vcasecmp(k, "SERVER") == 0) ||
(mg_vcasecmp(k, "LOCATION") == 0)) {
printf("\t%.*s -> %.*s\n", (int) k->len, k->buf, (int) v->len,
v->buf);
struct mg_str k = hm.headers[i].name, v = hm.headers[i].value;
if ((mg_strcasecmp(k, mg_str("SERVER")) == 0) ||
(mg_strcasecmp(k, mg_str("LOCATION")) == 0)) {
printf("\t%.*s -> %.*s\n", (int) k.len, k.buf, (int) v.len,
v.buf);
}
}
printf("\n");