Commit 03e73782 authored by Sergey Lyubka's avatar Sergey Lyubka

Made URI handler work

parent 95d88814
...@@ -66,6 +66,7 @@ typedef struct _stati64 file_stat_t; ...@@ -66,6 +66,7 @@ typedef struct _stati64 file_stat_t;
#define S_ISDIR(x) ((x) & _S_IFDIR) #define S_ISDIR(x) ((x) & _S_IFDIR)
#define sleep(x) Sleep((x) * 1000) #define sleep(x) Sleep((x) * 1000)
#define stat(x, y) _stati64((x), (y)) #define stat(x, y) _stati64((x), (y))
#define to64(x) _atoi64(x)
#ifndef va_copy #ifndef va_copy
#define va_copy(x,y) x = y #define va_copy(x,y) x = y
#endif // MINGW #defines va_copy #endif // MINGW #defines va_copy
...@@ -89,6 +90,7 @@ typedef struct stat file_stat_t; ...@@ -89,6 +90,7 @@ typedef struct stat file_stat_t;
#define mutex_unlock(x) pthread_mutex_unlock(x) #define mutex_unlock(x) pthread_mutex_unlock(x)
#define INVALID_SOCKET ((sock_t) -1) #define INVALID_SOCKET ((sock_t) -1)
#define INT64_FMT PRId64 #define INT64_FMT PRId64
#define to64(x) strtoll(x, NULL, 10)
#endif #endif
//#include "mongoose.h" //#include "mongoose.h"
...@@ -120,7 +122,7 @@ struct linked_list_link { struct linked_list_link *prev, *next; }; ...@@ -120,7 +122,7 @@ struct linked_list_link { struct linked_list_link *prev, *next; };
#endif #endif
#ifdef ENABLE_DBG #ifdef ENABLE_DBG
#define DBG(x) do { printf("%s::%s() ", __FILE__, __func__); \ #define DBG(x) do { printf("%s::%s ", __FILE__, __func__); \
printf x; putchar('\n'); fflush(stdout); } while(0) printf x; putchar('\n'); fflush(stdout); } while(0)
#else #else
#define DBG(x) #define DBG(x)
...@@ -197,6 +199,7 @@ struct iobuf { ...@@ -197,6 +199,7 @@ struct iobuf {
union endpoint { union endpoint {
int fd; // Opened regular local file int fd; // Opened regular local file
void *ssl; // SSL descriptor void *ssl; // SSL descriptor
struct uri_handler *uh; // URI handler user function
}; };
enum endpoint_type { EP_NONE, EP_FILE, EP_USER }; enum endpoint_type { EP_NONE, EP_FILE, EP_USER };
...@@ -207,16 +210,15 @@ struct connection { ...@@ -207,16 +210,15 @@ struct connection {
struct linked_list_link link; // Linkage to server->active_connections struct linked_list_link link; // Linkage to server->active_connections
struct mg_server *server; struct mg_server *server;
sock_t client_sock; // Connected client sock_t client_sock; // Connected client
union socket_address csa; // Client's socket address
struct iobuf local_iobuf; struct iobuf local_iobuf;
struct iobuf remote_iobuf; struct iobuf remote_iobuf;
union endpoint endpoint; union endpoint endpoint;
enum endpoint_type endpoint_type; enum endpoint_type endpoint_type;
time_t expire_time; time_t expire_time;
char *path_info; char *path_info;
int request_len; char *request;
int flags; int request_len; // Request length, including last \r\n after last header
int status_code; int flags; // CONN_* flags: CONN_CLOSE, CONN_SPOOL_DONE, etc
mutex_t mutex; // Guards concurrent mg_write() calls mutex_t mutex; // Guards concurrent mg_write() calls
}; };
...@@ -326,20 +328,6 @@ int mg_start_thread(void *(*f)(void *), void *p) { ...@@ -326,20 +328,6 @@ int mg_start_thread(void *(*f)(void *), void *p) {
#endif #endif
} }
#if 0
static int call_user(int event_type, struct mg_connection *conn, void *p) {
if (conn != NULL && conn->server != NULL) {
conn->event.user_data = conn->server->user_data;
conn->event.type = event_type;
conn->event.event_param = p;
conn->event.request_info = &conn->request_info;
conn->event.conn = conn;
}
return !conn || !conn->server || !conn->server->event_handler ? 0 :
conn->server->event_handler(&conn->event);
}
#endif
static void set_close_on_exec(int fd) { static void set_close_on_exec(int fd) {
#ifdef _WIN32 #ifdef _WIN32
(void) SetHandleInformation((HANDLE) fd, HANDLE_FLAG_INHERIT, 0); (void) SetHandleInformation((HANDLE) fd, HANDLE_FLAG_INHERIT, 0);
...@@ -565,29 +553,19 @@ static struct connection *accept_new_connection(struct mg_server *server) { ...@@ -565,29 +553,19 @@ static struct connection *accept_new_connection(struct mg_server *server) {
sock_t sock = INVALID_SOCKET; sock_t sock = INVALID_SOCKET;
struct connection *conn = NULL; struct connection *conn = NULL;
// NOTE(lsm): on Windows, sock is always > FD_SETSIZE
if ((sock = accept(server->listening_sock, &sa.sa, &len)) == INVALID_SOCKET) { if ((sock = accept(server->listening_sock, &sa.sa, &len)) == INVALID_SOCKET) {
#if 0
} else if (sock >= FD_SETSIZE) {
DBG((">fd_setsize"));
closesocket(sock);
#endif
} else if (!check_acl(server->config_options[ACCESS_CONTROL_LIST], } else if (!check_acl(server->config_options[ACCESS_CONTROL_LIST],
ntohl(* (uint32_t *) &sa.sin.sin_addr))) { ntohl(* (uint32_t *) &sa.sin.sin_addr))) {
// NOTE(lsm): check_acl doesn't work for IPv6
closesocket(sock); closesocket(sock);
} else if ((conn = (struct connection *) } else if ((conn = (struct connection *) calloc(1, sizeof(*conn))) == NULL) {
calloc(1, sizeof(*conn) + MAX_REQUEST_SIZE)) == NULL) {
closesocket(sock); closesocket(sock);
} else { } else {
// Put so socket structure into the queue
set_close_on_exec(sock); set_close_on_exec(sock);
set_non_blocking_mode(sock); set_non_blocking_mode(sock);
conn->server = server; conn->server = server;
conn->client_sock = sock; conn->client_sock = sock;
conn->csa = sa;
conn->local_iobuf.buf = (char *) conn + sizeof(*conn);
conn->local_iobuf.size = MAX_REQUEST_SIZE;
conn->remote_iobuf.buf = (char *) calloc(1, IOBUF_SIZE);
conn->remote_iobuf.size = IOBUF_SIZE;
mutex_init(&conn->mutex); mutex_init(&conn->mutex);
LINKED_LIST_ADD_TO_FRONT(&server->active_connections, &conn->link); LINKED_LIST_ADD_TO_FRONT(&server->active_connections, &conn->link);
DBG(("added conn %p", conn)); DBG(("added conn %p", conn));
...@@ -600,7 +578,9 @@ static void close_conn(struct connection *conn) { ...@@ -600,7 +578,9 @@ static void close_conn(struct connection *conn) {
DBG(("closing %p", conn)); DBG(("closing %p", conn));
LINKED_LIST_REMOVE(&conn->link); LINKED_LIST_REMOVE(&conn->link);
closesocket(conn->client_sock); closesocket(conn->client_sock);
free(conn->remote_iobuf.buf); free(conn->request); // It's OK to free(NULL)
free(conn->remote_iobuf.buf); // It's OK to free(NULL)
free(conn->local_iobuf.buf); // It's OK to free(NULL)
mutex_destroy(&conn->mutex); mutex_destroy(&conn->mutex);
free(conn); free(conn);
} }
...@@ -648,6 +628,50 @@ static char *skip(char **buf, const char *delimiters) { ...@@ -648,6 +628,50 @@ static char *skip(char **buf, const char *delimiters) {
return begin_word; return begin_word;
} }
// Protect against directory disclosure attack by removing '..',
// excessive '/' and '\' characters
static void remove_double_dots_and_double_slashes(char *s) {
char *p = s;
while (*s != '\0') {
*p++ = *s++;
if (s[-1] == '/' || s[-1] == '\\') {
// Skip all following slashes, backslashes and double-dots
while (s[0] != '\0') {
if (s[0] == '/' || s[0] == '\\') { s++; }
else if (s[0] == '.' && s[1] == '.') { s += 2; }
else { break; }
}
}
}
*p = '\0';
}
int mg_url_decode(const char *src, int src_len, char *dst,
int dst_len, int is_form_url_encoded) {
int i, j, a, b;
#define HEXTOI(x) (isdigit(x) ? x - '0' : x - 'W')
for (i = j = 0; i < src_len && j < dst_len - 1; i++, j++) {
if (src[i] == '%' && i < src_len - 2 &&
isxdigit(* (const unsigned char *) (src + i + 1)) &&
isxdigit(* (const unsigned char *) (src + i + 2))) {
a = tolower(* (const unsigned char *) (src + i + 1));
b = tolower(* (const unsigned char *) (src + i + 2));
dst[j] = (char) ((HEXTOI(a) << 4) | HEXTOI(b));
i += 2;
} else if (is_form_url_encoded && src[i] == '+') {
dst[j] = ' ';
} else {
dst[j] = src[i];
}
}
dst[j] = '\0'; // Null-terminate the destination
return i >= src_len ? j : -1;
}
// Parse HTTP headers from the given buffer, advance buffer to the point // Parse HTTP headers from the given buffer, advance buffer to the point
// where parsing stopped. // where parsing stopped.
static void parse_http_headers(char **buf, struct mg_connection *ri) { static void parse_http_headers(char **buf, struct mg_connection *ri) {
...@@ -674,12 +698,12 @@ static int is_valid_http_method(const char *method) { ...@@ -674,12 +698,12 @@ static int is_valid_http_method(const char *method) {
// This function modifies the buffer by NUL-terminating // This function modifies the buffer by NUL-terminating
// HTTP request components, header names and header values. // HTTP request components, header names and header values.
static int parse_http_message(char *buf, int len, struct mg_connection *ri) { static int parse_http_message(char *buf, int len, struct mg_connection *ri) {
int is_request, request_len = get_request_len((unsigned char *) buf, len); int is_request, n;
if (request_len > 0) {
// Reset attributes. DO NOT TOUCH is_ssl, remote_ip, remote_port // Reset attributes. DO NOT TOUCH remote_ip, remote_port
ri->request_method = ri->uri = ri->http_version = NULL; ri->request_method = ri->uri = ri->http_version = NULL;
ri->num_headers = 0; ri->num_headers = 0;
buf[request_len - 1] = '\0'; buf[len - 1] = '\0';
// RFC says that all initial whitespaces should be ingored // RFC says that all initial whitespaces should be ingored
while (*buf != '\0' && isspace(* (unsigned char *) buf)) { while (*buf != '\0' && isspace(* (unsigned char *) buf)) {
...@@ -694,15 +718,22 @@ static int parse_http_message(char *buf, int len, struct mg_connection *ri) { ...@@ -694,15 +718,22 @@ static int parse_http_message(char *buf, int len, struct mg_connection *ri) {
is_request = is_valid_http_method(ri->request_method); is_request = is_valid_http_method(ri->request_method);
if ((is_request && memcmp(ri->http_version, "HTTP/", 5) != 0) || if ((is_request && memcmp(ri->http_version, "HTTP/", 5) != 0) ||
(!is_request && memcmp(ri->request_method, "HTTP/", 5) != 0)) { (!is_request && memcmp(ri->request_method, "HTTP/", 5) != 0)) {
request_len = -1; len = -1;
} else { } else {
if (is_request) { if (is_request) {
ri->http_version += 5; ri->http_version += 5;
} }
parse_http_headers(&buf, ri); parse_http_headers(&buf, ri);
if ((ri->query_string = strchr(ri->uri, '?')) != NULL) {
*(char *) ri->query_string++ = '\0';
} }
n = (int) strlen(ri->uri);
mg_url_decode(ri->uri, n, (char *) ri->uri, n + 1, 0);
remove_double_dots_and_double_slashes((char *) ri->uri);
} }
return request_len;
return len;
} }
static int lowercase(const char *s) { static int lowercase(const char *s) {
...@@ -770,50 +801,6 @@ static int match_prefix(const char *pattern, int pattern_len, const char *str) { ...@@ -770,50 +801,6 @@ static int match_prefix(const char *pattern, int pattern_len, const char *str) {
return j; return j;
} }
// Protect against directory disclosure attack by removing '..',
// excessive '/' and '\' characters
static void remove_double_dots_and_double_slashes(char *s) {
char *p = s;
while (*s != '\0') {
*p++ = *s++;
if (s[-1] == '/' || s[-1] == '\\') {
// Skip all following slashes, backslashes and double-dots
while (s[0] != '\0') {
if (s[0] == '/' || s[0] == '\\') { s++; }
else if (s[0] == '.' && s[1] == '.') { s += 2; }
else { break; }
}
}
}
*p = '\0';
}
int mg_url_decode(const char *src, int src_len, char *dst,
int dst_len, int is_form_url_encoded) {
int i, j, a, b;
#define HEXTOI(x) (isdigit(x) ? x - '0' : x - 'W')
for (i = j = 0; i < src_len && j < dst_len - 1; i++, j++) {
if (src[i] == '%' && i < src_len - 2 &&
isxdigit(* (const unsigned char *) (src + i + 1)) &&
isxdigit(* (const unsigned char *) (src + i + 2))) {
a = tolower(* (const unsigned char *) (src + i + 1));
b = tolower(* (const unsigned char *) (src + i + 2));
dst[j] = (char) ((HEXTOI(a) << 4) | HEXTOI(b));
i += 2;
} else if (is_form_url_encoded && src[i] == '+') {
dst[j] = ' ';
} else {
dst[j] = src[i];
}
}
dst[j] = '\0'; // Null-terminate the destination
return i >= src_len ? j : -1;
}
// Return HTTP header value, or NULL if not found. // Return HTTP header value, or NULL if not found.
const char *mg_get_header(const struct mg_connection *ri, const char *s) { const char *mg_get_header(const struct mg_connection *ri, const char *s) {
int i; int i;
...@@ -867,67 +854,40 @@ static int convert_uri_to_file_name(struct connection *conn, char *buf, ...@@ -867,67 +854,40 @@ static int convert_uri_to_file_name(struct connection *conn, char *buf,
} }
static int spool(struct iobuf *io, const void *buf, int len) { static int spool(struct iobuf *io, const void *buf, int len) {
static const float mult = 1.2;
char *p = NULL; char *p = NULL;
int new_len = io->len + len; int new_len = 0;
DBG(("%d %d %d", len, io->len, io->size)); assert(io->len >= 0);
assert(io->len <= io->size);
//DBG(("1. %d %d %d", len, io->len, io->size));
if (len <= 0) { if (len <= 0) {
} else if (new_len < io->size) { } else if ((new_len = io->len + len) < io->size) {
memcpy(io->buf + io->len, buf, len); memcpy(io->buf + io->len, buf, len);
io->len += len; io->len = new_len;
} else if ((p = (char *) realloc(io->buf, new_len * 2)) != NULL) { } else if ((p = (char *) realloc(io->buf, new_len * mult)) != NULL) {
io->buf = p; io->buf = p;
memcpy(io->buf + io->len, buf, len); memcpy(io->buf + io->len, buf, len);
io->len = new_len; io->len = new_len;
io->size = new_len * 2; io->size = new_len * mult;
} else {
len = 0;
}
return len;
}
static int vspool(struct iobuf *io, const char *fmt, va_list ap) {
char *ptr = io->buf;
va_list ap_copy;
int len;
va_copy(ap_copy, ap);
len = vsnprintf(NULL, 0, fmt, ap_copy);
if (len <= 0) {
} else if (len < io->size - io->len ||
(ptr = (char *) realloc(io->buf, io->len + len + 1 - io->size))
!= NULL) {
io->buf = ptr;
vsnprintf(io->buf + io->len, len + 1, fmt, ap);
io->len += len;
} else { } else {
len = 0; len = 0;
} }
DBG(("%d %d %d", len, io->len, io->size));
return len; return len;
} }
int mg_printf(struct mg_connection *c, const char *fmt, ...) {
struct connection *conn = (struct connection *) c;
va_list ap;
int ret;
va_start(ap, fmt);
mutex_lock(&conn->mutex);
ret = vspool(&conn->remote_iobuf, fmt, ap);
mutex_unlock(&conn->mutex);
va_end(ap);
send(conn->server->ctl[1], ".", 1, 0); // Wake up select call
return ret;
}
int mg_write(struct mg_connection *c, const void *buf, int len) { int mg_write(struct mg_connection *c, const void *buf, int len) {
struct connection *conn = (struct connection *) c; struct connection *conn = (struct connection *) c;
int ret; int ret;
mutex_lock(&conn->mutex); mutex_lock(&conn->mutex);
ret = spool(&conn->remote_iobuf, buf, len); ret = spool(&conn->remote_iobuf, buf, len);
mutex_unlock(&conn->mutex); mutex_unlock(&conn->mutex);
send(conn->server->ctl[1], ".", 1, 0); // Wake up select call send(conn->server->ctl[1], ".", 1, 0); // Wake up select call
return ret; return ret;
} }
...@@ -1010,16 +970,15 @@ static void construct_etag(char *buf, size_t buf_len, const file_stat_t *st) { ...@@ -1010,16 +970,15 @@ static void construct_etag(char *buf, size_t buf_len, const file_stat_t *st) {
static void open_file_endpoint(struct connection *conn, const char *path, static void open_file_endpoint(struct connection *conn, const char *path,
file_stat_t *st) { file_stat_t *st) {
char date[64], lm[64], etag[64], range[64]; char date[64], lm[64], etag[64], range[64], headers[500];
const char *msg = "OK", *hdr; const char *msg = "OK", *hdr;
time_t curtime = time(NULL); time_t curtime = time(NULL);
int64_t cl, r1, r2; int64_t cl, r1, r2;
struct vec mime_vec; struct vec mime_vec;
int n; int n, status_code = 200;
conn->endpoint_type = EP_FILE; conn->endpoint_type = EP_FILE;
set_close_on_exec(conn->endpoint.fd); set_close_on_exec(conn->endpoint.fd);
conn->status_code = 200;
get_mime_type(conn->server, path, &mime_vec); get_mime_type(conn->server, path, &mime_vec);
cl = st->st_size; cl = st->st_size;
...@@ -1030,7 +989,7 @@ static void open_file_endpoint(struct connection *conn, const char *path, ...@@ -1030,7 +989,7 @@ static void open_file_endpoint(struct connection *conn, const char *path,
hdr = mg_get_header(&conn->mg_conn, "Range"); hdr = mg_get_header(&conn->mg_conn, "Range");
if (hdr != NULL && (n = parse_range_header(hdr, &r1, &r2)) > 0 && if (hdr != NULL && (n = parse_range_header(hdr, &r1, &r2)) > 0 &&
r1 >= 0 && r2 >= 0) { r1 >= 0 && r2 >= 0) {
conn->status_code = 206; status_code = 206;
cl = n == 2 ? (r2 > cl ? cl : r2) - r1 + 1: cl - r1; cl = n == 2 ? (r2 > cl ? cl : r2) - r1 + 1: cl - r1;
snprintf(range, sizeof(range), "Content-Range: bytes " snprintf(range, sizeof(range), "Content-Range: bytes "
"%" INT64_FMT "-%" INT64_FMT "/%" INT64_FMT "\r\n", "%" INT64_FMT "-%" INT64_FMT "/%" INT64_FMT "\r\n",
...@@ -1045,7 +1004,7 @@ static void open_file_endpoint(struct connection *conn, const char *path, ...@@ -1045,7 +1004,7 @@ static void open_file_endpoint(struct connection *conn, const char *path,
gmt_time_string(lm, sizeof(lm), &st->st_mtime); gmt_time_string(lm, sizeof(lm), &st->st_mtime);
construct_etag(etag, sizeof(etag), st); construct_etag(etag, sizeof(etag), st);
mg_printf(&conn->mg_conn, n = snprintf(headers, sizeof(headers),
"HTTP/1.1 %d %s\r\n" "HTTP/1.1 %d %s\r\n"
"Date: %s\r\n" "Date: %s\r\n"
"Last-Modified: %s\r\n" "Last-Modified: %s\r\n"
...@@ -1055,8 +1014,9 @@ static void open_file_endpoint(struct connection *conn, const char *path, ...@@ -1055,8 +1014,9 @@ static void open_file_endpoint(struct connection *conn, const char *path,
"Connection: %s\r\n" "Connection: %s\r\n"
"Accept-Ranges: bytes\r\n" "Accept-Ranges: bytes\r\n"
"%s%s\r\n", "%s%s\r\n",
conn->status_code, msg, date, lm, etag, (int) mime_vec.len, status_code, msg, date, lm, etag, (int) mime_vec.len,
mime_vec.ptr, cl, "keep-alive", range, EXTRA_HTTP_HEADERS); mime_vec.ptr, cl, "keep-alive", range, EXTRA_HTTP_HEADERS);
spool(&conn->remote_iobuf, headers, n);
if (!strcmp(conn->mg_conn.request_method, "HEAD")) { if (!strcmp(conn->mg_conn.request_method, "HEAD")) {
conn->flags |= CONN_SPOOL_DONE; conn->flags |= CONN_SPOOL_DONE;
...@@ -1135,7 +1095,7 @@ static struct uri_handler *find_uri_handler(struct mg_server *server, ...@@ -1135,7 +1095,7 @@ static struct uri_handler *find_uri_handler(struct mg_server *server,
// For given directory path, substitute it to valid index file. // For given directory path, substitute it to valid index file.
// Return 0 if index file has been found, -1 if not found. // Return 0 if index file has been found, -1 if not found.
// If the file is found, it's stats is returned in stp. // If the file is found, it's stats is returned in stp.
static int substitute_index_file(struct connection *conn, char *path, static int find_index_file(struct connection *conn, char *path,
size_t path_len, file_stat_t *stp) { size_t path_len, file_stat_t *stp) {
const char *list = conn->server->config_options[INDEX_FILES]; const char *list = conn->server->config_options[INDEX_FILES];
file_stat_t st; file_stat_t st;
...@@ -1179,33 +1139,40 @@ static int substitute_index_file(struct connection *conn, char *path, ...@@ -1179,33 +1139,40 @@ static int substitute_index_file(struct connection *conn, char *path,
} }
static void send_http_error(struct connection *conn, const char *fmt, ...) { static void send_http_error(struct connection *conn, const char *fmt, ...) {
char buf[500];
int len;
va_list ap; va_list ap;
va_start(ap, fmt); va_start(ap, fmt);
vspool(&conn->remote_iobuf, fmt, ap); len = vsnprintf(buf, sizeof(buf), fmt, ap);
va_end(ap); va_end(ap);
spool(&conn->remote_iobuf, buf, len);
conn->flags |= CONN_SPOOL_DONE; conn->flags |= CONN_SPOOL_DONE;
} }
static void exec_lua_script(struct connection *conn, const char *path) { static void call_uri_handler_if_data_is_buffered(struct connection *conn) {
send_http_error(conn, "%s", "HTTP/1.1 501 Not Implemented\r\n\r\n"); if (conn->local_iobuf.len >= conn->mg_conn.content_len) {
conn->endpoint.uh->handler(&conn->mg_conn);
close_local_endpoint(conn);
}
} }
static void open_local_endpoint(struct connection *conn) { static void open_local_endpoint(struct connection *conn) {
char path[MAX_PATH_SIZE] = {'\0'}; char path[MAX_PATH_SIZE] = {'\0'};
file_stat_t st; file_stat_t st;
int uri_len, exists = 0, is_directory = 0; int exists = 0, is_directory = 0;
struct uri_handler *uh; const char *cl_hdr = mg_get_header(&conn->mg_conn, "Content-Length");
if ((conn->mg_conn.query_string = strchr(conn->mg_conn.uri, '?')) != NULL) { conn->mg_conn.content_len = cl_hdr == NULL ? 0 : to64(cl_hdr);
* ((char *) conn->mg_conn.query_string++) = '\0';
}
uri_len = (int) strlen(conn->mg_conn.uri);
mg_url_decode(conn->mg_conn.uri, uri_len, (char *) conn->mg_conn.uri,
uri_len + 1, 0);
remove_double_dots_and_double_slashes((char *) conn->mg_conn.uri);
if ((uh = find_uri_handler(conn->server, conn->mg_conn.uri)) != NULL) { // 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->endpoint_type = EP_USER;
conn->mg_conn.content = conn->local_iobuf.buf;
call_uri_handler_if_data_is_buffered(conn);
return;
} }
exists = convert_uri_to_file_name(conn, path, sizeof(path), &st); exists = convert_uri_to_file_name(conn, path, sizeof(path), &st);
...@@ -1213,14 +1180,10 @@ static void open_local_endpoint(struct connection *conn) { ...@@ -1213,14 +1180,10 @@ static void open_local_endpoint(struct connection *conn) {
if (!exists) { if (!exists) {
send_http_error(conn, "%s", "HTTP/1.1 404 Not Found\r\n\r\n"); send_http_error(conn, "%s", "HTTP/1.1 404 Not Found\r\n\r\n");
} else if (is_directory && conn->mg_conn.uri[uri_len - 1] != '/') { } else if (is_directory && !find_index_file(conn, path, sizeof(path), &st)) {
send_http_error(conn, "HTTP/1.1 301 Moved Permanently\r\n"
"Location: %s/\r\n\r\n", conn->mg_conn.uri);
} else if (is_directory &&
!substitute_index_file(conn, path, sizeof(path), &st)) {
send_http_error(conn, "%s", "HTTP/1.1 403 Listing Denied\r\n\r\n"); send_http_error(conn, "%s", "HTTP/1.1 403 Listing Denied\r\n\r\n");
} else if (match_prefix(LUA_SCRIPT_PATTERN, 6, path) > 0) { } else if (match_prefix(LUA_SCRIPT_PATTERN, 6, path) > 0) {
exec_lua_script(conn, path); send_http_error(conn, "%s", "HTTP/1.1 501 Not Implemented\r\n\r\n");
conn->flags |= CONN_SPOOL_DONE; conn->flags |= CONN_SPOOL_DONE;
} else if (is_not_modified(conn, &st)) { } else if (is_not_modified(conn, &st)) {
send_http_error(conn, "%s", "HTTP/1.1 304 Not Modified\r\n\r\n"); send_http_error(conn, "%s", "HTTP/1.1 304 Not Modified\r\n\r\n");
...@@ -1231,40 +1194,54 @@ static void open_local_endpoint(struct connection *conn) { ...@@ -1231,40 +1194,54 @@ static void open_local_endpoint(struct connection *conn) {
} }
} }
static int io_space(const struct iobuf *io) { static void send_continue_if_expected(struct connection *conn) {
return io->size - io->len; 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) {
spool(&conn->remote_iobuf, expect_response, sizeof(expect_response) - 1);
}
} }
static void process_request(struct connection *conn) { static void process_request(struct connection *conn) {
struct iobuf *io = &conn->local_iobuf; struct iobuf *io = &conn->local_iobuf;
//DBG(("parse_http_message(%d [%.*s])", io->len, io->len, io->buf)); //DBG(("parse_http_message(%d [%.*s])", io->len, io->len, io->buf));
if (conn->request_len == 0) { if (conn->request_len == 0 &&
conn->request_len = parse_http_message(io->buf, io->len, &conn->mg_conn); (conn->request_len = get_request_len((unsigned char *) 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 ivalid.
conn->request = (char *) malloc(conn->request_len);
memcpy(conn->request, io->buf, conn->request_len);
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(("request_len = %d", conn->request_len));
} }
DBG(("parse_http_message() -> %d", conn->request_len));
if (conn->request_len < 0 || if (conn->request_len < 0 ||
(conn->request_len == 0 && io_space(io) <= 0)) { (conn->request_len == 0 && io->len > MAX_REQUEST_SIZE)) {
// Invalid request, or request is too big: close the connection // Invalid request, or request is too big: close the connection
conn->flags |= CONN_CLOSE; conn->flags |= CONN_CLOSE;
} else if (conn->request_len > 0 && conn->endpoint_type == EP_NONE) { } else if (conn->request_len > 0 && conn->endpoint_type == EP_NONE) {
send_continue_if_expected(conn);
open_local_endpoint(conn); open_local_endpoint(conn);
} else if (conn->endpoint_type == EP_USER) {
call_uri_handler_if_data_is_buffered(conn);
} }
} }
static void read_from_client(struct connection *conn) { static void read_from_client(struct connection *conn) {
struct iobuf *io = &conn->local_iobuf; char buf[IOBUF_SIZE];
int n = recv(conn->client_sock, io->buf + io->len, io->size - io->len, 0); int n = recv(conn->client_sock, buf, sizeof(buf), 0);
//DBG(("Read: %d [%.*s]", n, n, io->buf + io->len));
assert(io->len >= 0);
assert(io->len <= io->size);
if (is_error(n)) { if (is_error(n)) {
conn->flags |= CONN_CLOSE; conn->flags |= CONN_CLOSE;
} else if (n > 0) { } else if (n > 0) {
io->len += n; spool(&conn->local_iobuf, buf, n);
process_request(conn); process_request(conn);
} }
} }
...@@ -1279,22 +1256,22 @@ static int should_keep_alive(const struct connection *conn) { ...@@ -1279,22 +1256,22 @@ static int should_keep_alive(const struct connection *conn) {
} }
static void close_local_endpoint(struct connection *conn) { static void close_local_endpoint(struct connection *conn) {
struct iobuf *io = &conn->local_iobuf; int keep_alive = should_keep_alive(conn); // Must be done before free()
int keep_alive = should_keep_alive(conn); // Must be done before memmove
//DBG(("Closing for conn %p", conn));
// Close file descriptor // Close file descriptor
switch (conn->endpoint_type) { switch (conn->endpoint_type) {
case EP_FILE: close(conn->endpoint.fd); break; case EP_FILE: close(conn->endpoint.fd); break;
case EP_USER: break;
case EP_NONE: break; case EP_NONE: break;
default: assert(0); break; default: assert(0); break;
} }
// Get rid of that request from the buffer. NOTE: order is important here
assert(conn->request_len <= io->len);
memmove(io->buf, io->buf + conn->request_len, io->len - conn->request_len);
io->len -= conn->request_len;
conn->endpoint_type = EP_NONE; conn->endpoint_type = EP_NONE;
conn->request_len = 0; conn->request_len = 0;
free(conn->request);
conn->request = NULL;
if (keep_alive) { if (keep_alive) {
process_request(conn); // Can call us recursively if pipelining is used process_request(conn); // Can call us recursively if pipelining is used
...@@ -1304,16 +1281,13 @@ static void close_local_endpoint(struct connection *conn) { ...@@ -1304,16 +1281,13 @@ static void close_local_endpoint(struct connection *conn) {
} }
static void transfer_file_data(struct connection *conn) { static void transfer_file_data(struct connection *conn) {
struct iobuf *io = &conn->remote_iobuf; char buf[IOBUF_SIZE];
int n, rem_space = io_space(io); int n = read(conn->endpoint.fd, buf, sizeof(buf));
if (rem_space <= 0) return;
n = read(conn->endpoint.fd, io->buf + io->len, rem_space);
if (is_error(n)) { if (is_error(n)) {
close_local_endpoint(conn); close_local_endpoint(conn);
} else if (n > 0) { } else if (n > 0) {
io->len += n; spool(&conn->remote_iobuf, buf, n);
} }
} }
...@@ -1488,22 +1462,6 @@ struct mg_server *mg_create_server(void *server_data) { ...@@ -1488,22 +1462,6 @@ struct mg_server *mg_create_server(void *server_data) {
server->server_data = server_data; server->server_data = server_data;
server->listening_sock = INVALID_SOCKET; server->listening_sock = INVALID_SOCKET;
#if 0
while (opts != NULL && (name = *opts++) != NULL) {
if ((i = get_option_index(name)) == -1) {
snprintf(error_msg, sizeof(error_msg), "Invalid option: [%s]", name);
} else if ((value = *opts++) == NULL) {
snprintf(error_msg, sizeof(error_msg), "[%s] cannot be NULL", name);
} else {
if (server->config_options[i] != NULL) {
free(server->config_options[i]);
}
server->config_options[i] = mg_strdup(value);
DBG(("[%s] -> [%s]", name, value));
}
}
#endif
// Set default options values // Set default options values
for (i = 0; static_config_options[i * 2] != NULL; i++) { for (i = 0; static_config_options[i * 2] != NULL; i++) {
value = static_config_options[i * 2 + 1]; value = static_config_options[i * 2 + 1];
...@@ -1518,7 +1476,9 @@ struct mg_server *mg_create_server(void *server_data) { ...@@ -1518,7 +1476,9 @@ struct mg_server *mg_create_server(void *server_data) {
// End of library, start of the application code // End of library, start of the application code
static void iterate_callback(struct mg_connection *c, void *param) { static void iterate_callback(struct mg_connection *c, void *param) {
if (c->connection_param != NULL) {
mg_write(c, "%d", * (int *) param); mg_write(c, "%d", * (int *) param);
}
} }
static void *timer_thread(void *param) { static void *timer_thread(void *param) {
...@@ -1534,7 +1494,17 @@ static void *timer_thread(void *param) { ...@@ -1534,7 +1494,17 @@ static void *timer_thread(void *param) {
} }
static int websocket_handler(struct mg_connection *conn) { static int websocket_handler(struct mg_connection *conn) {
mg_printf(conn, "%s", "HTTP/1.0 200 OK\r\n\r\n:-)\n"); char headers[500], content[500];
int headers_len, content_len;
content_len = snprintf(content, sizeof(content), "%s %s, POST len %d\n",
conn->request_method, conn->uri, conn->content_len);
headers_len = snprintf(headers, sizeof(headers), "HTTP/1.0 200 OK\r\n"
"Content-Length: %d\r\n\r\n", content_len);
mg_write(conn, headers, headers_len);
mg_write(conn, content, content_len);
return 1; return 1;
} }
...@@ -1549,7 +1519,7 @@ int main(void) { ...@@ -1549,7 +1519,7 @@ int main(void) {
printf("Started on port %s\n", mg_get_option(server, "listening_port")); printf("Started on port %s\n", mg_get_option(server, "listening_port"));
for (;;) { for (;;) {
mg_poll_server(server, 1000); mg_poll_server(server, 3000);
} }
mg_destroy_server(&server); mg_destroy_server(&server);
......
...@@ -33,15 +33,19 @@ struct mg_connection { ...@@ -33,15 +33,19 @@ struct mg_connection {
const char *uri; // URL-decoded URI const char *uri; // URL-decoded URI
const char *http_version; // E.g. "1.0", "1.1" const char *http_version; // E.g. "1.0", "1.1"
const char *query_string; // URL part after '?', not including '?', or NULL const char *query_string; // URL part after '?', not including '?', or NULL
long remote_ip; // Client's IP address long remote_ip; // Client's IP address
int remote_port; // Client's port int remote_port; // Client's port
int is_ssl; // 1 if SSL-ed, 0 if not
int num_headers; // Number of HTTP headers int num_headers; // Number of HTTP headers
struct mg_header { struct mg_header {
const char *name; // HTTP header name const char *name; // HTTP header name
const char *value; // HTTP header value const char *value; // HTTP header value
} http_headers[64]; // Maximum 64 headers } http_headers[64]; // Maximum 64 headers
char *content; // POST (or websocket message) data, or NULL
int content_len; // content length
void *server_param; // Parameter passed to mg_add_uri_handler() void *server_param; // Parameter passed to mg_add_uri_handler()
void *connection_param; // Placeholder for connection-specific data void *connection_param; // Placeholder for connection-specific data
}; };
...@@ -64,16 +68,16 @@ const char **mg_get_valid_option_names(void); ...@@ -64,16 +68,16 @@ const char **mg_get_valid_option_names(void);
const char *mg_get_option(const struct mg_server *server, const char *name); const char *mg_get_option(const struct mg_server *server, const char *name);
// Websocket functions // Websocket functions
void mg_websocket_handshake(struct mg_connection *); //void mg_websocket_handshake(struct mg_connection *);
int mg_websocket_read(struct mg_connection *, int *bits, char **data); //int mg_websocket_read(struct mg_connection *, int *bits, char **data);
int mg_websocket_write(struct mg_connection* conn, int opcode, //int mg_websocket_write(struct mg_connection* conn, int opcode,
const char *data, size_t data_len); // const char *data, size_t data_len);
// Connection management functions // Connection management functions
int mg_write(struct mg_connection *, const void *buf, int len); int mg_write(struct mg_connection *, const void *buf, int len);
int mg_printf(struct mg_connection *, const char *fmt, ...);
#if 0 #if 0
int mg_printf(struct mg_connection *, const char *fmt, ...);
void mg_send_file(struct mg_connection *, const char *path); void mg_send_file(struct mg_connection *, const char *path);
int mg_read(struct mg_connection *, void *buf, int len); int mg_read(struct mg_connection *, void *buf, int len);
const char *mg_get_header(const struct mg_connection *, const char *name); const char *mg_get_header(const struct mg_connection *, const char *name);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment