Newer
Older
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
3014
3015
3016
3017
3018
3019
3020
3021
3022
3023
3024
3025
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035
3036
3037
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;
}
3052
3053
3054
3055
3056
3057
3058
3059
3060
3061
3062
3063
3064
3065
3066
3067
3068
3069
3070
3071
3072
3073
3074
3075
3076
3077
3078
3079
3080
3081
3082
3083
3084
3085
3086
3087
3088
3089
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;
3094
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
3133
3134
3135
3136
3137
3138
3139
3140
3141
3142
3143
3144
3145
3146
} 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
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
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);
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);
#ifndef NO_AUTH
} else if ((!is_dangerous_dav_request(conn) && !is_authorized(conn, path)) ||
(is_dangerous_dav_request(conn) && !is_authorized_for_dav(conn))) {
send_authorization_request(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 (strcmp(conn->mg_conn.request_method, "POST") &&
strcmp(conn->mg_conn.request_method, "HEAD") &&
strcmp(conn->mg_conn.request_method, "GET")) {
send_http_error(conn, 501, NULL);
} else {
#if !defined(NO_CGI)
open_cgi_endpoint(conn, path);
#else
send_http_error(conn, 501, NULL);
} 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", conn, conn->request_len, io->len));
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 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
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);
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[^]]]:%d%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);
}
Sergey Lyubka
committed
void mg_set_listening_socket(struct mg_server *server, int sock) {
if (server->listening_sock != INVALID_SOCKET) {
closesocket(server->listening_sock);
}
server->listening_sock = sock;
}
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 {
mg_socketpair(server->ctl);
} while (server->ctl[0] == INVALID_SOCKET);
server->server_data = server_data;
server->listening_sock = INVALID_SOCKET;
set_default_option_values(server->config_options);