Commit dbb38fff authored by Alexander Alashkin's avatar Alexander Alashkin Committed by Marko Mikulicic

Make (http) proto_data persistent

    PUBLISHED_FROM=8210f4730a3411b3298274d792fc33da79f47b8a
parent 672a6821
...@@ -2147,6 +2147,9 @@ void mg_if_poll(struct mg_connection *nc, time_t now) { ...@@ -2147,6 +2147,9 @@ void mg_if_poll(struct mg_connection *nc, time_t now) {
} }
static void mg_destroy_conn(struct mg_connection *conn) { static void mg_destroy_conn(struct mg_connection *conn) {
if (conn->proto_data != NULL && conn->proto_data_destructor != NULL) {
conn->proto_data_destructor(conn->proto_data);
}
mg_if_destroy_conn(conn); mg_if_destroy_conn(conn);
#ifdef MG_ENABLE_SSL #ifdef MG_ENABLE_SSL
if (conn->ssl != NULL) SSL_free(conn->ssl); if (conn->ssl != NULL) SSL_free(conn->ssl);
...@@ -2155,9 +2158,6 @@ static void mg_destroy_conn(struct mg_connection *conn) { ...@@ -2155,9 +2158,6 @@ static void mg_destroy_conn(struct mg_connection *conn) {
mbuf_free(&conn->recv_mbuf); mbuf_free(&conn->recv_mbuf);
mbuf_free(&conn->send_mbuf); mbuf_free(&conn->send_mbuf);
mbuf_free(&conn->endpoints); mbuf_free(&conn->endpoints);
#ifdef MG_ENABLE_HTTP_STREAMING_MULTIPART
mbuf_free(&conn->strm_state);
#endif
memset(conn, 0, sizeof(*conn)); memset(conn, 0, sizeof(*conn));
MG_FREE(conn); MG_FREE(conn);
...@@ -2598,7 +2598,6 @@ struct mg_connection *mg_if_accept_new_conn(struct mg_connection *lc) { ...@@ -2598,7 +2598,6 @@ struct mg_connection *mg_if_accept_new_conn(struct mg_connection *lc) {
nc = mg_create_connection(lc->mgr, lc->handler, opts); nc = mg_create_connection(lc->mgr, lc->handler, opts);
if (nc == NULL) return NULL; if (nc == NULL) return NULL;
nc->listener = lc; nc->listener = lc;
nc->proto_data = lc->proto_data;
nc->proto_handler = lc->proto_handler; nc->proto_handler = lc->proto_handler;
nc->user_data = lc->user_data; nc->user_data = lc->user_data;
nc->recv_mbuf_limit = lc->recv_mbuf_limit; nc->recv_mbuf_limit = lc->recv_mbuf_limit;
...@@ -2686,7 +2685,6 @@ void mg_if_recv_udp_cb(struct mg_connection *nc, void *buf, int len, ...@@ -2686,7 +2685,6 @@ void mg_if_recv_udp_cb(struct mg_connection *nc, void *buf, int len,
nc->sock = lc->sock; nc->sock = lc->sock;
nc->listener = lc; nc->listener = lc;
nc->sa = *sa; nc->sa = *sa;
nc->proto_data = lc->proto_data;
nc->proto_handler = lc->proto_handler; nc->proto_handler = lc->proto_handler;
nc->user_data = lc->user_data; nc->user_data = lc->user_data;
nc->recv_mbuf_limit = lc->recv_mbuf_limit; nc->recv_mbuf_limit = lc->recv_mbuf_limit;
...@@ -4161,19 +4159,82 @@ int mg_normalize_uri_path(const struct mg_str *in, struct mg_str *out) { ...@@ -4161,19 +4159,82 @@ int mg_normalize_uri_path(const struct mg_str *in, struct mg_str *out) {
/* Amalgamated: #include "common/sha1.h" */ /* Amalgamated: #include "common/sha1.h" */
/* Amalgamated: #include "common/md5.h" */ /* Amalgamated: #include "common/md5.h" */
enum http_proto_data_type { DATA_NONE, DATA_FILE, DATA_PUT, DATA_CGI }; enum mg_http_proto_data_type { DATA_NONE, DATA_FILE, DATA_PUT };
struct proto_data_http { struct mg_http_proto_data_file {
#ifndef MG_DISABLE_FILESYSTEM
FILE *fp; /* Opened file. */ FILE *fp; /* Opened file. */
#endif
int64_t cl; /* Content-Length. How many bytes to send. */ int64_t cl; /* Content-Length. How many bytes to send. */
int64_t sent; /* How many bytes have been already sent. */ int64_t sent; /* How many bytes have been already sent. */
int64_t body_len; /* How many bytes of chunked body was reassembled. */ enum mg_http_proto_data_type type;
};
struct mg_http_proto_data_cgi {
struct mg_connection *cgi_nc; struct mg_connection *cgi_nc;
enum http_proto_data_type type;
}; };
struct mg_http_proto_data_chuncked {
int64_t body_len; /* How many bytes of chunked body was reassembled. */
};
struct mg_http_proto_data {
#ifndef MG_DISABLE_FILESYSTEM
struct mg_http_proto_data_file file;
#endif
#ifndef MG_DISABLE_CGI
struct mg_http_proto_data_cgi cgi;
#endif
#ifdef MG_ENABLE_HTTP_STREAMING_MULTIPART
struct mbuf strm_state; /* Used by multi-part streaming */
#endif
struct mg_http_proto_data_chuncked chunk;
};
static void mg_http_conn_destructor(void *proto_data);
static struct mg_http_proto_data *mg_http_get_proto_data(
struct mg_connection *c) {
if (c->proto_data == NULL) {
c->proto_data = MG_CALLOC(1, sizeof(struct mg_http_proto_data));
c->proto_data_destructor = mg_http_conn_destructor;
}
return (struct mg_http_proto_data *) c->proto_data;
}
#ifndef MG_DISABLE_FILESYSTEM
static void mg_http_free_proto_data_file(struct mg_http_proto_data_file *d) {
if (d != NULL) {
if (d->fp != NULL) {
fclose(d->fp);
}
memset(d, 0, sizeof(struct mg_http_proto_data_file));
}
}
#endif
#ifndef MG_DISABLE_CGI
static void mg_http_free_proto_data_cgi(struct mg_http_proto_data_cgi *d) {
if (d != NULL) {
if (d->cgi_nc != NULL) d->cgi_nc->flags |= MG_F_CLOSE_IMMEDIATELY;
memset(d, 0, sizeof(struct mg_http_proto_data_cgi));
}
}
#endif
static void mg_http_conn_destructor(void *proto_data) {
struct mg_http_proto_data *pd = (struct mg_http_proto_data *) proto_data;
#ifndef MG_DISABLE_FILESYSTEM
mg_http_free_proto_data_file(&pd->file);
#endif
#ifndef MG_DISABLE_CGI
mg_http_free_proto_data_cgi(&pd->cgi);
#endif
#ifdef MG_ENABLE_HTTP_STREAMING_MULTIPART
mbuf_free(&pd->strm_state);
#endif
free(proto_data);
}
/* /*
* This structure helps to create an environment for the spawned CGI program. * This structure helps to create an environment for the spawned CGI program.
* Environment is an array of "VARIABLE=VALUE\0" ASCIIZ strings, * Environment is an array of "VARIABLE=VALUE\0" ASCIIZ strings,
...@@ -4184,7 +4245,7 @@ struct proto_data_http { ...@@ -4184,7 +4245,7 @@ struct proto_data_http {
* We satisfy both worlds: we create an envp array (which is vars), all * We satisfy both worlds: we create an envp array (which is vars), all
* entries are actually pointers inside buf. * entries are actually pointers inside buf.
*/ */
struct cgi_env_block { struct mg_cgi_env_block {
struct mg_connection *nc; struct mg_connection *nc;
char buf[MG_CGI_ENVIRONMENT_SIZE]; /* Environment buffer */ char buf[MG_CGI_ENVIRONMENT_SIZE]; /* Environment buffer */
const char *vars[MG_MAX_CGI_ENVIR_VARS]; /* char *envp[] */ const char *vars[MG_MAX_CGI_ENVIR_VARS]; /* char *envp[] */
...@@ -4198,7 +4259,7 @@ static const struct { ...@@ -4198,7 +4259,7 @@ static const struct {
const char *extension; const char *extension;
size_t ext_len; size_t ext_len;
const char *mime_type; const char *mime_type;
} static_builtin_mime_types[] = { } mg_static_builtin_mime_types[] = {
MIME_ENTRY("html", "text/html"), MIME_ENTRY("html", "text/html"),
MIME_ENTRY("html", "text/html"), MIME_ENTRY("html", "text/html"),
MIME_ENTRY("htm", "text/html"), MIME_ENTRY("htm", "text/html"),
...@@ -4262,7 +4323,7 @@ static int mg_mkdir(const char *path, uint32_t mode) { ...@@ -4262,7 +4323,7 @@ static int mg_mkdir(const char *path, uint32_t mode) {
} }
#endif #endif
static struct mg_str get_mime_type(const char *path, const char *dflt, static struct mg_str mg_get_mime_type(const char *path, const char *dflt,
const struct mg_serve_http_opts *opts) { const struct mg_serve_http_opts *opts) {
const char *ext, *overrides; const char *ext, *overrides;
size_t i, path_len; size_t i, path_len;
...@@ -4278,11 +4339,11 @@ static struct mg_str get_mime_type(const char *path, const char *dflt, ...@@ -4278,11 +4339,11 @@ static struct mg_str get_mime_type(const char *path, const char *dflt,
} }
} }
for (i = 0; static_builtin_mime_types[i].extension != NULL; i++) { for (i = 0; mg_static_builtin_mime_types[i].extension != NULL; i++) {
ext = path + (path_len - static_builtin_mime_types[i].ext_len); ext = path + (path_len - mg_static_builtin_mime_types[i].ext_len);
if (path_len > static_builtin_mime_types[i].ext_len && ext[-1] == '.' && if (path_len > mg_static_builtin_mime_types[i].ext_len && ext[-1] == '.' &&
mg_casecmp(ext, static_builtin_mime_types[i].extension) == 0) { mg_casecmp(ext, mg_static_builtin_mime_types[i].extension) == 0) {
r.p = static_builtin_mime_types[i].mime_type; r.p = mg_static_builtin_mime_types[i].mime_type;
r.len = strlen(r.p); r.len = strlen(r.p);
return r; return r;
} }
...@@ -4300,7 +4361,7 @@ static struct mg_str get_mime_type(const char *path, const char *dflt, ...@@ -4300,7 +4361,7 @@ static struct mg_str get_mime_type(const char *path, const char *dflt,
* 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 char *s, int buf_len) { static int mg_http_get_request_len(const char *s, int buf_len) {
const unsigned char *buf = (unsigned char *) s; const unsigned char *buf = (unsigned char *) s;
int i; int i;
...@@ -4318,8 +4379,8 @@ static int get_request_len(const char *s, int buf_len) { ...@@ -4318,8 +4379,8 @@ static int get_request_len(const char *s, int buf_len) {
return 0; return 0;
} }
static const char *parse_http_headers(const char *s, const char *end, int len, static const char *mg_http_parse_headers(const char *s, const char *end,
struct http_message *req) { int len, struct http_message *req) {
int i; int i;
for (i = 0; i < (int) ARRAY_SIZE(req->header_names) - 1; i++) { for (i = 0; i < (int) ARRAY_SIZE(req->header_names) - 1; i++) {
struct mg_str *k = &req->header_names[i], *v = &req->header_values[i]; struct mg_str *k = &req->header_names[i], *v = &req->header_values[i];
...@@ -4348,7 +4409,7 @@ static const char *parse_http_headers(const char *s, const char *end, int len, ...@@ -4348,7 +4409,7 @@ static const char *parse_http_headers(const char *s, const char *end, int len,
int mg_parse_http(const char *s, int n, struct http_message *hm, int is_req) { int mg_parse_http(const char *s, int n, struct http_message *hm, int is_req) {
const char *end, *qs; const char *end, *qs;
int len = get_request_len(s, n); int len = mg_http_get_request_len(s, n);
if (len <= 0) return len; if (len <= 0) return len;
...@@ -4383,7 +4444,7 @@ int mg_parse_http(const char *s, int n, struct http_message *hm, int is_req) { ...@@ -4383,7 +4444,7 @@ int mg_parse_http(const char *s, int n, struct http_message *hm, int is_req) {
s = mg_skip(s, end, "\r\n", &hm->resp_status_msg); s = mg_skip(s, end, "\r\n", &hm->resp_status_msg);
} }
s = parse_http_headers(s, end, len, hm); s = mg_http_parse_headers(s, end, len, hm);
/* /*
* mg_parse_http() is used to parse both HTTP requests and HTTP * mg_parse_http() is used to parse both HTTP requests and HTTP
...@@ -4424,15 +4485,15 @@ struct mg_str *mg_get_http_header(struct http_message *hm, const char *name) { ...@@ -4424,15 +4485,15 @@ struct mg_str *mg_get_http_header(struct http_message *hm, const char *name) {
#ifndef MG_DISABLE_HTTP_WEBSOCKET #ifndef MG_DISABLE_HTTP_WEBSOCKET
static int is_ws_fragment(unsigned char flags) { static int mg_is_ws_fragment(unsigned char flags) {
return (flags & 0x80) == 0 || (flags & 0x0f) == 0; return (flags & 0x80) == 0 || (flags & 0x0f) == 0;
} }
static int is_ws_first_fragment(unsigned char flags) { static int mg_is_ws_first_fragment(unsigned char flags) {
return (flags & 0x80) == 0 && (flags & 0x0f) != 0; return (flags & 0x80) == 0 && (flags & 0x0f) != 0;
} }
static void handle_incoming_websocket_frame(struct mg_connection *nc, static void mg_handle_incoming_websocket_frame(struct mg_connection *nc,
struct websocket_message *wsm) { struct websocket_message *wsm) {
if (wsm->flags & 0x8) { if (wsm->flags & 0x8) {
mg_call(nc, nc->handler, MG_EV_WEBSOCKET_CONTROL_FRAME, wsm); mg_call(nc, nc->handler, MG_EV_WEBSOCKET_CONTROL_FRAME, wsm);
...@@ -4441,19 +4502,19 @@ static void handle_incoming_websocket_frame(struct mg_connection *nc, ...@@ -4441,19 +4502,19 @@ static void handle_incoming_websocket_frame(struct mg_connection *nc,
} }
} }
static int deliver_websocket_data(struct mg_connection *nc) { static int mg_deliver_websocket_data(struct mg_connection *nc) {
/* Using unsigned char *, cause of integer arithmetic below */ /* Using unsigned char *, cause of integer arithmetic below */
uint64_t i, data_len = 0, frame_len = 0, buf_len = nc->recv_mbuf.len, len, uint64_t i, data_len = 0, frame_len = 0, buf_len = nc->recv_mbuf.len, len,
mask_len = 0, header_len = 0; mask_len = 0, header_len = 0;
unsigned char *p = (unsigned char *) nc->recv_mbuf.buf, *buf = p, unsigned char *p = (unsigned char *) nc->recv_mbuf.buf, *buf = p,
*e = p + buf_len; *e = p + buf_len;
unsigned *sizep = (unsigned *) &p[1]; /* Size ptr for defragmented frames */ unsigned *sizep = (unsigned *) &p[1]; /* Size ptr for defragmented frames */
int ok, reass = buf_len > 0 && is_ws_fragment(p[0]) && int ok, reass = buf_len > 0 && mg_is_ws_fragment(p[0]) &&
!(nc->flags & MG_F_WEBSOCKET_NO_DEFRAG); !(nc->flags & MG_F_WEBSOCKET_NO_DEFRAG);
/* If that's a continuation frame that must be reassembled, handle it */ /* If that's a continuation frame that must be reassembled, handle it */
if (reass && !is_ws_first_fragment(p[0]) && buf_len >= 1 + sizeof(*sizep) && if (reass && !mg_is_ws_first_fragment(p[0]) &&
buf_len >= 1 + sizeof(*sizep) + *sizep) { buf_len >= 1 + sizeof(*sizep) && buf_len >= 1 + sizeof(*sizep) + *sizep) {
buf += 1 + sizeof(*sizep) + *sizep; buf += 1 + sizeof(*sizep) + *sizep;
buf_len -= 1 + sizeof(*sizep) + *sizep; buf_len -= 1 + sizeof(*sizep) + *sizep;
} }
...@@ -4493,7 +4554,7 @@ static int deliver_websocket_data(struct mg_connection *nc) { ...@@ -4493,7 +4554,7 @@ static int deliver_websocket_data(struct mg_connection *nc) {
if (reass) { if (reass) {
/* On first fragmented frame, nullify size */ /* On first fragmented frame, nullify size */
if (is_ws_first_fragment(wsm.flags)) { if (mg_is_ws_first_fragment(wsm.flags)) {
mbuf_resize(&nc->recv_mbuf, nc->recv_mbuf.size + sizeof(*sizep)); mbuf_resize(&nc->recv_mbuf, nc->recv_mbuf.size + sizeof(*sizep));
p[0] &= ~0x0f; /* Next frames will be treated as continuation */ p[0] &= ~0x0f; /* Next frames will be treated as continuation */
buf = p + 1 + sizeof(*sizep); buf = p + 1 + sizeof(*sizep);
...@@ -4509,12 +4570,12 @@ static int deliver_websocket_data(struct mg_connection *nc) { ...@@ -4509,12 +4570,12 @@ static int deliver_websocket_data(struct mg_connection *nc) {
if (wsm.flags & 0x80) { if (wsm.flags & 0x80) {
wsm.data = p + 1 + sizeof(*sizep); wsm.data = p + 1 + sizeof(*sizep);
wsm.size = *sizep; wsm.size = *sizep;
handle_incoming_websocket_frame(nc, &wsm); mg_handle_incoming_websocket_frame(nc, &wsm);
mbuf_remove(&nc->recv_mbuf, 1 + sizeof(*sizep) + *sizep); mbuf_remove(&nc->recv_mbuf, 1 + sizeof(*sizep) + *sizep);
} }
} else { } else {
/* TODO(lsm): properly handle OOB control frames during defragmentation */ /* TODO(lsm): properly handle OOB control frames during defragmentation */
handle_incoming_websocket_frame(nc, &wsm); mg_handle_incoming_websocket_frame(nc, &wsm);
mbuf_remove(&nc->recv_mbuf, (size_t) frame_len); /* Cleanup frame */ mbuf_remove(&nc->recv_mbuf, (size_t) frame_len); /* Cleanup frame */
} }
...@@ -4532,7 +4593,7 @@ struct ws_mask_ctx { ...@@ -4532,7 +4593,7 @@ struct ws_mask_ctx {
uint32_t mask; uint32_t mask;
}; };
static uint32_t ws_random_mask(void) { static uint32_t mg_ws_random_mask(void) {
/* /*
* The spec requires WS client to generate hard to * The spec requires WS client to generate hard to
* guess mask keys. From RFC6455, Section 5.3: * guess mask keys. From RFC6455, Section 5.3:
...@@ -4586,7 +4647,7 @@ static void mg_send_ws_header(struct mg_connection *nc, int op, size_t len, ...@@ -4586,7 +4647,7 @@ static void mg_send_ws_header(struct mg_connection *nc, int op, size_t len,
if (nc->listener == NULL) { if (nc->listener == NULL) {
header[1] |= 1 << 7; /* set masking flag */ header[1] |= 1 << 7; /* set masking flag */
mg_send(nc, header, header_len); mg_send(nc, header, header_len);
ctx->mask = ws_random_mask(); ctx->mask = mg_ws_random_mask();
mg_send(nc, &ctx->mask, sizeof(ctx->mask)); mg_send(nc, &ctx->mask, sizeof(ctx->mask));
ctx->pos = nc->send_mbuf.len; ctx->pos = nc->send_mbuf.len;
} else { } else {
...@@ -4595,7 +4656,7 @@ static void mg_send_ws_header(struct mg_connection *nc, int op, size_t len, ...@@ -4595,7 +4656,7 @@ static void mg_send_ws_header(struct mg_connection *nc, int op, size_t len,
} }
} }
static void ws_mask_frame(struct mbuf *mbuf, struct ws_mask_ctx *ctx) { static void mg_ws_mask_frame(struct mbuf *mbuf, struct ws_mask_ctx *ctx) {
size_t i; size_t i;
if (ctx->pos == 0) return; if (ctx->pos == 0) return;
for (i = 0; i < (mbuf->len - ctx->pos); i++) { for (i = 0; i < (mbuf->len - ctx->pos); i++) {
...@@ -4609,7 +4670,7 @@ void mg_send_websocket_frame(struct mg_connection *nc, int op, const void *data, ...@@ -4609,7 +4670,7 @@ void mg_send_websocket_frame(struct mg_connection *nc, int op, const void *data,
mg_send_ws_header(nc, op, len, &ctx); mg_send_ws_header(nc, op, len, &ctx);
mg_send(nc, data, len); mg_send(nc, data, len);
ws_mask_frame(&nc->send_mbuf, &ctx); mg_ws_mask_frame(&nc->send_mbuf, &ctx);
if (op == WEBSOCKET_OP_CLOSE) { if (op == WEBSOCKET_OP_CLOSE) {
nc->flags |= MG_F_SEND_AND_CLOSE; nc->flags |= MG_F_SEND_AND_CLOSE;
...@@ -4631,7 +4692,7 @@ void mg_send_websocket_framev(struct mg_connection *nc, int op, ...@@ -4631,7 +4692,7 @@ void mg_send_websocket_framev(struct mg_connection *nc, int op,
mg_send(nc, strv[i].p, strv[i].len); mg_send(nc, strv[i].p, strv[i].len);
} }
ws_mask_frame(&nc->send_mbuf, &ctx); mg_ws_mask_frame(&nc->send_mbuf, &ctx);
if (op == WEBSOCKET_OP_CLOSE) { if (op == WEBSOCKET_OP_CLOSE) {
nc->flags |= MG_F_SEND_AND_CLOSE; nc->flags |= MG_F_SEND_AND_CLOSE;
...@@ -4655,13 +4716,14 @@ void mg_printf_websocket_frame(struct mg_connection *nc, int op, ...@@ -4655,13 +4716,14 @@ void mg_printf_websocket_frame(struct mg_connection *nc, int op,
} }
} }
static void websocket_handler(struct mg_connection *nc, int ev, void *ev_data) { static void mg_websocket_handler(struct mg_connection *nc, int ev,
void *ev_data) {
mg_call(nc, nc->handler, ev, ev_data); mg_call(nc, nc->handler, ev, ev_data);
switch (ev) { switch (ev) {
case MG_EV_RECV: case MG_EV_RECV:
do { do {
} while (deliver_websocket_data(nc)); } while (mg_deliver_websocket_data(nc));
break; break;
case MG_EV_POLL: case MG_EV_POLL:
/* Ping idle websocket connections */ /* Ping idle websocket connections */
...@@ -4678,7 +4740,8 @@ static void websocket_handler(struct mg_connection *nc, int ev, void *ev_data) { ...@@ -4678,7 +4740,8 @@ static void websocket_handler(struct mg_connection *nc, int ev, void *ev_data) {
} }
} }
static void ws_handshake(struct mg_connection *nc, const struct mg_str *key) { static void mg_ws_handshake(struct mg_connection *nc,
const struct mg_str *key) {
static const char *magic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; static const char *magic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
char buf[MG_VPRINTF_BUFFER_SIZE], sha[20], b64_sha[sizeof(sha) * 2]; char buf[MG_VPRINTF_BUFFER_SIZE], sha[20], b64_sha[sizeof(sha) * 2];
cs_sha1_ctx sha_ctx; cs_sha1_ctx sha_ctx;
...@@ -4700,28 +4763,14 @@ static void ws_handshake(struct mg_connection *nc, const struct mg_str *key) { ...@@ -4700,28 +4763,14 @@ static void ws_handshake(struct mg_connection *nc, const struct mg_str *key) {
#endif /* MG_DISABLE_HTTP_WEBSOCKET */ #endif /* MG_DISABLE_HTTP_WEBSOCKET */
static void free_http_proto_data(struct mg_connection *nc) {
struct proto_data_http *dp = (struct proto_data_http *) nc->proto_data;
if (dp != NULL) {
#ifndef MG_DISABLE_FILESYSTEM
if (dp->fp != NULL) fclose(dp->fp);
#endif
#ifndef MG_DISABLE_CGI
if (dp->cgi_nc != NULL) dp->cgi_nc->flags |= MG_F_CLOSE_IMMEDIATELY;
#endif
MG_FREE(dp);
nc->proto_data = NULL;
}
}
#ifndef MG_DISABLE_FILESYSTEM #ifndef MG_DISABLE_FILESYSTEM
static void transfer_file_data(struct mg_connection *nc) { static void mg_http_transfer_file_data(struct mg_connection *nc) {
struct proto_data_http *dp = (struct proto_data_http *) nc->proto_data; struct mg_http_proto_data *pd = mg_http_get_proto_data(nc);
char buf[MG_MAX_HTTP_SEND_MBUF]; char buf[MG_MAX_HTTP_SEND_MBUF];
int64_t left = dp->cl - dp->sent; int64_t left = pd->file.cl - pd->file.sent;
size_t n = 0, to_read = 0; size_t n = 0, to_read = 0;
if (dp->type == DATA_FILE) { if (pd->file.type == DATA_FILE) {
struct mbuf *io = &nc->send_mbuf; struct mbuf *io = &nc->send_mbuf;
if (io->len < sizeof(buf)) { if (io->len < sizeof(buf)) {
to_read = sizeof(buf) - io->len; to_read = sizeof(buf) - io->len;
...@@ -4733,38 +4782,42 @@ static void transfer_file_data(struct mg_connection *nc) { ...@@ -4733,38 +4782,42 @@ static void transfer_file_data(struct mg_connection *nc) {
if (to_read == 0) { if (to_read == 0) {
/* Rate limiting. send_mbuf is too full, wait until it's drained. */ /* Rate limiting. send_mbuf is too full, wait until it's drained. */
} else if (dp->sent < dp->cl && (n = fread(buf, 1, to_read, dp->fp)) > 0) { } else if (pd->file.sent < pd->file.cl &&
(n = fread(buf, 1, to_read, pd->file.fp)) > 0) {
mg_send(nc, buf, n); mg_send(nc, buf, n);
dp->sent += n; pd->file.sent += n;
} else { } else {
free_http_proto_data(nc); mg_http_free_proto_data_file(&pd->file);
#ifdef MG_DISABLE_HTTP_KEEP_ALIVE #ifdef MG_DISABLE_HTTP_KEEP_ALIVE
nc->flags |= MG_F_SEND_AND_CLOSE; nc->flags |= MG_F_SEND_AND_CLOSE;
#endif #endif
} }
} else if (dp->type == DATA_PUT) { } else if (pd->file.type == DATA_PUT) {
struct mbuf *io = &nc->recv_mbuf; struct mbuf *io = &nc->recv_mbuf;
size_t to_write = size_t to_write =
left <= 0 ? 0 : left < (int64_t) io->len ? (size_t) left : io->len; left <= 0 ? 0 : left < (int64_t) io->len ? (size_t) left : io->len;
size_t n = fwrite(io->buf, 1, to_write, dp->fp); size_t n = fwrite(io->buf, 1, to_write, pd->file.fp);
if (n > 0) { if (n > 0) {
mbuf_remove(io, n); mbuf_remove(io, n);
dp->sent += n; pd->file.sent += n;
} }
if (n == 0 || dp->sent >= dp->cl) { if (n == 0 || pd->file.sent >= pd->file.cl) {
free_http_proto_data(nc); mg_http_free_proto_data_file(&pd->file);
#ifdef MG_DISABLE_HTTP_KEEP_ALIVE #ifdef MG_DISABLE_HTTP_KEEP_ALIVE
nc->flags |= MG_F_SEND_AND_CLOSE; nc->flags |= MG_F_SEND_AND_CLOSE;
#endif #endif
} }
} else if (dp->type == DATA_CGI) { }
#ifndef MG_DISABLE_CGI
else if (pd->cgi.cgi_nc != NULL) {
/* This is POST data that needs to be forwarded to the CGI process */ /* This is POST data that needs to be forwarded to the CGI process */
if (dp->cgi_nc != NULL) { if (pd->cgi.cgi_nc != NULL) {
mg_forward(nc, dp->cgi_nc); mg_forward(nc, pd->cgi.cgi_nc);
} else { } else {
nc->flags |= MG_F_SEND_AND_CLOSE; nc->flags |= MG_F_SEND_AND_CLOSE;
} }
} }
#endif
} }
#endif /* MG_DISABLE_FILESYSTEM */ #endif /* MG_DISABLE_FILESYSTEM */
...@@ -4773,7 +4826,7 @@ static void transfer_file_data(struct mg_connection *nc) { ...@@ -4773,7 +4826,7 @@ static void transfer_file_data(struct mg_connection *nc) {
* if it's incomplete. If the chunk is fully buffered, return total number of * if it's incomplete. If the chunk is fully buffered, return total number of
* bytes in a chunk, and store data in `data`, `data_len`. * bytes in a chunk, and store data in `data`, `data_len`.
*/ */
static size_t parse_chunk(char *buf, size_t len, char **chunk_data, static size_t mg_http_parse_chunk(char *buf, size_t len, char **chunk_data,
size_t *chunk_len) { size_t *chunk_len) {
unsigned char *s = (unsigned char *) buf; unsigned char *s = (unsigned char *) buf;
size_t n = 0; /* scanned chunk length */ size_t n = 0; /* scanned chunk length */
...@@ -4809,24 +4862,17 @@ static size_t parse_chunk(char *buf, size_t len, char **chunk_data, ...@@ -4809,24 +4862,17 @@ static size_t parse_chunk(char *buf, size_t len, char **chunk_data,
MG_INTERNAL size_t mg_handle_chunked(struct mg_connection *nc, MG_INTERNAL size_t mg_handle_chunked(struct mg_connection *nc,
struct http_message *hm, char *buf, struct http_message *hm, char *buf,
size_t blen) { size_t blen) {
struct proto_data_http *dp; struct mg_http_proto_data *pd = mg_http_get_proto_data(nc);
char *data; char *data;
size_t i, n, data_len, body_len, zero_chunk_received = 0; size_t i, n, data_len, body_len, zero_chunk_received = 0;
/* If not allocated, allocate proto_data to hold reassembled offset */
if (nc->proto_data == NULL &&
(nc->proto_data = MG_CALLOC(1, sizeof(*dp))) == NULL) {
nc->flags |= MG_F_CLOSE_IMMEDIATELY;
return 0;
}
/* Find out piece of received data that is not yet reassembled */ /* Find out piece of received data that is not yet reassembled */
dp = (struct proto_data_http *) nc->proto_data; body_len = pd->chunk.body_len;
body_len = dp->body_len;
assert(blen >= body_len); assert(blen >= body_len);
/* Traverse all fully buffered chunks */ /* Traverse all fully buffered chunks */
for (i = body_len; (n = parse_chunk(buf + i, blen - i, &data, &data_len)) > 0; for (i = body_len;
(n = mg_http_parse_chunk(buf + i, blen - i, &data, &data_len)) > 0;
i += n) { i += n) {
/* Collapse chunk data to the rest of HTTP body */ /* Collapse chunk data to the rest of HTTP body */
memmove(buf + body_len, data, data_len); memmove(buf + body_len, data, data_len);
...@@ -4846,7 +4892,7 @@ MG_INTERNAL size_t mg_handle_chunked(struct mg_connection *nc, ...@@ -4846,7 +4892,7 @@ MG_INTERNAL size_t mg_handle_chunked(struct mg_connection *nc,
memmove(buf + body_len, buf + i, blen - i); memmove(buf + body_len, buf + i, blen - i);
memset(buf + body_len + blen - i, 0, i - body_len); memset(buf + body_len + blen - i, 0, i - body_len);
nc->recv_mbuf.len -= i - body_len; nc->recv_mbuf.len -= i - body_len;
dp->body_len = body_len; pd->chunk.body_len = body_len;
/* Send MG_EV_HTTP_CHUNK event */ /* Send MG_EV_HTTP_CHUNK event */
nc->flags &= ~MG_F_DELETE_CHUNK; nc->flags &= ~MG_F_DELETE_CHUNK;
...@@ -4857,19 +4903,19 @@ MG_INTERNAL size_t mg_handle_chunked(struct mg_connection *nc, ...@@ -4857,19 +4903,19 @@ MG_INTERNAL size_t mg_handle_chunked(struct mg_connection *nc,
memset(buf, 0, body_len); memset(buf, 0, body_len);
memmove(buf, buf + body_len, blen - i); memmove(buf, buf + body_len, blen - i);
nc->recv_mbuf.len -= body_len; nc->recv_mbuf.len -= body_len;
hm->body.len = dp->body_len = 0; hm->body.len = pd->chunk.body_len = 0;
} }
if (zero_chunk_received) { if (zero_chunk_received) {
hm->message.len = dp->body_len + blen - i; hm->message.len = pd->chunk.body_len + blen - i;
} }
} }
return body_len; return body_len;
} }
static mg_event_handler_t get_endpoint_handler(struct mg_connection *nc, static mg_event_handler_t mg_http_get_endpoint_handler(
struct mg_str *uri_path) { struct mg_connection *nc, struct mg_str *uri_path) {
size_t pos = 0; size_t pos = 0;
mg_event_handler_t ret = NULL; mg_event_handler_t ret = NULL;
int matched, matched_max = 0; int matched, matched_max = 0;
...@@ -4900,7 +4946,7 @@ static mg_event_handler_t get_endpoint_handler(struct mg_connection *nc, ...@@ -4900,7 +4946,7 @@ static mg_event_handler_t get_endpoint_handler(struct mg_connection *nc,
} }
#ifdef MG_ENABLE_HTTP_STREAMING_MULTIPART #ifdef MG_ENABLE_HTTP_STREAMING_MULTIPART
struct stream_info { struct mg_http_stream_info {
struct mg_str endpoint; struct mg_str endpoint;
struct mg_str boundary; struct mg_str boundary;
struct mg_str var_name; struct mg_str var_name;
...@@ -4914,7 +4960,8 @@ struct stream_info { ...@@ -4914,7 +4960,8 @@ struct stream_info {
* related data these function can be replaced with usual structs * related data these function can be replaced with usual structs
* TODO(alashkin): replace once those way will be implemented * TODO(alashkin): replace once those way will be implemented
*/ */
static void mg_parse_stream_info(struct mbuf *buf, struct stream_info *si) { static void mg_http_parse_stream_info(struct mbuf *buf,
struct mg_http_stream_info *si) {
const char *ptr = buf->buf; const char *ptr = buf->buf;
memcpy(&si->endpoint.len, ptr, sizeof(si->endpoint.len)); memcpy(&si->endpoint.len, ptr, sizeof(si->endpoint.len));
ptr += sizeof(si->endpoint.len); ptr += sizeof(si->endpoint.len);
...@@ -4934,7 +4981,8 @@ static void mg_parse_stream_info(struct mbuf *buf, struct stream_info *si) { ...@@ -4934,7 +4981,8 @@ static void mg_parse_stream_info(struct mbuf *buf, struct stream_info *si) {
ptr += si->file_name.len + 1; ptr += si->file_name.len + 1;
} }
static void mg_store_stream_info(struct mbuf *buf, struct stream_info *si) { static void mg_http_store_stream_info(struct mbuf *buf,
struct mg_http_stream_info *si) {
char zero = 0; char zero = 0;
mbuf_remove(buf, buf->len); mbuf_remove(buf, buf->len);
mbuf_append(buf, &si->endpoint.len, sizeof(si->endpoint.len)); mbuf_append(buf, &si->endpoint.len, sizeof(si->endpoint.len));
...@@ -4951,19 +4999,20 @@ static void mg_store_stream_info(struct mbuf *buf, struct stream_info *si) { ...@@ -4951,19 +4999,20 @@ static void mg_store_stream_info(struct mbuf *buf, struct stream_info *si) {
} }
#endif /* MG_ENABLE_HTTP_STREAMING_MULTIPART */ #endif /* MG_ENABLE_HTTP_STREAMING_MULTIPART */
static void mg_call_endpoint_handler(struct mg_connection *nc, int ev, static void mg_http_call_endpoint_handler(struct mg_connection *nc, int ev,
struct http_message *hm) { struct http_message *hm) {
mg_event_handler_t uri_handler = mg_event_handler_t uri_handler =
ev == MG_EV_HTTP_REQUEST ? get_endpoint_handler(nc->listener, &hm->uri) ev == MG_EV_HTTP_REQUEST
? mg_http_get_endpoint_handler(nc->listener, &hm->uri)
: NULL; : NULL;
mg_call(nc, uri_handler ? uri_handler : nc->handler, ev, hm); mg_call(nc, uri_handler ? uri_handler : nc->handler, ev, hm);
} }
#ifdef MG_ENABLE_HTTP_STREAMING_MULTIPART #ifdef MG_ENABLE_HTTP_STREAMING_MULTIPART
static void mg_multipart_continue(struct mg_connection *nc, struct mbuf *io, static void mg_http_multipart_continue(struct mg_connection *nc,
int ev, void *ev_data); struct mbuf *io, int ev, void *ev_data);
static void mg_multipart_begin(struct mg_connection *nc, static void mg_http_multipart_begin(struct mg_connection *nc,
struct http_message *hm, struct mbuf *io, struct http_message *hm, struct mbuf *io,
int req_len); int req_len);
...@@ -4975,21 +5024,22 @@ static void mg_multipart_begin(struct mg_connection *nc, ...@@ -4975,21 +5024,22 @@ static void mg_multipart_begin(struct mg_connection *nc,
* even bigger (round up to 4k, from 700 bytes of actual size). * even bigger (round up to 4k, from 700 bytes of actual size).
*/ */
#ifdef __xtensa__ #ifdef __xtensa__
static void http_handler2(struct mg_connection *nc, int ev, void *ev_data, static void mg_http_handler2(struct mg_connection *nc, int ev, void *ev_data,
struct http_message *hm) __attribute__((noinline)); struct http_message *hm) __attribute__((noinline));
void http_handler(struct mg_connection *nc, int ev, void *ev_data) { void mg_http_handler(struct mg_connection *nc, int ev, void *ev_data) {
struct http_message hm; struct http_message hm;
http_handler2(nc, ev, ev_data, &hm); mg_http_handler2(nc, ev, ev_data, &hm);
} }
static void http_handler2(struct mg_connection *nc, int ev, void *ev_data, static void mg_http_handler2(struct mg_connection *nc, int ev, void *ev_data,
struct http_message *hm) { struct http_message *hm) {
#else /* !__XTENSA__ */ #else /* !__XTENSA__ */
void http_handler(struct mg_connection *nc, int ev, void *ev_data) { void mg_http_handler(struct mg_connection *nc, int ev, void *ev_data) {
struct http_message shm; struct http_message shm;
struct http_message *hm = &shm; struct http_message *hm = &shm;
#endif /* __XTENSA__ */ #endif /* __XTENSA__ */
struct mg_http_proto_data *pd = mg_http_get_proto_data(nc);
struct mbuf *io = &nc->recv_mbuf; struct mbuf *io = &nc->recv_mbuf;
int req_len; int req_len;
const int is_req = (nc->listener != NULL); const int is_req = (nc->listener != NULL);
...@@ -5005,14 +5055,13 @@ void http_handler(struct mg_connection *nc, int ev, void *ev_data) { ...@@ -5005,14 +5055,13 @@ void http_handler(struct mg_connection *nc, int ev, void *ev_data) {
int ev2 = is_req ? MG_EV_HTTP_REQUEST : MG_EV_HTTP_REPLY; int ev2 = is_req ? MG_EV_HTTP_REQUEST : MG_EV_HTTP_REPLY;
hm->message.len = io->len; hm->message.len = io->len;
hm->body.len = io->buf + io->len - hm->body.p; hm->body.len = io->buf + io->len - hm->body.p;
mg_call_endpoint_handler(nc, ev2, hm); mg_http_call_endpoint_handler(nc, ev2, hm);
} }
free_http_proto_data(nc);
} }
#ifndef MG_DISABLE_FILESYSTEM #ifndef MG_DISABLE_FILESYSTEM
if (nc->proto_data != NULL) { if (pd->file.fp != NULL) {
transfer_file_data(nc); mg_http_transfer_file_data(nc);
} }
#endif #endif
...@@ -5022,8 +5071,8 @@ void http_handler(struct mg_connection *nc, int ev, void *ev_data) { ...@@ -5022,8 +5071,8 @@ void http_handler(struct mg_connection *nc, int ev, void *ev_data) {
struct mg_str *s; struct mg_str *s;
#ifdef MG_ENABLE_HTTP_STREAMING_MULTIPART #ifdef MG_ENABLE_HTTP_STREAMING_MULTIPART
if (nc->strm_state.len != 0) { if (pd->strm_state.len != 0) {
mg_multipart_continue(nc, io, ev, ev_data); mg_http_multipart_continue(nc, io, ev, ev_data);
return; return;
} }
#endif /* MG_ENABLE_HTTP_STREAMING_MULTIPART */ #endif /* MG_ENABLE_HTTP_STREAMING_MULTIPART */
...@@ -5050,25 +5099,25 @@ void http_handler(struct mg_connection *nc, int ev, void *ev_data) { ...@@ -5050,25 +5099,25 @@ void http_handler(struct mg_connection *nc, int ev, void *ev_data) {
/* We're websocket client, got handshake response from server. */ /* We're websocket client, got handshake response from server. */
/* TODO(lsm): check the validity of accept Sec-WebSocket-Accept */ /* TODO(lsm): check the validity of accept Sec-WebSocket-Accept */
mbuf_remove(io, req_len); mbuf_remove(io, req_len);
nc->proto_handler = websocket_handler; nc->proto_handler = mg_websocket_handler;
nc->flags |= MG_F_IS_WEBSOCKET; nc->flags |= MG_F_IS_WEBSOCKET;
mg_call(nc, nc->handler, MG_EV_WEBSOCKET_HANDSHAKE_DONE, NULL); mg_call(nc, nc->handler, MG_EV_WEBSOCKET_HANDSHAKE_DONE, NULL);
websocket_handler(nc, MG_EV_RECV, ev_data); mg_websocket_handler(nc, MG_EV_RECV, ev_data);
} else if (nc->listener != NULL && } else if (nc->listener != NULL &&
(vec = mg_get_http_header(hm, "Sec-WebSocket-Key")) != NULL) { (vec = mg_get_http_header(hm, "Sec-WebSocket-Key")) != NULL) {
/* This is a websocket request. Switch protocol handlers. */ /* This is a websocket request. Switch protocol handlers. */
mbuf_remove(io, req_len); mbuf_remove(io, req_len);
nc->proto_handler = websocket_handler; nc->proto_handler = mg_websocket_handler;
nc->flags |= MG_F_IS_WEBSOCKET; nc->flags |= MG_F_IS_WEBSOCKET;
/* Send handshake */ /* Send handshake */
mg_call(nc, nc->handler, MG_EV_WEBSOCKET_HANDSHAKE_REQUEST, hm); mg_call(nc, nc->handler, MG_EV_WEBSOCKET_HANDSHAKE_REQUEST, hm);
if (!(nc->flags & MG_F_CLOSE_IMMEDIATELY)) { if (!(nc->flags & MG_F_CLOSE_IMMEDIATELY)) {
if (nc->send_mbuf.len == 0) { if (nc->send_mbuf.len == 0) {
ws_handshake(nc, vec); mg_ws_handshake(nc, vec);
} }
mg_call(nc, nc->handler, MG_EV_WEBSOCKET_HANDSHAKE_DONE, NULL); mg_call(nc, nc->handler, MG_EV_WEBSOCKET_HANDSHAKE_DONE, NULL);
websocket_handler(nc, MG_EV_RECV, ev_data); mg_websocket_handler(nc, MG_EV_RECV, ev_data);
} }
#endif /* MG_DISABLE_HTTP_WEBSOCKET */ #endif /* MG_DISABLE_HTTP_WEBSOCKET */
} else if (hm->message.len <= io->len) { } else if (hm->message.len <= io->len) {
...@@ -5119,29 +5168,30 @@ void http_handler(struct mg_connection *nc, int ev, void *ev_data) { ...@@ -5119,29 +5168,30 @@ void http_handler(struct mg_connection *nc, int ev, void *ev_data) {
if (js_callback_handled_request) { if (js_callback_handled_request) {
nc->flags |= MG_F_SEND_AND_CLOSE; nc->flags |= MG_F_SEND_AND_CLOSE;
} else { } else {
mg_call_endpoint_handler(nc, trigger_ev, hm); mg_http_call_endpoint_handler(nc, trigger_ev, hm);
} }
#else #else
mg_call_endpoint_handler(nc, trigger_ev, hm); mg_http_call_endpoint_handler(nc, trigger_ev, hm);
#endif #endif
mbuf_remove(io, hm->message.len); mbuf_remove(io, hm->message.len);
#ifdef MG_ENABLE_HTTP_STREAMING_MULTIPART #ifdef MG_ENABLE_HTTP_STREAMING_MULTIPART
} else { } else {
mg_multipart_begin(nc, hm, io, req_len); mg_http_multipart_begin(nc, hm, io, req_len);
#endif /* MG_ENABLE_HTTP_STREAMING_MULTIPART */ #endif /* MG_ENABLE_HTTP_STREAMING_MULTIPART */
} }
} }
} }
#ifdef MG_ENABLE_HTTP_STREAMING_MULTIPART #ifdef MG_ENABLE_HTTP_STREAMING_MULTIPART
static void mg_multipart_begin(struct mg_connection *nc, static void mg_http_multipart_begin(struct mg_connection *nc,
struct http_message *hm, struct mbuf *io, struct http_message *hm, struct mbuf *io,
int req_len) { int req_len) {
struct mg_http_proto_data *pd = mg_http_get_proto_data(nc);
struct mg_str *ct; struct mg_str *ct;
const char multipart[] = "multipart"; const char multipart[] = "multipart";
char boundary[100]; char boundary[100];
int boundary_len; int boundary_len;
struct stream_info si; struct mg_http_stream_info si;
mg_event_handler_t handler; mg_event_handler_t handler;
if (nc->listener == NULL) { if (nc->listener == NULL) {
...@@ -5176,13 +5226,13 @@ static void mg_multipart_begin(struct mg_connection *nc, ...@@ -5176,13 +5226,13 @@ static void mg_multipart_begin(struct mg_connection *nc,
/* If we reach this place - that is multipart request */ /* If we reach this place - that is multipart request */
if (nc->strm_state.len != 0) { if (pd->strm_state.len != 0) {
/* /*
* Another streaming request was in progress, * Another streaming request was in progress,
* looks like protocol error * looks like protocol error
*/ */
nc->flags |= MG_F_CLOSE_IMMEDIATELY; nc->flags |= MG_F_CLOSE_IMMEDIATELY;
mbuf_free(&nc->strm_state); mbuf_free(&pd->strm_state);
} else { } else {
si.endpoint = hm->uri; si.endpoint = hm->uri;
si.boundary.p = boundary; si.boundary.p = boundary;
...@@ -5190,8 +5240,8 @@ static void mg_multipart_begin(struct mg_connection *nc, ...@@ -5190,8 +5240,8 @@ static void mg_multipart_begin(struct mg_connection *nc,
si.var_name.p = si.file_name.p = NULL; si.var_name.p = si.file_name.p = NULL;
si.var_name.len = si.file_name.len = 0; si.var_name.len = si.file_name.len = 0;
mg_store_stream_info(&nc->strm_state, &si); mg_http_store_stream_info(&pd->strm_state, &si);
handler = get_endpoint_handler(nc->listener, &si.endpoint); handler = mg_http_get_endpoint_handler(nc->listener, &si.endpoint);
mg_call(nc, handler ? handler : nc->handler, MG_EV_HTTP_MULTIPART_REQUEST, mg_call(nc, handler ? handler : nc->handler, MG_EV_HTTP_MULTIPART_REQUEST,
hm); hm);
...@@ -5201,17 +5251,18 @@ exit_mp: ...@@ -5201,17 +5251,18 @@ exit_mp:
; ;
} }
static void mg_multipart_continue(struct mg_connection *nc, struct mbuf *io, static void mg_http_multipart_continue(struct mg_connection *nc,
int ev, void *ev_data) { struct mbuf *io, int ev, void *ev_data) {
/* Continue to stream multipart */ /* Continue to stream multipart */
struct stream_info si; struct mg_http_stream_info si;
mg_event_handler_t handler; mg_event_handler_t handler;
struct mg_http_multipart_part mp; struct mg_http_multipart_part mp;
const char *boundary; const char *boundary;
int req_len; int req_len;
struct mg_http_proto_data *pd = mg_http_get_proto_data(nc);
mg_parse_stream_info(&nc->strm_state, &si); mg_http_parse_stream_info(&pd->strm_state, &si);
handler = get_endpoint_handler(nc->listener, &si.endpoint); handler = mg_http_get_endpoint_handler(nc->listener, &si.endpoint);
memset(&mp, 0, sizeof(mp)); memset(&mp, 0, sizeof(mp));
mp.var_name = si.var_name.p; mp.var_name = si.var_name.p;
...@@ -5250,7 +5301,7 @@ static void mg_multipart_continue(struct mg_connection *nc, struct mbuf *io, ...@@ -5250,7 +5301,7 @@ static void mg_multipart_continue(struct mg_connection *nc, struct mbuf *io,
sizeof(filename), &data, &data_len); sizeof(filename), &data, &data_len);
mp.var_name = varname; mp.var_name = varname;
mp.file_name = filename; mp.file_name = filename;
if ((req_len = get_request_len(io->buf, io->len)) > 0) { if ((req_len = mg_http_get_request_len(io->buf, io->len)) > 0) {
const char *tmp; const char *tmp;
mg_call(nc, handler ? handler : nc->handler, MG_EV_HTTP_PART_BEGIN, mg_call(nc, handler ? handler : nc->handler, MG_EV_HTTP_PART_BEGIN,
&mp); &mp);
...@@ -5258,7 +5309,7 @@ static void mg_multipart_continue(struct mg_connection *nc, struct mbuf *io, ...@@ -5258,7 +5309,7 @@ static void mg_multipart_continue(struct mg_connection *nc, struct mbuf *io,
si.var_name.len = strlen(mp.var_name); si.var_name.len = strlen(mp.var_name);
si.file_name.p = mp.file_name; si.file_name.p = mp.file_name;
si.file_name.len = strlen(mp.file_name); si.file_name.len = strlen(mp.file_name);
mg_store_stream_info(&nc->strm_state, &si); mg_http_store_stream_info(&pd->strm_state, &si);
mbuf_remove(io, req_len); mbuf_remove(io, req_len);
mp.data.p = io->buf; mp.data.p = io->buf;
...@@ -5283,7 +5334,7 @@ static void mg_multipart_continue(struct mg_connection *nc, struct mbuf *io, ...@@ -5283,7 +5334,7 @@ static void mg_multipart_continue(struct mg_connection *nc, struct mbuf *io,
} }
if (io->len != 0) { if (io->len != 0) {
http_handler(nc, ev, ev_data); mg_http_handler(nc, ev, ev_data);
} }
} /* else wait for data */ } /* else wait for data */
} else if (has_prefix && has_suffix) { } else if (has_prefix && has_suffix) {
...@@ -5298,7 +5349,7 @@ static void mg_multipart_continue(struct mg_connection *nc, struct mbuf *io, ...@@ -5298,7 +5349,7 @@ static void mg_multipart_continue(struct mg_connection *nc, struct mbuf *io,
/* Skip epilogue (if any) */ /* Skip epilogue (if any) */
mbuf_remove(io, io->len); mbuf_remove(io, io->len);
mbuf_free(&nc->strm_state); mbuf_free(&pd->strm_state);
} else { } else {
/* Malformed request */ /* Malformed request */
nc->flags |= MG_F_CLOSE_IMMEDIATELY; nc->flags |= MG_F_CLOSE_IMMEDIATELY;
...@@ -5309,7 +5360,7 @@ static void mg_multipart_continue(struct mg_connection *nc, struct mbuf *io, ...@@ -5309,7 +5360,7 @@ static void mg_multipart_continue(struct mg_connection *nc, struct mbuf *io,
#endif /* MG_ENABLE_HTTP_STREAMING_MULTIPART */ #endif /* MG_ENABLE_HTTP_STREAMING_MULTIPART */
void mg_set_protocol_http_websocket(struct mg_connection *nc) { void mg_set_protocol_http_websocket(struct mg_connection *nc) {
nc->proto_handler = http_handler; nc->proto_handler = mg_http_handler;
} }
#ifndef MG_DISABLE_HTTP_WEBSOCKET #ifndef MG_DISABLE_HTTP_WEBSOCKET
...@@ -5384,16 +5435,16 @@ void mg_serve_http(struct mg_connection *nc, struct http_message *hm, ...@@ -5384,16 +5435,16 @@ void mg_serve_http(struct mg_connection *nc, struct http_message *hm,
mg_send_head(nc, 501, 0, NULL); mg_send_head(nc, 501, 0, NULL);
} }
#else #else
static void send_http_error(struct mg_connection *nc, int code, static void mg_http_send_http_error(struct mg_connection *nc, int code,
const char *reason) { const char *reason) {
(void) reason; (void) reason;
mg_send_head(nc, code, 0, NULL); mg_send_head(nc, code, 0, NULL);
} }
#ifndef MG_DISABLE_SSI #ifndef MG_DISABLE_SSI
static void send_ssi_file(struct mg_connection *, const char *, FILE *, int, static void mg_send_ssi_file(struct mg_connection *, const char *, FILE *, int,
const struct mg_serve_http_opts *); const struct mg_serve_http_opts *);
static void send_file_data(struct mg_connection *nc, FILE *fp) { static void mg_send_file_data(struct mg_connection *nc, FILE *fp) {
char buf[BUFSIZ]; char buf[BUFSIZ];
size_t n; size_t n;
while ((n = fread(buf, 1, sizeof(buf), fp)) > 0) { while ((n = fread(buf, 1, sizeof(buf), fp)) > 0) {
...@@ -5401,8 +5452,8 @@ static void send_file_data(struct mg_connection *nc, FILE *fp) { ...@@ -5401,8 +5452,8 @@ static void send_file_data(struct mg_connection *nc, FILE *fp) {
} }
} }
static void do_ssi_include(struct mg_connection *nc, const char *ssi, char *tag, static void mg_do_ssi_include(struct mg_connection *nc, const char *ssi,
int include_level, char *tag, int include_level,
const struct mg_serve_http_opts *opts) { const struct mg_serve_http_opts *opts) {
char file_name[BUFSIZ], path[MAX_PATH_SIZE], *p; char file_name[BUFSIZ], path[MAX_PATH_SIZE], *p;
FILE *fp; FILE *fp;
...@@ -5439,9 +5490,9 @@ static void do_ssi_include(struct mg_connection *nc, const char *ssi, char *tag, ...@@ -5439,9 +5490,9 @@ static void do_ssi_include(struct mg_connection *nc, const char *ssi, char *tag,
mg_set_close_on_exec(fileno(fp)); mg_set_close_on_exec(fileno(fp));
if (mg_match_prefix(opts->ssi_pattern, strlen(opts->ssi_pattern), path) > if (mg_match_prefix(opts->ssi_pattern, strlen(opts->ssi_pattern), path) >
0) { 0) {
send_ssi_file(nc, path, fp, include_level + 1, opts); mg_send_ssi_file(nc, path, fp, include_level + 1, opts);
} else { } else {
send_file_data(nc, fp); mg_send_file_data(nc, fp);
} }
fclose(fp); fclose(fp);
} }
...@@ -5457,13 +5508,13 @@ static void do_ssi_exec(struct mg_connection *nc, char *tag) { ...@@ -5457,13 +5508,13 @@ static void do_ssi_exec(struct mg_connection *nc, char *tag) {
} else if ((fp = popen(cmd, "r")) == NULL) { } else if ((fp = popen(cmd, "r")) == NULL) {
mg_printf(nc, "Cannot SSI #exec: [%s]: %s", cmd, strerror(errno)); mg_printf(nc, "Cannot SSI #exec: [%s]: %s", cmd, strerror(errno));
} else { } else {
send_file_data(nc, fp); mg_send_file_data(nc, fp);
pclose(fp); pclose(fp);
} }
} }
#endif /* !MG_DISABLE_POPEN */ #endif /* !MG_DISABLE_POPEN */
static void do_ssi_call(struct mg_connection *nc, char *tag) { static void mg_do_ssi_call(struct mg_connection *nc, char *tag) {
mg_call(nc, NULL, MG_EV_SSI_CALL, tag); mg_call(nc, NULL, MG_EV_SSI_CALL, tag);
} }
...@@ -5471,8 +5522,8 @@ static void do_ssi_call(struct mg_connection *nc, char *tag) { ...@@ -5471,8 +5522,8 @@ static void do_ssi_call(struct mg_connection *nc, char *tag) {
* SSI directive has the following format: * SSI directive has the following format:
* <!--#directive parameter=value parameter=value --> * <!--#directive parameter=value parameter=value -->
*/ */
static void send_ssi_file(struct mg_connection *nc, const char *path, FILE *fp, static void mg_send_ssi_file(struct mg_connection *nc, const char *path,
int include_level, FILE *fp, int include_level,
const struct mg_serve_http_opts *opts) { const struct mg_serve_http_opts *opts) {
static const struct mg_str btag = MG_MK_STR("<!--#"); static const struct mg_str btag = MG_MK_STR("<!--#");
static const struct mg_str d_include = MG_MK_STR("include"); static const struct mg_str d_include = MG_MK_STR("include");
...@@ -5502,9 +5553,9 @@ static void send_ssi_file(struct mg_connection *nc, const char *path, FILE *fp, ...@@ -5502,9 +5553,9 @@ static void send_ssi_file(struct mg_connection *nc, const char *path, FILE *fp,
/* Handle known SSI directives */ /* Handle known SSI directives */
if (memcmp(p, d_include.p, d_include.len) == 0) { if (memcmp(p, d_include.p, d_include.len) == 0) {
do_ssi_include(nc, path, p + d_include.len + 1, include_level, opts); mg_do_ssi_include(nc, path, p + d_include.len + 1, include_level, opts);
} else if (memcmp(p, d_call.p, d_call.len) == 0) { } else if (memcmp(p, d_call.p, d_call.len) == 0) {
do_ssi_call(nc, p + d_call.len + 1); mg_do_ssi_call(nc, p + d_call.len + 1);
#ifndef MG_DISABLE_POPEN #ifndef MG_DISABLE_POPEN
} else if (memcmp(p, d_exec.p, d_exec.len) == 0) { } else if (memcmp(p, d_exec.p, d_exec.len) == 0) {
do_ssi_exec(nc, p + d_exec.len + 1); do_ssi_exec(nc, p + d_exec.len + 1);
...@@ -5544,45 +5595,46 @@ static void send_ssi_file(struct mg_connection *nc, const char *path, FILE *fp, ...@@ -5544,45 +5595,46 @@ static void send_ssi_file(struct mg_connection *nc, const char *path, FILE *fp,
} }
} }
static void handle_ssi_request(struct mg_connection *nc, const char *path, static void mg_handle_ssi_request(struct mg_connection *nc, const char *path,
const struct mg_serve_http_opts *opts) { const struct mg_serve_http_opts *opts) {
FILE *fp; FILE *fp;
struct mg_str mime_type; struct mg_str mime_type;
if ((fp = fopen(path, "rb")) == NULL) { if ((fp = fopen(path, "rb")) == NULL) {
send_http_error(nc, 404, NULL); mg_http_send_http_error(nc, 404, NULL);
} else { } else {
mg_set_close_on_exec(fileno(fp)); mg_set_close_on_exec(fileno(fp));
mime_type = get_mime_type(path, "text/plain", opts); mime_type = mg_get_mime_type(path, "text/plain", opts);
mg_send_response_line(nc, 200, opts->extra_headers); mg_send_response_line(nc, 200, opts->extra_headers);
mg_printf(nc, mg_printf(nc,
"Content-Type: %.*s\r\n" "Content-Type: %.*s\r\n"
"Connection: close\r\n\r\n", "Connection: close\r\n\r\n",
(int) mime_type.len, mime_type.p); (int) mime_type.len, mime_type.p);
send_ssi_file(nc, path, fp, 0, opts); mg_send_ssi_file(nc, path, fp, 0, opts);
fclose(fp); fclose(fp);
nc->flags |= MG_F_SEND_AND_CLOSE; nc->flags |= MG_F_SEND_AND_CLOSE;
} }
} }
#else #else
static void handle_ssi_request(struct mg_connection *nc, const char *path, static void mg_handle_ssi_request(struct mg_connection *nc, const char *path,
const struct mg_serve_http_opts *opts) { const struct mg_serve_http_opts *opts) {
(void) path; (void) path;
(void) opts; (void) opts;
send_http_error(nc, 500, "SSI disabled"); mg_http_send_http_error(nc, 500, "SSI disabled");
} }
#endif /* MG_DISABLE_SSI */ #endif /* MG_DISABLE_SSI */
static void construct_etag(char *buf, size_t buf_len, const cs_stat_t *st) { static void mg_http_construct_etag(char *buf, size_t buf_len,
const cs_stat_t *st) {
snprintf(buf, buf_len, "\"%lx.%" INT64_FMT "\"", (unsigned long) st->st_mtime, snprintf(buf, buf_len, "\"%lx.%" INT64_FMT "\"", (unsigned long) st->st_mtime,
(int64_t) st->st_size); (int64_t) st->st_size);
} }
static void gmt_time_string(char *buf, size_t buf_len, time_t *t) { static void mg_gmt_time_string(char *buf, size_t buf_len, time_t *t) {
strftime(buf, buf_len, "%a, %d %b %Y %H:%M:%S GMT", gmtime(t)); strftime(buf, buf_len, "%a, %d %b %Y %H:%M:%S GMT", gmtime(t));
} }
static int parse_range_header(const struct mg_str *header, int64_t *a, static int mg_http_parse_range_header(const struct mg_str *header, int64_t *a,
int64_t *b) { int64_t *b) {
/* /*
* There is no snscanf. Headers are not guaranteed to be NUL-terminated, * There is no snscanf. Headers are not guaranteed to be NUL-terminated,
...@@ -5598,17 +5650,15 @@ static int parse_range_header(const struct mg_str *header, int64_t *a, ...@@ -5598,17 +5650,15 @@ static int parse_range_header(const struct mg_str *header, int64_t *a,
return result; return result;
} }
static void mg_send_http_file2(struct mg_connection *nc, const char *path, static void mg_http_send_file2(struct mg_connection *nc, const char *path,
cs_stat_t *st, struct http_message *hm, cs_stat_t *st, struct http_message *hm,
struct mg_serve_http_opts *opts) { struct mg_serve_http_opts *opts) {
struct proto_data_http *dp; struct mg_http_proto_data *pd = mg_http_get_proto_data(nc);
struct mg_str mime_type; struct mg_str mime_type;
DBG(("%p [%s]", nc, path)); DBG(("%p [%s]", nc, path));
free_http_proto_data(nc); mg_http_free_proto_data_file(&pd->file);
if ((dp = (struct proto_data_http *) MG_CALLOC(1, sizeof(*dp))) == NULL) { if ((pd->file.fp = fopen(path, "rb")) == NULL) {
send_http_error(nc, 500, "Server Error"); /* LCOV_EXCL_LINE */
} else if ((dp->fp = fopen(path, "rb")) == NULL) {
int code; int code;
switch (errno) { switch (errno) {
case EACCES: case EACCES:
...@@ -5620,13 +5670,10 @@ static void mg_send_http_file2(struct mg_connection *nc, const char *path, ...@@ -5620,13 +5670,10 @@ static void mg_send_http_file2(struct mg_connection *nc, const char *path,
default: default:
code = 500; code = 500;
}; };
MG_FREE(dp); mg_http_send_http_error(nc, code, "Open failed");
nc->proto_data = NULL;
send_http_error(nc, code, "Open failed");
} else if (mg_match_prefix(opts->ssi_pattern, strlen(opts->ssi_pattern), } else if (mg_match_prefix(opts->ssi_pattern, strlen(opts->ssi_pattern),
path) > 0) { path) > 0) {
nc->proto_data = (void *) dp; mg_handle_ssi_request(nc, path, opts);
handle_ssi_request(nc, path, opts);
} else { } else {
char etag[50], current_time[50], last_modified[50], range[50]; char etag[50], current_time[50], last_modified[50], range[50];
time_t t = time(NULL); time_t t = time(NULL);
...@@ -5637,7 +5684,7 @@ static void mg_send_http_file2(struct mg_connection *nc, const char *path, ...@@ -5637,7 +5684,7 @@ static void mg_send_http_file2(struct mg_connection *nc, const char *path,
/* Handle Range header */ /* Handle Range header */
range[0] = '\0'; range[0] = '\0';
if (range_hdr != NULL && if (range_hdr != NULL &&
(n = parse_range_header(range_hdr, &r1, &r2)) > 0 && r1 >= 0 && (n = mg_http_parse_range_header(range_hdr, &r1, &r2)) > 0 && r1 >= 0 &&
r2 >= 0) { r2 >= 0) {
/* If range is specified like "400-", set second limit to content len */ /* If range is specified like "400-", set second limit to content len */
if (n == 1) { if (n == 1) {
...@@ -5655,14 +5702,14 @@ static void mg_send_http_file2(struct mg_connection *nc, const char *path, ...@@ -5655,14 +5702,14 @@ static void mg_send_http_file2(struct mg_connection *nc, const char *path,
snprintf(range, sizeof(range), "Content-Range: bytes %" INT64_FMT snprintf(range, sizeof(range), "Content-Range: bytes %" INT64_FMT
"-%" INT64_FMT "/%" INT64_FMT "\r\n", "-%" INT64_FMT "/%" INT64_FMT "\r\n",
r1, r1 + cl - 1, (int64_t) st->st_size); r1, r1 + cl - 1, (int64_t) st->st_size);
fseeko(dp->fp, r1, SEEK_SET); fseeko(pd->file.fp, r1, SEEK_SET);
} }
} }
construct_etag(etag, sizeof(etag), st); mg_http_construct_etag(etag, sizeof(etag), st);
gmt_time_string(current_time, sizeof(current_time), &t); mg_gmt_time_string(current_time, sizeof(current_time), &t);
gmt_time_string(last_modified, sizeof(last_modified), &st->st_mtime); mg_gmt_time_string(last_modified, sizeof(last_modified), &st->st_mtime);
mime_type = get_mime_type(path, "text/plain", opts); mime_type = mg_get_mime_type(path, "text/plain", opts);
/* /*
* Content length casted to size_t because: * Content length casted to size_t because:
* 1) that's the maximum buffer size anyway * 1) that's the maximum buffer size anyway
...@@ -5685,10 +5732,9 @@ static void mg_send_http_file2(struct mg_connection *nc, const char *path, ...@@ -5685,10 +5732,9 @@ static void mg_send_http_file2(struct mg_connection *nc, const char *path,
current_time, last_modified, (int) mime_type.len, mime_type.p, current_time, last_modified, (int) mime_type.len, mime_type.p,
(size_t) cl, range, etag); (size_t) cl, range, etag);
nc->proto_data = (void *) dp; pd->file.cl = cl;
dp->cl = cl; pd->file.type = DATA_FILE;
dp->type = DATA_FILE; mg_http_transfer_file_data(nc);
transfer_file_data(nc);
} }
} }
...@@ -5854,7 +5900,7 @@ int mg_http_parse_header(struct mg_str *hdr, const char *var_name, char *buf, ...@@ -5854,7 +5900,7 @@ int mg_http_parse_header(struct mg_str *hdr, const char *var_name, char *buf,
} }
#ifndef MG_DISABLE_FILESYSTEM #ifndef MG_DISABLE_FILESYSTEM
static int is_file_hidden(const char *path, static int mg_is_file_hidden(const char *path,
const struct mg_serve_http_opts *opts, const struct mg_serve_http_opts *opts,
int exclude_specials) { int exclude_specials) {
const char *p1 = opts->per_directory_auth_file; const char *p1 = opts->per_directory_auth_file;
...@@ -5873,7 +5919,7 @@ static int is_file_hidden(const char *path, ...@@ -5873,7 +5919,7 @@ static int is_file_hidden(const char *path,
} }
#ifndef MG_DISABLE_HTTP_DIGEST_AUTH #ifndef MG_DISABLE_HTTP_DIGEST_AUTH
static void mkmd5resp(const char *method, size_t method_len, const char *uri, static void mg_mkmd5resp(const char *method, size_t method_len, const char *uri,
size_t uri_len, const char *ha1, size_t ha1_len, size_t uri_len, const char *ha1, size_t ha1_len,
const char *nonce, size_t nonce_len, const char *nc, const char *nonce, size_t nonce_len, const char *nc,
size_t nc_len, const char *cnonce, size_t cnonce_len, size_t nc_len, const char *cnonce, size_t cnonce_len,
...@@ -5900,7 +5946,7 @@ int mg_http_create_digest_auth_header(char *buf, size_t buf_len, ...@@ -5900,7 +5946,7 @@ int mg_http_create_digest_auth_header(char *buf, size_t buf_len,
cs_md5(ha1, user, (size_t) strlen(user), colon, one, auth_domain, cs_md5(ha1, user, (size_t) strlen(user), colon, one, auth_domain,
(size_t) strlen(auth_domain), colon, one, passwd, (size_t) strlen(auth_domain), colon, one, passwd,
(size_t) strlen(passwd), NULL); (size_t) strlen(passwd), NULL);
mkmd5resp(method, strlen(method), uri, strlen(uri), ha1, sizeof(ha1) - 1, mg_mkmd5resp(method, strlen(method), uri, strlen(uri), ha1, sizeof(ha1) - 1,
cnonce, strlen(cnonce), "1", one, cnonce, strlen(cnonce), qop, cnonce, strlen(cnonce), "1", one, cnonce, strlen(cnonce), qop,
sizeof(qop) - 1, resp); sizeof(qop) - 1, resp);
return snprintf(buf, buf_len, return snprintf(buf, buf_len,
...@@ -5916,7 +5962,7 @@ int mg_http_create_digest_auth_header(char *buf, size_t buf_len, ...@@ -5916,7 +5962,7 @@ int mg_http_create_digest_auth_header(char *buf, size_t buf_len,
* to prevent replay attacks. * to prevent replay attacks.
* Assumption: nonce is a hexadecimal number of seconds since 1970. * Assumption: nonce is a hexadecimal number of seconds since 1970.
*/ */
static int check_nonce(const char *nonce) { static int mg_check_nonce(const char *nonce) {
unsigned long now = (unsigned long) time(NULL); unsigned long now = (unsigned long) time(NULL);
unsigned long val = (unsigned long) strtoul(nonce, NULL, 16); unsigned long val = (unsigned long) strtoul(nonce, NULL, 16);
return now < val || now - val < 3600; return now < val || now - val < 3600;
...@@ -5943,7 +5989,7 @@ static int mg_http_check_digest_auth(struct http_message *hm, ...@@ -5943,7 +5989,7 @@ static int mg_http_check_digest_auth(struct http_message *hm,
mg_http_parse_header(hdr, "qop", qop, sizeof(qop)) == 0 || mg_http_parse_header(hdr, "qop", qop, sizeof(qop)) == 0 ||
mg_http_parse_header(hdr, "nc", nc, sizeof(nc)) == 0 || mg_http_parse_header(hdr, "nc", nc, sizeof(nc)) == 0 ||
mg_http_parse_header(hdr, "nonce", nonce, sizeof(nonce)) == 0 || mg_http_parse_header(hdr, "nonce", nonce, sizeof(nonce)) == 0 ||
check_nonce(nonce) == 0) { mg_check_nonce(nonce) == 0) {
return 0; return 0;
} }
...@@ -5958,7 +6004,7 @@ static int mg_http_check_digest_auth(struct http_message *hm, ...@@ -5958,7 +6004,7 @@ static int mg_http_check_digest_auth(struct http_message *hm,
/* NOTE(lsm): due to a bug in MSIE, we do not compare URIs */ /* NOTE(lsm): due to a bug in MSIE, we do not compare URIs */
strcmp(auth_domain, f_domain) == 0) { strcmp(auth_domain, f_domain) == 0) {
/* User and domain matched, check the password */ /* User and domain matched, check the password */
mkmd5resp( mg_mkmd5resp(
hm->method.p, hm->method.len, hm->uri.p, hm->method.p, hm->method.len, hm->uri.p,
hm->uri.len + (hm->query_string.len ? hm->query_string.len + 1 : 0), hm->uri.len + (hm->query_string.len ? hm->query_string.len + 1 : 0),
f_ha1, strlen(f_ha1), nonce, strlen(nonce), nc, strlen(nc), cnonce, f_ha1, strlen(f_ha1), nonce, strlen(nonce), nc, strlen(nc), cnonce,
...@@ -5971,9 +6017,10 @@ static int mg_http_check_digest_auth(struct http_message *hm, ...@@ -5971,9 +6017,10 @@ static int mg_http_check_digest_auth(struct http_message *hm,
return 0; return 0;
} }
static int is_authorized(struct http_message *hm, const char *path, static int mg_is_authorized(struct http_message *hm, const char *path,
int is_directory, const char *domain, int is_directory, const char *domain,
const char *passwords_file, int is_global_pass_file) { const char *passwords_file,
int is_global_pass_file) {
char buf[MG_MAX_PATH]; char buf[MG_MAX_PATH];
const char *p; const char *p;
FILE *fp; FILE *fp;
...@@ -6004,9 +6051,10 @@ static int is_authorized(struct http_message *hm, const char *path, ...@@ -6004,9 +6051,10 @@ static int is_authorized(struct http_message *hm, const char *path,
return authorized; return authorized;
} }
#else #else
static int is_authorized(struct http_message *hm, const char *path, static int mg_is_authorized(struct http_message *hm, const char *path,
int is_directory, const char *domain, int is_directory, const char *domain,
const char *passwords_file, int is_global_pass_file) { const char *passwords_file,
int is_global_pass_file) {
(void) hm; (void) hm;
(void) path; (void) path;
(void) is_directory; (void) is_directory;
...@@ -6040,7 +6088,7 @@ static size_t mg_url_encode(const char *src, size_t s_len, char *dst, ...@@ -6040,7 +6088,7 @@ static size_t mg_url_encode(const char *src, size_t s_len, char *dst,
return j; return j;
} }
static void escape(const char *src, char *dst, size_t dst_len) { static void mg_escape(const char *src, char *dst, size_t dst_len) {
size_t n = 0; size_t n = 0;
while (*src != '\0' && n + 5 < dst_len) { while (*src != '\0' && n + 5 < dst_len) {
unsigned char ch = *(unsigned char *) src++; unsigned char ch = *(unsigned char *) src++;
...@@ -6053,7 +6101,7 @@ static void escape(const char *src, char *dst, size_t dst_len) { ...@@ -6053,7 +6101,7 @@ static void escape(const char *src, char *dst, size_t dst_len) {
dst[n] = '\0'; dst[n] = '\0';
} }
static void print_dir_entry(struct mg_connection *nc, const char *file_name, static void mg_print_dir_entry(struct mg_connection *nc, const char *file_name,
cs_stat_t *stp) { cs_stat_t *stp) {
char size[64], mod[64], href[MAX_PATH_SIZE * 3], path[MAX_PATH_SIZE]; char size[64], mod[64], href[MAX_PATH_SIZE * 3], path[MAX_PATH_SIZE];
int64_t fsize = stp->st_size; int64_t fsize = stp->st_size;
...@@ -6078,7 +6126,7 @@ static void print_dir_entry(struct mg_connection *nc, const char *file_name, ...@@ -6078,7 +6126,7 @@ static void print_dir_entry(struct mg_connection *nc, const char *file_name,
} }
} }
strftime(mod, sizeof(mod), "%d-%b-%Y %H:%M", localtime(&stp->st_mtime)); strftime(mod, sizeof(mod), "%d-%b-%Y %H:%M", localtime(&stp->st_mtime));
escape(file_name, path, sizeof(path)); mg_escape(file_name, path, sizeof(path));
mg_url_encode(file_name, strlen(file_name), href, sizeof(href)); mg_url_encode(file_name, strlen(file_name), href, sizeof(href));
mg_printf_http_chunk(nc, mg_printf_http_chunk(nc,
"<tr><td><a href=\"%s%s\">%s%s</a></td>" "<tr><td><a href=\"%s%s\">%s%s</a></td>"
...@@ -6087,7 +6135,7 @@ static void print_dir_entry(struct mg_connection *nc, const char *file_name, ...@@ -6087,7 +6135,7 @@ static void print_dir_entry(struct mg_connection *nc, const char *file_name,
size); size);
} }
static void scan_directory(struct mg_connection *nc, const char *dir, static void mg_scan_directory(struct mg_connection *nc, const char *dir,
const struct mg_serve_http_opts *opts, const struct mg_serve_http_opts *opts,
void (*func)(struct mg_connection *, const char *, void (*func)(struct mg_connection *, const char *,
cs_stat_t *)) { cs_stat_t *)) {
...@@ -6100,7 +6148,7 @@ static void scan_directory(struct mg_connection *nc, const char *dir, ...@@ -6100,7 +6148,7 @@ static void scan_directory(struct mg_connection *nc, const char *dir,
if ((dirp = (opendir(dir))) != NULL) { if ((dirp = (opendir(dir))) != NULL) {
while ((dp = readdir(dirp)) != NULL) { while ((dp = readdir(dirp)) != NULL) {
/* Do not show current dir and hidden files */ /* Do not show current dir and hidden files */
if (is_file_hidden((const char *) dp->d_name, opts, 1)) { if (mg_is_file_hidden((const char *) dp->d_name, opts, 1)) {
continue; continue;
} }
snprintf(path, sizeof(path), "%s/%s", dir, dp->d_name); snprintf(path, sizeof(path), "%s/%s", dir, dp->d_name);
...@@ -6114,7 +6162,7 @@ static void scan_directory(struct mg_connection *nc, const char *dir, ...@@ -6114,7 +6162,7 @@ static void scan_directory(struct mg_connection *nc, const char *dir,
} }
} }
static void send_directory_listing(struct mg_connection *nc, const char *dir, static void mg_send_directory_listing(struct mg_connection *nc, const char *dir,
struct http_message *hm, struct http_message *hm,
struct mg_serve_http_opts *opts) { struct mg_serve_http_opts *opts) {
static const char *sort_js_code = static const char *sort_js_code =
...@@ -6149,7 +6197,7 @@ static void send_directory_listing(struct mg_connection *nc, const char *dir, ...@@ -6149,7 +6197,7 @@ static void send_directory_listing(struct mg_connection *nc, const char *dir,
"<tr><td colspan=\"3\"><hr></td></tr></thead><tbody id=tb>", "<tr><td colspan=\"3\"><hr></td></tr></thead><tbody id=tb>",
(int) hm->uri.len, hm->uri.p, sort_js_code, sort_js_code2, (int) hm->uri.len, hm->uri.p, sort_js_code, sort_js_code2,
(int) hm->uri.len, hm->uri.p); (int) hm->uri.len, hm->uri.p);
scan_directory(nc, dir, opts, print_dir_entry); mg_scan_directory(nc, dir, opts, mg_print_dir_entry);
mg_printf_http_chunk(nc, "%s", "</tbody></body></html>"); mg_printf_http_chunk(nc, "%s", "</tbody></body></html>");
mg_send_http_chunk(nc, "", 0); mg_send_http_chunk(nc, "", 0);
/* TODO(rojer): Remove when cesanta/dev/issues/197 is fixed. */ /* TODO(rojer): Remove when cesanta/dev/issues/197 is fixed. */
...@@ -6158,11 +6206,11 @@ static void send_directory_listing(struct mg_connection *nc, const char *dir, ...@@ -6158,11 +6206,11 @@ static void send_directory_listing(struct mg_connection *nc, const char *dir,
#endif /* MG_DISABLE_DIRECTORY_LISTING */ #endif /* MG_DISABLE_DIRECTORY_LISTING */
#ifndef MG_DISABLE_DAV #ifndef MG_DISABLE_DAV
static void print_props(struct mg_connection *nc, const char *name, static void mg_print_props(struct mg_connection *nc, const char *name,
cs_stat_t *stp) { cs_stat_t *stp) {
char mtime[64], buf[MAX_PATH_SIZE * 3]; char mtime[64], buf[MAX_PATH_SIZE * 3];
time_t t = stp->st_mtime; /* store in local variable for NDK compile */ time_t t = stp->st_mtime; /* store in local variable for NDK compile */
gmt_time_string(mtime, sizeof(mtime), &t); mg_gmt_time_string(mtime, sizeof(mtime), &t);
mg_url_encode(name, strlen(name), buf, sizeof(buf)); mg_url_encode(name, strlen(name), buf, sizeof(buf));
mg_printf(nc, mg_printf(nc,
"<d:response>" "<d:response>"
...@@ -6181,7 +6229,7 @@ static void print_props(struct mg_connection *nc, const char *name, ...@@ -6181,7 +6229,7 @@ static void print_props(struct mg_connection *nc, const char *name,
(int64_t) stp->st_size, mtime); (int64_t) stp->st_size, mtime);
} }
static void handle_propfind(struct mg_connection *nc, const char *path, static void mg_handle_propfind(struct mg_connection *nc, const char *path,
cs_stat_t *stp, struct http_message *hm, cs_stat_t *stp, struct http_message *hm,
struct mg_serve_http_opts *opts) { struct mg_serve_http_opts *opts) {
static const char header[] = static const char header[] =
...@@ -6201,9 +6249,9 @@ static void handle_propfind(struct mg_connection *nc, const char *path, ...@@ -6201,9 +6249,9 @@ static void handle_propfind(struct mg_connection *nc, const char *path,
char uri[MAX_PATH_SIZE]; char uri[MAX_PATH_SIZE];
mg_send(nc, header, sizeof(header) - 1); mg_send(nc, header, sizeof(header) - 1);
snprintf(uri, sizeof(uri), "%.*s", (int) hm->uri.len, hm->uri.p); snprintf(uri, sizeof(uri), "%.*s", (int) hm->uri.len, hm->uri.p);
print_props(nc, uri, stp); mg_print_props(nc, uri, stp);
if (S_ISDIR(stp->st_mode) && (depth == NULL || mg_vcmp(depth, "0") != 0)) { if (S_ISDIR(stp->st_mode) && (depth == NULL || mg_vcmp(depth, "0") != 0)) {
scan_directory(nc, path, opts, print_props); mg_scan_directory(nc, path, opts, mg_print_props);
} }
mg_send(nc, footer, sizeof(footer) - 1); mg_send(nc, footer, sizeof(footer) - 1);
nc->flags |= MG_F_SEND_AND_CLOSE; nc->flags |= MG_F_SEND_AND_CLOSE;
...@@ -6222,7 +6270,7 @@ static void handle_propfind(struct mg_connection *nc, const char *path, ...@@ -6222,7 +6270,7 @@ static void handle_propfind(struct mg_connection *nc, const char *path,
* NOTE: that is not DAV LOCK imlementation, it is just a way to shut up * NOTE: that is not DAV LOCK imlementation, it is just a way to shut up
* Windows native DAV client. This is why FAKE LOCK is not enabed by default * Windows native DAV client. This is why FAKE LOCK is not enabed by default
*/ */
static void handle_lock(struct mg_connection *nc, const char *path) { static void mg_handle_lock(struct mg_connection *nc, const char *path) {
static const char *reply = static const char *reply =
"HTTP/1.1 207 Multi-Status\r\n" "HTTP/1.1 207 Multi-Status\r\n"
"Connection: close\r\n" "Connection: close\r\n"
...@@ -6244,7 +6292,7 @@ static void handle_lock(struct mg_connection *nc, const char *path) { ...@@ -6244,7 +6292,7 @@ static void handle_lock(struct mg_connection *nc, const char *path) {
} }
#endif #endif
static void handle_mkcol(struct mg_connection *nc, const char *path, static void mg_handle_mkcol(struct mg_connection *nc, const char *path,
struct http_message *hm) { struct http_message *hm) {
int status_code = 500; int status_code = 500;
if (hm->body.len != (size_t) ~0 && hm->body.len > 0) { if (hm->body.len != (size_t) ~0 && hm->body.len > 0) {
...@@ -6260,10 +6308,10 @@ static void handle_mkcol(struct mg_connection *nc, const char *path, ...@@ -6260,10 +6308,10 @@ static void handle_mkcol(struct mg_connection *nc, const char *path,
} else { } else {
status_code = 500; status_code = 500;
} }
send_http_error(nc, status_code, NULL); mg_http_send_http_error(nc, status_code, NULL);
} }
static int remove_directory(const struct mg_serve_http_opts *opts, static int mg_remove_directory(const struct mg_serve_http_opts *opts,
const char *dir) { const char *dir) {
char path[MAX_PATH_SIZE]; char path[MAX_PATH_SIZE];
struct dirent *dp; struct dirent *dp;
...@@ -6273,13 +6321,13 @@ static int remove_directory(const struct mg_serve_http_opts *opts, ...@@ -6273,13 +6321,13 @@ static int remove_directory(const struct mg_serve_http_opts *opts,
if ((dirp = opendir(dir)) == NULL) return 0; if ((dirp = opendir(dir)) == NULL) return 0;
while ((dp = readdir(dirp)) != NULL) { while ((dp = readdir(dirp)) != NULL) {
if (is_file_hidden((const char *) dp->d_name, opts, 1)) { if (mg_is_file_hidden((const char *) dp->d_name, opts, 1)) {
continue; continue;
} }
snprintf(path, sizeof(path), "%s%c%s", dir, '/', dp->d_name); snprintf(path, sizeof(path), "%s%c%s", dir, '/', dp->d_name);
mg_stat(path, &st); mg_stat(path, &st);
if (S_ISDIR(st.st_mode)) { if (S_ISDIR(st.st_mode)) {
remove_directory(opts, path); mg_remove_directory(opts, path);
} else { } else {
remove(path); remove(path);
} }
...@@ -6290,12 +6338,12 @@ static int remove_directory(const struct mg_serve_http_opts *opts, ...@@ -6290,12 +6338,12 @@ static int remove_directory(const struct mg_serve_http_opts *opts,
return 1; return 1;
} }
static void handle_move(struct mg_connection *c, static void mg_handle_move(struct mg_connection *c,
const struct mg_serve_http_opts *opts, const char *path, const struct mg_serve_http_opts *opts,
struct http_message *hm) { const char *path, struct http_message *hm) {
const struct mg_str *dest = mg_get_http_header(hm, "Destination"); const struct mg_str *dest = mg_get_http_header(hm, "Destination");
if (dest == NULL) { if (dest == NULL) {
send_http_error(c, 411, NULL); mg_http_send_http_error(c, 411, NULL);
} else { } else {
const char *p = (char *) memchr(dest->p, '/', dest->len); const char *p = (char *) memchr(dest->p, '/', dest->len);
if (p != NULL && p[1] == '/' && if (p != NULL && p[1] == '/' &&
...@@ -6304,34 +6352,34 @@ static void handle_move(struct mg_connection *c, ...@@ -6304,34 +6352,34 @@ static void handle_move(struct mg_connection *c,
snprintf(buf, sizeof(buf), "%s%.*s", opts->dav_document_root, snprintf(buf, sizeof(buf), "%s%.*s", opts->dav_document_root,
(int) (dest->p + dest->len - p), p); (int) (dest->p + dest->len - p), p);
if (rename(path, buf) == 0) { if (rename(path, buf) == 0) {
send_http_error(c, 200, NULL); mg_http_send_http_error(c, 200, NULL);
} else { } else {
send_http_error(c, 418, NULL); mg_http_send_http_error(c, 418, NULL);
} }
} else { } else {
send_http_error(c, 500, NULL); mg_http_send_http_error(c, 500, NULL);
} }
} }
} }
static void handle_delete(struct mg_connection *nc, static void mg_handle_delete(struct mg_connection *nc,
const struct mg_serve_http_opts *opts, const struct mg_serve_http_opts *opts,
const char *path) { const char *path) {
cs_stat_t st; cs_stat_t st;
if (mg_stat(path, &st) != 0) { if (mg_stat(path, &st) != 0) {
send_http_error(nc, 404, NULL); mg_http_send_http_error(nc, 404, NULL);
} else if (S_ISDIR(st.st_mode)) { } else if (S_ISDIR(st.st_mode)) {
remove_directory(opts, path); mg_remove_directory(opts, path);
send_http_error(nc, 204, NULL); mg_http_send_http_error(nc, 204, NULL);
} else if (remove(path) == 0) { } else if (remove(path) == 0) {
send_http_error(nc, 204, NULL); mg_http_send_http_error(nc, 204, NULL);
} else { } else {
send_http_error(nc, 423, NULL); mg_http_send_http_error(nc, 423, NULL);
} }
} }
/* Return -1 on error, 1 on success. */ /* Return -1 on error, 1 on success. */
static int create_itermediate_directories(const char *path) { static int mg_create_itermediate_directories(const char *path) {
const char *s; const char *s;
/* Create intermediate directories if they do not exist */ /* Create intermediate directories if they do not exist */
...@@ -6350,47 +6398,43 @@ static int create_itermediate_directories(const char *path) { ...@@ -6350,47 +6398,43 @@ static int create_itermediate_directories(const char *path) {
return 1; return 1;
} }
static void handle_put(struct mg_connection *nc, const char *path, static void mg_handle_put(struct mg_connection *nc, const char *path,
struct http_message *hm) { struct http_message *hm) {
struct mg_http_proto_data *pd = mg_http_get_proto_data(nc);
cs_stat_t st; cs_stat_t st;
const struct mg_str *cl_hdr = mg_get_http_header(hm, "Content-Length"); const struct mg_str *cl_hdr = mg_get_http_header(hm, "Content-Length");
int rc, status_code = mg_stat(path, &st) == 0 ? 200 : 201; int rc, status_code = mg_stat(path, &st) == 0 ? 200 : 201;
struct proto_data_http *dp = (struct proto_data_http *) nc->proto_data;
free_http_proto_data(nc); mg_http_free_proto_data_file(&pd->file);
if ((rc = create_itermediate_directories(path)) == 0) { if ((rc = mg_create_itermediate_directories(path)) == 0) {
mg_printf(nc, "HTTP/1.1 %d OK\r\nContent-Length: 0\r\n\r\n", status_code); mg_printf(nc, "HTTP/1.1 %d OK\r\nContent-Length: 0\r\n\r\n", status_code);
} else if (rc == -1) { } else if (rc == -1) {
send_http_error(nc, 500, NULL); mg_http_send_http_error(nc, 500, NULL);
} else if (cl_hdr == NULL) { } else if (cl_hdr == NULL) {
send_http_error(nc, 411, NULL); mg_http_send_http_error(nc, 411, NULL);
} else if ((dp = (struct proto_data_http *) MG_CALLOC(1, sizeof(*dp))) == } else if ((pd->file.fp = fopen(path, "w+b")) == NULL) {
NULL) { mg_http_send_http_error(nc, 500, NULL);
send_http_error(nc, 500, NULL); /* LCOV_EXCL_LINE */
} else if ((dp->fp = fopen(path, "w+b")) == NULL) {
send_http_error(nc, 500, NULL);
free_http_proto_data(nc);
} else { } else {
const struct mg_str *range_hdr = mg_get_http_header(hm, "Content-Range"); const struct mg_str *range_hdr = mg_get_http_header(hm, "Content-Range");
int64_t r1 = 0, r2 = 0; int64_t r1 = 0, r2 = 0;
dp->type = DATA_PUT; pd->file.type = DATA_PUT;
mg_set_close_on_exec(fileno(dp->fp)); mg_set_close_on_exec(fileno(pd->file.fp));
dp->cl = to64(cl_hdr->p); pd->file.cl = to64(cl_hdr->p);
if (range_hdr != NULL && parse_range_header(range_hdr, &r1, &r2) > 0) { if (range_hdr != NULL &&
mg_http_parse_range_header(range_hdr, &r1, &r2) > 0) {
status_code = 206; status_code = 206;
fseeko(dp->fp, r1, SEEK_SET); fseeko(pd->file.fp, r1, SEEK_SET);
dp->cl = r2 > r1 ? r2 - r1 + 1 : dp->cl - r1; pd->file.cl = r2 > r1 ? r2 - r1 + 1 : pd->file.cl - r1;
} }
mg_printf(nc, "HTTP/1.1 %d OK\r\nContent-Length: 0\r\n\r\n", status_code); mg_printf(nc, "HTTP/1.1 %d OK\r\nContent-Length: 0\r\n\r\n", status_code);
nc->proto_data = dp;
/* Remove HTTP request from the mbuf, leave only payload */ /* Remove HTTP request from the mbuf, leave only payload */
mbuf_remove(&nc->recv_mbuf, hm->message.len - hm->body.len); mbuf_remove(&nc->recv_mbuf, hm->message.len - hm->body.len);
transfer_file_data(nc); mg_http_transfer_file_data(nc);
} }
} }
#endif /* MG_DISABLE_DAV */ #endif /* MG_DISABLE_DAV */
static int is_dav_request(const struct mg_str *s) { static int mg_is_dav_request(const struct mg_str *s) {
static const char *methods[] = {"PUT", "DELETE", "MKCOL", "PROPFIND", "MOVE" static const char *methods[] = {"PUT", "DELETE", "MKCOL", "PROPFIND", "MOVE"
#ifdef MG_ENABLE_FAKE_DAVLOCK #ifdef MG_ENABLE_FAKE_DAVLOCK
, ,
...@@ -6415,7 +6459,7 @@ static int is_dav_request(const struct mg_str *s) { ...@@ -6415,7 +6459,7 @@ static int is_dav_request(const struct mg_str *s) {
* appended to the `path`, stat-ed, and result of `stat()` passed to `stp`. * appended to the `path`, stat-ed, and result of `stat()` passed to `stp`.
* If index file is not found, then `path` and `stp` remain unchanged. * If index file is not found, then `path` and `stp` remain unchanged.
*/ */
MG_INTERNAL void find_index_file(const char *path, const char *list, MG_INTERNAL void mg_find_index_file(const char *path, const char *list,
char **index_file, cs_stat_t *stp) { char **index_file, cs_stat_t *stp) {
struct mg_str vec; struct mg_str vec;
size_t path_len = strlen(path); size_t path_len = strlen(path);
...@@ -6446,8 +6490,8 @@ MG_INTERNAL void find_index_file(const char *path, const char *list, ...@@ -6446,8 +6490,8 @@ MG_INTERNAL void find_index_file(const char *path, const char *list,
DBG(("[%s] [%s]", path, (*index_file ? *index_file : ""))); DBG(("[%s] [%s]", path, (*index_file ? *index_file : "")));
} }
static int send_port_based_redirect(struct mg_connection *c, static int mg_http_send_port_based_redirect(
struct http_message *hm, struct mg_connection *c, struct http_message *hm,
const struct mg_serve_http_opts *opts) { const struct mg_serve_http_opts *opts) {
const char *rewrites = opts->url_rewrites; const char *rewrites = opts->url_rewrites;
struct mg_str a, b; struct mg_str a, b;
...@@ -6521,7 +6565,7 @@ MG_INTERNAL int mg_uri_to_local_path(struct http_message *hm, ...@@ -6521,7 +6565,7 @@ MG_INTERNAL int mg_uri_to_local_path(struct http_message *hm,
/* If no rewrite rules matched, use DAV or regular document root. */ /* If no rewrite rules matched, use DAV or regular document root. */
if (root.p == NULL) { if (root.p == NULL) {
#ifndef MG_DISABLE_DAV #ifndef MG_DISABLE_DAV
if (opts->dav_document_root != NULL && is_dav_request(&hm->method)) { if (opts->dav_document_root != NULL && mg_is_dav_request(&hm->method)) {
root.p = opts->dav_document_root; root.p = opts->dav_document_root;
root.len = strlen(opts->dav_document_root); root.len = strlen(opts->dav_document_root);
} else } else
...@@ -6627,25 +6671,25 @@ out: ...@@ -6627,25 +6671,25 @@ out:
#ifndef MG_DISABLE_CGI #ifndef MG_DISABLE_CGI
#ifdef _WIN32 #ifdef _WIN32
struct threadparam { struct mg_threadparam {
sock_t s; sock_t s;
HANDLE hPipe; HANDLE hPipe;
}; };
static int wait_until_ready(sock_t sock, int for_read) { static int mg_wait_until_ready(sock_t sock, int for_read) {
fd_set set; fd_set set;
FD_ZERO(&set); FD_ZERO(&set);
FD_SET(sock, &set); FD_SET(sock, &set);
return select(sock + 1, for_read ? &set : 0, for_read ? 0 : &set, 0, 0) == 1; return select(sock + 1, for_read ? &set : 0, for_read ? 0 : &set, 0, 0) == 1;
} }
static void *push_to_stdin(void *arg) { static void *mg_push_to_stdin(void *arg) {
struct threadparam *tp = (struct threadparam *) arg; struct mg_threadparam *tp = (struct mg_threadparam *) arg;
int n, sent, stop = 0; int n, sent, stop = 0;
DWORD k; DWORD k;
char buf[BUFSIZ]; char buf[BUFSIZ];
while (!stop && wait_until_ready(tp->s, 1) && while (!stop && mg_wait_until_ready(tp->s, 1) &&
(n = recv(tp->s, buf, sizeof(buf), 0)) > 0) { (n = recv(tp->s, buf, sizeof(buf), 0)) > 0) {
if (n == -1 && GetLastError() == WSAEWOULDBLOCK) continue; if (n == -1 && GetLastError() == WSAEWOULDBLOCK) continue;
for (sent = 0; !stop && sent < n; sent += k) { for (sent = 0; !stop && sent < n; sent += k) {
...@@ -6659,15 +6703,15 @@ static void *push_to_stdin(void *arg) { ...@@ -6659,15 +6703,15 @@ static void *push_to_stdin(void *arg) {
return NULL; return NULL;
} }
static void *pull_from_stdout(void *arg) { static void *mg_pull_from_stdout(void *arg) {
struct threadparam *tp = (struct threadparam *) arg; struct mg_threadparam *tp = (struct mg_threadparam *) arg;
int k = 0, stop = 0; int k = 0, stop = 0;
DWORD n, sent; DWORD n, sent;
char buf[BUFSIZ]; char buf[BUFSIZ];
while (!stop && ReadFile(tp->hPipe, buf, sizeof(buf), &n, NULL)) { while (!stop && ReadFile(tp->hPipe, buf, sizeof(buf), &n, NULL)) {
for (sent = 0; !stop && sent < n; sent += k) { for (sent = 0; !stop && sent < n; sent += k) {
if (wait_until_ready(tp->s, 0) && if (mg_wait_until_ready(tp->s, 0) &&
(k = send(tp->s, buf + sent, n - sent, 0)) <= 0) (k = send(tp->s, buf + sent, n - sent, 0)) <= 0)
stop = 1; stop = 1;
} }
...@@ -6681,9 +6725,9 @@ static void *pull_from_stdout(void *arg) { ...@@ -6681,9 +6725,9 @@ static void *pull_from_stdout(void *arg) {
return NULL; return NULL;
} }
static void spawn_stdio_thread(sock_t sock, HANDLE hPipe, static void mg_spawn_stdio_thread(sock_t sock, HANDLE hPipe,
void *(*func)(void *)) { void *(*func)(void *)) {
struct threadparam *tp = (struct threadparam *) MG_MALLOC(sizeof(*tp)); struct mg_threadparam *tp = (struct mg_threadparam *) MG_MALLOC(sizeof(*tp));
if (tp != NULL) { if (tp != NULL) {
tp->s = sock; tp->s = sock;
tp->hPipe = hPipe; tp->hPipe = hPipe;
...@@ -6691,15 +6735,16 @@ static void spawn_stdio_thread(sock_t sock, HANDLE hPipe, ...@@ -6691,15 +6735,16 @@ static void spawn_stdio_thread(sock_t sock, HANDLE hPipe,
} }
} }
static void abs_path(const char *utf8_path, char *abs_path, size_t len) { static void mg_abs_path(const char *utf8_path, char *abs_path, size_t len) {
wchar_t buf[MAX_PATH_SIZE], buf2[MAX_PATH_SIZE]; wchar_t buf[MAX_PATH_SIZE], buf2[MAX_PATH_SIZE];
to_wchar(utf8_path, buf, ARRAY_SIZE(buf)); to_wchar(utf8_path, buf, ARRAY_SIZE(buf));
GetFullPathNameW(buf, ARRAY_SIZE(buf2), buf2, NULL); GetFullPathNameW(buf, ARRAY_SIZE(buf2), buf2, NULL);
WideCharToMultiByte(CP_UTF8, 0, buf2, wcslen(buf2) + 1, abs_path, len, 0, 0); WideCharToMultiByte(CP_UTF8, 0, buf2, wcslen(buf2) + 1, abs_path, len, 0, 0);
} }
static pid_t start_process(const char *interp, const char *cmd, const char *env, static pid_t mg_start_process(const char *interp, const char *cmd,
const char *envp[], const char *dir, sock_t sock) { const char *env, const char *envp[],
const char *dir, sock_t sock) {
STARTUPINFOW si; STARTUPINFOW si;
PROCESS_INFORMATION pi; PROCESS_INFORMATION pi;
HANDLE a[2], b[2], me = GetCurrentProcess(); HANDLE a[2], b[2], me = GetCurrentProcess();
...@@ -6737,13 +6782,13 @@ static pid_t start_process(const char *interp, const char *cmd, const char *env, ...@@ -6737,13 +6782,13 @@ static pid_t start_process(const char *interp, const char *cmd, const char *env,
} }
snprintf(buf, sizeof(buf), "%s/%s", dir, cmd); snprintf(buf, sizeof(buf), "%s/%s", dir, cmd);
abs_path(buf, buf2, ARRAY_SIZE(buf2)); mg_abs_path(buf, buf2, ARRAY_SIZE(buf2));
abs_path(dir, buf5, ARRAY_SIZE(buf5)); mg_abs_path(dir, buf5, ARRAY_SIZE(buf5));
to_wchar(dir, full_dir, ARRAY_SIZE(full_dir)); to_wchar(dir, full_dir, ARRAY_SIZE(full_dir));
if (interp != NULL) { if (interp != NULL) {
abs_path(interp, buf4, ARRAY_SIZE(buf4)); mg_abs_path(interp, buf4, ARRAY_SIZE(buf4));
snprintf(cmdline, sizeof(cmdline), "%s \"%s\"", buf4, buf2); snprintf(cmdline, sizeof(cmdline), "%s \"%s\"", buf4, buf2);
} else { } else {
snprintf(cmdline, sizeof(cmdline), "\"%s\"", buf2); snprintf(cmdline, sizeof(cmdline), "\"%s\"", buf2);
...@@ -6752,8 +6797,8 @@ static pid_t start_process(const char *interp, const char *cmd, const char *env, ...@@ -6752,8 +6797,8 @@ static pid_t start_process(const char *interp, const char *cmd, const char *env,
if (CreateProcessW(NULL, wcmd, NULL, NULL, TRUE, CREATE_NEW_PROCESS_GROUP, if (CreateProcessW(NULL, wcmd, NULL, NULL, TRUE, CREATE_NEW_PROCESS_GROUP,
(void *) env, full_dir, &si, &pi) != 0) { (void *) env, full_dir, &si, &pi) != 0) {
spawn_stdio_thread(sock, a[1], push_to_stdin); mg_spawn_stdio_thread(sock, a[1], mg_push_to_stdin);
spawn_stdio_thread(sock, b[0], pull_from_stdout); mg_spawn_stdio_thread(sock, b[0], mg_pull_from_stdout);
} else { } else {
CloseHandle(a[1]); CloseHandle(a[1]);
CloseHandle(b[0]); CloseHandle(b[0]);
...@@ -6771,8 +6816,9 @@ static pid_t start_process(const char *interp, const char *cmd, const char *env, ...@@ -6771,8 +6816,9 @@ static pid_t start_process(const char *interp, const char *cmd, const char *env,
return pi.hProcess; return pi.hProcess;
} }
#else #else
static pid_t start_process(const char *interp, const char *cmd, const char *env, static pid_t mg_start_process(const char *interp, const char *cmd,
const char *envp[], const char *dir, sock_t sock) { const char *env, const char *envp[],
const char *dir, sock_t sock) {
char buf[500]; char buf[500];
pid_t pid = fork(); pid_t pid = fork();
(void) env; (void) env;
...@@ -6818,7 +6864,7 @@ static pid_t start_process(const char *interp, const char *cmd, const char *env, ...@@ -6818,7 +6864,7 @@ static pid_t start_process(const char *interp, const char *cmd, const char *env,
* Append VARIABLE=VALUE\0 string to the buffer, and add a respective * Append VARIABLE=VALUE\0 string to the buffer, and add a respective
* pointer into the vars array. * pointer into the vars array.
*/ */
static char *addenv(struct cgi_env_block *block, const char *fmt, ...) { static char *mg_addenv(struct mg_cgi_env_block *block, const char *fmt, ...) {
int n, space; int n, space;
char *added = block->buf + block->len; char *added = block->buf + block->len;
va_list ap; va_list ap;
...@@ -6844,16 +6890,17 @@ static char *addenv(struct cgi_env_block *block, const char *fmt, ...) { ...@@ -6844,16 +6890,17 @@ static char *addenv(struct cgi_env_block *block, const char *fmt, ...) {
return added; return added;
} }
static void addenv2(struct cgi_env_block *blk, const char *name) { static void mg_addenv2(struct mg_cgi_env_block *blk, const char *name) {
const char *s; const char *s;
if ((s = getenv(name)) != NULL) addenv(blk, "%s=%s", name, s); if ((s = getenv(name)) != NULL) mg_addenv(blk, "%s=%s", name, s);
} }
static void prepare_cgi_environment(struct mg_connection *nc, const char *prog, static void mg_prepare_cgi_environment(struct mg_connection *nc,
const char *prog,
const struct mg_str *path_info, const struct mg_str *path_info,
const struct http_message *hm, const struct http_message *hm,
const struct mg_serve_http_opts *opts, const struct mg_serve_http_opts *opts,
struct cgi_env_block *blk) { struct mg_cgi_env_block *blk) {
const char *s; const char *s;
struct mg_str *h; struct mg_str *h;
char *p; char *p;
...@@ -6863,87 +6910,87 @@ static void prepare_cgi_environment(struct mg_connection *nc, const char *prog, ...@@ -6863,87 +6910,87 @@ static void prepare_cgi_environment(struct mg_connection *nc, const char *prog,
blk->nc = nc; blk->nc = nc;
if ((s = getenv("SERVER_NAME")) != NULL) { if ((s = getenv("SERVER_NAME")) != NULL) {
addenv(blk, "SERVER_NAME=%s", s); mg_addenv(blk, "SERVER_NAME=%s", s);
} else { } else {
char buf[100]; char buf[100];
mg_sock_to_str(nc->sock, buf, sizeof(buf), 3); mg_sock_to_str(nc->sock, buf, sizeof(buf), 3);
addenv(blk, "SERVER_NAME=%s", buf); mg_addenv(blk, "SERVER_NAME=%s", buf);
} }
addenv(blk, "SERVER_ROOT=%s", opts->document_root); mg_addenv(blk, "SERVER_ROOT=%s", opts->document_root);
addenv(blk, "DOCUMENT_ROOT=%s", opts->document_root); mg_addenv(blk, "DOCUMENT_ROOT=%s", opts->document_root);
addenv(blk, "SERVER_SOFTWARE=%s/%s", "Mongoose", MG_VERSION); mg_addenv(blk, "SERVER_SOFTWARE=%s/%s", "Mongoose", MG_VERSION);
/* Prepare the environment block */ /* Prepare the environment block */
addenv(blk, "%s", "GATEWAY_INTERFACE=CGI/1.1"); mg_addenv(blk, "%s", "GATEWAY_INTERFACE=CGI/1.1");
addenv(blk, "%s", "SERVER_PROTOCOL=HTTP/1.1"); mg_addenv(blk, "%s", "SERVER_PROTOCOL=HTTP/1.1");
addenv(blk, "%s", "REDIRECT_STATUS=200"); /* For PHP */ mg_addenv(blk, "%s", "REDIRECT_STATUS=200"); /* For PHP */
/* TODO(lsm): fix this for IPv6 case */ /* TODO(lsm): fix this for IPv6 case */
/*addenv(blk, "SERVER_PORT=%d", ri->remote_port); */ /*addenv(blk, "SERVER_PORT=%d", ri->remote_port); */
addenv(blk, "REQUEST_METHOD=%.*s", (int) hm->method.len, hm->method.p); mg_addenv(blk, "REQUEST_METHOD=%.*s", (int) hm->method.len, hm->method.p);
#if 0 #if 0
addenv(blk, "REMOTE_ADDR=%s", ri->remote_ip); addenv(blk, "REMOTE_ADDR=%s", ri->remote_ip);
addenv(blk, "REMOTE_PORT=%d", ri->remote_port); addenv(blk, "REMOTE_PORT=%d", ri->remote_port);
#endif #endif
addenv(blk, "REQUEST_URI=%.*s%s%.*s", (int) hm->uri.len, hm->uri.p, mg_addenv(blk, "REQUEST_URI=%.*s%s%.*s", (int) hm->uri.len, hm->uri.p,
hm->query_string.len == 0 ? "" : "?", (int) hm->query_string.len, hm->query_string.len == 0 ? "" : "?", (int) hm->query_string.len,
hm->query_string.p); hm->query_string.p);
s = hm->uri.p + hm->uri.len - path_info->len - 1; s = hm->uri.p + hm->uri.len - path_info->len - 1;
if (*s == '/') { if (*s == '/') {
const char *base_name = strrchr(prog, DIRSEP); const char *base_name = strrchr(prog, DIRSEP);
addenv(blk, "SCRIPT_NAME=%.*s/%s", (int) (s - hm->uri.p), hm->uri.p, mg_addenv(blk, "SCRIPT_NAME=%.*s/%s", (int) (s - hm->uri.p), hm->uri.p,
(base_name != NULL ? base_name + 1 : prog)); (base_name != NULL ? base_name + 1 : prog));
} else { } else {
addenv(blk, "SCRIPT_NAME=%.*s", (int) (s - hm->uri.p + 1), hm->uri.p); mg_addenv(blk, "SCRIPT_NAME=%.*s", (int) (s - hm->uri.p + 1), hm->uri.p);
} }
addenv(blk, "SCRIPT_FILENAME=%s", prog); mg_addenv(blk, "SCRIPT_FILENAME=%s", prog);
if (path_info != NULL && path_info->len > 0) { if (path_info != NULL && path_info->len > 0) {
addenv(blk, "PATH_INFO=%.*s", (int) path_info->len, path_info->p); mg_addenv(blk, "PATH_INFO=%.*s", (int) path_info->len, path_info->p);
/* Not really translated... */ /* Not really translated... */
addenv(blk, "PATH_TRANSLATED=%.*s", (int) path_info->len, path_info->p); mg_addenv(blk, "PATH_TRANSLATED=%.*s", (int) path_info->len, path_info->p);
} }
addenv(blk, "HTTPS=%s", nc->ssl != NULL ? "on" : "off"); mg_addenv(blk, "HTTPS=%s", nc->ssl != NULL ? "on" : "off");
if ((h = mg_get_http_header((struct http_message *) hm, "Content-Type")) != if ((h = mg_get_http_header((struct http_message *) hm, "Content-Type")) !=
NULL) { NULL) {
addenv(blk, "CONTENT_TYPE=%.*s", (int) h->len, h->p); mg_addenv(blk, "CONTENT_TYPE=%.*s", (int) h->len, h->p);
} }
if (hm->query_string.len > 0) { if (hm->query_string.len > 0) {
addenv(blk, "QUERY_STRING=%.*s", (int) hm->query_string.len, mg_addenv(blk, "QUERY_STRING=%.*s", (int) hm->query_string.len,
hm->query_string.p); hm->query_string.p);
} }
if ((h = mg_get_http_header((struct http_message *) hm, "Content-Length")) != if ((h = mg_get_http_header((struct http_message *) hm, "Content-Length")) !=
NULL) { NULL) {
addenv(blk, "CONTENT_LENGTH=%.*s", (int) h->len, h->p); mg_addenv(blk, "CONTENT_LENGTH=%.*s", (int) h->len, h->p);
} }
addenv2(blk, "PATH"); mg_addenv2(blk, "PATH");
addenv2(blk, "TMP"); mg_addenv2(blk, "TMP");
addenv2(blk, "TEMP"); mg_addenv2(blk, "TEMP");
addenv2(blk, "TMPDIR"); mg_addenv2(blk, "TMPDIR");
addenv2(blk, "PERLLIB"); mg_addenv2(blk, "PERLLIB");
addenv2(blk, MG_ENV_EXPORT_TO_CGI); mg_addenv2(blk, MG_ENV_EXPORT_TO_CGI);
#if defined(_WIN32) #if defined(_WIN32)
addenv2(blk, "COMSPEC"); mg_addenv2(blk, "COMSPEC");
addenv2(blk, "SYSTEMROOT"); mg_addenv2(blk, "SYSTEMROOT");
addenv2(blk, "SystemDrive"); mg_addenv2(blk, "SystemDrive");
addenv2(blk, "ProgramFiles"); mg_addenv2(blk, "ProgramFiles");
addenv2(blk, "ProgramFiles(x86)"); mg_addenv2(blk, "ProgramFiles(x86)");
addenv2(blk, "CommonProgramFiles(x86)"); mg_addenv2(blk, "CommonProgramFiles(x86)");
#else #else
addenv2(blk, "LD_LIBRARY_PATH"); mg_addenv2(blk, "LD_LIBRARY_PATH");
#endif /* _WIN32 */ #endif /* _WIN32 */
/* Add all headers as HTTP_* variables */ /* Add all headers as HTTP_* variables */
for (i = 0; hm->header_names[i].len > 0; i++) { for (i = 0; hm->header_names[i].len > 0; i++) {
p = addenv(blk, "HTTP_%.*s=%.*s", (int) hm->header_names[i].len, p = mg_addenv(blk, "HTTP_%.*s=%.*s", (int) hm->header_names[i].len,
hm->header_names[i].p, (int) hm->header_values[i].len, hm->header_names[i].p, (int) hm->header_values[i].len,
hm->header_values[i].p); hm->header_values[i].p);
...@@ -6958,7 +7005,7 @@ static void prepare_cgi_environment(struct mg_connection *nc, const char *prog, ...@@ -6958,7 +7005,7 @@ static void prepare_cgi_environment(struct mg_connection *nc, const char *prog,
blk->buf[blk->len++] = '\0'; blk->buf[blk->len++] = '\0';
} }
static void cgi_ev_handler(struct mg_connection *cgi_nc, int ev, static void mg_cgi_ev_handler(struct mg_connection *cgi_nc, int ev,
void *ev_data) { void *ev_data) {
struct mg_connection *nc = (struct mg_connection *) cgi_nc->user_data; struct mg_connection *nc = (struct mg_connection *) cgi_nc->user_data;
(void) ev_data; (void) ev_data;
...@@ -6982,16 +7029,16 @@ static void cgi_ev_handler(struct mg_connection *cgi_nc, int ev, ...@@ -6982,16 +7029,16 @@ static void cgi_ev_handler(struct mg_connection *cgi_nc, int ev,
*/ */
if (nc->flags & MG_F_USER_1) { if (nc->flags & MG_F_USER_1) {
struct mbuf *io = &cgi_nc->recv_mbuf; struct mbuf *io = &cgi_nc->recv_mbuf;
int len = get_request_len(io->buf, io->len); int len = mg_http_get_request_len(io->buf, io->len);
if (len == 0) break; if (len == 0) break;
if (len < 0 || io->len > MG_MAX_HTTP_REQUEST_SIZE) { if (len < 0 || io->len > MG_MAX_HTTP_REQUEST_SIZE) {
cgi_nc->flags |= MG_F_CLOSE_IMMEDIATELY; cgi_nc->flags |= MG_F_CLOSE_IMMEDIATELY;
send_http_error(nc, 500, "Bad headers"); mg_http_send_http_error(nc, 500, "Bad headers");
} else { } else {
struct http_message hm; struct http_message hm;
struct mg_str *h; struct mg_str *h;
parse_http_headers(io->buf, io->buf + io->len, io->len, &hm); mg_http_parse_headers(io->buf, io->buf + io->len, io->len, &hm);
if (mg_get_http_header(&hm, "Location") != NULL) { if (mg_get_http_header(&hm, "Location") != NULL) {
mg_printf(nc, "%s", "HTTP/1.1 302 Moved\r\n"); mg_printf(nc, "%s", "HTTP/1.1 302 Moved\r\n");
} else if ((h = mg_get_http_header(&hm, "Status")) != NULL) { } else if ((h = mg_get_http_header(&hm, "Status")) != NULL) {
...@@ -7007,25 +7054,23 @@ static void cgi_ev_handler(struct mg_connection *cgi_nc, int ev, ...@@ -7007,25 +7054,23 @@ static void cgi_ev_handler(struct mg_connection *cgi_nc, int ev,
} }
break; break;
case MG_EV_CLOSE: case MG_EV_CLOSE:
free_http_proto_data(cgi_nc); mg_http_free_proto_data_cgi(&mg_http_get_proto_data(cgi_nc)->cgi);
nc->flags |= MG_F_SEND_AND_CLOSE; nc->flags |= MG_F_SEND_AND_CLOSE;
nc->user_data = NULL;
break; break;
} }
} }
static void handle_cgi(struct mg_connection *nc, const char *prog, static void mg_handle_cgi(struct mg_connection *nc, const char *prog,
const struct mg_str *path_info, const struct mg_str *path_info,
const struct http_message *hm, const struct http_message *hm,
const struct mg_serve_http_opts *opts) { const struct mg_serve_http_opts *opts) {
struct proto_data_http *dp; struct mg_cgi_env_block blk;
struct cgi_env_block blk;
char dir[MAX_PATH_SIZE]; char dir[MAX_PATH_SIZE];
const char *p; const char *p;
sock_t fds[2]; sock_t fds[2];
DBG(("%p [%s]", nc, prog)); DBG(("%p [%s]", nc, prog));
prepare_cgi_environment(nc, prog, path_info, hm, opts, &blk); mg_prepare_cgi_environment(nc, prog, path_info, hm, opts, &blk);
/* /*
* CGI must be executed in its own directory. 'dir' must point to the * CGI must be executed in its own directory. 'dir' must point to the
* directory containing executable program, 'p' must point to the * directory containing executable program, 'p' must point to the
...@@ -7047,25 +7092,23 @@ static void handle_cgi(struct mg_connection *nc, const char *prog, ...@@ -7047,25 +7092,23 @@ static void handle_cgi(struct mg_connection *nc, const char *prog,
mg_socketpair(fds, SOCK_STREAM); mg_socketpair(fds, SOCK_STREAM);
} while (fds[0] == INVALID_SOCKET); } while (fds[0] == INVALID_SOCKET);
free_http_proto_data(nc); if (mg_start_process(opts->cgi_interpreter, prog, blk.buf, blk.vars, dir,
if ((dp = (struct proto_data_http *) MG_CALLOC(1, sizeof(*dp))) == NULL) {
send_http_error(nc, 500, "OOM"); /* LCOV_EXCL_LINE */
} else if (start_process(opts->cgi_interpreter, prog, blk.buf, blk.vars, dir,
fds[1]) != 0) { fds[1]) != 0) {
size_t n = nc->recv_mbuf.len - (hm->message.len - hm->body.len); size_t n = nc->recv_mbuf.len - (hm->message.len - hm->body.len);
dp->type = DATA_CGI; struct mg_connection *cgi_nc =
dp->cgi_nc = mg_add_sock(nc->mgr, fds[0], cgi_ev_handler); mg_add_sock(nc->mgr, fds[0], mg_cgi_ev_handler);
dp->cgi_nc->user_data = nc; struct mg_http_proto_data *cgi_pd = mg_http_get_proto_data(cgi_nc);
dp->cgi_nc->proto_data = dp; cgi_pd->cgi.cgi_nc = cgi_nc;
cgi_pd->cgi.cgi_nc->user_data = nc;
nc->flags |= MG_F_USER_1; nc->flags |= MG_F_USER_1;
/* Push POST data to the CGI */ /* Push POST data to the CGI */
if (n > 0 && n < nc->recv_mbuf.len) { if (n > 0 && n < nc->recv_mbuf.len) {
mg_send(dp->cgi_nc, hm->body.p, n); mg_send(cgi_pd->cgi.cgi_nc, hm->body.p, n);
} }
mbuf_remove(&nc->recv_mbuf, nc->recv_mbuf.len); mbuf_remove(&nc->recv_mbuf, nc->recv_mbuf.len);
} else { } else {
closesocket(fds[0]); closesocket(fds[0]);
send_http_error(nc, 500, "CGI failure"); mg_http_send_http_error(nc, 500, "CGI failure");
} }
#ifndef _WIN32 #ifndef _WIN32
...@@ -7119,7 +7162,7 @@ MG_INTERNAL int mg_is_not_modified(struct http_message *hm, cs_stat_t *st) { ...@@ -7119,7 +7162,7 @@ MG_INTERNAL int mg_is_not_modified(struct http_message *hm, cs_stat_t *st) {
struct mg_str *hdr; struct mg_str *hdr;
if ((hdr = mg_get_http_header(hm, "If-None-Match")) != NULL) { if ((hdr = mg_get_http_header(hm, "If-None-Match")) != NULL) {
char etag[64]; char etag[64];
construct_etag(etag, sizeof(etag), st); mg_http_construct_etag(etag, sizeof(etag), st);
return mg_vcasecmp(hdr, etag) == 0; return mg_vcasecmp(hdr, etag) == 0;
} else if ((hdr = mg_get_http_header(hm, "If-Modified-Since")) != NULL) { } else if ((hdr = mg_get_http_header(hm, "If-Modified-Since")) != NULL) {
return st->st_mtime <= mg_parse_date_string(hdr->p); return st->st_mtime <= mg_parse_date_string(hdr->p);
...@@ -7128,7 +7171,7 @@ MG_INTERNAL int mg_is_not_modified(struct http_message *hm, cs_stat_t *st) { ...@@ -7128,7 +7171,7 @@ MG_INTERNAL int mg_is_not_modified(struct http_message *hm, cs_stat_t *st) {
} }
} }
static void mg_send_digest_auth_request(struct mg_connection *c, static void mg_http_send_digest_auth_request(struct mg_connection *c,
const char *domain) { const char *domain) {
mg_printf(c, mg_printf(c,
"HTTP/1.1 401 Unauthorized\r\n" "HTTP/1.1 401 Unauthorized\r\n"
...@@ -7138,7 +7181,7 @@ static void mg_send_digest_auth_request(struct mg_connection *c, ...@@ -7138,7 +7181,7 @@ static void mg_send_digest_auth_request(struct mg_connection *c,
domain, (unsigned long) time(NULL)); domain, (unsigned long) time(NULL));
} }
static void send_options(struct mg_connection *nc) { static void mg_http_send_options(struct mg_connection *nc) {
mg_printf(nc, "%s", mg_printf(nc, "%s",
"HTTP/1.1 200 OK\r\nAllow: GET, POST, HEAD, CONNECT, OPTIONS" "HTTP/1.1 200 OK\r\nAllow: GET, POST, HEAD, CONNECT, OPTIONS"
#ifndef MG_DISABLE_DAV #ifndef MG_DISABLE_DAV
...@@ -7148,7 +7191,7 @@ static void send_options(struct mg_connection *nc) { ...@@ -7148,7 +7191,7 @@ static void send_options(struct mg_connection *nc) {
nc->flags |= MG_F_SEND_AND_CLOSE; nc->flags |= MG_F_SEND_AND_CLOSE;
} }
static int is_creation_request(const struct http_message *hm) { static int mg_is_creation_request(const struct http_message *hm) {
return mg_vcmp(&hm->method, "MKCOL") == 0 || mg_vcmp(&hm->method, "PUT") == 0; return mg_vcmp(&hm->method, "MKCOL") == 0 || mg_vcmp(&hm->method, "PUT") == 0;
} }
...@@ -7156,7 +7199,7 @@ MG_INTERNAL void mg_send_http_file(struct mg_connection *nc, char *path, ...@@ -7156,7 +7199,7 @@ MG_INTERNAL void mg_send_http_file(struct mg_connection *nc, char *path,
const struct mg_str *path_info, const struct mg_str *path_info,
struct http_message *hm, struct http_message *hm,
struct mg_serve_http_opts *opts) { struct mg_serve_http_opts *opts) {
int exists, is_directory, is_dav = is_dav_request(&hm->method); int exists, is_directory, is_dav = mg_is_dav_request(&hm->method);
int is_cgi; int is_cgi;
char *index_file = NULL; char *index_file = NULL;
cs_stat_t st; cs_stat_t st;
...@@ -7164,7 +7207,8 @@ MG_INTERNAL void mg_send_http_file(struct mg_connection *nc, char *path, ...@@ -7164,7 +7207,8 @@ MG_INTERNAL void mg_send_http_file(struct mg_connection *nc, char *path,
exists = (mg_stat(path, &st) == 0); exists = (mg_stat(path, &st) == 0);
is_directory = exists && S_ISDIR(st.st_mode); is_directory = exists && S_ISDIR(st.st_mode);
if (is_directory) find_index_file(path, opts->index_files, &index_file, &st); if (is_directory)
mg_find_index_file(path, opts->index_files, &index_file, &st);
is_cgi = is_cgi =
(mg_match_prefix(opts->cgi_file_pattern, strlen(opts->cgi_file_pattern), (mg_match_prefix(opts->cgi_file_pattern, strlen(opts->cgi_file_pattern),
...@@ -7185,68 +7229,69 @@ MG_INTERNAL void mg_send_http_file(struct mg_connection *nc, char *path, ...@@ -7185,68 +7229,69 @@ MG_INTERNAL void mg_send_http_file(struct mg_connection *nc, char *path,
/* If we have path_info, the only way to handle it is CGI. */ /* If we have path_info, the only way to handle it is CGI. */
if (path_info->len > 0 && !is_cgi) { if (path_info->len > 0 && !is_cgi) {
send_http_error(nc, 501, NULL); mg_http_send_http_error(nc, 501, NULL);
return; return;
} else if (is_cgi) { } else if (is_cgi) {
#if !defined(MG_DISABLE_CGI) #if !defined(MG_DISABLE_CGI)
handle_cgi(nc, index_file ? index_file : path, path_info, hm, opts); mg_handle_cgi(nc, index_file ? index_file : path, path_info, hm, opts);
#else #else
send_http_error(nc, 501, NULL); mg_http_send_http_error(nc, 501, NULL);
#endif /* MG_DISABLE_CGI */ #endif /* MG_DISABLE_CGI */
MG_FREE(index_file); MG_FREE(index_file);
return; return;
} }
if (is_dav && opts->dav_document_root == NULL) { if (is_dav && opts->dav_document_root == NULL) {
send_http_error(nc, 501, NULL); mg_http_send_http_error(nc, 501, NULL);
} else if (!is_authorized(hm, path, is_directory, opts->auth_domain, } else if (!mg_is_authorized(hm, path, is_directory, opts->auth_domain,
opts->global_auth_file, 1) || opts->global_auth_file, 1) ||
!is_authorized(hm, path, is_directory, opts->auth_domain, !mg_is_authorized(hm, path, is_directory, opts->auth_domain,
opts->per_directory_auth_file, 0)) { opts->per_directory_auth_file, 0)) {
mg_send_digest_auth_request(nc, opts->auth_domain); mg_http_send_digest_auth_request(nc, opts->auth_domain);
} else if ((!exists || is_file_hidden(path, opts, 0 /* specials are ok */)) && } else if ((!exists ||
!is_creation_request(hm)) { mg_is_file_hidden(path, opts, 0 /* specials are ok */)) &&
send_http_error(nc, 404, NULL); !mg_is_creation_request(hm)) {
mg_http_send_http_error(nc, 404, NULL);
#ifndef MG_DISABLE_DAV #ifndef MG_DISABLE_DAV
} else if (!mg_vcmp(&hm->method, "PROPFIND")) { } else if (!mg_vcmp(&hm->method, "PROPFIND")) {
handle_propfind(nc, path, &st, hm, opts); mg_handle_propfind(nc, path, &st, hm, opts);
#ifndef MG_DISABLE_DAV_AUTH #ifndef MG_DISABLE_DAV_AUTH
} else if (is_dav && } else if (is_dav &&
(opts->dav_auth_file == NULL || (opts->dav_auth_file == NULL ||
(strcmp(opts->dav_auth_file, "-") != 0 && (strcmp(opts->dav_auth_file, "-") != 0 &&
!is_authorized(hm, path, is_directory, opts->auth_domain, !mg_is_authorized(hm, path, is_directory, opts->auth_domain,
opts->dav_auth_file, 1)))) { opts->dav_auth_file, 1)))) {
mg_send_digest_auth_request(nc, opts->auth_domain); mg_http_send_digest_auth_request(nc, opts->auth_domain);
#endif #endif
} else if (!mg_vcmp(&hm->method, "MKCOL")) { } else if (!mg_vcmp(&hm->method, "MKCOL")) {
handle_mkcol(nc, path, hm); mg_handle_mkcol(nc, path, hm);
} else if (!mg_vcmp(&hm->method, "DELETE")) { } else if (!mg_vcmp(&hm->method, "DELETE")) {
handle_delete(nc, opts, path); mg_handle_delete(nc, opts, path);
} else if (!mg_vcmp(&hm->method, "PUT")) { } else if (!mg_vcmp(&hm->method, "PUT")) {
handle_put(nc, path, hm); mg_handle_put(nc, path, hm);
} else if (!mg_vcmp(&hm->method, "MOVE")) { } else if (!mg_vcmp(&hm->method, "MOVE")) {
handle_move(nc, opts, path, hm); mg_handle_move(nc, opts, path, hm);
#ifdef MG_ENABLE_FAKE_DAVLOCK #ifdef MG_ENABLE_FAKE_DAVLOCK
} else if (!mg_vcmp(&hm->method, "LOCK")) { } else if (!mg_vcmp(&hm->method, "LOCK")) {
handle_lock(nc, path); mg_handle_lock(nc, path);
#endif #endif
#endif #endif
} else if (!mg_vcmp(&hm->method, "OPTIONS")) { } else if (!mg_vcmp(&hm->method, "OPTIONS")) {
send_options(nc); mg_http_send_options(nc);
} else if (is_directory && index_file == NULL) { } else if (is_directory && index_file == NULL) {
#ifndef MG_DISABLE_DIRECTORY_LISTING #ifndef MG_DISABLE_DIRECTORY_LISTING
if (strcmp(opts->enable_directory_listing, "yes") == 0) { if (strcmp(opts->enable_directory_listing, "yes") == 0) {
send_directory_listing(nc, path, hm, opts); mg_send_directory_listing(nc, path, hm, opts);
} else { } else {
send_http_error(nc, 403, NULL); mg_http_send_http_error(nc, 403, NULL);
} }
#else #else
send_http_error(nc, 501, NULL); mg_http_send_http_error(nc, 501, NULL);
#endif #endif
} else if (mg_is_not_modified(hm, &st)) { } else if (mg_is_not_modified(hm, &st)) {
send_http_error(nc, 304, "Not Modified"); mg_http_send_http_error(nc, 304, "Not Modified");
} else { } else {
mg_send_http_file2(nc, index_file ? index_file : path, &st, hm, opts); mg_http_send_file2(nc, index_file ? index_file : path, &st, hm, opts);
} }
MG_FREE(index_file); MG_FREE(index_file);
} }
...@@ -7259,12 +7304,12 @@ void mg_serve_http(struct mg_connection *nc, struct http_message *hm, ...@@ -7259,12 +7304,12 @@ void mg_serve_http(struct mg_connection *nc, struct http_message *hm,
if (mg_check_ip_acl(opts.ip_acl, remote_ip) != 1) { if (mg_check_ip_acl(opts.ip_acl, remote_ip) != 1) {
/* Not allowed to connect */ /* Not allowed to connect */
send_http_error(nc, 403, NULL); mg_http_send_http_error(nc, 403, NULL);
nc->flags |= MG_F_SEND_AND_CLOSE; nc->flags |= MG_F_SEND_AND_CLOSE;
return; return;
} }
if (send_port_based_redirect(nc, hm, &opts)) { if (mg_http_send_port_based_redirect(nc, hm, &opts)) {
return; return;
} }
...@@ -7288,11 +7333,11 @@ void mg_serve_http(struct mg_connection *nc, struct http_message *hm, ...@@ -7288,11 +7333,11 @@ void mg_serve_http(struct mg_connection *nc, struct http_message *hm,
} }
/* Normalize path - resolve "." and ".." (in-place). */ /* Normalize path - resolve "." and ".." (in-place). */
if (!mg_normalize_uri_path(&hm->uri, &hm->uri)) { if (!mg_normalize_uri_path(&hm->uri, &hm->uri)) {
send_http_error(nc, 400, NULL); mg_http_send_http_error(nc, 400, NULL);
return; return;
} }
if (mg_uri_to_local_path(hm, &opts, &path, &path_info) == 0) { if (mg_uri_to_local_path(hm, &opts, &path, &path_info) == 0) {
send_http_error(nc, 404, NULL); mg_http_send_http_error(nc, 404, NULL);
return; return;
} }
mg_send_http_file(nc, path, &path_info, hm, &opts); mg_send_http_file(nc, path, &path_info, hm, &opts);
...@@ -7382,7 +7427,7 @@ cleanup: ...@@ -7382,7 +7427,7 @@ cleanup:
return nc; return nc;
} }
static size_t get_line_len(const char *buf, size_t buf_len) { static size_t mg_get_line_len(const char *buf, size_t buf_len) {
size_t len = 0; size_t len = 0;
while (len < buf_len && buf[len] != '\n') len++; while (len < buf_len && buf[len] != '\n') len++;
return buf[len] == '\n' ? len + 1 : 0; return buf[len] == '\n' ? len + 1 : 0;
...@@ -7396,15 +7441,15 @@ size_t mg_parse_multipart(const char *buf, size_t buf_len, char *var_name, ...@@ -7396,15 +7441,15 @@ size_t mg_parse_multipart(const char *buf, size_t buf_len, char *var_name,
size_t hl, bl, n, ll, pos, cdl = sizeof(cd) - 1; size_t hl, bl, n, ll, pos, cdl = sizeof(cd) - 1;
if (buf == NULL || buf_len <= 0) return 0; if (buf == NULL || buf_len <= 0) return 0;
if ((hl = get_request_len(buf, buf_len)) <= 0) return 0; if ((hl = mg_http_get_request_len(buf, buf_len)) <= 0) return 0;
if (buf[0] != '-' || buf[1] != '-' || buf[2] == '\n') return 0; if (buf[0] != '-' || buf[1] != '-' || buf[2] == '\n') return 0;
/* Get boundary length */ /* Get boundary length */
bl = get_line_len(buf, buf_len); bl = mg_get_line_len(buf, buf_len);
/* Loop through headers, fetch variable name and file name */ /* Loop through headers, fetch variable name and file name */
var_name[0] = file_name[0] = '\0'; var_name[0] = file_name[0] = '\0';
for (n = bl; (ll = get_line_len(buf + n, hl - n)) > 0; n += ll) { for (n = bl; (ll = mg_get_line_len(buf + n, hl - n)) > 0; n += ll) {
if (mg_ncasecmp(cd, buf + n, cdl) == 0) { if (mg_ncasecmp(cd, buf + n, cdl) == 0) {
struct mg_str header; struct mg_str header;
header.p = buf + n + cdl; header.p = buf + n + cdl;
......
...@@ -1075,6 +1075,7 @@ struct mg_connection { ...@@ -1075,6 +1075,7 @@ struct mg_connection {
double ev_timer_time; /* Timestamp of the future MG_EV_TIMER */ double ev_timer_time; /* Timestamp of the future MG_EV_TIMER */
mg_event_handler_t proto_handler; /* Protocol-specific event handler */ mg_event_handler_t proto_handler; /* Protocol-specific event handler */
void *proto_data; /* Protocol-specific data */ void *proto_data; /* Protocol-specific data */
void (*proto_data_destructor)(void *proto_data);
mg_event_handler_t handler; /* Event handler function */ mg_event_handler_t handler; /* Event handler function */
void *user_data; /* User-specific data */ void *user_data; /* User-specific data */
union { union {
...@@ -1088,9 +1089,6 @@ struct mg_connection { ...@@ -1088,9 +1089,6 @@ struct mg_connection {
void *priv_2; /* Used by mg_enable_multithreading() */ void *priv_2; /* Used by mg_enable_multithreading() */
struct mbuf endpoints; /* Used by mg_register_http_endpoint */ struct mbuf endpoints; /* Used by mg_register_http_endpoint */
void *mgr_data; /* Implementation-specific event manager's data. */ void *mgr_data; /* Implementation-specific event manager's data. */
#ifdef MG_ENABLE_HTTP_STREAMING_MULTIPART
struct mbuf strm_state; /* Used by multi-part streaming */
#endif
unsigned long flags; unsigned long flags;
/* Flags set by Mongoose */ /* Flags set by Mongoose */
#define MG_F_LISTENING (1 << 0) /* This connection is listening */ #define MG_F_LISTENING (1 << 0) /* This connection is listening */
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment