mirror of
https://github.com/cesanta/mongoose.git
synced 2026-11-09 16:21:45 +00:00
Adjusting unit test for core
This commit is contained in:
1 parent
2cb325c981
commit
af7efd980c
4 files changed
+610
-136
No files matched your search
@@ -70,6 +70,9 @@ all:
|
|||||||
unix_unit_test: $(LUA_SOURCES) Makefile ../test/unit_test.c
|
unix_unit_test: $(LUA_SOURCES) Makefile ../test/unit_test.c
|
||||||
$(CC) ../test/unit_test.c lua_5.2.1.c $(CFLAGS) -g -O0 -o t && ./t
|
$(CC) ../test/unit_test.c lua_5.2.1.c $(CFLAGS) -g -O0 -o t && ./t
|
||||||
|
|
||||||
|
core_unit_test: Makefile ../test/unit_test.c
|
||||||
|
$(CC) ../test/unit_test.c $(CFLAGS) -g -O0 -o t && ./t
|
||||||
|
|
||||||
# Make sure that the compiler flags come last in the compilation string.
|
# Make sure that the compiler flags come last in the compilation string.
|
||||||
# If not so, this can break some on some Linux distros which use
|
# If not so, this can break some on some Linux distros which use
|
||||||
# "-Wl,--as-needed" turned on by default in cc command.
|
# "-Wl,--as-needed" turned on by default in cc command.
|
||||||
|
|||||||
+479
-16
@@ -78,6 +78,7 @@ typedef struct _stati64 file_stat_t;
|
|||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
|
#include <arpa/inet.h> // For inet_pton() when USE_IPV6 is defined
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <sys/select.h>
|
#include <sys/select.h>
|
||||||
@@ -766,7 +767,8 @@ static void close_conn(struct connection *conn) {
|
|||||||
// -1 if request is malformed
|
// -1 if request is malformed
|
||||||
// 0 if request is not yet fully buffered
|
// 0 if request is not yet fully buffered
|
||||||
// >0 actual request length, including last \r\n\r\n
|
// >0 actual request length, including last \r\n\r\n
|
||||||
static int get_request_len(const unsigned char *buf, int buf_len) {
|
static int get_request_len(const char *s, int buf_len) {
|
||||||
|
const unsigned char *buf = (unsigned char *) s;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < buf_len; i++) {
|
for (i = 0; i < buf_len; i++) {
|
||||||
@@ -874,6 +876,7 @@ static int is_valid_http_method(const char *method) {
|
|||||||
// Parse HTTP request, fill in mg_request structure.
|
// Parse HTTP request, fill in mg_request structure.
|
||||||
// This function modifies the buffer by NUL-terminating
|
// This function modifies the buffer by NUL-terminating
|
||||||
// HTTP request components, header names and header values.
|
// HTTP request components, header names and header values.
|
||||||
|
// Note that len must point to the last \n of HTTP headers.
|
||||||
static int parse_http_message(char *buf, int len, struct mg_connection *ri) {
|
static int parse_http_message(char *buf, int len, struct mg_connection *ri) {
|
||||||
int is_request, n;
|
int is_request, n;
|
||||||
|
|
||||||
@@ -1890,6 +1893,460 @@ static void send_options(struct connection *conn) {
|
|||||||
conn->flags |= CONN_SPOOL_DONE;
|
conn->flags |= CONN_SPOOL_DONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int mg_printf(struct connection *conn, const char *fmt, ...) {
|
||||||
|
char buf[IOBUF_SIZE];
|
||||||
|
va_list ap;
|
||||||
|
int len;
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
len = vsnprintf(buf, sizeof(buf), fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
return spool(&conn->remote_iobuf, buf, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef NO_AUTH
|
||||||
|
static void send_authorization_request(struct connection *conn) {
|
||||||
|
conn->mg_conn.status_code = 401;
|
||||||
|
mg_printf(conn,
|
||||||
|
"HTTP/1.1 401 Unauthorized\r\n"
|
||||||
|
"Content-Length: 0\r\n"
|
||||||
|
"WWW-Authenticate: Digest qop=\"auth\", "
|
||||||
|
"realm=\"%s\", nonce=\"%lu\"\r\n\r\n",
|
||||||
|
conn->server->config_options[AUTH_DOMAIN],
|
||||||
|
(unsigned long) time(NULL));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use the global passwords file, if specified by auth_gpass option,
|
||||||
|
// or search for .htpasswd in the requested directory.
|
||||||
|
static FILE *open_auth_file(struct connection *conn, const char *path) {
|
||||||
|
char name[MAX_PATH_SIZE];
|
||||||
|
const char *p, *gpass = conn->server->config_options[GLOBAL_AUTH_FILE];
|
||||||
|
file_stat_t st;
|
||||||
|
FILE *fp = NULL;
|
||||||
|
|
||||||
|
if (gpass != NULL) {
|
||||||
|
// Use global passwords file
|
||||||
|
fp = fopen(gpass, "r");
|
||||||
|
} else if (!stat(path, &st) && S_ISDIR(st.st_mode)) {
|
||||||
|
snprintf(name, sizeof(name), "%s%c%s", path, '/', PASSWORDS_FILE_NAME);
|
||||||
|
fp = fopen(name, "r");
|
||||||
|
} else {
|
||||||
|
// Try to find .htpasswd in requested directory.
|
||||||
|
if ((p = strrchr(path, '/')) == NULL) p = path;
|
||||||
|
snprintf(name, sizeof(name), "%.*s%c%s",
|
||||||
|
(int) (p - path), path, '/', PASSWORDS_FILE_NAME);
|
||||||
|
fp = fopen(name, "r");
|
||||||
|
}
|
||||||
|
|
||||||
|
return fp;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef HAVE_MD5
|
||||||
|
typedef struct MD5Context {
|
||||||
|
uint32_t buf[4];
|
||||||
|
uint32_t bits[2];
|
||||||
|
unsigned char in[64];
|
||||||
|
} MD5_CTX;
|
||||||
|
|
||||||
|
static void byteReverse(unsigned char *buf, unsigned longs) {
|
||||||
|
uint32_t t;
|
||||||
|
|
||||||
|
// Forrest: MD5 expect LITTLE_ENDIAN, swap if BIG_ENDIAN
|
||||||
|
if (is_big_endian()) {
|
||||||
|
do {
|
||||||
|
t = (uint32_t) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
|
||||||
|
((unsigned) buf[1] << 8 | buf[0]);
|
||||||
|
* (uint32_t *) buf = t;
|
||||||
|
buf += 4;
|
||||||
|
} while (--longs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define F1(x, y, z) (z ^ (x & (y ^ z)))
|
||||||
|
#define F2(x, y, z) F1(z, x, y)
|
||||||
|
#define F3(x, y, z) (x ^ y ^ z)
|
||||||
|
#define F4(x, y, z) (y ^ (x | ~z))
|
||||||
|
|
||||||
|
#define MD5STEP(f, w, x, y, z, data, s) \
|
||||||
|
( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
|
||||||
|
|
||||||
|
// Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
|
||||||
|
// initialization constants.
|
||||||
|
static void MD5Init(MD5_CTX *ctx) {
|
||||||
|
ctx->buf[0] = 0x67452301;
|
||||||
|
ctx->buf[1] = 0xefcdab89;
|
||||||
|
ctx->buf[2] = 0x98badcfe;
|
||||||
|
ctx->buf[3] = 0x10325476;
|
||||||
|
|
||||||
|
ctx->bits[0] = 0;
|
||||||
|
ctx->bits[1] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void MD5Transform(uint32_t buf[4], uint32_t const in[16]) {
|
||||||
|
register uint32_t a, b, c, d;
|
||||||
|
|
||||||
|
a = buf[0];
|
||||||
|
b = buf[1];
|
||||||
|
c = buf[2];
|
||||||
|
d = buf[3];
|
||||||
|
|
||||||
|
MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
|
||||||
|
MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
|
||||||
|
MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
|
||||||
|
MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
|
||||||
|
MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
|
||||||
|
MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
|
||||||
|
MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
|
||||||
|
MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
|
||||||
|
MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
|
||||||
|
MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
|
||||||
|
MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
|
||||||
|
MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
|
||||||
|
MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
|
||||||
|
MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
|
||||||
|
MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
|
||||||
|
MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
|
||||||
|
|
||||||
|
MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
|
||||||
|
MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
|
||||||
|
MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
|
||||||
|
MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
|
||||||
|
MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
|
||||||
|
MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
|
||||||
|
MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
|
||||||
|
MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
|
||||||
|
MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
|
||||||
|
MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
|
||||||
|
MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
|
||||||
|
MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
|
||||||
|
MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
|
||||||
|
MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
|
||||||
|
MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
|
||||||
|
MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
|
||||||
|
|
||||||
|
MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
|
||||||
|
MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
|
||||||
|
MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
|
||||||
|
MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
|
||||||
|
MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
|
||||||
|
MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
|
||||||
|
MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
|
||||||
|
MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
|
||||||
|
MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
|
||||||
|
MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
|
||||||
|
MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
|
||||||
|
MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
|
||||||
|
MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
|
||||||
|
MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
|
||||||
|
MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
|
||||||
|
MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
|
||||||
|
|
||||||
|
MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
|
||||||
|
MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
|
||||||
|
MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
|
||||||
|
MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
|
||||||
|
MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
|
||||||
|
MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
|
||||||
|
MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
|
||||||
|
MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
|
||||||
|
MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
|
||||||
|
MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
|
||||||
|
MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
|
||||||
|
MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
|
||||||
|
MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
|
||||||
|
MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
|
||||||
|
MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
|
||||||
|
MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
|
||||||
|
|
||||||
|
buf[0] += a;
|
||||||
|
buf[1] += b;
|
||||||
|
buf[2] += c;
|
||||||
|
buf[3] += d;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void MD5Update(MD5_CTX *ctx, unsigned char const *buf, unsigned len) {
|
||||||
|
uint32_t t;
|
||||||
|
|
||||||
|
t = ctx->bits[0];
|
||||||
|
if ((ctx->bits[0] = t + ((uint32_t) len << 3)) < t)
|
||||||
|
ctx->bits[1]++;
|
||||||
|
ctx->bits[1] += len >> 29;
|
||||||
|
|
||||||
|
t = (t >> 3) & 0x3f;
|
||||||
|
|
||||||
|
if (t) {
|
||||||
|
unsigned char *p = (unsigned char *) ctx->in + t;
|
||||||
|
|
||||||
|
t = 64 - t;
|
||||||
|
if (len < t) {
|
||||||
|
memcpy(p, buf, len);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
memcpy(p, buf, t);
|
||||||
|
byteReverse(ctx->in, 16);
|
||||||
|
MD5Transform(ctx->buf, (uint32_t *) ctx->in);
|
||||||
|
buf += t;
|
||||||
|
len -= t;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (len >= 64) {
|
||||||
|
memcpy(ctx->in, buf, 64);
|
||||||
|
byteReverse(ctx->in, 16);
|
||||||
|
MD5Transform(ctx->buf, (uint32_t *) ctx->in);
|
||||||
|
buf += 64;
|
||||||
|
len -= 64;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(ctx->in, buf, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void MD5Final(unsigned char digest[16], MD5_CTX *ctx) {
|
||||||
|
unsigned count;
|
||||||
|
unsigned char *p;
|
||||||
|
uint32_t *a;
|
||||||
|
|
||||||
|
count = (ctx->bits[0] >> 3) & 0x3F;
|
||||||
|
|
||||||
|
p = ctx->in + count;
|
||||||
|
*p++ = 0x80;
|
||||||
|
count = 64 - 1 - count;
|
||||||
|
if (count < 8) {
|
||||||
|
memset(p, 0, count);
|
||||||
|
byteReverse(ctx->in, 16);
|
||||||
|
MD5Transform(ctx->buf, (uint32_t *) ctx->in);
|
||||||
|
memset(ctx->in, 0, 56);
|
||||||
|
} else {
|
||||||
|
memset(p, 0, count - 8);
|
||||||
|
}
|
||||||
|
byteReverse(ctx->in, 14);
|
||||||
|
|
||||||
|
a = (uint32_t *)ctx->in;
|
||||||
|
a[14] = ctx->bits[0];
|
||||||
|
a[15] = ctx->bits[1];
|
||||||
|
|
||||||
|
MD5Transform(ctx->buf, (uint32_t *) ctx->in);
|
||||||
|
byteReverse((unsigned char *) ctx->buf, 4);
|
||||||
|
memcpy(digest, ctx->buf, 16);
|
||||||
|
memset((char *) ctx, 0, sizeof(*ctx));
|
||||||
|
}
|
||||||
|
#endif // !HAVE_MD5
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Stringify binary data. Output buffer must be twice as big as input,
|
||||||
|
// because each byte takes 2 bytes in string representation
|
||||||
|
static void bin2str(char *to, const unsigned char *p, size_t len) {
|
||||||
|
static const char *hex = "0123456789abcdef";
|
||||||
|
|
||||||
|
for (; len--; p++) {
|
||||||
|
*to++ = hex[p[0] >> 4];
|
||||||
|
*to++ = hex[p[0] & 0x0f];
|
||||||
|
}
|
||||||
|
*to = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return stringified MD5 hash for list of strings. Buffer must be 33 bytes.
|
||||||
|
char *mg_md5(char buf[33], ...) {
|
||||||
|
unsigned char hash[16];
|
||||||
|
const char *p;
|
||||||
|
va_list ap;
|
||||||
|
MD5_CTX ctx;
|
||||||
|
|
||||||
|
MD5Init(&ctx);
|
||||||
|
|
||||||
|
va_start(ap, buf);
|
||||||
|
while ((p = va_arg(ap, const char *)) != NULL) {
|
||||||
|
MD5Update(&ctx, (const unsigned char *) p, (unsigned) strlen(p));
|
||||||
|
}
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
MD5Final(hash, &ctx);
|
||||||
|
bin2str(buf, hash, sizeof(hash));
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the user's password, return 1 if OK
|
||||||
|
static int check_password(const char *method, const char *ha1, const char *uri,
|
||||||
|
const char *nonce, const char *nc, const char *cnonce,
|
||||||
|
const char *qop, const char *response) {
|
||||||
|
char ha2[32 + 1], expected_response[32 + 1];
|
||||||
|
|
||||||
|
// Some of the parameters may be NULL
|
||||||
|
if (method == NULL || nonce == NULL || nc == NULL || cnonce == NULL ||
|
||||||
|
qop == NULL || response == NULL) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE(lsm): due to a bug in MSIE, we do not compare the URI
|
||||||
|
// TODO(lsm): check for authentication timeout
|
||||||
|
if (// strcmp(dig->uri, c->ouri) != 0 ||
|
||||||
|
strlen(response) != 32
|
||||||
|
// || now - strtoul(dig->nonce, NULL, 10) > 3600
|
||||||
|
) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
mg_md5(ha2, method, ":", uri, NULL);
|
||||||
|
mg_md5(expected_response, ha1, ":", nonce, ":", nc,
|
||||||
|
":", cnonce, ":", qop, ":", ha2, NULL);
|
||||||
|
|
||||||
|
return mg_strcasecmp(response, expected_response) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Authorize against the opened passwords file. Return 1 if authorized.
|
||||||
|
static int authorize(struct connection *conn, FILE *fp) {
|
||||||
|
const char *hdr = mg_get_header(&conn->mg_conn, "Authorization");
|
||||||
|
char line[256], f_user[256], ha1[256], f_domain[256], user[100], uri[100],
|
||||||
|
cnonce[100], resp[100], qop[100], nc[100], nonce[100];
|
||||||
|
|
||||||
|
if (hdr == NULL || mg_strncasecmp(hdr, "Digest ", 7) != 0) return 0;
|
||||||
|
if (!mg_parse_header(hdr, "username", user, sizeof(user))) return 0;
|
||||||
|
if (!mg_parse_header(hdr, "cnonce", cnonce, sizeof(cnonce))) return 0;
|
||||||
|
if (!mg_parse_header(hdr, "response", resp, sizeof(resp))) return 0;
|
||||||
|
if (!mg_parse_header(hdr, "uri", uri, sizeof(uri))) return 0;
|
||||||
|
if (!mg_parse_header(hdr, "qop", qop, sizeof(qop))) return 0;
|
||||||
|
if (!mg_parse_header(hdr, "nc", nc, sizeof(nc))) return 0;
|
||||||
|
if (!mg_parse_header(hdr, "nonce", nonce, sizeof(nonce))) return 0;
|
||||||
|
|
||||||
|
while (fgets(line, sizeof(line), fp) != NULL) {
|
||||||
|
if (sscanf(line, "%[^:]:%[^:]:%s", f_user, f_domain, ha1) == 3 &&
|
||||||
|
!strcmp(user, f_user) &&
|
||||||
|
!strcmp(conn->server->config_options[AUTH_DOMAIN], f_domain))
|
||||||
|
return check_password(conn->mg_conn.request_method, ha1, uri,
|
||||||
|
nonce, nc, cnonce, qop, resp);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Return 1 if request is authorised, 0 otherwise.
|
||||||
|
static int is_authorized(struct connection *conn, const char *path) {
|
||||||
|
FILE *fp;
|
||||||
|
int authorized = 1;
|
||||||
|
|
||||||
|
if ((fp = open_auth_file(conn, path)) != NULL) {
|
||||||
|
authorized = authorize(conn, fp);
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
|
|
||||||
|
return authorized;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int is_authorized_for_put(struct connection *conn) {
|
||||||
|
const char *auth_file = conn->server->config_options[PUT_DELETE_AUTH_FILE];
|
||||||
|
FILE *fp;
|
||||||
|
int authorized = 0;
|
||||||
|
|
||||||
|
if (auth_file != NULL && (fp = fopen(auth_file, "r")) != NULL) {
|
||||||
|
authorized = authorize(conn, fp);
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
|
|
||||||
|
return authorized;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int is_put_or_delete_request(const struct connection *conn) {
|
||||||
|
const char *s = conn->mg_conn.request_method;
|
||||||
|
return s && (!strcmp(s, "PUT") || !strcmp(s, "DELETE") ||
|
||||||
|
!strcmp(s, "MKCOL"));
|
||||||
|
}
|
||||||
|
#endif // NO_AUTH
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
// Parsed Authorization header
|
||||||
|
struct ah { char *user, *uri, *cnonce, *response, *qop, *nc, *nonce; };
|
||||||
|
|
||||||
|
// Return 1 on success. Always initializes the ah structure.
|
||||||
|
static int parse_auth_header(struct mg_connection *conn, char *buf,
|
||||||
|
size_t buf_size, struct ah *ah) {
|
||||||
|
char *name, *value, *s;
|
||||||
|
const char *auth_header;
|
||||||
|
|
||||||
|
memset(ah, 0, sizeof(*ah));
|
||||||
|
if ((auth_header = mg_get_header(conn, "Authorization")) == NULL ||
|
||||||
|
mg_strncasecmp(auth_header, "Digest ", 7) != 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make modifiable copy of the auth header
|
||||||
|
strncpy(buf, auth_header + 7, buf_size);
|
||||||
|
s = buf;
|
||||||
|
|
||||||
|
// Parse authorization header
|
||||||
|
for (;;) {
|
||||||
|
// Gobble initial spaces
|
||||||
|
while (isspace(* (unsigned char *) s)) {
|
||||||
|
s++;
|
||||||
|
}
|
||||||
|
name = skip_quoted(&s, "=", " ", 0);
|
||||||
|
// Value is either quote-delimited, or ends at first comma or space.
|
||||||
|
if (s[0] == '\"') {
|
||||||
|
s++;
|
||||||
|
value = skip_quoted(&s, "\"", " ", '\\');
|
||||||
|
if (s[0] == ',') {
|
||||||
|
s++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
value = skip_quoted(&s, ", ", " ", 0); // IE uses commas, FF uses spaces
|
||||||
|
}
|
||||||
|
if (*name == '\0') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!strcmp(name, "username")) {
|
||||||
|
ah->user = value;
|
||||||
|
} else if (!strcmp(name, "cnonce")) {
|
||||||
|
ah->cnonce = value;
|
||||||
|
} else if (!strcmp(name, "response")) {
|
||||||
|
ah->response = value;
|
||||||
|
} else if (!strcmp(name, "uri")) {
|
||||||
|
ah->uri = value;
|
||||||
|
} else if (!strcmp(name, "qop")) {
|
||||||
|
ah->qop = value;
|
||||||
|
} else if (!strcmp(name, "nc")) {
|
||||||
|
ah->nc = value;
|
||||||
|
} else if (!strcmp(name, "nonce")) {
|
||||||
|
ah->nonce = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CGI needs it as REMOTE_USER
|
||||||
|
if (ah->user != NULL) {
|
||||||
|
conn->request_info.remote_user = mg_strdup(ah->user);
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
int mg_parse_header(const char *str, const char *var_name, char *buf,
|
||||||
|
size_t buf_size) {
|
||||||
|
int ch = ' ', len = 0, n = strlen(var_name);
|
||||||
|
const char *p, *s = str == NULL ? NULL : strstr(str, var_name);
|
||||||
|
|
||||||
|
if (s != NULL && s[n] == '=' && s[n + 1] != '\0') {
|
||||||
|
s += n + 1;
|
||||||
|
if (*s == '"' || *s == '\'') {
|
||||||
|
ch = *s++;
|
||||||
|
}
|
||||||
|
if (*s != '\0' && (((p = strchr(s, ch)) != NULL &&
|
||||||
|
(size_t) (p - s) < buf_size) ||
|
||||||
|
(p == NULL && ch == ' ' && strlen(s) < buf_size &&
|
||||||
|
(p = s + strlen(s)) != NULL))) {
|
||||||
|
len = p - s;
|
||||||
|
memcpy(buf, s, len);
|
||||||
|
buf[len] = '\0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
static void open_local_endpoint(struct connection *conn) {
|
static void open_local_endpoint(struct connection *conn) {
|
||||||
char path[MAX_PATH_SIZE] = {'\0'};
|
char path[MAX_PATH_SIZE] = {'\0'};
|
||||||
file_stat_t st;
|
file_stat_t st;
|
||||||
@@ -1916,6 +2373,11 @@ static void open_local_endpoint(struct connection *conn) {
|
|||||||
send_options(conn);
|
send_options(conn);
|
||||||
} else if (conn->server->config_options[DOCUMENT_ROOT] == NULL) {
|
} else if (conn->server->config_options[DOCUMENT_ROOT] == NULL) {
|
||||||
send_http_error(conn, "%s", "HTTP/1.1 404 Not Found\r\n\r\n");
|
send_http_error(conn, "%s", "HTTP/1.1 404 Not Found\r\n\r\n");
|
||||||
|
#ifndef NO_AUTH
|
||||||
|
} else if ((!is_put_or_delete_request(conn) && !is_authorized(conn, path)) ||
|
||||||
|
(is_put_or_delete_request(conn) && !is_authorized_for_put(conn))) {
|
||||||
|
send_authorization_request(conn);
|
||||||
|
#endif
|
||||||
#ifndef NO_DAV
|
#ifndef NO_DAV
|
||||||
} else if (!strcmp(conn->mg_conn.request_method, "PROPFIND")) {
|
} else if (!strcmp(conn->mg_conn.request_method, "PROPFIND")) {
|
||||||
propfind(conn, path, &st);
|
propfind(conn, path, &st);
|
||||||
@@ -1970,8 +2432,7 @@ static void process_request(struct connection *conn) {
|
|||||||
struct iobuf *io = &conn->local_iobuf;
|
struct iobuf *io = &conn->local_iobuf;
|
||||||
|
|
||||||
if (conn->request_len == 0 &&
|
if (conn->request_len == 0 &&
|
||||||
(conn->request_len = get_request_len((unsigned char *) io->buf,
|
(conn->request_len = get_request_len(io->buf, io->len)) > 0) {
|
||||||
io->len)) > 0) {
|
|
||||||
// If request is buffered in, remove it from the iobuf. This is because
|
// If request is buffered in, remove it from the iobuf. This is because
|
||||||
// iobuf could be reallocated, and pointers in parsed request could
|
// iobuf could be reallocated, and pointers in parsed request could
|
||||||
// become invalid.
|
// become invalid.
|
||||||
@@ -2023,10 +2484,10 @@ static void read_from_cgi(struct connection *conn) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int should_keep_alive(const struct connection *conn) {
|
static int should_keep_alive(const struct mg_connection *conn) {
|
||||||
const char *method = conn->mg_conn.request_method;
|
const char *method = conn->request_method;
|
||||||
const char *http_version = conn->mg_conn.http_version;
|
const char *http_version = conn->http_version;
|
||||||
const char *header = mg_get_header(&conn->mg_conn, "Connection");
|
const char *header = mg_get_header(conn, "Connection");
|
||||||
return method != NULL && !strcmp(method, "GET") &&
|
return method != NULL && !strcmp(method, "GET") &&
|
||||||
((header != NULL && !strcmp(header, "keep-alive")) ||
|
((header != NULL && !strcmp(header, "keep-alive")) ||
|
||||||
(header == NULL && http_version && !strcmp(http_version, "1.1")));
|
(header == NULL && http_version && !strcmp(http_version, "1.1")));
|
||||||
@@ -2034,7 +2495,8 @@ static int should_keep_alive(const struct connection *conn) {
|
|||||||
|
|
||||||
static void close_local_endpoint(struct connection *conn) {
|
static void close_local_endpoint(struct connection *conn) {
|
||||||
// Must be done before free()
|
// Must be done before free()
|
||||||
int keep_alive = should_keep_alive(conn) && conn->endpoint_type == EP_FILE;
|
int keep_alive = should_keep_alive(&conn->mg_conn) &&
|
||||||
|
conn->endpoint_type == EP_FILE;
|
||||||
|
|
||||||
switch (conn->endpoint_type) {
|
switch (conn->endpoint_type) {
|
||||||
case EP_FILE: close(conn->endpoint.fd); break;
|
case EP_FILE: close(conn->endpoint.fd); break;
|
||||||
@@ -2226,7 +2688,8 @@ static void set_default_option_values(char **opts) {
|
|||||||
// Valid listening port spec is: [ip_address:]port, e.g. "80", "127.0.0.1:3128"
|
// Valid listening port spec is: [ip_address:]port, e.g. "80", "127.0.0.1:3128"
|
||||||
static int parse_port_string(const char *str, union socket_address *sa) {
|
static int parse_port_string(const char *str, union socket_address *sa) {
|
||||||
unsigned int a, b, c, d, port;
|
unsigned int a, b, c, d, port;
|
||||||
#if defined(USE_IPV6)
|
int len = 0;
|
||||||
|
#ifdef USE_IPV6
|
||||||
char buf[100];
|
char buf[100];
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -2236,25 +2699,25 @@ static int parse_port_string(const char *str, union socket_address *sa) {
|
|||||||
memset(sa, 0, sizeof(*sa));
|
memset(sa, 0, sizeof(*sa));
|
||||||
sa->sin.sin_family = AF_INET;
|
sa->sin.sin_family = AF_INET;
|
||||||
|
|
||||||
if (sscanf(str, "%u.%u.%u.%u:%u", &a, &b, &c, &d, &port) == 5) {
|
if (sscanf(str, "%u.%u.%u.%u:%u%n", &a, &b, &c, &d, &port, &len) == 5) {
|
||||||
// Bind to a specific IPv4 address, e.g. 192.168.1.5:8080
|
// Bind to a specific IPv4 address, e.g. 192.168.1.5:8080
|
||||||
sa->sin.sin_addr.s_addr = htonl((a << 24) | (b << 16) | (c << 8) | d);
|
sa->sin.sin_addr.s_addr = htonl((a << 24) | (b << 16) | (c << 8) | d);
|
||||||
sa->sin.sin_port = htons((uint16_t) port);
|
sa->sin.sin_port = htons((uint16_t) port);
|
||||||
#if defined(USE_IPV6)
|
#if defined(USE_IPV6)
|
||||||
} else if (sscanf(str, "[%49[^]]]:%d%n", buf, &port, &len) == 2 &&
|
} else if (sscanf(str, "[%49[^]]]:%d%n", buf, &port, &len) == 2 &&
|
||||||
inet_pton(AF_INET6, buf, &so->lsa.sin6.sin6_addr)) {
|
inet_pton(AF_INET6, buf, &sa->sin6.sin6_addr)) {
|
||||||
// IPv6 address, e.g. [3ffe:2a00:100:7031::1]:8080
|
// IPv6 address, e.g. [3ffe:2a00:100:7031::1]:8080
|
||||||
so->lsa.sin6.sin6_family = AF_INET6;
|
sa->sin6.sin6_family = AF_INET6;
|
||||||
so->lsa.sin6.sin6_port = htons((uint16_t) port);
|
sa->sin6.sin6_port = htons((uint16_t) port);
|
||||||
#endif
|
#endif
|
||||||
} else if (sscanf(str, "%u", &port) == 1) {
|
} else if (sscanf(str, "%u%n", &port, &len) == 1) {
|
||||||
// If only port is specified, bind to IPv4, INADDR_ANY
|
// If only port is specified, bind to IPv4, INADDR_ANY
|
||||||
sa->sin.sin_port = htons((uint16_t) port);
|
sa->sin.sin_port = htons((uint16_t) port);
|
||||||
} else {
|
} else {
|
||||||
port = 0; // Parsing failure. Make port invalid.
|
port = 0; // Parsing failure. Make port invalid.
|
||||||
}
|
}
|
||||||
|
|
||||||
return port > 0 && port < 0xffff;
|
return port > 0 && port < 0xffff && str[len] == '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *mg_set_option(struct mg_server *server, const char *name,
|
const char *mg_set_option(struct mg_server *server, const char *name,
|
||||||
@@ -2288,7 +2751,7 @@ const char *mg_set_option(struct mg_server *server, const char *name,
|
|||||||
}
|
}
|
||||||
|
|
||||||
const char *mg_get_option(const struct mg_server *server, const char *name) {
|
const char *mg_get_option(const struct mg_server *server, const char *name) {
|
||||||
const char *const *opts = server->config_options;
|
const char **opts = (const char **) server->config_options;
|
||||||
int i = get_option_index(name);
|
int i = get_option_index(name);
|
||||||
return i == -1 ? NULL : opts[i] == NULL ? "" : opts[i];
|
return i == -1 ? NULL : opts[i] == NULL ? "" : opts[i];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,6 +81,7 @@ int mg_get_var(const struct mg_connection *conn,
|
|||||||
const char *var_name, char *dst, size_t dst_len);
|
const char *var_name, char *dst, size_t dst_len);
|
||||||
int mg_get_cookie(const char *cookie, const char *var_name,
|
int mg_get_cookie(const char *cookie, const char *var_name,
|
||||||
char *buf, size_t buf_len);
|
char *buf, size_t buf_len);
|
||||||
|
int mg_parse_header(const char *hdr, const char *var_name, char *buf, size_t);
|
||||||
|
|
||||||
// Utility functions
|
// Utility functions
|
||||||
int mg_start_thread(void *(*func)(void *), void *param);
|
int mg_start_thread(void *(*func)(void *), void *param);
|
||||||
|
|||||||
+127
-120
@@ -4,37 +4,34 @@
|
|||||||
#define USE_LUA
|
#define USE_LUA
|
||||||
|
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
#define __cdecl
|
|
||||||
#define USE_IPV6
|
#define USE_IPV6
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// USE_* definitions must be made before #include "mongoose.c" !
|
// USE_* definitions must be made before #include "mongoose.c" !
|
||||||
|
#include "src/core.c"
|
||||||
|
|
||||||
#include "mongoose.c"
|
#define FAIL(str, line) do { \
|
||||||
|
printf("Fail on line %d: [%s]\n", line, str); \
|
||||||
static int s_total_tests = 0;
|
return str; \
|
||||||
static int s_failed_tests = 0;
|
|
||||||
|
|
||||||
#define FAIL(str, line) do { \
|
|
||||||
printf("Fail on line %d: [%s]\n", line, str); \
|
|
||||||
s_failed_tests++; \
|
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define ASSERT(expr) do { \
|
#define ASSERT(expr) do { \
|
||||||
s_total_tests++; \
|
static_num_tests++; \
|
||||||
if (!(expr)) FAIL(#expr, __LINE__); \
|
if (!(expr)) FAIL(#expr, __LINE__); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define HTTP_PORT "56789"
|
#define RUN_TEST(test) do { const char *msg = test(); \
|
||||||
#define HTTPS_PORT "56790"
|
if (msg) return msg; } while (0)
|
||||||
#define HTTP_PORT2 "56791"
|
|
||||||
#define LISTENING_ADDR \
|
|
||||||
"127.0.0.1:" HTTP_PORT "r" \
|
|
||||||
",127.0.0.1:" HTTPS_PORT "s" \
|
|
||||||
",127.0.0.1:" HTTP_PORT2
|
|
||||||
|
|
||||||
static void test_parse_http_message() {
|
#define HTTP_PORT "45772"
|
||||||
struct mg_request_info ri;
|
#define LISTENING_ADDR "127.0.0.1:" HTTP_PORT
|
||||||
|
|
||||||
|
static int static_num_tests = 0;
|
||||||
|
//static const char *fetch_data = "hello world!\n";
|
||||||
|
//static const char *upload_ok_message = "upload successful";
|
||||||
|
|
||||||
|
static const char *test_parse_http_message() {
|
||||||
|
struct mg_connection ri;
|
||||||
char req1[] = "GET / HTTP/1.1\r\n\r\n";
|
char req1[] = "GET / HTTP/1.1\r\n\r\n";
|
||||||
char req2[] = "BLAH / HTTP/1.1\r\n\r\n";
|
char req2[] = "BLAH / HTTP/1.1\r\n\r\n";
|
||||||
char req3[] = "GET / HTTP/1.1\r\nBah\r\n";
|
char req3[] = "GET / HTTP/1.1\r\nBah\r\n";
|
||||||
@@ -52,19 +49,19 @@ static void test_parse_http_message() {
|
|||||||
ASSERT(get_request_len("\n\r\n", 3) == 3);
|
ASSERT(get_request_len("\n\r\n", 3) == 3);
|
||||||
ASSERT(get_request_len("\xdd\xdd", 2) == 0);
|
ASSERT(get_request_len("\xdd\xdd", 2) == 0);
|
||||||
ASSERT(get_request_len("\xdd\x03", 2) == -1);
|
ASSERT(get_request_len("\xdd\x03", 2) == -1);
|
||||||
|
ASSERT(get_request_len(req3, sizeof(req3) - 1) == 0);
|
||||||
|
ASSERT(get_request_len(req6, sizeof(req6) - 1) == 0);
|
||||||
|
ASSERT(get_request_len(req7, sizeof(req7) - 1) == 0);
|
||||||
|
|
||||||
ASSERT(parse_http_message(req9, sizeof(req9), &ri) == sizeof(req9) - 1);
|
ASSERT(parse_http_message(req9, sizeof(req9) - 1, &ri) == sizeof(req9) - 1);
|
||||||
ASSERT(ri.num_headers == 1);
|
ASSERT(ri.num_headers == 1);
|
||||||
|
|
||||||
ASSERT(parse_http_message(req1, sizeof(req1), &ri) == sizeof(req1) - 1);
|
ASSERT(parse_http_message(req1, sizeof(req1) - 1, &ri) == sizeof(req1) - 1);
|
||||||
ASSERT(strcmp(ri.http_version, "1.1") == 0);
|
ASSERT(strcmp(ri.http_version, "1.1") == 0);
|
||||||
ASSERT(ri.num_headers == 0);
|
ASSERT(ri.num_headers == 0);
|
||||||
|
|
||||||
ASSERT(parse_http_message(req2, sizeof(req2) - 1, &ri) == -1);
|
ASSERT(parse_http_message(req2, sizeof(req2) - 1, &ri) == -1);
|
||||||
ASSERT(parse_http_message(req3, sizeof(req3) - 1, &ri) == 0);
|
ASSERT(parse_http_message(req6, 0, &ri) == -1);
|
||||||
ASSERT(parse_http_message(req6, sizeof(req6) - 1, &ri) == 0);
|
|
||||||
ASSERT(parse_http_message(req7, sizeof(req7) - 1, &ri) == 0);
|
|
||||||
ASSERT(parse_http_message("", 0, &ri) == 0);
|
|
||||||
ASSERT(parse_http_message(req8, sizeof(req8) - 1, &ri) == sizeof(req8) - 1);
|
ASSERT(parse_http_message(req8, sizeof(req8) - 1, &ri) == sizeof(req8) - 1);
|
||||||
|
|
||||||
// TODO(lsm): Fix this. Header value may span multiple lines.
|
// TODO(lsm): Fix this. Header value may span multiple lines.
|
||||||
@@ -81,49 +78,34 @@ static void test_parse_http_message() {
|
|||||||
ASSERT(parse_http_message(req5, sizeof(req5) - 1, &ri) == sizeof(req5) - 1);
|
ASSERT(parse_http_message(req5, sizeof(req5) - 1, &ri) == sizeof(req5) - 1);
|
||||||
ASSERT(strcmp(ri.request_method, "GET") == 0);
|
ASSERT(strcmp(ri.request_method, "GET") == 0);
|
||||||
ASSERT(strcmp(ri.http_version, "1.1") == 0);
|
ASSERT(strcmp(ri.http_version, "1.1") == 0);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void test_should_keep_alive(void) {
|
static const char *test_should_keep_alive(void) {
|
||||||
struct mg_connection conn;
|
struct mg_connection conn;
|
||||||
struct mg_context ctx;
|
|
||||||
char req1[] = "GET / HTTP/1.1\r\n\r\n";
|
char req1[] = "GET / HTTP/1.1\r\n\r\n";
|
||||||
char req2[] = "GET / HTTP/1.0\r\n\r\n";
|
char req2[] = "GET / HTTP/1.0\r\n\r\n";
|
||||||
char req3[] = "GET / HTTP/1.1\r\nConnection: close\r\n\r\n";
|
char req3[] = "GET / HTTP/1.1\r\nConnection: close\r\n\r\n";
|
||||||
char req4[] = "GET / HTTP/1.1\r\nConnection: keep-alive\r\n\r\n";
|
char req4[] = "GET / HTTP/1.1\r\nConnection: keep-alive\r\n\r\n";
|
||||||
|
|
||||||
memset(&conn, 0, sizeof(conn));
|
memset(&conn, 0, sizeof(conn));
|
||||||
conn.ctx = &ctx;
|
ASSERT(parse_http_message(req1, sizeof(req1) - 1, &conn) == sizeof(req1) - 1);
|
||||||
ASSERT(parse_http_message(req1, sizeof(req1), &conn.request_info) ==
|
ASSERT(should_keep_alive(&conn) != 0);
|
||||||
sizeof(req1) - 1);
|
|
||||||
|
|
||||||
ctx.config[ENABLE_KEEP_ALIVE] = "no";
|
parse_http_message(req2, sizeof(req2) - 1, &conn);
|
||||||
ASSERT(should_keep_alive(&conn) == 0);
|
ASSERT(should_keep_alive(&conn) == 0);
|
||||||
|
|
||||||
ctx.config[ENABLE_KEEP_ALIVE] = "yes";
|
parse_http_message(req3, sizeof(req3) - 1, &conn);
|
||||||
ASSERT(should_keep_alive(&conn) == 1);
|
|
||||||
|
|
||||||
conn.must_close = 1;
|
|
||||||
ASSERT(should_keep_alive(&conn) == 0);
|
ASSERT(should_keep_alive(&conn) == 0);
|
||||||
|
|
||||||
conn.must_close = 0;
|
parse_http_message(req4, sizeof(req4) - 1, &conn);
|
||||||
parse_http_message(req2, sizeof(req2), &conn.request_info);
|
ASSERT(should_keep_alive(&conn) != 0);
|
||||||
ASSERT(should_keep_alive(&conn) == 0);
|
|
||||||
|
|
||||||
parse_http_message(req3, sizeof(req3), &conn.request_info);
|
return NULL;
|
||||||
ASSERT(should_keep_alive(&conn) == 0);
|
|
||||||
|
|
||||||
parse_http_message(req4, sizeof(req4), &conn.request_info);
|
|
||||||
ASSERT(should_keep_alive(&conn) == 1);
|
|
||||||
|
|
||||||
conn.status_code = 401;
|
|
||||||
ASSERT(should_keep_alive(&conn) == 0);
|
|
||||||
|
|
||||||
conn.status_code = 200;
|
|
||||||
conn.must_close = 1;
|
|
||||||
ASSERT(should_keep_alive(&conn) == 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void test_match_prefix(void) {
|
static const char *test_match_prefix(void) {
|
||||||
ASSERT(match_prefix("/api", 4, "/api") == 4);
|
ASSERT(match_prefix("/api", 4, "/api") == 4);
|
||||||
ASSERT(match_prefix("/a/", 3, "/a/b/c") == 3);
|
ASSERT(match_prefix("/a/", 3, "/a/b/c") == 3);
|
||||||
ASSERT(match_prefix("/a/", 3, "/ab/c") == -1);
|
ASSERT(match_prefix("/a/", 3, "/ab/c") == -1);
|
||||||
@@ -152,9 +134,10 @@ static void test_match_prefix(void) {
|
|||||||
ASSERT(match_prefix("**.a$|**.b$", 11, "/a/b.b") == 6);
|
ASSERT(match_prefix("**.a$|**.b$", 11, "/a/b.b") == 6);
|
||||||
ASSERT(match_prefix("**.a$|**.b$", 11, "/a/B.A") == 6);
|
ASSERT(match_prefix("**.a$|**.b$", 11, "/a/B.A") == 6);
|
||||||
ASSERT(match_prefix("**o$", 4, "HELLO") == 5);
|
ASSERT(match_prefix("**o$", 4, "HELLO") == 5);
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void test_remove_double_dots() {
|
static const char *test_remove_double_dots() {
|
||||||
struct { char before[20], after[20]; } data[] = {
|
struct { char before[20], after[20]; } data[] = {
|
||||||
{"////a", "/a"},
|
{"////a", "/a"},
|
||||||
{"/.....", "/."},
|
{"/.....", "/."},
|
||||||
@@ -173,8 +156,11 @@ static void test_remove_double_dots() {
|
|||||||
remove_double_dots_and_double_slashes(data[i].before);
|
remove_double_dots_and_double_slashes(data[i].before);
|
||||||
ASSERT(strcmp(data[i].before, data[i].after) == 0);
|
ASSERT(strcmp(data[i].before, data[i].after) == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
static char *read_file(const char *path, int *size) {
|
static char *read_file(const char *path, int *size) {
|
||||||
FILE *fp;
|
FILE *fp;
|
||||||
struct stat st;
|
struct stat st;
|
||||||
@@ -188,9 +174,6 @@ static char *read_file(const char *path, int *size) {
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char *fetch_data = "hello world!\n";
|
|
||||||
static const char *upload_ok_message = "upload successful";
|
|
||||||
|
|
||||||
static void test_upload(struct mg_connection *conn, const char *orig_path,
|
static void test_upload(struct mg_connection *conn, const char *orig_path,
|
||||||
const char *uploaded_path) {
|
const char *uploaded_path) {
|
||||||
int len1, len2;
|
int len1, len2;
|
||||||
@@ -378,18 +361,6 @@ static void test_mg_upload(void) {
|
|||||||
mg_stop(ctx);
|
mg_stop(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void test_base64_encode(void) {
|
|
||||||
const char *in[] = {"a", "ab", "abc", "abcd", NULL};
|
|
||||||
const char *out[] = {"YQ==", "YWI=", "YWJj", "YWJjZA=="};
|
|
||||||
char buf[100];
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; in[i] != NULL; i++) {
|
|
||||||
base64_encode((unsigned char *) in[i], strlen(in[i]), buf);
|
|
||||||
ASSERT(!strcmp(buf, out[i]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void test_mg_get_var(void) {
|
static void test_mg_get_var(void) {
|
||||||
static const char *post[] = {
|
static const char *post[] = {
|
||||||
"a=1&&b=2&d&=&c=3%20&e=",
|
"a=1&&b=2&d&=&c=3%20&e=",
|
||||||
@@ -560,6 +531,16 @@ static int api_cb(struct mg_event *event) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char *test_mg_strcasestr(void) {
|
||||||
|
static const char *big1 = "abcdef";
|
||||||
|
ASSERT(mg_strcasestr("Y", "X") == NULL);
|
||||||
|
ASSERT(mg_strcasestr("Y", "y") != NULL);
|
||||||
|
ASSERT(mg_strcasestr(big1, "X") == NULL);
|
||||||
|
ASSERT(mg_strcasestr(big1, "CD") == big1 + 2);
|
||||||
|
ASSERT(mg_strcasestr("aa", "AAB") == NULL);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static void test_api_calls(void) {
|
static void test_api_calls(void) {
|
||||||
char ebuf[100];
|
char ebuf[100];
|
||||||
struct mg_connection *conn;
|
struct mg_connection *conn;
|
||||||
@@ -576,7 +557,24 @@ static void test_api_calls(void) {
|
|||||||
mg_stop(ctx);
|
mg_stop(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void test_url_decode(void) {
|
static void test_mg_get_cookie(void) {
|
||||||
|
char buf[20];
|
||||||
|
|
||||||
|
ASSERT(mg_get_cookie("", "foo", NULL, sizeof(buf)) == -2);
|
||||||
|
ASSERT(mg_get_cookie("", "foo", buf, 0) == -2);
|
||||||
|
ASSERT(mg_get_cookie("", "foo", buf, sizeof(buf)) == -1);
|
||||||
|
ASSERT(mg_get_cookie("", NULL, buf, sizeof(buf)) == -1);
|
||||||
|
ASSERT(mg_get_cookie("a=1; b=2; c; d", "a", buf, sizeof(buf)) == 1);
|
||||||
|
ASSERT(strcmp(buf, "1") == 0);
|
||||||
|
ASSERT(mg_get_cookie("a=1; b=2; c; d", "b", buf, sizeof(buf)) == 1);
|
||||||
|
ASSERT(strcmp(buf, "2") == 0);
|
||||||
|
ASSERT(mg_get_cookie("a=1; b=123", "b", buf, sizeof(buf)) == 3);
|
||||||
|
ASSERT(strcmp(buf, "123") == 0);
|
||||||
|
ASSERT(mg_get_cookie("a=1; b=2; c; d", "c", buf, sizeof(buf)) == -1);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static const char *test_url_decode(void) {
|
||||||
char buf[100];
|
char buf[100];
|
||||||
|
|
||||||
ASSERT(mg_url_decode("foo", 3, buf, 3, 0) == -1); // No space for \0
|
ASSERT(mg_url_decode("foo", 3, buf, 3, 0) == -1); // No space for \0
|
||||||
@@ -597,78 +595,85 @@ static void test_url_decode(void) {
|
|||||||
|
|
||||||
ASSERT(mg_url_decode("%61", 3, buf, sizeof(buf), 1) == 1);
|
ASSERT(mg_url_decode("%61", 3, buf, sizeof(buf), 1) == 1);
|
||||||
ASSERT(strcmp(buf, "a") == 0);
|
ASSERT(strcmp(buf, "a") == 0);
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void test_mg_strcasestr(void) {
|
static const char *test_to64(void) {
|
||||||
static const char *big1 = "abcdef";
|
|
||||||
ASSERT(mg_strcasestr("Y", "X") == NULL);
|
|
||||||
ASSERT(mg_strcasestr("Y", "y") != NULL);
|
|
||||||
ASSERT(mg_strcasestr(big1, "X") == NULL);
|
|
||||||
ASSERT(mg_strcasestr(big1, "CD") == big1 + 2);
|
|
||||||
ASSERT(mg_strcasestr("aa", "AAB") == NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void test_mg_get_cookie(void) {
|
|
||||||
char buf[20];
|
|
||||||
|
|
||||||
ASSERT(mg_get_cookie("", "foo", NULL, sizeof(buf)) == -2);
|
|
||||||
ASSERT(mg_get_cookie("", "foo", buf, 0) == -2);
|
|
||||||
ASSERT(mg_get_cookie("", "foo", buf, sizeof(buf)) == -1);
|
|
||||||
ASSERT(mg_get_cookie("", NULL, buf, sizeof(buf)) == -1);
|
|
||||||
ASSERT(mg_get_cookie("a=1; b=2; c; d", "a", buf, sizeof(buf)) == 1);
|
|
||||||
ASSERT(strcmp(buf, "1") == 0);
|
|
||||||
ASSERT(mg_get_cookie("a=1; b=2; c; d", "b", buf, sizeof(buf)) == 1);
|
|
||||||
ASSERT(strcmp(buf, "2") == 0);
|
|
||||||
ASSERT(mg_get_cookie("a=1; b=123", "b", buf, sizeof(buf)) == 3);
|
|
||||||
ASSERT(strcmp(buf, "123") == 0);
|
|
||||||
ASSERT(mg_get_cookie("a=1; b=2; c; d", "c", buf, sizeof(buf)) == -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void test_strtoll(void) {
|
|
||||||
ASSERT(strtoll("0", NULL, 10) == 0);
|
ASSERT(strtoll("0", NULL, 10) == 0);
|
||||||
ASSERT(strtoll("123", NULL, 10) == 123);
|
ASSERT(strtoll("123", NULL, 10) == 123);
|
||||||
ASSERT(strtoll("-34", NULL, 10) == -34);
|
ASSERT(strtoll("-34", NULL, 10) == -34);
|
||||||
ASSERT(strtoll("3566626116", NULL, 10) == 3566626116);
|
ASSERT(strtoll("3566626116", NULL, 10) == 3566626116);
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void test_parse_port_string(void) {
|
static const char *test_parse_port_string(void) {
|
||||||
static const char *valid[] = {
|
static const char *valid[] = {
|
||||||
"1", "1s", "1r", "1.2.3.4:1", "1.2.3.4:1s", "1.2.3.4:1r",
|
"1", "1.2.3.4:1",
|
||||||
#if defined(USE_IPV6)
|
#if defined(USE_IPV6)
|
||||||
"[::1]:123", "[3ffe:2a00:100:7031::1]:900",
|
"[::1]:123", "[3ffe:2a00:100:7031::1]:900",
|
||||||
#endif
|
#endif
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
static const char *invalid[] = {
|
static const char *invalid[] = {
|
||||||
"0", "99999", "1k", "1.2.3", "1.2.3.4:", "1.2.3.4:2p",
|
"0", "99999", "1k", "1.2.3", "1.2.3.4:", "1.2.3.4:2p", NULL
|
||||||
NULL
|
|
||||||
};
|
};
|
||||||
struct socket so;
|
union socket_address sa;
|
||||||
struct vec vec;
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; valid[i] != NULL; i++) {
|
for (i = 0; valid[i] != NULL; i++) {
|
||||||
vec.ptr = valid[i];
|
ASSERT(parse_port_string(valid[i], &sa) != 0);
|
||||||
vec.len = strlen(vec.ptr);
|
|
||||||
ASSERT(parse_port_string(&vec, &so) != 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; invalid[i] != NULL; i++) {
|
for (i = 0; invalid[i] != NULL; i++) {
|
||||||
vec.ptr = invalid[i];
|
ASSERT(parse_port_string(invalid[i], &sa) == 0);
|
||||||
vec.len = strlen(vec.ptr);
|
|
||||||
ASSERT(parse_port_string(&vec, &so) == 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
int __cdecl main(void) {
|
static const char *test_base64_encode(void) {
|
||||||
test_parse_port_string();
|
const char *in[] = {"a", "ab", "abc", "abcd", NULL};
|
||||||
|
const char *out[] = {"YQ==", "YWI=", "YWJj", "YWJjZA=="};
|
||||||
|
char buf[100];
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; in[i] != NULL; i++) {
|
||||||
|
base64_encode((unsigned char *) in[i], strlen(in[i]), buf);
|
||||||
|
ASSERT(!strcmp(buf, out[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *test_mg_parse_header() {
|
||||||
|
const char *str = "xx yy, ert=234 ii zz='aa bb', gf=\"xx d=1234";
|
||||||
|
char buf[10];
|
||||||
|
ASSERT(mg_parse_header(str, "yy", buf, sizeof(buf)) == 0);
|
||||||
|
ASSERT(mg_parse_header(str, "ert", buf, sizeof(buf)) == 3);
|
||||||
|
ASSERT(strcmp(buf, "234") == 0);
|
||||||
|
ASSERT(mg_parse_header(str, "ert", buf, 2) == 0);
|
||||||
|
ASSERT(mg_parse_header(str, "ert", buf, 3) == 0);
|
||||||
|
ASSERT(mg_parse_header(str, "ert", buf, 4) == 3);
|
||||||
|
ASSERT(mg_parse_header(str, "gf", buf, sizeof(buf)) == 0);
|
||||||
|
ASSERT(mg_parse_header(str, "zz", buf, sizeof(buf)) == 5);
|
||||||
|
ASSERT(strcmp(buf, "aa bb") == 0);
|
||||||
|
ASSERT(mg_parse_header(str, "d", buf, sizeof(buf)) == 4);
|
||||||
|
ASSERT(strcmp(buf, "1234") == 0);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *run_all_tests(void) {
|
||||||
|
RUN_TEST(test_should_keep_alive);
|
||||||
|
RUN_TEST(test_match_prefix);
|
||||||
|
RUN_TEST(test_remove_double_dots);
|
||||||
|
RUN_TEST(test_parse_http_message);
|
||||||
|
RUN_TEST(test_parse_port_string);
|
||||||
|
RUN_TEST(test_to64);
|
||||||
|
RUN_TEST(test_url_decode);
|
||||||
|
RUN_TEST(test_base64_encode);
|
||||||
|
RUN_TEST(test_mg_parse_header);
|
||||||
|
#if 0
|
||||||
test_mg_strcasestr();
|
test_mg_strcasestr();
|
||||||
test_alloc_vprintf();
|
|
||||||
test_base64_encode();
|
|
||||||
test_match_prefix();
|
|
||||||
test_remove_double_dots();
|
|
||||||
test_parse_http_message();
|
|
||||||
test_should_keep_alive();
|
|
||||||
test_mg_download();
|
test_mg_download();
|
||||||
test_mg_get_var();
|
test_mg_get_var();
|
||||||
test_next_option();
|
test_next_option();
|
||||||
@@ -677,14 +682,16 @@ int __cdecl main(void) {
|
|||||||
test_mg_upload();
|
test_mg_upload();
|
||||||
test_request_replies();
|
test_request_replies();
|
||||||
test_api_calls();
|
test_api_calls();
|
||||||
test_url_decode();
|
|
||||||
test_mg_get_cookie();
|
test_mg_get_cookie();
|
||||||
test_strtoll();
|
|
||||||
#ifdef USE_LUA
|
#ifdef USE_LUA
|
||||||
test_lua();
|
test_lua();
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
printf("TOTAL TESTS: %d, FAILED: %d\n", s_total_tests, s_failed_tests);
|
int __cdecl main(void) {
|
||||||
|
const char *fail_msg = run_all_tests();
|
||||||
return s_failed_tests == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
|
printf("%s, tests run: %d\n", fail_msg ? "FAIL" : "PASS", static_num_tests);
|
||||||
|
return fail_msg == NULL ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user