Newer
Older
send_http_error(conn, 403, NULL);
} else if (match_prefix(lua_pat, sizeof(lua_pat) - 1, path) > 0) {
send_http_error(conn, 501, NULL);
} else if (match_prefix(cgi_pat, strlen(cgi_pat), path) > 0) {
#if !defined(MONGOOSE_NO_CGI)
#endif // !MONGOOSE_NO_CGI
} else if (is_not_modified(conn, &st)) {
send_http_error(conn, 304, NULL);
} else if ((conn->endpoint.fd = open(path, O_RDONLY | O_BINARY)) != -1) {
// O_BINARY is required for Windows, otherwise in default text mode
// two bytes \r\n will be read as one.
open_file_endpoint(conn, path, &st);
} else {
send_http_error(conn, 404, NULL);
#endif // MONGOOSE_NO_FILESYSTEM
static void send_continue_if_expected(struct connection *conn) {
static const char expect_response[] = "HTTP/1.1 100 Continue\r\n\r\n";
const char *expect_hdr = mg_get_header(&conn->mg_conn, "Expect");
if (expect_hdr != NULL && !mg_strcasecmp(expect_hdr, "100-continue")) {
ns_send(conn->ns_conn, expect_response, sizeof(expect_response) - 1);
static int is_valid_uri(const char *uri) {
// Conform to http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.2
// URI can be an asterisk (*) or should start with slash.
return uri[0] == '/' || (uri[0] == '*' && uri[1] == '\0');
}
static void try_parse(struct connection *conn) {
struct iobuf *io = &conn->ns_conn->recv_iobuf;
if (conn->request_len == 0 &&
(conn->request_len = get_request_len(io->buf, io->len)) > 0) {
// If request is buffered in, remove it from the iobuf. This is because
// iobuf could be reallocated, and pointers in parsed request could
// become invalid.
conn->request = (char *) malloc(conn->request_len);
memcpy(conn->request, io->buf, conn->request_len);
DBG(("%p [%.*s]", conn, conn->request_len, conn->request));
conn->request_len = parse_http_message(conn->request, conn->request_len,
&conn->mg_conn);
if (conn->request_len > 0) {
const char *cl_hdr = mg_get_header(&conn->mg_conn, "Content-Length");
conn->cl = cl_hdr == NULL ? 0 : to64(cl_hdr);
conn->mg_conn.content_len = (long int) conn->cl;
}
static void process_request(struct connection *conn) {
try_parse(conn);
DBG(("%p %d %d %d [%.*s]", conn, conn->request_len, io->len,
conn->ns_conn->flags, io->len, io->buf));
if (conn->request_len < 0 ||
(conn->request_len > 0 && !is_valid_uri(conn->mg_conn.uri))) {
send_http_error(conn, 400, NULL);
} else if (conn->request_len == 0 && io->len > MAX_REQUEST_SIZE) {
send_http_error(conn, 413, NULL);
} else if (conn->request_len > 0 &&
strcmp(conn->mg_conn.http_version, "1.0") != 0 &&
strcmp(conn->mg_conn.http_version, "1.1") != 0) {
send_http_error(conn, 505, NULL);
} else if (conn->request_len > 0 && conn->endpoint_type == EP_NONE) {
#ifndef MONGOOSE_NO_WEBSOCKET
send_websocket_handshake_if_requested(&conn->mg_conn);
#endif
send_continue_if_expected(conn);
open_local_endpoint(conn, 0);
if (conn->endpoint_type == EP_CGI && io->len > 0) {
forward_post_data(conn);
}
#endif
if (conn->endpoint_type == EP_USER) {
call_request_handler_if_data_is_buffered(conn);
if (conn->endpoint_type == EP_PUT && io->len > 0) {
forward_put_data(conn);
static void call_http_client_handler(struct connection *conn, int code) {
conn->mg_conn.status_code = code;
// For responses without Content-Lengh, use the whole buffer
if (conn->cl == 0 && code == MG_DOWNLOAD_SUCCESS) {
conn->mg_conn.content_len = conn->ns_conn->recv_iobuf.len;
conn->mg_conn.content = conn->ns_conn->recv_iobuf.buf;
if (conn->handler(&conn->mg_conn) || code == MG_CONNECT_FAILURE ||
code == MG_DOWNLOAD_FAILURE) {
iobuf_remove(&conn->ns_conn->recv_iobuf, conn->mg_conn.content_len);
conn->mg_conn.status_code = 0;
conn->cl = conn->num_bytes_sent = conn->request_len = 0;
free(conn->request);
conn->request = NULL;
}
static void process_response(struct connection *conn) {
DBG(("%p %d %d [%.*s]", conn, conn->request_len, io->len,
io->len > 40 ? 40 : io->len, io->buf));
if (conn->request_len < 0 ||
(conn->request_len == 0 && io->len > MAX_REQUEST_SIZE)) {
call_http_client_handler(conn, MG_DOWNLOAD_FAILURE);
}
if (io->len >= conn->cl) {
call_http_client_handler(conn, MG_DOWNLOAD_SUCCESS);
}
}
int mg_connect(struct mg_server *server, const char *host, int port,
int use_ssl, mg_handler_t handler, void *param) {
struct ns_connection *nsconn;
struct connection *conn;
nsconn = ns_connect(&server->ns_server, host, port, use_ssl, param);
if (nsconn == NULL) return 0;
if ((conn = (struct connection *) calloc(1, sizeof(*conn))) == NULL) {
nsconn->flags |= NSF_CLOSE_IMMEDIATELY;
// Interlink two structs
conn->ns_conn = nsconn;
nsconn->connection_data = conn;
conn->endpoint_type = EP_CLIENT;
conn->handler = handler;
conn->mg_conn.server_param = server->ns_server.server_data;
conn->birth_time = time(NULL);
conn->ns_conn->flags = NSF_CONNECTING;
conn->mg_conn.status_code = MG_CONNECT_FAILURE;
#ifndef MONGOOSE_NO_LOGGING
static void log_header(const struct mg_connection *conn, const char *header,
FILE *fp) {
const char *header_value;
if ((header_value = mg_get_header(conn, header)) == NULL) {
(void) fprintf(fp, "%s", " -");
} else {
(void) fprintf(fp, " \"%s\"", header_value);
static void log_access(const struct connection *conn, const char *path) {
const struct mg_connection *c = &conn->mg_conn;
FILE *fp = (path == NULL) ? NULL : fopen(path, "a+");
if (fp == NULL) return;
strftime(date, sizeof(date), "%d/%b/%Y:%H:%M:%S %z",
localtime(&conn->birth_time));
flockfile(fp);
mg_parse_header(mg_get_header(&conn->mg_conn, "Authorization"), "username",
user, sizeof(user));
fprintf(fp, "%s - %s [%s] \"%s %s HTTP/%s\" %d %" INT64_FMT,
c->remote_ip, user[0] == '\0' ? "-" : user, date,
c->request_method ? c->request_method : "-",
c->uri ? c->uri : "-", c->http_version,
c->status_code, conn->num_bytes_sent);
log_header(c, "Referer", fp);
log_header(c, "User-Agent", fp);
fputc('\n', fp);
fflush(fp);
static void close_local_endpoint(struct connection *conn) {
struct mg_connection *c = &conn->mg_conn;
// Must be done before free()
int keep_alive = should_keep_alive(&conn->mg_conn) &&
(conn->endpoint_type == EP_FILE || conn->endpoint_type == EP_USER);
DBG(("%p %d %d %d", conn, conn->endpoint_type, keep_alive,
conn->ns_conn->flags));
case EP_PUT:
case EP_FILE:
close(conn->endpoint.fd);
break;
case EP_CGI:
if (conn->endpoint.cgi_conn != NULL) {
conn->endpoint.cgi_conn->flags |= NSF_CLOSE_IMMEDIATELY;
conn->endpoint.cgi_conn->connection_data = NULL;
}
break;
#ifndef MONGOOSE_NO_LOGGING
if (c->status_code > 0 && conn->endpoint_type != EP_CLIENT &&
c->status_code != 400) {
log_access(conn, conn->server->config_options[ACCESS_LOG_FILE]);
}
#endif
// Gobble possible POST data sent to the URI handler
iobuf_remove(&conn->ns_conn->recv_iobuf, conn->mg_conn.content_len);
conn->cl = conn->num_bytes_sent = conn->request_len = 0;
conn->ns_conn->flags = conn->ns_conn->flags & NSF_ACCEPTED ? NSF_ACCEPTED : 0;
c->request_method = c->uri = c->http_version = c->query_string = NULL;
c->num_headers = c->status_code = c->is_websocket = c->content_len = 0;
free(conn->request); conn->request = NULL;
free(conn->path_info); conn->path_info = NULL;
if (keep_alive) {
process_request(conn); // Can call us recursively if pipelining is used
conn->ns_conn->flags |= conn->ns_conn->send_iobuf.len == 0 ?
NSF_CLOSE_IMMEDIATELY : NSF_FINISHED_SENDING_DATA;
static void transfer_file_data(struct connection *conn) {
char buf[IOBUF_SIZE];
int n = read(conn->endpoint.fd, buf, conn->cl < (int64_t) sizeof(buf) ?
(int) conn->cl : (int) sizeof(buf));
close_local_endpoint(conn);
} else if (n > 0) {
conn->cl -= n;
if (conn->cl <= 0) {
close_local_endpoint(conn);
int mg_poll_server(struct mg_server *server, int milliseconds) {
return ns_server_poll(&server->ns_server, milliseconds);
void mg_destroy_server(struct mg_server **server) {
if (server != NULL && *server != NULL) {
for (i = 0; i < (int) ARRAY_SIZE(s->config_options); i++) {
free(s->config_options[i]); // It is OK to free(NULL)
struct mg_iterator {
mg_handler_t cb;
void *param;
};
static void iter(struct ns_connection *nsconn, enum ns_event ev, void *param) {
if (ev == NS_POLL) {
struct mg_iterator *it = (struct mg_iterator *) param;
struct connection *c = (struct connection *) nsconn->connection_data;
c->mg_conn.callback_param = it->param;
it->cb(&c->mg_conn);
}
}
Sergey Lyubka
committed
// Apply function to all active connections.
void mg_iterate_over_connections(struct mg_server *server, mg_handler_t handler,
Sergey Lyubka
committed
void *param) {
struct mg_iterator it = { handler, param };
ns_iterate(&server->ns_server, iter, &it);
static int get_var(const char *data, size_t data_len, const char *name,
char *dst, size_t dst_len) {
const char *p, *e, *s;
size_t name_len;
int len;
if (dst == NULL || dst_len == 0) {
len = -2;
} else if (data == NULL || name == NULL || data_len == 0) {
len = -1;
dst[0] = '\0';
} else {
name_len = strlen(name);
e = data + data_len;
len = -1;
dst[0] = '\0';
// data is "var1=val1&var2=val2...". Find variable first
for (p = data; p + name_len < e; p++) {
if ((p == data || p[-1] == '&') && p[name_len] == '=' &&
!mg_strncasecmp(name, p, name_len)) {
// Point p to variable value
p += name_len + 1;
// Point s to the end of the value
s = (const char *) memchr(p, '&', (size_t)(e - p));
if (s == NULL) {
s = e;
}
assert(s >= p);
// Decode variable into destination buffer
len = mg_url_decode(p, (size_t)(s - p), dst, dst_len, 1);
// Redirect error code from -1 to -2 (destination buffer too small).
if (len == -1) {
len = -2;
}
break;
}
}
}
int mg_get_var(const struct mg_connection *conn, const char *name,
char *dst, size_t dst_len) {
int len = get_var(conn->query_string, conn->query_string == NULL ? 0 :
strlen(conn->query_string), name, dst, dst_len);
if (len < 0) {
len = get_var(conn->content, conn->content_len, name, dst, dst_len);
4362
4363
4364
4365
4366
4367
4368
4369
4370
4371
4372
4373
4374
4375
4376
4377
4378
4379
4380
4381
4382
4383
4384
4385
4386
4387
4388
4389
4390
4391
4392
4393
4394
4395
4396
4397
4398
4399
4400
4401
4402
4403
4404
4405
4406
static int get_line_len(const char *buf, int buf_len) {
int len = 0;
while (len < buf_len && buf[len] != '\n') len++;
return buf[len] == '\n' ? len + 1: -1;
}
int mg_parse_multipart(const char *buf, int buf_len,
char *var_name, int var_name_len,
char *file_name, int file_name_len,
const char **data, int *data_len) {
static const char cd[] = "Content-Disposition: ";
//struct mg_connection c;
int hl, bl, n, ll, pos, cdl = sizeof(cd) - 1;
//char *p;
if (buf == NULL || buf_len <= 0) return 0;
if ((hl = get_request_len(buf, buf_len)) <= 0) return 0;
if (buf[0] != '-' || buf[1] != '-' || buf[2] == '\n') return 0;
// Get boundary length
bl = get_line_len(buf, buf_len);
// Loop through headers, fetch variable name and file name
var_name[0] = file_name[0] = '\0';
for (n = bl; (ll = get_line_len(buf + n, hl - n)) > 0; n += ll) {
if (mg_strncasecmp(cd, buf + n, cdl) == 0) {
parse_header(buf + n + cdl, ll - (cdl + 2), "name",
var_name, var_name_len);
parse_header(buf + n + cdl, ll - (cdl + 2), "filename",
file_name, file_name_len);
}
}
// Scan body, search for terminating boundary
for (pos = hl; pos + (bl - 2) < buf_len; pos++) {
if (buf[pos] == '-' && !memcmp(buf, &buf[pos], bl - 2)) {
if (data_len != NULL) *data_len = (pos - 2) - hl;
if (data != NULL) *data = buf + hl;
return pos;
}
}
return 0;
}
const char **mg_get_valid_option_names(void) {
return static_config_options;
static int get_option_index(const char *name) {
for (i = 0; static_config_options[i * 2] != NULL; i++) {
if (strcmp(static_config_options[i * 2], name) == 0) {
return i;
}
return -1;
}
static void set_default_option_values(char **opts) {
const char *value, **all_opts = mg_get_valid_option_names();
int i;
for (i = 0; all_opts[i * 2] != NULL; i++) {
value = all_opts[i * 2 + 1];
if (opts[i] == NULL && value != NULL) {
opts[i] = mg_strdup(value);
const char *mg_set_option(struct mg_server *server, const char *name,
const char *value) {
int ind = get_option_index(name);
const char *error_msg = NULL;
if (ind < 0) {
error_msg = "No such option";
if (server->config_options[ind] != NULL) {
free(server->config_options[ind]);
}
server->config_options[ind] = mg_strdup(value);
int port = ns_bind(&server->ns_server, value);
if (port < 0) {
error_msg = "Cannot bind to port";
Sergey Lyubka
committed
} else {
sockaddr_to_string(server->local_ip, sizeof(server->local_ip),
Sergey Lyubka
committed
if (!strcmp(value, "0")) {
char buf[10];
Sergey Lyubka
committed
free(server->config_options[ind]);
server->config_options[ind] = mg_strdup(buf);
}
struct passwd *pw;
if ((pw = getpwnam(value)) == NULL) {
error_msg = "Unknown user";
} else if (setgid(pw->pw_gid) != 0) {
error_msg = "setgid() failed";
} else if (setuid(pw->pw_uid) != 0) {
error_msg = "setuid() failed";
}
} else if (ind == SSL_CERTIFICATE) {
int res = ns_set_ssl_cert(&server->ns_server, value);
if (res == -2) {
error_msg = "Cannot load PEM";
} else if (res == -3) {
error_msg = "SSL not enabled";
} else if (res == -1) {
4490
4491
4492
4493
4494
4495
4496
4497
4498
4499
4500
4501
4502
4503
4504
4505
4506
4507
4508
4509
4510
4511
4512
4513
4514
4515
4516
4517
4518
4519
4520
4521
4522
4523
4524
4525
4526
4527
4528
4529
4530
4531
4532
4533
4534
4535
static void on_accept(struct ns_connection *nc, union socket_address *sa) {
struct mg_server *server = (struct mg_server *) nc->server;
struct connection *conn;
if (!check_acl(server->config_options[ACCESS_CONTROL_LIST],
ntohl(* (uint32_t *) &sa->sin.sin_addr)) ||
(conn = (struct connection *) calloc(1, sizeof(*conn))) == NULL) {
nc->flags |= NSF_CLOSE_IMMEDIATELY;
} else {
conn->server = (struct mg_server *) nc->server;
sockaddr_to_string(conn->mg_conn.remote_ip,
sizeof(conn->mg_conn.remote_ip), sa);
conn->mg_conn.remote_port = ntohs(sa->sin.sin_port);
conn->mg_conn.server_param = nc->server->server_data;
conn->mg_conn.local_ip = server->local_ip;
conn->mg_conn.local_port =
ntohs(server->ns_server.listening_sa.sin.sin_port);
// Circularly link two connection structures
nc->connection_data = conn;
conn->ns_conn = nc;
}
}
static void mg_ev_handler(struct ns_connection *nc, enum ns_event ev, void *p) {
struct connection *conn = (struct connection *) nc->connection_data;
switch (ev) {
case NS_ACCEPT:
on_accept(nc, (union socket_address *) p);
break;
case NS_CONNECT:
{
int ok = * (int *) p;
conn->mg_conn.status_code = ok == 0 ?
MG_CONNECT_SUCCESS : MG_CONNECT_FAILURE;
if (conn->handler(&conn->mg_conn) != 0 || ok != 0) {
nc->flags |= NSF_CLOSE_IMMEDIATELY;
}
}
break;
case NS_RECV:
if (nc->flags & NSF_ACCEPTED) {
process_request(conn);
#ifndef MONGOOSE_NO_CGI
} else if (nc->flags & MG_CGI_CONN) {
on_cgi_data(nc);
} else {
process_response(conn);
}
break;
case NS_SEND:
break;
case NS_CLOSE:
nc->connection_data = NULL;
if ((nc->flags & MG_CGI_CONN) && conn && conn->ns_conn) {
conn->ns_conn->flags &= ~NSF_BUFFER_BUT_DONT_SEND;
4552
4553
4554
4555
4556
4557
4558
4559
4560
4561
4562
4563
4564
4565
4566
4567
4568
4569
4570
4571
4572
4573
4574
4575
4576
4577
4578
4579
conn->ns_conn->flags |= conn->ns_conn->send_iobuf.len > 0 ?
NSF_FINISHED_SENDING_DATA : NSF_CLOSE_IMMEDIATELY;
conn->endpoint.cgi_conn = NULL;
} else if (conn != NULL) {
DBG(("%p %d closing", conn, conn->endpoint_type));
if (conn->endpoint_type == EP_CLIENT && nc->recv_iobuf.len > 0) {
call_http_client_handler(conn, MG_DOWNLOAD_SUCCESS);
}
if (conn->server->http_close_handler) {
conn->server->http_close_handler(&conn->mg_conn);
}
close_local_endpoint(conn);
free(conn);
}
break;
case NS_POLL:
if (conn != NULL && conn->endpoint_type == EP_FILE) {
transfer_file_data(conn);
}
// Expire idle connections
{
time_t current_time = * (time_t *) p;
if (conn != NULL && conn->mg_conn.is_websocket) {
ping_idle_websocket_connection(conn, current_time);
}
if (nc->last_io_time + MONGOOSE_IDLE_TIMEOUT_SECONDS < current_time) {
mg_ev_handler(nc, NS_CLOSE, NULL);
nc->flags |= NSF_CLOSE_IMMEDIATELY;
}
}
break;
default:
break;
}
}
void mg_set_request_handler(struct mg_server *server, mg_handler_t handler) {
server->request_handler = handler;
}
Andrea Guzzo
committed
void mg_set_http_close_handler(struct mg_server *server, mg_handler_t handler) {
server->http_close_handler = handler;
}
void mg_set_http_error_handler(struct mg_server *server, mg_handler_t handler) {
server->error_handler = handler;
}
void mg_set_auth_handler(struct mg_server *server, mg_handler_t handler) {
server->auth_handler = handler;
}
Sergey Lyubka
committed
void mg_set_listening_socket(struct mg_server *server, int sock) {
if (server->ns_server.listening_sock != INVALID_SOCKET) {
closesocket(server->ns_server.listening_sock);
Sergey Lyubka
committed
}
server->ns_server.listening_sock = (sock_t) sock;
Sergey Lyubka
committed
}
int mg_get_listening_socket(struct mg_server *server) {
Sergey Lyubka
committed
}
const char *mg_get_option(const struct mg_server *server, const char *name) {
const char **opts = (const char **) server->config_options;
int i = get_option_index(name);
return i == -1 ? NULL : opts[i] == NULL ? "" : opts[i];
}
struct mg_server *mg_create_server(void *server_data) {
struct mg_server *server = (struct mg_server *) calloc(1, sizeof(*server));
ns_server_init(&server->ns_server, server_data, mg_ev_handler);
set_default_option_values(server->config_options);
return server;