From 3c531897b665e93f82e5bde8d7786573150328d9 Mon Sep 17 00:00:00 2001 From: "Sergio R. Caprile" Date: Tue, 30 Apr 2024 18:19:42 -0300 Subject: [PATCH] cleanup 'str' API --- mongoose.c | 203 +++++++++++---------------- mongoose.h | 97 ++++++------- src/arch_freertos.h | 2 +- src/arch_rtx.h | 2 +- src/http.c | 52 ++++--- src/mqtt.c | 5 +- src/net.c | 2 +- src/rpc.c | 5 +- src/str.c | 124 ++++------------ src/str.h | 14 +- src/tls_builtin.c | 3 +- src/tls_mbed.c | 2 +- src/util.c | 12 ++ src/util.h | 1 + test/fuzz.c | 2 +- test/mip_tap_test.c | 2 +- test/unit_test.c | 203 +++++++++++++++------------ tutorials/core/multi-threaded/main.c | 6 +- tutorials/mqtt/mqtt-server/main.c | 3 +- tutorials/udp/ssdp-search/main.c | 10 +- 20 files changed, 350 insertions(+), 400 deletions(-) diff --git a/mongoose.c b/mongoose.c index 1c20636e..210e2c0f 100644 --- a/mongoose.c +++ b/mongoose.c @@ -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) { diff --git a/mongoose.h b/mongoose.h index 519352a6..d12d2cc5 100644 --- a/mongoose.h +++ b/mongoose.h @@ -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 diff --git a/src/arch_freertos.h b/src/arch_freertos.h index e3d81bd7..9ad78998 100644 --- a/src/arch_freertos.h +++ b/src/arch_freertos.h @@ -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) { diff --git a/src/arch_rtx.h b/src/arch_rtx.h index a6008db7..cbf79d25 100644 --- a/src/arch_rtx.h +++ b/src/arch_rtx.h @@ -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 diff --git a/src/http.c b/src/http.c index 37845820..c46c5e6d 100644 --- a/src/http.c +++ b/src/http.c @@ -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) diff --git a/src/mqtt.c b/src/mqtt.c index c0e60b4f..a4a2d8ca 100644 --- a/src/mqtt.c +++ b/src/mqtt.c @@ -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); } diff --git a/src/net.c b/src/net.c index debf310c..8fe49cd5 100644 --- a/src/net.c +++ b/src/net.c @@ -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; diff --git a/src/rpc.c b/src/rpc.c index 36304a7c..509a2be4 100644 --- a/src/rpc.c +++ b/src/rpc.c @@ -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; } } diff --git a/src/str.c b/src/str.c index 26902b8f..4b0fa9a8 100644 --- a/src/str.c +++ b/src/str.c @@ -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; -} diff --git a/src/str.h b/src/str.h index 422fef29..85c301b8 100644 --- a/src/str.h +++ b/src/str.h @@ -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 diff --git a/src/tls_builtin.c b/src/tls_builtin.c index 781fdb9d..ece5dad5 100644 --- a/src/tls_builtin.c +++ b/src/tls_builtin.c @@ -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) { diff --git a/src/tls_mbed.c b/src/tls_mbed.c index 67ed7b31..61396aa6 100644 --- a/src/tls_mbed.c +++ b/src/tls_mbed.c @@ -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; diff --git a/src/util.c b/src/util.c index 6974275f..cfb25db2 100644 --- a/src/util.c +++ b/src/util.c @@ -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) { diff --git a/src/util.h b/src/util.h index 93292a60..dde1d58f 100644 --- a/src/util.h +++ b/src/util.h @@ -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) diff --git a/test/fuzz.c b/test/fuzz.c index 7fb1b773..1405cc9f 100644 --- a/test/fuzz.c +++ b/test/fuzz.c @@ -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; diff --git a/test/mip_tap_test.c b/test/mip_tap_test.c index a4c70f5b..3a06dc54 100644 --- a/test/mip_tap_test.c +++ b/test/mip_tap_test.c @@ -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) { diff --git a/test/unit_test.c b/test/unit_test.c index b59e122b..ce28da0a 100644 --- a/test/unit_test.c +++ b/test/unit_test.c @@ -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(); diff --git a/tutorials/core/multi-threaded/main.c b/tutorials/core/multi-threaded/main.c index 86ccc910..efc439ee 100644 --- a/tutorials/core/multi-threaded/main.c +++ b/tutorials/core/multi-threaded/main.c @@ -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 diff --git a/tutorials/mqtt/mqtt-server/main.c b/tutorials/mqtt/mqtt-server/main.c index 5d188c27..4d32eef6 100644 --- a/tutorials/mqtt/mqtt-server/main.c +++ b/tutorials/mqtt/mqtt-server/main.c @@ -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( diff --git a/tutorials/udp/ssdp-search/main.c b/tutorials/udp/ssdp-search/main.c index db51e8b3..70664bae 100644 --- a/tutorials/udp/ssdp-search/main.c +++ b/tutorials/udp/ssdp-search/main.c @@ -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");