|
@@ -518,6 +518,14 @@ enum {
|
|
GLOBAL_PASSWORDS_FILE, INDEX_FILES, ENABLE_KEEP_ALIVE, ACCESS_CONTROL_LIST,
|
|
GLOBAL_PASSWORDS_FILE, INDEX_FILES, ENABLE_KEEP_ALIVE, ACCESS_CONTROL_LIST,
|
|
EXTRA_MIME_TYPES, LISTENING_PORTS, DOCUMENT_ROOT, SSL_CERTIFICATE,
|
|
EXTRA_MIME_TYPES, LISTENING_PORTS, DOCUMENT_ROOT, SSL_CERTIFICATE,
|
|
NUM_THREADS, RUN_AS_USER, REWRITE, HIDE_FILES, REQUEST_TIMEOUT,
|
|
NUM_THREADS, RUN_AS_USER, REWRITE, HIDE_FILES, REQUEST_TIMEOUT,
|
|
|
|
+
|
|
|
|
+#if defined(USE_LUA)
|
|
|
|
+ LUA_SCRIPT_EXTENSIONS, LUA_SERVER_PAGE_EXTENSIONS,
|
|
|
|
+#endif
|
|
|
|
+#if defined(USE_WEBSOCKET)
|
|
|
|
+ WEBSOCKET_ROOT,
|
|
|
|
+#endif
|
|
|
|
+
|
|
NUM_OPTIONS
|
|
NUM_OPTIONS
|
|
};
|
|
};
|
|
|
|
|
|
@@ -535,7 +543,11 @@ static const char *config_options[] = {
|
|
"error_log_file", NULL,
|
|
"error_log_file", NULL,
|
|
"global_auth_file", NULL,
|
|
"global_auth_file", NULL,
|
|
"index_files",
|
|
"index_files",
|
|
- "index.html,index.htm,index.cgi,index.shtml,index.php,index.lp",
|
|
|
|
|
|
+#ifdef USE_LUA
|
|
|
|
+ "index.html,index.htm,index.lp,index.lsp,index.lua,index.cgi,index.shtml,index.php",
|
|
|
|
+#else
|
|
|
|
+ "index.html,index.htm,index.cgi,index.shtml,index.php",
|
|
|
|
+#endif
|
|
"enable_keep_alive", "no",
|
|
"enable_keep_alive", "no",
|
|
"access_control_list", NULL,
|
|
"access_control_list", NULL,
|
|
"extra_mime_types", NULL,
|
|
"extra_mime_types", NULL,
|
|
@@ -547,6 +559,15 @@ static const char *config_options[] = {
|
|
"url_rewrite_patterns", NULL,
|
|
"url_rewrite_patterns", NULL,
|
|
"hide_files_patterns", NULL,
|
|
"hide_files_patterns", NULL,
|
|
"request_timeout_ms", "30000",
|
|
"request_timeout_ms", "30000",
|
|
|
|
+
|
|
|
|
+#if defined(USE_LUA)
|
|
|
|
+ "lua_script_pattern", "**.lua$",
|
|
|
|
+ "lua_server_page_pattern", "**.lp$|**.lsp$",
|
|
|
|
+#endif
|
|
|
|
+#if defined(USE_WEBSOCKET)
|
|
|
|
+ "websocket_root", NULL,
|
|
|
|
+#endif
|
|
|
|
+
|
|
NULL
|
|
NULL
|
|
};
|
|
};
|
|
|
|
|
|
@@ -610,6 +631,9 @@ struct mg_connection {
|
|
int64_t last_throttle_bytes;/* Bytes sent this second */
|
|
int64_t last_throttle_bytes;/* Bytes sent this second */
|
|
pthread_mutex_t mutex; /* Used by mg_lock/mg_unlock to ensure atomic
|
|
pthread_mutex_t mutex; /* Used by mg_lock/mg_unlock to ensure atomic
|
|
transmissions for websockets */
|
|
transmissions for websockets */
|
|
|
|
+#if defined(USE_LUA) && defined(USE_WEBSOCKET)
|
|
|
|
+ void * lua_websocket_state; /* Lua_State for a websocket connection */
|
|
|
|
+#endif
|
|
};
|
|
};
|
|
|
|
|
|
static pthread_key_t sTlsKey; /* Thread local storage index */
|
|
static pthread_key_t sTlsKey; /* Thread local storage index */
|
|
@@ -629,6 +653,10 @@ struct de {
|
|
struct file file;
|
|
struct file file;
|
|
};
|
|
};
|
|
|
|
|
|
|
|
+#if defined(USE_WEBSOCKET)
|
|
|
|
+static int is_websocket_request(const struct mg_connection *conn);
|
|
|
|
+#endif
|
|
|
|
+
|
|
const char **mg_get_valid_option_names(void)
|
|
const char **mg_get_valid_option_names(void)
|
|
{
|
|
{
|
|
return config_options;
|
|
return config_options;
|
|
@@ -709,13 +737,27 @@ static void sockaddr_to_string(char *buf, size_t len,
|
|
(void *) &usa->sin.sin_addr :
|
|
(void *) &usa->sin.sin_addr :
|
|
(void *) &usa->sin6.sin6_addr, buf, len);
|
|
(void *) &usa->sin6.sin6_addr, buf, len);
|
|
#elif defined(_WIN32)
|
|
#elif defined(_WIN32)
|
|
- /* Only Windoze Vista (and newer) have inet_ntop() */
|
|
|
|
|
|
+ /* Only Windows Vista (and newer) have inet_ntop() */
|
|
strncpy(buf, inet_ntoa(usa->sin.sin_addr), len);
|
|
strncpy(buf, inet_ntoa(usa->sin.sin_addr), len);
|
|
#else
|
|
#else
|
|
inet_ntop(usa->sa.sa_family, (void *) &usa->sin.sin_addr, buf, len);
|
|
inet_ntop(usa->sa.sa_family, (void *) &usa->sin.sin_addr, buf, len);
|
|
#endif
|
|
#endif
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/* Convert time_t to a string. According to RFC2616, Sec 14.18, this must be included in all responses other than 100, 101, 5xx. */
|
|
|
|
+static void gmt_time_string(char *buf, size_t buf_len, time_t *t)
|
|
|
|
+{
|
|
|
|
+ struct tm *tm;
|
|
|
|
+
|
|
|
|
+ tm = gmtime(t);
|
|
|
|
+ if (tm != NULL) {
|
|
|
|
+ strftime(buf, buf_len, "%a, %d %b %Y %H:%M:%S GMT", tm);
|
|
|
|
+ } else {
|
|
|
|
+ strncpy(buf, "Thu, 01 Jan 1970 00:00:00 GMT", buf_len);
|
|
|
|
+ buf[buf_len - 1] = '\0';
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
/* Print error message to the opened error log stream. */
|
|
/* Print error message to the opened error log stream. */
|
|
void mg_cry(struct mg_connection *conn, const char *fmt, ...)
|
|
void mg_cry(struct mg_connection *conn, const char *fmt, ...)
|
|
{
|
|
{
|
|
@@ -1072,12 +1114,16 @@ static void send_http_error(struct mg_connection *conn, int status,
|
|
char buf[MG_BUF_LEN];
|
|
char buf[MG_BUF_LEN];
|
|
va_list ap;
|
|
va_list ap;
|
|
int len = 0;
|
|
int len = 0;
|
|
|
|
+ char date[64];
|
|
|
|
+ time_t curtime = time(NULL);
|
|
|
|
|
|
conn->status_code = status;
|
|
conn->status_code = status;
|
|
if (conn->ctx->callbacks.http_error == NULL ||
|
|
if (conn->ctx->callbacks.http_error == NULL ||
|
|
conn->ctx->callbacks.http_error(conn, status)) {
|
|
conn->ctx->callbacks.http_error(conn, status)) {
|
|
buf[0] = '\0';
|
|
buf[0] = '\0';
|
|
|
|
|
|
|
|
+ gmt_time_string(date, sizeof(date), &curtime);
|
|
|
|
+
|
|
/* Errors 1xx, 204 and 304 MUST NOT send a body */
|
|
/* Errors 1xx, 204 and 304 MUST NOT send a body */
|
|
if (status > 199 && status != 204 && status != 304) {
|
|
if (status > 199 && status != 204 && status != 304) {
|
|
len = mg_snprintf(conn, buf, sizeof(buf), "Error %d: %s", status, reason);
|
|
len = mg_snprintf(conn, buf, sizeof(buf), "Error %d: %s", status, reason);
|
|
@@ -1090,9 +1136,11 @@ static void send_http_error(struct mg_connection *conn, int status,
|
|
DEBUG_TRACE(("[%s]", buf));
|
|
DEBUG_TRACE(("[%s]", buf));
|
|
|
|
|
|
mg_printf(conn, "HTTP/1.1 %d %s\r\n"
|
|
mg_printf(conn, "HTTP/1.1 %d %s\r\n"
|
|
- "Content-Length: %d\r\n"
|
|
|
|
- "Connection: %s\r\n\r\n", status, reason, len,
|
|
|
|
- suggest_connection_header(conn));
|
|
|
|
|
|
+ "Content-Length: %d\r\n"
|
|
|
|
+ "Date: %s\r\n"
|
|
|
|
+ "Connection: %s\r\n\r\n",
|
|
|
|
+ status, reason, len, date,
|
|
|
|
+ suggest_connection_header(conn));
|
|
conn->num_bytes_sent += mg_printf(conn, "%s", buf);
|
|
conn->num_bytes_sent += mg_printf(conn, "%s", buf);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@@ -2199,7 +2247,8 @@ int mg_get_cookie(const char *cookie_header, const char *var_name,
|
|
}
|
|
}
|
|
|
|
|
|
static void convert_uri_to_file_name(struct mg_connection *conn, char *buf,
|
|
static void convert_uri_to_file_name(struct mg_connection *conn, char *buf,
|
|
- size_t buf_len, struct file *filep)
|
|
|
|
|
|
+ size_t buf_len, struct file *filep,
|
|
|
|
+ int * is_script_ressource)
|
|
{
|
|
{
|
|
struct vec a, b;
|
|
struct vec a, b;
|
|
const char *rewrite, *uri = conn->request_info.uri,
|
|
const char *rewrite, *uri = conn->request_info.uri,
|
|
@@ -2209,6 +2258,14 @@ static void convert_uri_to_file_name(struct mg_connection *conn, char *buf,
|
|
char gz_path[PATH_MAX];
|
|
char gz_path[PATH_MAX];
|
|
char const* accept_encoding;
|
|
char const* accept_encoding;
|
|
|
|
|
|
|
|
+ *is_script_ressource = 0;
|
|
|
|
+
|
|
|
|
+#if defined(USE_WEBSOCKET)
|
|
|
|
+ if (is_websocket_request(conn) && conn->ctx->config[WEBSOCKET_ROOT]) {
|
|
|
|
+ root = conn->ctx->config[WEBSOCKET_ROOT];
|
|
|
|
+ }
|
|
|
|
+#endif
|
|
|
|
+
|
|
/* Using buf_len - 1 because memmove() for PATH_INFO may shift part
|
|
/* Using buf_len - 1 because memmove() for PATH_INFO may shift part
|
|
of the path one byte on the right.
|
|
of the path one byte on the right.
|
|
If document_root is NULL, leave the file empty. */
|
|
If document_root is NULL, leave the file empty. */
|
|
@@ -2247,9 +2304,14 @@ static void convert_uri_to_file_name(struct mg_connection *conn, char *buf,
|
|
for (p = buf + strlen(buf); p > buf + 1; p--) {
|
|
for (p = buf + strlen(buf); p > buf + 1; p--) {
|
|
if (*p == '/') {
|
|
if (*p == '/') {
|
|
*p = '\0';
|
|
*p = '\0';
|
|
- if (match_prefix(conn->ctx->config[CGI_EXTENSIONS],
|
|
|
|
- (int)strlen(conn->ctx->config[CGI_EXTENSIONS]), buf) > 0 &&
|
|
|
|
- mg_stat(conn, buf, filep)) {
|
|
|
|
|
|
+ if ((match_prefix(conn->ctx->config[CGI_EXTENSIONS],
|
|
|
|
+ (int)strlen(conn->ctx->config[CGI_EXTENSIONS]), buf) > 0
|
|
|
|
+#ifdef USE_LUA
|
|
|
|
+ ||
|
|
|
|
+ match_prefix(conn->ctx->config[LUA_SCRIPT_EXTENSIONS],
|
|
|
|
+ (int)strlen(conn->ctx->config[LUA_SCRIPT_EXTENSIONS]), buf) > 0
|
|
|
|
+#endif
|
|
|
|
+ ) && mg_stat(conn, buf, filep)) {
|
|
/* Shift PATH_INFO block one character right, e.g.
|
|
/* Shift PATH_INFO block one character right, e.g.
|
|
"/x.cgi/foo/bar\x00" => "/x.cgi\x00/foo/bar\x00"
|
|
"/x.cgi/foo/bar\x00" => "/x.cgi\x00/foo/bar\x00"
|
|
conn->path_info is pointing to the local variable "path"
|
|
conn->path_info is pointing to the local variable "path"
|
|
@@ -2259,6 +2321,7 @@ static void convert_uri_to_file_name(struct mg_connection *conn, char *buf,
|
|
memmove(p + 2, p + 1, strlen(p + 1) + 1); /* +1 is for
|
|
memmove(p + 2, p + 1, strlen(p + 1) + 1); /* +1 is for
|
|
trailing \0 */
|
|
trailing \0 */
|
|
p[1] = '/';
|
|
p[1] = '/';
|
|
|
|
+ *is_script_ressource = 1;
|
|
break;
|
|
break;
|
|
} else {
|
|
} else {
|
|
*p = '/';
|
|
*p = '/';
|
|
@@ -2766,12 +2829,21 @@ static int check_authorization(struct mg_connection *conn, const char *path)
|
|
|
|
|
|
static void send_authorization_request(struct mg_connection *conn)
|
|
static void send_authorization_request(struct mg_connection *conn)
|
|
{
|
|
{
|
|
|
|
+ char date[64];
|
|
|
|
+ time_t curtime = time(NULL);
|
|
|
|
+
|
|
conn->status_code = 401;
|
|
conn->status_code = 401;
|
|
|
|
+ conn->must_close = 1;
|
|
|
|
+
|
|
|
|
+ gmt_time_string(date, sizeof(date), &curtime);
|
|
|
|
+
|
|
mg_printf(conn,
|
|
mg_printf(conn,
|
|
"HTTP/1.1 401 Unauthorized\r\n"
|
|
"HTTP/1.1 401 Unauthorized\r\n"
|
|
|
|
+ "Date: %s\r\n"
|
|
|
|
+ "Connection: %s\r\n"
|
|
"Content-Length: 0\r\n"
|
|
"Content-Length: 0\r\n"
|
|
- "WWW-Authenticate: Digest qop=\"auth\", "
|
|
|
|
- "realm=\"%s\", nonce=\"%lu\"\r\n\r\n",
|
|
|
|
|
|
+ "WWW-Authenticate: Digest qop=\"auth\", realm=\"%s\", nonce=\"%lu\"\r\n\r\n",
|
|
|
|
+ date, suggest_connection_header(conn),
|
|
conn->ctx->config[AUTHENTICATION_DOMAIN],
|
|
conn->ctx->config[AUTHENTICATION_DOMAIN],
|
|
(unsigned long) time(NULL));
|
|
(unsigned long) time(NULL));
|
|
}
|
|
}
|
|
@@ -3121,6 +3193,8 @@ static void handle_directory_request(struct mg_connection *conn,
|
|
{
|
|
{
|
|
int i, sort_direction;
|
|
int i, sort_direction;
|
|
struct dir_scan_data data = { NULL, 0, 128 };
|
|
struct dir_scan_data data = { NULL, 0, 128 };
|
|
|
|
+ char date[64];
|
|
|
|
+ time_t curtime = time(NULL);
|
|
|
|
|
|
if (!scan_directory(conn, dir, &data, dir_scan_callback)) {
|
|
if (!scan_directory(conn, dir, &data, dir_scan_callback)) {
|
|
send_http_error(conn, 500, "Cannot open directory",
|
|
send_http_error(conn, 500, "Cannot open directory",
|
|
@@ -3128,14 +3202,17 @@ static void handle_directory_request(struct mg_connection *conn,
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ gmt_time_string(date, sizeof(date), &curtime);
|
|
|
|
+
|
|
sort_direction = conn->request_info.query_string != NULL &&
|
|
sort_direction = conn->request_info.query_string != NULL &&
|
|
conn->request_info.query_string[1] == 'd' ? 'a' : 'd';
|
|
conn->request_info.query_string[1] == 'd' ? 'a' : 'd';
|
|
|
|
|
|
conn->must_close = 1;
|
|
conn->must_close = 1;
|
|
- mg_printf(conn, "%s",
|
|
|
|
- "HTTP/1.1 200 OK\r\n"
|
|
|
|
- "Connection: close\r\n"
|
|
|
|
- "Content-Type: text/html; charset=utf-8\r\n\r\n");
|
|
|
|
|
|
+ mg_printf(conn, "HTTP/1.1 200 OK\r\n"
|
|
|
|
+ "Date: %s\r\n"
|
|
|
|
+ "Connection: close\r\n"
|
|
|
|
+ "Content-Type: text/html; charset=utf-8\r\n\r\n",
|
|
|
|
+ date);
|
|
|
|
|
|
conn->num_bytes_sent += mg_printf(conn,
|
|
conn->num_bytes_sent += mg_printf(conn,
|
|
"<html><head><title>Index of %s</title>"
|
|
"<html><head><title>Index of %s</title>"
|
|
@@ -3218,19 +3295,6 @@ static int parse_range_header(const char *header, int64_t *a, int64_t *b)
|
|
return sscanf(header, "bytes=%" INT64_FMT "-%" INT64_FMT, a, b);
|
|
return sscanf(header, "bytes=%" INT64_FMT "-%" INT64_FMT, a, b);
|
|
}
|
|
}
|
|
|
|
|
|
-static void gmt_time_string(char *buf, size_t buf_len, time_t *t)
|
|
|
|
-{
|
|
|
|
- struct tm *tm;
|
|
|
|
-
|
|
|
|
- tm = gmtime(t);
|
|
|
|
- if (tm != NULL) {
|
|
|
|
- strftime(buf, buf_len, "%a, %d %b %Y %H:%M:%S GMT", tm);
|
|
|
|
- } else {
|
|
|
|
- strncpy(buf, "Thu, 01 Jan 1970 00:00:00 GMT", buf_len);
|
|
|
|
- buf[buf_len - 1] = '\0';
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
static void construct_etag(char *buf, size_t buf_len,
|
|
static void construct_etag(char *buf, size_t buf_len,
|
|
const struct file *filep)
|
|
const struct file *filep)
|
|
{
|
|
{
|
|
@@ -3927,6 +3991,9 @@ static void mkcol(struct mg_connection *conn, const char *path)
|
|
{
|
|
{
|
|
int rc, body_len;
|
|
int rc, body_len;
|
|
struct de de;
|
|
struct de de;
|
|
|
|
+ char date[64];
|
|
|
|
+ time_t curtime = time(NULL);
|
|
|
|
+
|
|
memset(&de.file, 0, sizeof(de.file));
|
|
memset(&de.file, 0, sizeof(de.file));
|
|
if (!mg_stat(conn, path, &de.file)) {
|
|
if (!mg_stat(conn, path, &de.file)) {
|
|
mg_cry(conn, "%s: mg_stat(%s) failed: %s",
|
|
mg_cry(conn, "%s: mg_stat(%s) failed: %s",
|
|
@@ -3950,7 +4017,9 @@ static void mkcol(struct mg_connection *conn, const char *path)
|
|
|
|
|
|
if (rc == 0) {
|
|
if (rc == 0) {
|
|
conn->status_code = 201;
|
|
conn->status_code = 201;
|
|
- mg_printf(conn, "HTTP/1.1 %d Created\r\n\r\n", conn->status_code);
|
|
|
|
|
|
+ gmt_time_string(date, sizeof(date), &curtime);
|
|
|
|
+ mg_printf(conn, "HTTP/1.1 %d Created\r\nDate: %s\r\nContent-Length: 0\r\nConnection: %s\r\n\r\n",
|
|
|
|
+ conn->status_code, date, suggest_connection_header(conn));
|
|
} else if (rc == -1) {
|
|
} else if (rc == -1) {
|
|
if(errno == EEXIST)
|
|
if(errno == EEXIST)
|
|
send_http_error(conn, 405, "Method Not Allowed",
|
|
send_http_error(conn, 405, "Method Not Allowed",
|
|
@@ -3973,11 +4042,15 @@ static void put_file(struct mg_connection *conn, const char *path)
|
|
const char *range;
|
|
const char *range;
|
|
int64_t r1, r2;
|
|
int64_t r1, r2;
|
|
int rc;
|
|
int rc;
|
|
|
|
+ char date[64];
|
|
|
|
+ time_t curtime = time(NULL);
|
|
|
|
|
|
conn->status_code = mg_stat(conn, path, &file) ? 200 : 201;
|
|
conn->status_code = mg_stat(conn, path, &file) ? 200 : 201;
|
|
|
|
|
|
if ((rc = put_dir(conn, path)) == 0) {
|
|
if ((rc = put_dir(conn, path)) == 0) {
|
|
- mg_printf(conn, "HTTP/1.1 %d OK\r\n\r\n", conn->status_code);
|
|
|
|
|
|
+ gmt_time_string(date, sizeof(date), &curtime);
|
|
|
|
+ mg_printf(conn, "HTTP/1.1 %d OK\r\nDate: %s\r\nContent-Length: 0\r\nConnection: %s\r\n\r\n",
|
|
|
|
+ conn->status_code, date, suggest_connection_header(conn));
|
|
} else if (rc == -1) {
|
|
} else if (rc == -1) {
|
|
send_http_error(conn, 500, http_500_error,
|
|
send_http_error(conn, 500, http_500_error,
|
|
"put_dir(%s): %s", path, strerror(ERRNO));
|
|
"put_dir(%s): %s", path, strerror(ERRNO));
|
|
@@ -3996,8 +4069,9 @@ static void put_file(struct mg_connection *conn, const char *path)
|
|
if (!forward_body_data(conn, file.fp, INVALID_SOCKET, NULL)) {
|
|
if (!forward_body_data(conn, file.fp, INVALID_SOCKET, NULL)) {
|
|
conn->status_code = 500;
|
|
conn->status_code = 500;
|
|
}
|
|
}
|
|
- mg_printf(conn, "HTTP/1.1 %d OK\r\nContent-Length: 0\r\n\r\n",
|
|
|
|
- conn->status_code);
|
|
|
|
|
|
+ gmt_time_string(date, sizeof(date), &curtime);
|
|
|
|
+ mg_printf(conn, "HTTP/1.1 %d OK\r\nDate: %s\r\nContent-Length: 0\r\nConnection: %s\r\n\r\n",
|
|
|
|
+ conn->status_code, date, suggest_connection_header(conn));
|
|
mg_fclose(&file);
|
|
mg_fclose(&file);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@@ -4147,16 +4221,21 @@ static void handle_ssi_file_request(struct mg_connection *conn,
|
|
const char *path)
|
|
const char *path)
|
|
{
|
|
{
|
|
struct file file = STRUCT_FILE_INITIALIZER;
|
|
struct file file = STRUCT_FILE_INITIALIZER;
|
|
|
|
+ char date[64];
|
|
|
|
+ time_t curtime = time(NULL);
|
|
|
|
|
|
if (!mg_fopen(conn, path, "rb", &file)) {
|
|
if (!mg_fopen(conn, path, "rb", &file)) {
|
|
send_http_error(conn, 500, http_500_error, "fopen(%s): %s", path,
|
|
send_http_error(conn, 500, http_500_error, "fopen(%s): %s", path,
|
|
strerror(ERRNO));
|
|
strerror(ERRNO));
|
|
} else {
|
|
} else {
|
|
conn->must_close = 1;
|
|
conn->must_close = 1;
|
|
|
|
+ gmt_time_string(date, sizeof(date), &curtime);
|
|
fclose_on_exec(&file, conn);
|
|
fclose_on_exec(&file, conn);
|
|
mg_printf(conn, "HTTP/1.1 200 OK\r\n"
|
|
mg_printf(conn, "HTTP/1.1 200 OK\r\n"
|
|
- "Content-Type: text/html\r\nConnection: %s\r\n\r\n",
|
|
|
|
- suggest_connection_header(conn));
|
|
|
|
|
|
+ "Date: %s\r\n"
|
|
|
|
+ "Content-Type: text/html\r\n"
|
|
|
|
+ "Connection: %s\r\n\r\n",
|
|
|
|
+ date, suggest_connection_header(conn));
|
|
send_ssi_file(conn, path, &file, 0);
|
|
send_ssi_file(conn, path, &file, 0);
|
|
mg_fclose(&file);
|
|
mg_fclose(&file);
|
|
}
|
|
}
|
|
@@ -4164,11 +4243,19 @@ static void handle_ssi_file_request(struct mg_connection *conn,
|
|
|
|
|
|
static void send_options(struct mg_connection *conn)
|
|
static void send_options(struct mg_connection *conn)
|
|
{
|
|
{
|
|
|
|
+ char date[64];
|
|
|
|
+ time_t curtime = time(NULL);
|
|
|
|
+
|
|
conn->status_code = 200;
|
|
conn->status_code = 200;
|
|
|
|
+ conn->must_close = 1;
|
|
|
|
+ gmt_time_string(date, sizeof(date), &curtime);
|
|
|
|
|
|
- mg_printf(conn, "%s", "HTTP/1.1 200 OK\r\n"
|
|
|
|
- "Allow: GET, POST, HEAD, CONNECT, PUT, DELETE, OPTIONS, PROPFIND, MKCOL\r\n"
|
|
|
|
- "DAV: 1\r\n\r\n");
|
|
|
|
|
|
+ mg_printf(conn, "HTTP/1.1 200 OK\r\n"
|
|
|
|
+ "Date: %s\r\n"
|
|
|
|
+ "Connection: %s\r\n"
|
|
|
|
+ "Allow: GET, POST, HEAD, CONNECT, PUT, DELETE, OPTIONS, PROPFIND, MKCOL\r\n"
|
|
|
|
+ "DAV: 1\r\n\r\n",
|
|
|
|
+ date, suggest_connection_header(conn));
|
|
}
|
|
}
|
|
|
|
|
|
/* Writes PROPFIND properties for a collection element */
|
|
/* Writes PROPFIND properties for a collection element */
|
|
@@ -4210,12 +4297,18 @@ static void handle_propfind(struct mg_connection *conn, const char *path,
|
|
struct file *filep)
|
|
struct file *filep)
|
|
{
|
|
{
|
|
const char *depth = mg_get_header(conn, "Depth");
|
|
const char *depth = mg_get_header(conn, "Depth");
|
|
|
|
+ char date[64];
|
|
|
|
+ time_t curtime = time(NULL);
|
|
|
|
+
|
|
|
|
+ gmt_time_string(date, sizeof(date), &curtime);
|
|
|
|
|
|
conn->must_close = 1;
|
|
conn->must_close = 1;
|
|
conn->status_code = 207;
|
|
conn->status_code = 207;
|
|
mg_printf(conn, "HTTP/1.1 207 Multi-Status\r\n"
|
|
mg_printf(conn, "HTTP/1.1 207 Multi-Status\r\n"
|
|
- "Connection: close\r\n"
|
|
|
|
- "Content-Type: text/xml; charset=utf-8\r\n\r\n");
|
|
|
|
|
|
+ "Date: %s\r\n"
|
|
|
|
+ "Connection: %s\r\n"
|
|
|
|
+ "Content-Type: text/xml; charset=utf-8\r\n\r\n",
|
|
|
|
+ date, suggest_connection_header(conn));
|
|
|
|
|
|
conn->num_bytes_sent += mg_printf(conn,
|
|
conn->num_bytes_sent += mg_printf(conn,
|
|
"<?xml version=\"1.0\" encoding=\"utf-8\"?>"
|
|
"<?xml version=\"1.0\" encoding=\"utf-8\"?>"
|
|
@@ -4244,6 +4337,10 @@ void mg_unlock(struct mg_connection* conn)
|
|
(void) pthread_mutex_unlock(&conn->mutex);
|
|
(void) pthread_mutex_unlock(&conn->mutex);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+#ifdef USE_LUA
|
|
|
|
+#include "mod_lua.inl"
|
|
|
|
+#endif /* USE_LUA */
|
|
|
|
+
|
|
#if defined(USE_WEBSOCKET)
|
|
#if defined(USE_WEBSOCKET)
|
|
|
|
|
|
/* START OF SHA-1 code
|
|
/* START OF SHA-1 code
|
|
@@ -4608,7 +4705,14 @@ static void read_websocket(struct mg_connection *conn)
|
|
/* Exit the loop if callback signalled to exit,
|
|
/* Exit the loop if callback signalled to exit,
|
|
or "connection close" opcode received. */
|
|
or "connection close" opcode received. */
|
|
if ((conn->ctx->callbacks.websocket_data != NULL &&
|
|
if ((conn->ctx->callbacks.websocket_data != NULL &&
|
|
|
|
+#ifdef USE_LUA
|
|
|
|
+ (conn->lua_websocket_state == NULL) &&
|
|
|
|
+#endif
|
|
!conn->ctx->callbacks.websocket_data(conn, mop, data, data_len)) ||
|
|
!conn->ctx->callbacks.websocket_data(conn, mop, data, data_len)) ||
|
|
|
|
+#ifdef USE_LUA
|
|
|
|
+ (conn->lua_websocket_state &&
|
|
|
|
+ !lua_websocket_data(conn, mop, data, data_len)) ||
|
|
|
|
+#endif
|
|
(buf[0] & 0xf) == WEBSOCKET_OPCODE_CONNECTION_CLOSE) { /* Opcode == 8, connection close */
|
|
(buf[0] & 0xf) == WEBSOCKET_OPCODE_CONNECTION_CLOSE) { /* Opcode == 8, connection close */
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
@@ -4670,20 +4774,37 @@ int mg_websocket_write(struct mg_connection* conn, int opcode, const char* data,
|
|
return retval;
|
|
return retval;
|
|
}
|
|
}
|
|
|
|
|
|
-static void handle_websocket_request(struct mg_connection *conn)
|
|
|
|
|
|
+static void handle_websocket_request(struct mg_connection *conn, const char *path, int is_script_resource)
|
|
{
|
|
{
|
|
const char *version = mg_get_header(conn, "Sec-WebSocket-Version");
|
|
const char *version = mg_get_header(conn, "Sec-WebSocket-Version");
|
|
if (version == NULL || strcmp(version, "13") != 0) {
|
|
if (version == NULL || strcmp(version, "13") != 0) {
|
|
send_http_error(conn, 426, "Upgrade Required", "%s", "Upgrade Required");
|
|
send_http_error(conn, 426, "Upgrade Required", "%s", "Upgrade Required");
|
|
} else if (conn->ctx->callbacks.websocket_connect != NULL &&
|
|
} else if (conn->ctx->callbacks.websocket_connect != NULL &&
|
|
conn->ctx->callbacks.websocket_connect(conn) != 0) {
|
|
conn->ctx->callbacks.websocket_connect(conn) != 0) {
|
|
- /* Callback has returned non-zero, do not proceed with handshake */
|
|
|
|
|
|
+ /* C callback has returned non-zero, do not proceed with handshake. */
|
|
|
|
+ /* The C callback is called before Lua and may prevent Lua from handling the websocket. */
|
|
} else {
|
|
} else {
|
|
- send_websocket_handshake(conn);
|
|
|
|
- if (conn->ctx->callbacks.websocket_ready != NULL) {
|
|
|
|
- conn->ctx->callbacks.websocket_ready(conn);
|
|
|
|
|
|
+#ifdef USE_LUA
|
|
|
|
+ if (match_prefix(conn->ctx->config[LUA_SCRIPT_EXTENSIONS],
|
|
|
|
+ (int)strlen(conn->ctx->config[LUA_SCRIPT_EXTENSIONS]),
|
|
|
|
+ path)) {
|
|
|
|
+ conn->lua_websocket_state = new_lua_websocket(path, conn);
|
|
|
|
+ if (conn->lua_websocket_state) {
|
|
|
|
+ send_websocket_handshake(conn);
|
|
|
|
+ if (lua_websocket_ready(conn)) {
|
|
|
|
+ read_websocket(conn);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ } else
|
|
|
|
+#endif
|
|
|
|
+ {
|
|
|
|
+ /* No Lua websock script specified. */
|
|
|
|
+ send_websocket_handshake(conn);
|
|
|
|
+ if (conn->ctx->callbacks.websocket_ready != NULL) {
|
|
|
|
+ conn->ctx->callbacks.websocket_ready(conn);
|
|
|
|
+ }
|
|
|
|
+ read_websocket(conn);
|
|
}
|
|
}
|
|
- read_websocket(conn);
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -4759,10 +4880,6 @@ static uint32_t get_remote_ip(const struct mg_connection *conn)
|
|
return ntohl(* (uint32_t *) &conn->client.rsa.sin.sin_addr);
|
|
return ntohl(* (uint32_t *) &conn->client.rsa.sin.sin_addr);
|
|
}
|
|
}
|
|
|
|
|
|
-#ifdef USE_LUA
|
|
|
|
-#include "mod_lua.inl"
|
|
|
|
-#endif /* USE_LUA */
|
|
|
|
-
|
|
|
|
int mg_upload(struct mg_connection *conn, const char *destination_dir)
|
|
int mg_upload(struct mg_connection *conn, const char *destination_dir)
|
|
{
|
|
{
|
|
const char *content_type_header, *boundary_start;
|
|
const char *content_type_header, *boundary_start;
|
|
@@ -5028,8 +5145,10 @@ static void handle_request(struct mg_connection *conn)
|
|
{
|
|
{
|
|
struct mg_request_info *ri = &conn->request_info;
|
|
struct mg_request_info *ri = &conn->request_info;
|
|
char path[PATH_MAX];
|
|
char path[PATH_MAX];
|
|
- int uri_len, ssl_index;
|
|
|
|
|
|
+ int uri_len, ssl_index, is_script_resource;
|
|
struct file file = STRUCT_FILE_INITIALIZER;
|
|
struct file file = STRUCT_FILE_INITIALIZER;
|
|
|
|
+ char date[64];
|
|
|
|
+ time_t curtime = time(NULL);
|
|
|
|
|
|
if ((conn->request_info.query_string = strchr(ri->uri, '?')) != NULL) {
|
|
if ((conn->request_info.query_string = strchr(ri->uri, '?')) != NULL) {
|
|
* ((char *) conn->request_info.query_string++) = '\0';
|
|
* ((char *) conn->request_info.query_string++) = '\0';
|
|
@@ -5038,7 +5157,7 @@ static void handle_request(struct mg_connection *conn)
|
|
mg_url_decode(ri->uri, uri_len, (char *) ri->uri, uri_len + 1, 0);
|
|
mg_url_decode(ri->uri, uri_len, (char *) ri->uri, uri_len + 1, 0);
|
|
remove_double_dots_and_double_slashes((char *) ri->uri);
|
|
remove_double_dots_and_double_slashes((char *) ri->uri);
|
|
path[0] = '\0';
|
|
path[0] = '\0';
|
|
- convert_uri_to_file_name(conn, path, sizeof(path), &file);
|
|
|
|
|
|
+ convert_uri_to_file_name(conn, path, sizeof(path), &file, &is_script_resource);
|
|
conn->throttle = set_throttle(conn->ctx->config[THROTTLE],
|
|
conn->throttle = set_throttle(conn->ctx->config[THROTTLE],
|
|
get_remote_ip(conn), ri->uri);
|
|
get_remote_ip(conn), ri->uri);
|
|
|
|
|
|
@@ -5049,7 +5168,7 @@ static void handle_request(struct mg_connection *conn)
|
|
if (!conn->client.is_ssl && conn->client.ssl_redir &&
|
|
if (!conn->client.is_ssl && conn->client.ssl_redir &&
|
|
(ssl_index = get_first_ssl_listener_index(conn->ctx)) > -1) {
|
|
(ssl_index = get_first_ssl_listener_index(conn->ctx)) > -1) {
|
|
redirect_to_https_port(conn, ssl_index);
|
|
redirect_to_https_port(conn, ssl_index);
|
|
- } else if (!is_put_or_delete_request(conn) &&
|
|
|
|
|
|
+ } else if (!is_script_resource && !is_put_or_delete_request(conn) &&
|
|
!check_authorization(conn, path)) {
|
|
!check_authorization(conn, path)) {
|
|
send_authorization_request(conn);
|
|
send_authorization_request(conn);
|
|
} else if (conn->ctx->callbacks.begin_request != NULL &&
|
|
} else if (conn->ctx->callbacks.begin_request != NULL &&
|
|
@@ -5060,20 +5179,20 @@ static void handle_request(struct mg_connection *conn)
|
|
/* Do nothing, callback has served the request */
|
|
/* Do nothing, callback has served the request */
|
|
#if defined(USE_WEBSOCKET)
|
|
#if defined(USE_WEBSOCKET)
|
|
} else if (is_websocket_request(conn)) {
|
|
} else if (is_websocket_request(conn)) {
|
|
- handle_websocket_request(conn);
|
|
|
|
|
|
+ handle_websocket_request(conn, path, is_script_resource);
|
|
#endif
|
|
#endif
|
|
- } else if (!strcmp(ri->request_method, "OPTIONS")) {
|
|
|
|
|
|
+ } else if (!is_script_resource && !strcmp(ri->request_method, "OPTIONS")) {
|
|
send_options(conn);
|
|
send_options(conn);
|
|
} else if (conn->ctx->config[DOCUMENT_ROOT] == NULL) {
|
|
} else if (conn->ctx->config[DOCUMENT_ROOT] == NULL) {
|
|
send_http_error(conn, 404, "Not Found", "Not Found");
|
|
send_http_error(conn, 404, "Not Found", "Not Found");
|
|
- } else if (is_put_or_delete_request(conn) &&
|
|
|
|
|
|
+ } else if (!is_script_resource && is_put_or_delete_request(conn) &&
|
|
(is_authorized_for_put(conn) != 1)) {
|
|
(is_authorized_for_put(conn) != 1)) {
|
|
send_authorization_request(conn);
|
|
send_authorization_request(conn);
|
|
- } else if (!strcmp(ri->request_method, "PUT")) {
|
|
|
|
|
|
+ } else if (!is_script_resource && !strcmp(ri->request_method, "PUT")) {
|
|
put_file(conn, path);
|
|
put_file(conn, path);
|
|
- } else if (!strcmp(ri->request_method, "MKCOL")) {
|
|
|
|
|
|
+ } else if (!is_script_resource && !strcmp(ri->request_method, "MKCOL")) {
|
|
mkcol(conn, path);
|
|
mkcol(conn, path);
|
|
- } else if (!strcmp(ri->request_method, "DELETE")) {
|
|
|
|
|
|
+ } else if (!is_script_resource && !strcmp(ri->request_method, "DELETE")) {
|
|
struct de de;
|
|
struct de de;
|
|
memset(&de.file, 0, sizeof(de.file));
|
|
memset(&de.file, 0, sizeof(de.file));
|
|
if(!mg_stat(conn, path, &de.file)) {
|
|
if(!mg_stat(conn, path, &de.file)) {
|
|
@@ -5098,9 +5217,14 @@ static void handle_request(struct mg_connection *conn)
|
|
must_hide_file(conn, path)) {
|
|
must_hide_file(conn, path)) {
|
|
send_http_error(conn, 404, "Not Found", "%s", "File not found");
|
|
send_http_error(conn, 404, "Not Found", "%s", "File not found");
|
|
} else if (file.is_directory && ri->uri[uri_len - 1] != '/') {
|
|
} else if (file.is_directory && ri->uri[uri_len - 1] != '/') {
|
|
|
|
+ gmt_time_string(date, sizeof(date), &curtime);
|
|
mg_printf(conn, "HTTP/1.1 301 Moved Permanently\r\n"
|
|
mg_printf(conn, "HTTP/1.1 301 Moved Permanently\r\n"
|
|
- "Location: %s/\r\n\r\n", ri->uri);
|
|
|
|
- } else if (!strcmp(ri->request_method, "PROPFIND")) {
|
|
|
|
|
|
+ "Location: %s/\r\n"
|
|
|
|
+ "Date: %s\r\n"
|
|
|
|
+ "Content-Length: 0\r\n"
|
|
|
|
+ "Connection: %s\r\n\r\n",
|
|
|
|
+ ri->uri, date, suggest_connection_header(conn));
|
|
|
|
+ } else if (!is_script_resource && !strcmp(ri->request_method, "PROPFIND")) {
|
|
handle_propfind(conn, path, &file);
|
|
handle_propfind(conn, path, &file);
|
|
} else if (file.is_directory &&
|
|
} else if (file.is_directory &&
|
|
!substitute_index_file(conn, path, sizeof(path), &file)) {
|
|
!substitute_index_file(conn, path, sizeof(path), &file)) {
|
|
@@ -5111,13 +5235,22 @@ static void handle_request(struct mg_connection *conn)
|
|
"Directory listing denied");
|
|
"Directory listing denied");
|
|
}
|
|
}
|
|
#ifdef USE_LUA
|
|
#ifdef USE_LUA
|
|
- } else if (match_prefix("**.lp$", 6, path) > 0) {
|
|
|
|
|
|
+ } else if (match_prefix(conn->ctx->config[LUA_SERVER_PAGE_EXTENSIONS],
|
|
|
|
+ (int)strlen(conn->ctx->config[LUA_SERVER_PAGE_EXTENSIONS]),
|
|
|
|
+ path) > 0) {
|
|
|
|
+ /* Lua server page: an SSI like page containing mostly plain html code plus some tags with server generated contents. */
|
|
handle_lsp_request(conn, path, &file, NULL);
|
|
handle_lsp_request(conn, path, &file, NULL);
|
|
|
|
+ } else if (match_prefix(conn->ctx->config[LUA_SCRIPT_EXTENSIONS],
|
|
|
|
+ (int)strlen(conn->ctx->config[LUA_SCRIPT_EXTENSIONS]),
|
|
|
|
+ path) > 0) {
|
|
|
|
+ /* Lua in-server module script: a CGI like script used to generate the entire reply. */
|
|
|
|
+ mg_exec_lua_script(conn, path, NULL);
|
|
#endif
|
|
#endif
|
|
#if !defined(NO_CGI)
|
|
#if !defined(NO_CGI)
|
|
} else if (match_prefix(conn->ctx->config[CGI_EXTENSIONS],
|
|
} else if (match_prefix(conn->ctx->config[CGI_EXTENSIONS],
|
|
(int)strlen(conn->ctx->config[CGI_EXTENSIONS]),
|
|
(int)strlen(conn->ctx->config[CGI_EXTENSIONS]),
|
|
path) > 0) {
|
|
path) > 0) {
|
|
|
|
+ /* TODO: check unsupported methods -> 501
|
|
if (strcmp(ri->request_method, "POST") &&
|
|
if (strcmp(ri->request_method, "POST") &&
|
|
strcmp(ri->request_method, "HEAD") &&
|
|
strcmp(ri->request_method, "HEAD") &&
|
|
strcmp(ri->request_method, "GET")) {
|
|
strcmp(ri->request_method, "GET")) {
|
|
@@ -5125,7 +5258,8 @@ static void handle_request(struct mg_connection *conn)
|
|
"Method %s is not implemented", ri->request_method);
|
|
"Method %s is not implemented", ri->request_method);
|
|
} else {
|
|
} else {
|
|
handle_cgi_request(conn, path);
|
|
handle_cgi_request(conn, path);
|
|
- }
|
|
|
|
|
|
+ } */
|
|
|
|
+ handle_cgi_request(conn, path);
|
|
#endif /* !NO_CGI */
|
|
#endif /* !NO_CGI */
|
|
} else if (match_prefix(conn->ctx->config[SSI_EXTENSIONS],
|
|
} else if (match_prefix(conn->ctx->config[SSI_EXTENSIONS],
|
|
(int)strlen(conn->ctx->config[SSI_EXTENSIONS]),
|
|
(int)strlen(conn->ctx->config[SSI_EXTENSIONS]),
|
|
@@ -5572,6 +5706,12 @@ static void close_socket_gracefully(struct mg_connection *conn)
|
|
|
|
|
|
static void close_connection(struct mg_connection *conn)
|
|
static void close_connection(struct mg_connection *conn)
|
|
{
|
|
{
|
|
|
|
+#if defined(USE_LUA) && defined(USE_WEBSOCKET)
|
|
|
|
+ if (conn->lua_websocket_state) {
|
|
|
|
+ lua_websocket_close(conn);
|
|
|
|
+ }
|
|
|
|
+#endif
|
|
|
|
+
|
|
/* call the connection_close callback if assigned */
|
|
/* call the connection_close callback if assigned */
|
|
if (conn->ctx->callbacks.connection_close != NULL)
|
|
if (conn->ctx->callbacks.connection_close != NULL)
|
|
conn->ctx->callbacks.connection_close(conn);
|
|
conn->ctx->callbacks.connection_close(conn);
|
|
@@ -6164,6 +6304,10 @@ struct mg_context *mg_start(const struct mg_callbacks *callbacks,
|
|
InitializeCriticalSection(&global_log_file_lock);
|
|
InitializeCriticalSection(&global_log_file_lock);
|
|
#endif /* _WIN32 && !__SYMBIAN32__ */
|
|
#endif /* _WIN32 && !__SYMBIAN32__ */
|
|
|
|
|
|
|
|
+ /* Check if the config_options and the corresponding enum have compatible sizes. */
|
|
|
|
+ /* Could use static_assert, once it is verified that all compilers support this. */
|
|
|
|
+ assert(sizeof(config_options)/sizeof(config_options[0]) == NUM_OPTIONS*2+1);
|
|
|
|
+
|
|
/* Allocate context and initialize reasonable general case defaults.
|
|
/* Allocate context and initialize reasonable general case defaults.
|
|
TODO(lsm): do proper error handling here. */
|
|
TODO(lsm): do proper error handling here. */
|
|
if ((ctx = (struct mg_context *) calloc(1, sizeof(*ctx))) == NULL) {
|
|
if ((ctx = (struct mg_context *) calloc(1, sizeof(*ctx))) == NULL) {
|