mirror of
https://github.com/cesanta/mongoose.git
synced 2024-12-25 22:20:49 +08:00
cleanup 'str' API
This commit is contained in:
parent
6c7672f4af
commit
3c531897b6
203
mongoose.c
203
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) {
|
||||
|
97
mongoose.h
97
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
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
52
src/http.c
52
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)
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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
124
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;
|
||||
}
|
||||
|
14
src/str.h
14
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
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
12
src/util.c
12
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) {
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
203
test/unit_test.c
203
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();
|
||||
|
@ -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
|
||||
|
@ -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(
|
||||
|
@ -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");
|
||||
|
Loading…
x
Reference in New Issue
Block a user