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
3058
3059
3060
3061
3062
3063
3064
3065
3066
3067
3068
3069
3070
3071
3072
3073
3074
3075
3076
3077
3078
3079
3080
3081
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;
}
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
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;
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
3190
} 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
3201
3202
3203
3204
3205
3206
3207
3208
3209
3210
3211
3212
3213
3214
3215
3216
3217
3218
3219
3220
3221
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) {
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];
// 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 try_http_parse_and_set_content_length(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));
discard_leading_iobuf_bytes(io, conn->request_len);
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) {
struct iobuf *io = &conn->local_iobuf;
try_http_parse_and_set_content_length(conn);
DBG(("%p %d %d %d [%.*s]", conn, conn->request_len, io->len, 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 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);
3479
3480
3481
3482
3483
3484
3485
3486
3487
3488
3489
3490
3491
3492
3493
3494
3495
3496
3497
3498
3499
3500
3501
3502
3503
3504
3505
3506
3507
3508
3509
3510
3511
3512
3513
3514
3515
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->local_iobuf.len;
}
conn->mg_conn.content = conn->local_iobuf.buf;
if (conn->handler(&conn->mg_conn) || code == MG_CONNECT_FAILURE ||
code == MG_DOWNLOAD_FAILURE) {
conn->flags |= CONN_CLOSE;
}
discard_leading_iobuf_bytes(&conn->local_iobuf, conn->mg_conn.content_len);
conn->flags = 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) {
struct iobuf *io = &conn->local_iobuf;
try_http_parse_and_set_content_length(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);
}
}
static void callback_http_client_on_connect(struct connection *conn) {
int ok;
socklen_t len = sizeof(ok);
if (getsockopt(conn->client_sock, SOL_SOCKET, SO_ERROR, (char *) &ok,
&len) == 0 && ok == 0) {
conn->mg_conn.status_code = MG_CONNECT_SUCCESS;
#ifdef USE_SSL
if (conn->ssl != NULL) {
switch (SSL_connect(conn->ssl)) {
case 1: conn->flags = CONN_SSL_HANDS_SHAKEN; break;
case 0: conn->flags &= ~CONN_CONNECTING; // Call this function again
default: ok = 1; break;
}
}
#endif
}
if (conn->handler(&conn->mg_conn) || ok != 0) {
conn->flags |= CONN_CLOSE;
}
}
static void read_from_socket(struct connection *conn) {
if (conn->endpoint_type == EP_CLIENT && conn->flags & CONN_CONNECTING) {
callback_http_client_on_connect(conn);
return;
}
if (conn->flags & CONN_SSL_HANDS_SHAKEN) {
n = SSL_read(conn->ssl, buf, sizeof(buf));
if (SSL_accept(conn->ssl) == 1) {
conn->flags |= CONN_SSL_HANDS_SHAKEN;
}
return;
n = recv(conn->client_sock, buf, sizeof(buf), 0);
}
DBG(("%p %d %d (1)", conn, n, conn->flags));
if (is_error(n)) {
if (conn->endpoint_type == EP_CLIENT && conn->local_iobuf.len > 0) {
call_http_client_handler(conn, MG_DOWNLOAD_SUCCESS);
}
conn->flags |= CONN_CLOSE;
} else if (n > 0) {
spool(&conn->local_iobuf, buf, n);
if (conn->endpoint_type == EP_CLIENT) {
process_response(conn);
} else {
DBG(("%p %d %d (2)", conn, n, conn->flags));
int mg_connect(struct mg_server *server, const char *host, int port,
int use_ssl, mg_handler_t handler, void *param) {
sock_t sock = INVALID_SOCKET;
struct sockaddr_in sin;
struct hostent *he = NULL;
struct connection *conn = NULL;
int connect_ret_val;
if (host == NULL || (he = gethostbyname(host)) == NULL ||
(sock = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) return 0;
#ifndef USE_SSL
if (use_ssl) return 0;
#endif
sin.sin_family = AF_INET;
sin.sin_port = htons((uint16_t) port);
sin.sin_addr = * (struct in_addr *) he->h_addr_list[0];
set_non_blocking_mode(sock);
connect_ret_val = connect(sock, (struct sockaddr *) &sin, sizeof(sin));
if (connect_ret_val != 0 && errno != EINPROGRESS) {
return 0;
} else if ((conn = (struct connection *) calloc(1, sizeof(*conn))) == NULL) {
closesocket(sock);
return 0;
}
conn->client_sock = sock;
conn->endpoint_type = EP_CLIENT;
conn->handler = handler;
conn->mg_conn.server_param = server->server_data;
conn->mg_conn.connection_param = param;
conn->birth_time = conn->last_activity_time = time(NULL);
conn->flags = CONN_CONNECTING;
conn->mg_conn.status_code = MG_CONNECT_FAILURE;
#ifdef USE_SSL
if (use_ssl && (conn->ssl = SSL_new(server->client_ssl_ctx)) != NULL) {
SSL_set_fd(conn->ssl, sock);
}
#endif
LINKED_LIST_ADD_TO_FRONT(&server->active_connections, &conn->link);
conn->mg_conn.status_code = MG_CONNECT_SUCCESS;
conn->flags &= ~CONN_CONNECTING;
conn->mg_conn.content = conn->local_iobuf.buf;
handler(&conn->mg_conn);
}
return 1;
}
#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);
DBG(("%p %d %d %d", conn, conn->endpoint_type, keep_alive, conn->flags));
case EP_PUT: close(conn->endpoint.fd); break;
case EP_FILE: close(conn->endpoint.fd); break;
case EP_CGI: closesocket(conn->endpoint.cgi_sock); break;
default: break;
}
if (conn->mg_conn.status_code > 0 && conn->endpoint_type != EP_CLIENT &&
conn->mg_conn.status_code != 400) {
log_access(conn, conn->server->config_options[ACCESS_LOG_FILE]);
}
#endif
// Gobble possible POST data sent to the URI handler
discard_leading_iobuf_bytes(&conn->local_iobuf, conn->mg_conn.content_len);
conn->flags = conn->mg_conn.status_code = 0;
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 { mg_handler_t f; 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);
conn->mg_conn.connection_param = msg[1].p;
msg[0].f(&conn->mg_conn);
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 -
USE_IDLE_TIMEOUT_SECONDS;
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_CLIENT && (conn->flags & CONN_CONNECTING)) {
add_to_set(conn->client_sock, &write_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)) {
if (conn->endpoint_type == EP_CLIENT &&
(conn->flags & CONN_CONNECTING)) {
read_from_socket(conn);
} else if (!(conn->flags & CONN_BUFFER)) {
conn->last_activity_time = current_time;
write_to_socket(conn);
}
// 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_LONG_RUNNING) {
conn->mg_conn.wsbits = conn->flags & CONN_CLOSE ? 1 : 0;
call_uri_handler(conn);
}
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
mg_poll_server(s, 0);
closesocket(s->listening_sock);
closesocket(s->ctl[0]);
closesocket(s->ctl[1]);
LINKED_LIST_FOREACH(&s->active_connections, lp, tmp) {
close_conn(LINKED_LIST_ENTRY(lp, struct connection, link));
LINKED_LIST_FOREACH(&s->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(s->config_options); i++) {
free(s->config_options[i]); // It is OK to free(NULL)
if (s->ssl_ctx != NULL) SSL_CTX_free((*server)->ssl_ctx);
if (s->client_ssl_ctx != NULL) SSL_CTX_free(s->client_ssl_ctx);
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) {
// Send closure (function + parameter) to the IO thread to execute
union { mg_handler_t f; void *p; } msg[2];
msg[0].f = handler;
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);
3939
3940
3941
3942
3943
3944
3945
3946
3947
3948
3949
3950
3951
3952
3953
3954
3955
3956
3957
3958
3959
3960
3961
3962
3963
3964
3965
3966
3967
3968
3969
3970
3971
3972
3973
3974
3975
3976
3977
3978
3979
3980
3981
3982
3983
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();