Newer
Older
int ch = ' ', len = 0, n = strlen(var_name);
const char *p, *end = str + str_len, *s = NULL;
if (buf != NULL && buf_size > 0) buf[0] = '\0';
for (s = str; s != NULL && s + n < end; s++) {
if ((s == str || s[-1] == ' ') && s[n] == '=' &&
!memcmp(s, var_name, n)) break;
s += n + 1;
if (*s == '"' || *s == '\'') ch = *s++;
p = s;
while (p < end && p[0] != ch && len < (int) buf_size) {
if (p[0] == '\\' && p[1] == ch) p++;
buf[len++] = *p++;
}
if (len >= (int) buf_size || (ch != ' ' && *p != ch)) {
len = 0;
} else {
if (len > 0 && s[len - 1] == ',') len--;
int mg_parse_header(const char *s, const char *var_name, char *buf,
return parse_header(s, s == NULL ? 0 : strlen(s), var_name, buf, buf_size);
#ifdef USE_LUA
#include "lua_5.2.1.h"
#ifdef _WIN32
static void *mmap(void *addr, int64_t len, int prot, int flags, int fd,
int offset) {
HANDLE fh = (HANDLE) _get_osfhandle(fd);
HANDLE mh = CreateFileMapping(fh, 0, PAGE_READONLY, 0, 0, 0);
void *p = MapViewOfFile(mh, FILE_MAP_READ, 0, 0, (size_t) len);
CloseHandle(mh);
return p;
}
#define munmap(x, y) UnmapViewOfFile(x)
#define MAP_FAILED NULL
#define MAP_PRIVATE 0
#define PROT_READ 0
#else
#include <sys/mman.h>
#endif
3057
3058
3059
3060
3061
3062
3063
3064
3065
3066
3067
3068
3069
3070
3071
3072
3073
3074
3075
3076
3077
3078
3079
3080
static void reg_string(struct lua_State *L, const char *name, const char *val) {
lua_pushstring(L, name);
lua_pushstring(L, val);
lua_rawset(L, -3);
}
static void reg_int(struct lua_State *L, const char *name, int val) {
lua_pushstring(L, name);
lua_pushinteger(L, val);
lua_rawset(L, -3);
}
static void reg_function(struct lua_State *L, const char *name,
lua_CFunction func, struct mg_connection *conn) {
lua_pushstring(L, name);
lua_pushlightuserdata(L, conn);
lua_pushcclosure(L, func, 1);
lua_rawset(L, -3);
}
static int lua_write(lua_State *L) {
int i, num_args;
const char *str;
size_t size;
struct mg_connection *conn = (struct mg_connection *)
lua_touserdata(L, lua_upvalueindex(1));
num_args = lua_gettop(L);
for (i = 1; i <= num_args; i++) {
if (lua_isstring(L, i)) {
str = lua_tolstring(L, i, &size);
mg_write(conn, str, size);
}
}
return 0;
}
3095
3096
3097
3098
3099
3100
3101
3102
3103
3104
3105
3106
3107
3108
3109
3110
3111
3112
3113
3114
3115
3116
3117
3118
3119
3120
3121
3122
3123
3124
3125
3126
3127
3128
3129
3130
3131
3132
static int lsp_sock_close(lua_State *L) {
if (lua_gettop(L) > 0 && lua_istable(L, -1)) {
lua_getfield(L, -1, "sock");
closesocket((sock_t) lua_tonumber(L, -1));
} else {
return luaL_error(L, "invalid :close() call");
}
return 1;
}
static int lsp_sock_recv(lua_State *L) {
char buf[2000];
int n;
if (lua_gettop(L) > 0 && lua_istable(L, -1)) {
lua_getfield(L, -1, "sock");
n = recv((sock_t) lua_tonumber(L, -1), buf, sizeof(buf), 0);
if (n <= 0) {
lua_pushnil(L);
} else {
lua_pushlstring(L, buf, n);
}
} else {
return luaL_error(L, "invalid :close() call");
}
return 1;
}
static int lsp_sock_send(lua_State *L) {
const char *buf;
size_t len, sent = 0;
int n, sock;
if (lua_gettop(L) > 1 && lua_istable(L, -2) && lua_isstring(L, -1)) {
buf = lua_tolstring(L, -1, &len);
lua_getfield(L, -2, "sock");
sock = (int) lua_tonumber(L, -1);
while (sent < len) {
if ((n = send(sock, buf + sent, len - sent, 0)) <= 0) break;
3137
3138
3139
3140
3141
3142
3143
3144
3145
3146
3147
3148
3149
3150
3151
3152
3153
3154
3155
3156
3157
3158
3159
3160
3161
3162
3163
3164
3165
3166
3167
3168
3169
3170
3171
3172
3173
3174
3175
3176
3177
3178
3179
3180
3181
3182
3183
3184
3185
3186
3187
3188
3189
} else {
return luaL_error(L, "invalid :close() call");
}
return 1;
}
static const struct luaL_Reg luasocket_methods[] = {
{"close", lsp_sock_close},
{"send", lsp_sock_send},
{"recv", lsp_sock_recv},
{NULL, NULL}
};
static sock_t conn2(const char *host, int port) {
struct sockaddr_in sin;
struct hostent *he = NULL;
sock_t sock = INVALID_SOCKET;
if (host != NULL &&
(he = gethostbyname(host)) != NULL &&
(sock = socket(PF_INET, SOCK_STREAM, 0)) != INVALID_SOCKET) {
set_close_on_exec(sock);
sin.sin_family = AF_INET;
sin.sin_port = htons((uint16_t) port);
sin.sin_addr = * (struct in_addr *) he->h_addr_list[0];
if (connect(sock, (struct sockaddr *) &sin, sizeof(sin)) != 0) {
closesocket(sock);
sock = INVALID_SOCKET;
}
}
return sock;
}
static int lsp_connect(lua_State *L) {
sock_t sock;
if (lua_isstring(L, -2) && lua_isnumber(L, -1)) {
sock = conn2(lua_tostring(L, -2), (int) lua_tonumber(L, -1));
if (sock == INVALID_SOCKET) {
lua_pushnil(L);
} else {
lua_newtable(L);
reg_int(L, "sock", sock);
reg_string(L, "host", lua_tostring(L, -4));
luaL_getmetatable(L, "luasocket");
lua_setmetatable(L, -2);
}
} else {
return luaL_error(L, "connect(host,port): invalid parameter given.");
}
return 1;
}
static void prepare_lua_environment(struct mg_connection *ri, lua_State *L) {
extern void luaL_openlibs(lua_State *);
int i;
luaL_openlibs(L);
#ifdef USE_LUA_SQLITE3
{ extern int luaopen_lsqlite3(lua_State *); luaopen_lsqlite3(L); }
#endif
3200
3201
3202
3203
3204
3205
3206
3207
3208
3209
3210
3211
3212
3213
3214
3215
3216
3217
3218
3219
3220
lua_pushliteral(L, "__index");
luaL_newlib(L, luasocket_methods);
lua_rawset(L, -3);
lua_pop(L, 1);
lua_register(L, "connect", lsp_connect);
if (ri == NULL) return;
// Register mg module
lua_newtable(L);
reg_function(L, "write", lua_write, ri);
// Export request_info
lua_pushstring(L, "request_info");
lua_newtable(L);
reg_string(L, "request_method", ri->request_method);
reg_string(L, "uri", ri->uri);
reg_string(L, "http_version", ri->http_version);
reg_string(L, "query_string", ri->query_string);
reg_string(L, "remote_ip", ri->remote_ip);
reg_int(L, "remote_port", ri->remote_port);
lua_pushstring(L, "content");
lua_pushlstring(L, ri->content == NULL ? "" : ri->content, 0);
lua_rawset(L, -3);
reg_int(L, "content_len", ri->content_len);
reg_int(L, "num_headers", ri->num_headers);
lua_pushstring(L, "http_headers");
lua_newtable(L);
for (i = 0; i < ri->num_headers; i++) {
reg_string(L, ri->http_headers[i].name, ri->http_headers[i].value);
}
lua_rawset(L, -3);
lua_rawset(L, -3);
lua_setglobal(L, "mg");
// Register default mg.onerror function
(void) luaL_dostring(L, "mg.onerror = function(e) mg.write('\\nLua "
"error:\\n', debug.traceback(e, 1)) end");
}
static int lua_error_handler(lua_State *L) {
const char *error_msg = lua_isstring(L, -1) ? lua_tostring(L, -1) : "?\n";
lua_getglobal(L, "mg");
if (!lua_isnil(L, -1)) {
lua_getfield(L, -1, "write"); // call mg.write()
lua_pushstring(L, error_msg);
lua_pushliteral(L, "\n");
lua_call(L, 2, 0);
(void) luaL_dostring(L, "mg.write(debug.traceback(), '\\n')");
} else {
printf("Lua error: [%s]\n", error_msg);
(void) luaL_dostring(L, "print(debug.traceback(), '\\n')");
}
// TODO(lsm): leave the stack balanced
return 0;
}
static void lsp(struct connection *conn, const char *p, int len, lua_State *L) {
int i, j, pos = 0;
for (i = 0; i < len; i++) {
if (p[i] == '<' && p[i + 1] == '?') {
for (j = i + 1; j < len ; j++) {
if (p[j] == '?' && p[j + 1] == '>') {
mg_write(&conn->mg_conn, p + pos, i - pos);
if (luaL_loadbuffer(L, p + (i + 2), j - (i + 2), "") == LUA_OK) {
lua_pcall(L, 0, LUA_MULTRET, 0);
}
pos = j + 2;
i = pos - 1;
break;
}
}
}
}
if (i > pos) mg_write(&conn->mg_conn, p + pos, i - pos);
}
static void handle_lsp_request(struct connection *conn, const char *path,
file_stat_t *st) {
void *p = NULL;
lua_State *L = NULL;
FILE *fp = NULL;
if ((fp = fopen(path, "r")) == NULL ||
(p = mmap(NULL, st->st_size, PROT_READ, MAP_PRIVATE,
fileno(fp), 0)) == MAP_FAILED ||
(L = luaL_newstate()) == NULL) {
send_http_error(conn, 500, "mmap(%s): %s", path, strerror(errno));
} else {
// We're not sending HTTP headers here, Lua page must do it.
prepare_lua_environment(&conn->mg_conn, L);
lua_pushcclosure(L, &lua_error_handler, 0);
lua_pushglobaltable(L);
lsp(conn, p, st->st_size, L);
close_local_endpoint(conn);
if (L != NULL) lua_close(L);
if (p != NULL) munmap(p, st->st_size);
if (fp != NULL) fclose(fp);
static void open_local_endpoint(struct connection *conn) {
const char *cl_hdr = mg_get_header(&conn->mg_conn, "Content-Length");
#ifndef NO_FILESYSTEM
static const char lua_pat[] = LUA_SCRIPT_PATTERN;
int exists = 0, is_directory = 0;
const char *cgi_pat = conn->server->config_options[CGI_PATTERN];
const char *dir_lst = conn->server->config_options[ENABLE_DIRECTORY_LISTING];
conn->mg_conn.content_len = cl_hdr == NULL ? 0 : (int) to64(cl_hdr);
// Call URI handler if one is registered for this URI
conn->endpoint.uh = find_uri_handler(conn->server, conn->mg_conn.uri);
if (conn->endpoint.uh != NULL) {
conn->endpoint_type = EP_USER;
conn->mg_conn.content = conn->local_iobuf.buf;
#if USE_POST_SIZE_LIMIT > 1
{
const char *cl = mg_get_header(&conn->mg_conn, "Content-Length");
if (!strcmp(conn->mg_conn.request_method, "POST") &&
(cl == NULL || to64(cl) > USE_POST_SIZE_LIMIT)) {
send_http_error(conn, 500, "POST size > %zu",
(size_t) USE_POST_SIZE_LIMIT);
#ifdef NO_FILESYSTEM
send_http_error(conn, 404, NULL);
#else
exists = convert_uri_to_file_name(conn, path, sizeof(path), &st);
is_directory = S_ISDIR(st.st_mode);
if (!strcmp(conn->mg_conn.request_method, "OPTIONS")) {
send_options(conn);
} else if (conn->server->config_options[DOCUMENT_ROOT] == NULL) {
send_http_error(conn, 404, NULL);
} else if ((!is_dav_mutation(conn) && !is_authorized(conn, path)) ||
(is_dav_mutation(conn) && !is_authorized_for_dav(conn))) {
mg_send_digest_auth_request(&conn->mg_conn);
close_local_endpoint(conn);
#ifndef NO_DAV
} else if (!strcmp(conn->mg_conn.request_method, "PROPFIND")) {
handle_propfind(conn, path, &st);
} else if (!strcmp(conn->mg_conn.request_method, "MKCOL")) {
handle_mkcol(conn, path);
} else if (!strcmp(conn->mg_conn.request_method, "DELETE")) {
handle_delete(conn, path);
} else if (!strcmp(conn->mg_conn.request_method, "PUT")) {
handle_put(conn, path);
} else if (!exists || must_hide_file(conn, path)) {
send_http_error(conn, 404, NULL);
} else if (is_directory &&
conn->mg_conn.uri[strlen(conn->mg_conn.uri) - 1] != '/') {
conn->mg_conn.status_code = 301;
mg_printf(&conn->mg_conn, "HTTP/1.1 301 Moved Permanently\r\n"
"Location: %s/\r\n\r\n", conn->mg_conn.uri);
close_local_endpoint(conn);
} else if (is_directory && !find_index_file(conn, path, sizeof(path), &st)) {
if (!mg_strcasecmp(dir_lst, "yes")) {
#ifndef NO_DIRECTORY_LISTING
send_directory_listing(conn, path);
#else
send_http_error(conn, 501, NULL);
send_http_error(conn, 403, NULL);
} else if (match_prefix(lua_pat, sizeof(lua_pat) - 1, path) > 0) {
#ifdef USE_LUA
send_http_error(conn, 501, NULL);
} else if (match_prefix(cgi_pat, strlen(cgi_pat), path) > 0) {
#if !defined(NO_CGI)
#endif // !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);
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")) {
spool(&conn->remote_iobuf, 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 process_request(struct connection *conn) {
struct iobuf *io = &conn->local_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));
memmove(io->buf, io->buf + conn->request_len, io->len - conn->request_len);
io->len -= conn->request_len;
conn->request_len = parse_http_message(conn->request, conn->request_len,
&conn->mg_conn);
}
DBG(("%p %d %d [%.*s]", conn, conn->request_len, io->len, 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 NO_WEBSOCKET
send_websocket_handshake_if_requested(&conn->mg_conn);
#endif
send_continue_if_expected(conn);
open_local_endpoint(conn);
#ifndef NO_CGI
if (conn->endpoint_type == EP_CGI && io->len > 0) {
forward_post_data(conn);
}
#endif
if (conn->endpoint_type == EP_USER) {
call_uri_handler_if_data_is_buffered(conn);
#ifndef NO_DAV
if (conn->endpoint_type == EP_PUT && io->len > 0) {
forward_put_data(conn);
static void read_from_client(struct connection *conn) {
char buf[IOBUF_SIZE];
int n = 0;
if (conn->ssl != NULL) {
#ifdef USE_SSL
if (conn->flags & CONN_SSL_HANDS_SHAKEN) {
n = SSL_read(conn->ssl, buf, sizeof(buf));
} else {
if (SSL_accept(conn->ssl) == 1) {
conn->flags |= CONN_SSL_HANDS_SHAKEN;
}
return;
}
#endif
} else {
n = recv(conn->client_sock, buf, sizeof(buf), 0);
}
if (is_error(n)) {
conn->flags |= CONN_CLOSE;
} else if (n > 0) {
spool(&conn->local_iobuf, buf, n);
process_request(conn);
#ifndef 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 gobble_prior_post_data(struct iobuf *io, int len) {
if (len > 0 && len <= io->len) {
memmove(io->buf, io->buf + len, io->len - len);
io->len -= len;
}
}
static void close_local_endpoint(struct connection *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);
switch (conn->endpoint_type) {
case EP_FILE: close(conn->endpoint.fd); break;
case EP_CGI: closesocket(conn->endpoint.cgi_sock); break;
default: break;
}
#ifndef NO_LOGGING
if (conn->mg_conn.status_code != 400) {
log_access(conn, conn->server->config_options[ACCESS_LOG_FILE]);
}
#endif
if (conn->endpoint_type == EP_USER) {
gobble_prior_post_data(&conn->local_iobuf, conn->mg_conn.content_len);
}
conn->cl = conn->num_bytes_sent = conn->request_len = 0;
free(conn->request);
conn->request = NULL;
if (keep_alive) {
process_request(conn); // Can call us recursively if pipelining is used
conn->flags |= conn->remote_iobuf.len == 0 ? CONN_CLOSE : CONN_SPOOL_DONE;
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));
if (is_error(n)) {
close_local_endpoint(conn);
} else if (n > 0) {
conn->cl -= n;
spool(&conn->remote_iobuf, buf, n);
if (conn->cl <= 0) {
close_local_endpoint(conn);
Sergey Lyubka
committed
static void execute_iteration(struct mg_server *server) {
struct ll *lp, *tmp;
struct connection *conn;
union { void (*f)(struct mg_connection *, void *); void *p; } msg[2];
Sergey Lyubka
committed
recv(server->ctl[1], (void *) msg, sizeof(msg), 0);
LINKED_LIST_FOREACH(&server->active_connections, lp, tmp) {
conn = LINKED_LIST_ENTRY(lp, struct connection, link);
msg[0].f(&conn->mg_conn, msg[1].p);
Sergey Lyubka
committed
}
}
void add_to_set(sock_t sock, fd_set *set, sock_t *max_fd) {
FD_SET(sock, set);
if (sock > *max_fd) {
*max_fd = sock;
Sergey Lyubka
committed
unsigned int mg_poll_server(struct mg_server *server, int milliseconds) {
struct connection *conn;
struct timeval tv;
fd_set read_set, write_set;
sock_t max_fd = -1;
time_t current_time = time(NULL), expire_time = current_time -
atoi(server->config_options[IDLE_TIMEOUT_MS]) / 1000;
Sergey Lyubka
committed
if (server->listening_sock == INVALID_SOCKET) return 0;
FD_ZERO(&read_set);
FD_ZERO(&write_set);
add_to_set(server->listening_sock, &read_set, &max_fd);
Sergey Lyubka
committed
add_to_set(server->ctl[1], &read_set, &max_fd);
LINKED_LIST_FOREACH(&server->active_connections, lp, tmp) {
conn = LINKED_LIST_ENTRY(lp, struct connection, link);
add_to_set(conn->client_sock, &read_set, &max_fd);
if (conn->endpoint_type == EP_FILE) {
transfer_file_data(conn);
} else if (conn->endpoint_type == EP_CGI) {
add_to_set(conn->endpoint.cgi_sock, &read_set, &max_fd);
}
if (conn->remote_iobuf.len > 0 && !(conn->flags & CONN_BUFFER)) {
add_to_set(conn->client_sock, &write_set, &max_fd);
} else if (conn->flags & CONN_CLOSE) {
close_conn(conn);
tv.tv_sec = milliseconds / 1000;
tv.tv_usec = (milliseconds % 1000) * 1000;
if (select(max_fd + 1, &read_set, &write_set, NULL, &tv) > 0) {
Sergey Lyubka
committed
if (FD_ISSET(server->ctl[1], &read_set)) {
execute_iteration(server);
// Accept new connections
if (FD_ISSET(server->listening_sock, &read_set)) {
while ((conn = accept_new_connection(server)) != NULL) {
conn->birth_time = conn->last_activity_time = current_time;
}
}
// Read/write from clients
LINKED_LIST_FOREACH(&server->active_connections, lp, tmp) {
conn = LINKED_LIST_ENTRY(lp, struct connection, link);
if (FD_ISSET(conn->client_sock, &read_set)) {
conn->last_activity_time = current_time;
if (conn->endpoint_type == EP_CGI &&
FD_ISSET(conn->endpoint.cgi_sock, &read_set)) {
read_from_cgi(conn);
if (FD_ISSET(conn->client_sock, &write_set) &&
!(conn->flags & CONN_BUFFER)) {
conn->last_activity_time = current_time;
// Close expired connections and those that need to be closed
LINKED_LIST_FOREACH(&server->active_connections, lp, tmp) {
conn = LINKED_LIST_ENTRY(lp, struct connection, link);
if (conn->mg_conn.is_websocket) {
ping_idle_websocket_connection(conn, current_time);
}
if (conn->flags & CONN_CLOSE || conn->last_activity_time < expire_time) {
Sergey Lyubka
committed
return (unsigned int) current_time;
void mg_destroy_server(struct mg_server **server) {
if (server != NULL && *server != NULL) {
// Do one last poll, see https://github.com/cesanta/mongoose/issues/286
Oliver Schneider
committed
mg_poll_server(*server, 0);
closesocket((*server)->listening_sock);
closesocket((*server)->ctl[0]);
closesocket((*server)->ctl[1]);
LINKED_LIST_FOREACH(&(*server)->active_connections, lp, tmp) {
free(LINKED_LIST_ENTRY(lp, struct connection, link));
LINKED_LIST_FOREACH(&(*server)->uri_handlers, lp, tmp) {
free(LINKED_LIST_ENTRY(lp, struct uri_handler, link)->uri);
free(LINKED_LIST_ENTRY(lp, struct uri_handler, link));
}
for (i = 0; i < (int) ARRAY_SIZE((*server)->config_options); i++) {
free((*server)->config_options[i]); // It is OK to free(NULL)
}
#ifdef USE_SSL
if ((*server)->ssl_ctx != NULL) SSL_CTX_free((*server)->ssl_ctx);
#endif
free(*server);
*server = NULL;
Sergey Lyubka
committed
// Apply function to all active connections.
void mg_iterate_over_connections(struct mg_server *server,
void (*func)(struct mg_connection *, void *),
void *param) {
// Send closure (function + parameter) to the IO thread to execute
union { void (*f)(struct mg_connection *, void *); void *p; } msg[2];
msg[0].f = func;
msg[1].p = param;
Sergey Lyubka
committed
send(server->ctl[0], (void *) msg, sizeof(msg), 0);
void mg_add_uri_handler(struct mg_server *server, const char *uri,
mg_handler_t handler) {
struct uri_handler *p = (struct uri_handler *) malloc(sizeof(*p));
if (p != NULL) {
LINKED_LIST_ADD_TO_FRONT(&server->uri_handlers, &p->link);
p->uri = mg_strdup(uri);
p->handler = handler;
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);
3800
3801
3802
3803
3804
3805
3806
3807
3808
3809
3810
3811
3812
3813
3814
3815
3816
3817
3818
3819
3820
3821
3822
3823
3824
3825
3826
3827
3828
3829
3830
3831
3832
3833
3834
3835
3836
3837
3838
3839
3840
3841
3842
3843
3844
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);
// Valid listening port spec is: [ip_address:]port, e.g. "80", "127.0.0.1:3128"
static int parse_port_string(const char *str, union socket_address *sa) {
unsigned int a, b, c, d, port;
int len = 0;
#ifdef USE_IPV6
char buf[100];
#endif
// MacOS needs that. If we do not zero it, subsequent bind() will fail.
// Also, all-zeroes in the socket address means binding to all addresses
// for both IPv4 and IPv6 (INADDR_ANY and IN6ADDR_ANY_INIT).
memset(sa, 0, sizeof(*sa));
sa->sin.sin_family = AF_INET;
if (sscanf(str, "%u.%u.%u.%u:%u%n", &a, &b, &c, &d, &port, &len) == 5) {
// Bind to a specific IPv4 address, e.g. 192.168.1.5:8080
sa->sin.sin_addr.s_addr = htonl((a << 24) | (b << 16) | (c << 8) | d);
sa->sin.sin_port = htons((uint16_t) port);
#if defined(USE_IPV6)
} else if (sscanf(str, "[%49[^]]]:%u%n", buf, &port, &len) == 2 &&
inet_pton(AF_INET6, buf, &sa->sin6.sin6_addr)) {
// IPv6 address, e.g. [3ffe:2a00:100:7031::1]:8080
sa->sin6.sin6_family = AF_INET6;
sa->sin6.sin6_port = htons((uint16_t) port);
#endif
} else if (sscanf(str, "%u%n", &port, &len) == 1) {
// If only port is specified, bind to IPv4, INADDR_ANY
sa->sin.sin_port = htons((uint16_t) port);
port = 0; // Parsing failure. Make port invalid.
return port > 0 && port < 0xffff && str[len] == '\0';
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);
DBG(("%s => %s", name, value));
if (ind == LISTENING_PORT) {
if (server->listening_sock != INVALID_SOCKET) {
closesocket(server->listening_sock);
}
parse_port_string(server->config_options[LISTENING_PORT], &server->lsa);
server->listening_sock = open_listening_socket(&server->lsa);
if (server->listening_sock == INVALID_SOCKET) {
error_msg = "Cannot bind to port";
} else {
set_non_blocking_mode(server->listening_sock);
}
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) {
SSL_library_init();
if ((server->ssl_ctx = SSL_CTX_new(SSLv23_server_method())) == NULL) {
error_msg = "SSL_CTX_new() failed";
} else if (SSL_CTX_use_certificate_file(server->ssl_ctx, value, 1) == 0 ||
SSL_CTX_use_PrivateKey_file(server->ssl_ctx, value, 1) == 0) {
error_msg = "Cannot load PEM file";
} else {
SSL_CTX_use_certificate_chain_file(server->ssl_ctx, value);
}
void mg_set_http_error_handler(struct mg_server *server, mg_handler_t handler) {
server->error_handler = handler;
}
Sergey Lyubka
committed
void mg_set_listening_socket(struct mg_server *server, int sock) {
if (server->listening_sock != INVALID_SOCKET) {
closesocket(server->listening_sock);
}
Sergey Lyubka
committed
}
int mg_get_listening_socket(struct mg_server *server) {
return server->listening_sock;
}
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));
#ifdef _WIN32
WSADATA data;
WSAStartup(MAKEWORD(2, 2), &data);
Sergey Lyubka
committed
#else
// Ignore SIGPIPE signal, so if browser cancels the request, it
// won't kill the whole process.
signal(SIGPIPE, SIG_IGN);
#endif
LINKED_LIST_INIT(&server->active_connections);
LINKED_LIST_INIT(&server->uri_handlers);
// Create control socket pair. Do it in a loop to protect from
// interrupted syscalls in mg_socketpair().
do {