Newer
Older
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);
Sergey Lyubka
committed
static void call_http_client_handler(struct connection *conn) {
//conn->mg_conn.status_code = code;
// For responses without Content-Lengh, use the whole buffer
Sergey Lyubka
committed
if (conn->cl == 0) {
conn->mg_conn.content_len = conn->ns_conn->recv_iobuf.len;
conn->mg_conn.content = conn->ns_conn->recv_iobuf.buf;
Sergey Lyubka
committed
if (call_user(conn, MG_REPLY) == MG_FALSE) {
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)) {
Sergey Lyubka
committed
call_http_client_handler(conn);
} else if (io->len >= conn->cl) {
call_http_client_handler(conn);
struct mg_connection *mg_connect(struct mg_server *server, const char *host,
int port, int use_ssl) {
struct ns_connection *nsconn;
struct connection *conn;
nsconn = ns_connect(&server->ns_server, host, port, use_ssl, NULL);
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->handler = handler;
conn->mg_conn.server_param = server->ns_server.server_data;
conn->ns_conn->flags = NSF_CONNECTING;
return &conn->mg_conn;
#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+");
now = time(NULL);
strftime(date, sizeof(date), "%d/%b/%Y:%H:%M:%S %z", localtime(&now));
flockfile(fp);
mg_parse_header(mg_get_header(&conn->mg_conn, "Authorization"), "username",
user, sizeof(user));
fprintf(fp, "%s - %s [%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->query_string ? "?" : "",
c->query_string ? c->query_string : "",
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 &= ~(NSF_FINISHED_SENDING_DATA |
NSF_BUFFER_BUT_DONT_SEND | NSF_CLOSE_IMMEDIATELY |
MG_HEADERS_SENT | MG_LONG_RUNNING);
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, MG_POLL);
Sergey Lyubka
committed
// Apply function to all active connections.
void mg_iterate_over_connections(struct mg_server *server, mg_handler_t cb,
void *param) {
struct mg_iterator it = { cb, 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);
4342
4343
4344
4345
4346
4347
4348
4349
4350
4351
4352
4353
4354
4355
4356
4357
4358
4359
4360
4361
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
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;
Sergey Lyubka
committed
char **v = NULL;
Sergey Lyubka
committed
if (ind < 0) return "No such option";
v = &server->config_options[ind];
Sergey Lyubka
committed
4423
4424
4425
4426
4427
4428
4429
4430
4431
4432
4433
4434
4435
4436
4437
4438
4439
4440
4441
4442
4443
4444
4445
4446
4447
4448
4449
4450
// Return success immediately if setting to the same value
if ((*v == NULL && value == NULL) ||
(value != NULL && *v != NULL && !strcmp(value, *v))) {
return NULL;
}
if (*v != NULL) {
free(*v);
*v = NULL;
}
if (value == NULL) return NULL;
*v = mg_strdup(value);
DBG(("%s [%s]", name, *v));
if (ind == LISTENING_PORT) {
int port = ns_bind(&server->ns_server, value);
if (port < 0) {
error_msg = "Cannot bind to port";
} else {
ns_sock_to_str(server->ns_server.listening_sock, server->local_ip,
sizeof(server->local_ip), 0);
if (!strcmp(value, "0")) {
char buf[10];
mg_snprintf(buf, sizeof(buf), "%d", port);
free(*v);
*v = mg_strdup(buf);
Sergey Lyubka
committed
}
Sergey Lyubka
committed
} else if (ind == RUN_AS_USER) {
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";
}
Sergey Lyubka
committed
} 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) {
error_msg = "SSL_CTX_new() failed";
Sergey Lyubka
committed
#endif
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 = server;
ns_sock_to_str(nc->sock, conn->mg_conn.remote_ip,
sizeof(conn->mg_conn.remote_ip), 0);
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->lsa.sin.sin_port);
// Circularly link two connection structures
nc->connection_data = conn;
conn->ns_conn = nc;
}
}
#ifndef MONGOOSE_NO_FILESYSTEM
static void hexdump(struct ns_connection *nc, const char *path,
int num_bytes, int is_sent) {
struct connection *mc = (struct connection *) nc->connection_data;
const struct iobuf *io = is_sent ? &nc->send_iobuf : &nc->recv_iobuf;
FILE *fp;
char *buf;
int buf_size = num_bytes * 5 + 100;
if (path != NULL && num_bytes > 0 && (fp = fopen(path, "a")) != NULL) {
fprintf(fp, "%lu %s:%d %s %s:%d %d\n", (unsigned long) time(NULL),
mc->mg_conn.local_ip, mc->mg_conn.local_port,
is_sent ? "->" : "<-",
mc->mg_conn.remote_ip, mc->mg_conn.remote_port,
num_bytes);
if ((buf = (char *) malloc(buf_size)) != NULL) {
ns_hexdump(io->buf + (is_sent ? 0 : io->len) - (is_sent ? 0 : num_bytes),
num_bytes, buf, buf_size);
fprintf(fp, "%s", buf);
free(buf);
}
fclose(fp);
}
}
static void mg_ev_handler(struct ns_connection *nc, enum ns_event ev, void *p) {
struct connection *conn = (struct connection *) nc->connection_data;
struct mg_server *server = (struct mg_server *) nc->server;
switch (ev) {
case NS_ACCEPT:
on_accept(nc, (union socket_address *) p);
break;
case NS_CONNECT:
conn->mg_conn.status_code = * (int *) p;
if (conn->mg_conn.status_code != 0 ||
call_user(conn, MG_CONNECT) == MG_FALSE) {
nc->flags |= NSF_CLOSE_IMMEDIATELY;
#ifndef MONGOOSE_NO_FILESYSTEM
hexdump(nc, server->config_options[HEXDUMP_FILE], * (int *) p, 0);
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:
#ifndef MONGOOSE_NO_FILESYSTEM
hexdump(nc, server->config_options[HEXDUMP_FILE], * (int *) p, 1);
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;
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_user(conn, MG_CLOSE);
close_local_endpoint(conn);
free(conn);
}
break;
case NS_POLL:
if (call_user(conn, MG_POLL) == MG_TRUE) {
nc->flags |= NSF_FINISHED_SENDING_DATA;
}
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_wakeup_server(struct mg_server *server) {
ns_server_wakeup(&server->ns_server);
}
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, mg_handler_t handler) {
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);
server->event_handler = handler;