Commit f105b74d authored by valenok's avatar valenok

experimental keep-alive, options defaults

parent fed9ffc1
...@@ -103,8 +103,9 @@ static void show_usage_and_exit(void) { ...@@ -103,8 +103,9 @@ static void show_usage_and_exit(void) {
fprintf(stderr, "OPTIONS:\n"); fprintf(stderr, "OPTIONS:\n");
names = mg_get_valid_option_names(); names = mg_get_valid_option_names();
for (i = 0; names[i] != NULL; i += 2) { for (i = 0; names[i] != NULL; i += 3) {
fprintf(stderr, " -%s %s\n", names[i], names[i + 1]); fprintf(stderr, " -%s %s (default: \"%s\")\n",
names[i], names[i + 1], names[i + 2] == NULL ? "" : names[i + 2]);
} }
fprintf(stderr, "See http://code.google.com/p/mongoose/wiki/MongooseManual" fprintf(stderr, "See http://code.google.com/p/mongoose/wiki/MongooseManual"
" for more details.\n"); " for more details.\n");
...@@ -164,6 +165,7 @@ static void process_command_line_arguments(char *argv[], char **options) { ...@@ -164,6 +165,7 @@ static void process_command_line_arguments(char *argv[], char **options) {
const char *config_file = NULL; const char *config_file = NULL;
char line[512], opt[512], val[512], path[PATH_MAX], *p; char line[512], opt[512], val[512], path[PATH_MAX], *p;
FILE *fp = NULL; FILE *fp = NULL;
struct stat st;
size_t i, line_no = 0; size_t i, line_no = 0;
/* Should we use a config file ? */ /* Should we use a config file ? */
...@@ -175,6 +177,9 @@ static void process_command_line_arguments(char *argv[], char **options) { ...@@ -175,6 +177,9 @@ static void process_command_line_arguments(char *argv[], char **options) {
snprintf(path, sizeof(path), "%.*s%s", snprintf(path, sizeof(path), "%.*s%s",
(int) (p - argv[0]) + 1, argv[0], CONFIG_FILE); (int) (p - argv[0]) + 1, argv[0], CONFIG_FILE);
} }
if (stat(path, &st) == 0) {
config_file = path;
}
} }
/* If config file was set in command line and open failed, exit */ /* If config file was set in command line and open failed, exit */
if (config_file != NULL && (fp = fopen(config_file, "r")) == NULL) { if (config_file != NULL && (fp = fopen(config_file, "r")) == NULL) {
......
...@@ -352,19 +352,31 @@ enum { ...@@ -352,19 +352,31 @@ enum {
CGI_INTERPRETER, CGI_ENVIRONMENT, SSI_EXTENSIONS, AUTHENTICATION_DOMAIN, CGI_INTERPRETER, CGI_ENVIRONMENT, SSI_EXTENSIONS, AUTHENTICATION_DOMAIN,
URI_PROTECTION, GLOBAL_PASSWORDS_FILE, PUT_DELETE_PASSWORDS_FILE, URI_PROTECTION, GLOBAL_PASSWORDS_FILE, PUT_DELETE_PASSWORDS_FILE,
ACCESS_LOG_FILE, ERROR_LOG_FILE, ACCESS_CONTROL_LIST, RUN_AS_USER, ACCESS_LOG_FILE, ERROR_LOG_FILE, ACCESS_CONTROL_LIST, RUN_AS_USER,
EXTRA_MIME_TYPES, ENABLE_DIRECTORY_LISTING, NUM_THREADS, EXTRA_MIME_TYPES, ENABLE_DIRECTORY_LISTING, ENABLE_KEEP_ALIVE, NUM_THREADS,
NUM_OPTIONS NUM_OPTIONS
}; };
// There are two entries for each option: a short and a long version. static const char *config_options[] = {
static const char *option_names[] = { "r", "document_root", ".",
"r", "document_root", "p", "listening_ports", "i", "index_files", "p", "listening_ports", "8080",
"s", "ssl_certificate", "C", "cgi_extensions", "I", "cgi_interpreter", "i", "index_files", "index.html,index.htm,index.cgi",
"E", "cgi_environment", "S", "ssi_extensions", "R", "authentication_domain", "s", "ssl_certificate", NULL,
"P", "protect_uri", "g", "global_passwords_file", "C", "cgi_extensions", ".cgi,.pl,.php",
"G", "put_delete_passwords_file", "a", "access_log_file", "I", "cgi_interpreter", NULL,
"e", "error_log_file", "l", "access_control_list", "u", "run_as_user", "E", "cgi_environment", NULL,
"m", "extra_mime_types", "d", "enable_directory_listing", "t", "num_threads", "S", "ssi_extensions", ".shtml,.shtm",
"R", "authentication_domain", "mydomain.com",
"P", "protect_uri", NULL,
"g", "global_passwords_file", NULL,
"G", "put_delete_passwords_file", NULL,
"a", "access_log_file", NULL,
"e", "error_log_file", NULL,
"l", "access_control_list", NULL,
"u", "run_as_user", NULL,
"m", "extra_mime_types", NULL,
"d", "enable_directory_listing", "yes",
"k", "enable_keep_alive", "no",
"t", "num_threads", "10",
NULL NULL
}; };
...@@ -402,7 +414,7 @@ struct mg_connection { ...@@ -402,7 +414,7 @@ struct mg_connection {
}; };
const char **mg_get_valid_option_names(void) { const char **mg_get_valid_option_names(void) {
return option_names; return config_options;
} }
static void *call_user(struct mg_connection *conn, enum mg_event event) { static void *call_user(struct mg_connection *conn, enum mg_event event) {
...@@ -412,10 +424,11 @@ static void *call_user(struct mg_connection *conn, enum mg_event event) { ...@@ -412,10 +424,11 @@ static void *call_user(struct mg_connection *conn, enum mg_event event) {
static int get_option_index(const char *name) { static int get_option_index(const char *name) {
int i; int i;
for (i = 0; option_names[i] != NULL; i += 2) { #define ENTRIES_PER_OPTION 3
if (strcmp(option_names[i], name) == 0 || for (i = 0; config_options[i] != NULL; i += ENTRIES_PER_OPTION) {
strcmp(option_names[i + 1], name) == 0) { if (strcmp(config_options[i], name) == 0 ||
return i / 2; strcmp(config_options[i + 1], name) == 0) {
return i / ENTRIES_PER_OPTION;
} }
} }
return -1; return -1;
...@@ -448,7 +461,7 @@ static void cry(struct mg_connection *conn, const char *fmt, ...) { ...@@ -448,7 +461,7 @@ static void cry(struct mg_connection *conn, const char *fmt, ...) {
// same way string option can. // same way string option can.
conn->request_info.log_message = buf; conn->request_info.log_message = buf;
if (call_user(conn, MG_EVENT_LOG) == NULL) { if (call_user(conn, MG_EVENT_LOG) == NULL) {
fp = conn->ctx->config[ERROR_LOG_FILE] == NULL ? stderr : fp = conn->ctx->config[ERROR_LOG_FILE] == NULL ? NULL :
mg_fopen(conn->ctx->config[ERROR_LOG_FILE], "a+"); mg_fopen(conn->ctx->config[ERROR_LOG_FILE], "a+");
if (fp != NULL) { if (fp != NULL) {
...@@ -673,6 +686,17 @@ static int match_extension(const char *path, const char *ext_list) { ...@@ -673,6 +686,17 @@ static int match_extension(const char *path, const char *ext_list) {
} }
#endif // !NO_CGI #endif // !NO_CGI
// HTTP 1.1 assumes keep alive if "Connection:" header is not set
static int should_keep_alive(const struct mg_connection *conn) {
const char *header = mg_get_header(conn, "Connection");
return (header == NULL && !strcmp(conn->request_info.http_version, "1.1")) ||
(header != NULL && !strcmp(header, "keep-alive"));
}
static const char *suggest_connection_header(const struct mg_connection *conn) {
return should_keep_alive(conn) ? "keep-alive" : "close";
}
static void send_http_error(struct mg_connection *conn, int status, static void send_http_error(struct mg_connection *conn, int status,
const char *reason, const char *fmt, ...) { const char *reason, const char *fmt, ...) {
char buf[BUFSIZ]; char buf[BUFSIZ];
...@@ -700,7 +724,8 @@ static void send_http_error(struct mg_connection *conn, int status, ...@@ -700,7 +724,8 @@ static void send_http_error(struct mg_connection *conn, int status,
mg_printf(conn, "HTTP/1.1 %d %s\r\n" mg_printf(conn, "HTTP/1.1 %d %s\r\n"
"Content-Type: text/plain\r\n" "Content-Type: text/plain\r\n"
"Content-Length: %d\r\n" "Content-Length: %d\r\n"
"Connection: close\r\n\r\n", status, reason, len); "Connection: %s\r\n\r\n", status, reason, len,
suggest_connection_header(conn));
conn->num_bytes_sent += mg_printf(conn, "%s", buf); conn->num_bytes_sent += mg_printf(conn, "%s", buf);
} }
} }
...@@ -1266,8 +1291,8 @@ static int pull(FILE *fp, SOCKET sock, SSL *ssl, char *buf, int len) { ...@@ -1266,8 +1291,8 @@ static int pull(FILE *fp, SOCKET sock, SSL *ssl, char *buf, int len) {
} }
int mg_read(struct mg_connection *conn, void *buf, size_t len) { int mg_read(struct mg_connection *conn, void *buf, size_t len) {
int n, buffered_data_len, nread; int n, buffered_len, nread;
const char *buffered_data; const char *buffered;
assert(conn->content_len >= conn->consumed_content); assert(conn->content_len >= conn->consumed_content);
DEBUG_TRACE(("%p %zu %lld %lld", buf, len, DEBUG_TRACE(("%p %zu %lld %lld", buf, len,
...@@ -1283,21 +1308,21 @@ int mg_read(struct mg_connection *conn, void *buf, size_t len) { ...@@ -1283,21 +1308,21 @@ int mg_read(struct mg_connection *conn, void *buf, size_t len) {
} }
// How many bytes of data we have buffered in the request buffer? // How many bytes of data we have buffered in the request buffer?
buffered_data = conn->buf + conn->request_len + conn->consumed_content; buffered = conn->buf + conn->request_len;
buffered_data_len = conn->data_len - conn->request_len; buffered_len = conn->data_len - conn->request_len;
assert(buffered_data_len >= 0); assert(buffered_len >= 0);
// Return buffered data back if we haven't done that yet. // Return buffered data back if we haven't done that yet.
if (conn->consumed_content < (int64_t) buffered_data_len) { if (conn->consumed_content < (int64_t) buffered_len) {
buffered_data_len -= conn->consumed_content; buffered_len -= conn->consumed_content;
if (len < (size_t) buffered_data_len) { if (len < (size_t) buffered_len) {
buffered_data_len = len; buffered_len = len;
} }
memcpy(buf, buffered_data, buffered_data_len); memcpy(buf, buffered, buffered_len);
len -= buffered_data_len; len -= buffered_len;
buf = (char *) buf + buffered_data_len; buf = (char *) buf + buffered_len;
conn->consumed_content += buffered_data_len; conn->consumed_content += buffered_len;
nread = buffered_data_len; nread = buffered_len;
} }
// We have returned all buffered data. Read new data from the remote socket. // We have returned all buffered data. Read new data from the remote socket.
...@@ -2449,11 +2474,11 @@ static void handle_file_request(struct mg_connection *conn, const char *path, ...@@ -2449,11 +2474,11 @@ static void handle_file_request(struct mg_connection *conn, const char *path,
"Etag: \"%s\"\r\n" "Etag: \"%s\"\r\n"
"Content-Type: %.*s\r\n" "Content-Type: %.*s\r\n"
"Content-Length: %" INT64_FMT "\r\n" "Content-Length: %" INT64_FMT "\r\n"
"Connection: close\r\n" "Connection: %s\r\n"
"Accept-Ranges: bytes\r\n" "Accept-Ranges: bytes\r\n"
"%s\r\n", "%s\r\n",
conn->request_info.status_code, msg, date, lm, etag, conn->request_info.status_code, msg, date, lm, etag,
mime_vec.len, mime_vec.ptr, cl, range); mime_vec.len, mime_vec.ptr, cl, suggest_connection_header(conn), range);
if (strcmp(conn->request_info.request_method, "HEAD") != 0) { if (strcmp(conn->request_info.request_method, "HEAD") != 0) {
send_file_data(conn, fp, cl); send_file_data(conn, fp, cl);
...@@ -2487,6 +2512,11 @@ static int is_valid_http_method(const char *method) { ...@@ -2487,6 +2512,11 @@ static int is_valid_http_method(const char *method) {
static int parse_http_request(char *buf, struct mg_request_info *ri) { static int parse_http_request(char *buf, struct mg_request_info *ri) {
int status = 0; int status = 0;
// RFC says that all initial whitespaces should be ingored
while (*buf != '\0' && isspace(* (unsigned char *) buf)) {
buf++;
}
ri->request_method = skip(&buf, " "); ri->request_method = skip(&buf, " ");
ri->uri = skip(&buf, " "); ri->uri = skip(&buf, " ");
ri->http_version = skip(&buf, "\r\n"); ri->http_version = skip(&buf, "\r\n");
...@@ -2580,10 +2610,9 @@ static int is_not_modified(const struct mg_connection *conn, ...@@ -2580,10 +2610,9 @@ static int is_not_modified(const struct mg_connection *conn,
} }
static int handle_request_body(struct mg_connection *conn, FILE *fp) { static int handle_request_body(struct mg_connection *conn, FILE *fp) {
const char *expect, *data; const char *expect, *buffered;
char buf[BUFSIZ]; char buf[BUFSIZ];
int to_read, nread, data_len; int to_read, nread, buffered_len, success = 0;
int success = 0;
expect = mg_get_header(conn, "Expect"); expect = mg_get_header(conn, "Expect");
assert(fp != NULL); assert(fp != NULL);
...@@ -2596,16 +2625,19 @@ static int handle_request_body(struct mg_connection *conn, FILE *fp) { ...@@ -2596,16 +2625,19 @@ static int handle_request_body(struct mg_connection *conn, FILE *fp) {
if (expect != NULL) { if (expect != NULL) {
(void) mg_printf(conn, "%s", "HTTP/1.1 100 Continue\r\n\r\n"); (void) mg_printf(conn, "%s", "HTTP/1.1 100 Continue\r\n\r\n");
} }
data = conn->buf + conn->request_len;
data_len = conn->data_len - conn->request_len;
assert(data_len >= 0);
if (conn->content_len <= (int64_t) data_len) { buffered = conn->buf + conn->request_len;
success = push(fp, INVALID_SOCKET, NULL, data, buffered_len = conn->data_len - conn->request_len;
conn->content_len) == conn->content_len; assert(buffered_len >= 0);
} else { assert(conn->consumed_content == 0);
push(fp, INVALID_SOCKET, NULL, data, (int64_t) data_len);
conn->consumed_content += data_len; if (buffered_len > 0) {
if ((int64_t) buffered_len > conn->content_len) {
buffered_len = conn->content_len;
}
push(fp, INVALID_SOCKET, NULL, buffered, (int64_t) buffered_len);
conn->consumed_content += buffered_len;
}
while (conn->consumed_content < conn->content_len) { while (conn->consumed_content < conn->content_len) {
to_read = sizeof(buf); to_read = sizeof(buf);
...@@ -2618,10 +2650,10 @@ static int handle_request_body(struct mg_connection *conn, FILE *fp) { ...@@ -2618,10 +2650,10 @@ static int handle_request_body(struct mg_connection *conn, FILE *fp) {
} }
conn->consumed_content += nread; conn->consumed_content += nread;
} }
if (conn->consumed_content == conn->content_len) { if (conn->consumed_content == conn->content_len) {
success = 1; success = 1;
} }
}
// Each error code path in this function must send an error // Each error code path in this function must send an error
if (!success) { if (!success) {
...@@ -3088,8 +3120,9 @@ static void handle_ssi_file_request(struct mg_connection *conn, ...@@ -3088,8 +3120,9 @@ static void handle_ssi_file_request(struct mg_connection *conn,
strerror(ERRNO)); strerror(ERRNO));
} else { } else {
set_close_on_exec(fileno(fp)); set_close_on_exec(fileno(fp));
(void) mg_printf(conn, "%s", "HTTP/1.1 200 OK\r\n" mg_printf(conn, "HTTP/1.1 200 OK\r\n"
"Content-Type: text/html\r\nConnection: close\r\n\r\n"); "Content-Type: text/html\r\nConnection: %s\r\n\r\n",
suggest_connection_header(conn));
send_ssi_file(conn, path, fp, 0); send_ssi_file(conn, path, fp, 0);
(void) fclose(fp); (void) fclose(fp);
} }
...@@ -3505,17 +3538,19 @@ static void close_connection(struct mg_connection *conn) { ...@@ -3505,17 +3538,19 @@ static void close_connection(struct mg_connection *conn) {
} }
static void discard_current_request_from_buffer(struct mg_connection *conn) { static void discard_current_request_from_buffer(struct mg_connection *conn) {
int over_len, body_len; char *buffered;
int buffered_len, body_len;
over_len = conn->data_len - conn->request_len; buffered = conn->buf + conn->request_len;
assert(over_len >= 0); buffered_len = conn->data_len - conn->request_len;
assert(buffered_len >= 0);
if (conn->content_len == -1) { if (conn->content_len == -1) {
body_len = 0; body_len = 0;
} else if (conn->content_len < (int64_t) over_len) { } else if (conn->content_len < (int64_t) buffered_len) {
body_len = (int) conn->content_len; body_len = (int) conn->content_len;
} else { } else {
body_len = over_len; body_len = buffered_len;
} }
conn->data_len -= conn->request_len + body_len; conn->data_len -= conn->request_len + body_len;
...@@ -3524,8 +3559,12 @@ static void discard_current_request_from_buffer(struct mg_connection *conn) { ...@@ -3524,8 +3559,12 @@ static void discard_current_request_from_buffer(struct mg_connection *conn) {
static void process_new_connection(struct mg_connection *conn) { static void process_new_connection(struct mg_connection *conn) {
struct mg_request_info *ri = &conn->request_info; struct mg_request_info *ri = &conn->request_info;
int keep_alive_enabled;
const char *cl; const char *cl;
keep_alive_enabled = !strcmp(conn->ctx->config[ENABLE_KEEP_ALIVE], "yes");
do {
reset_per_request_attributes(conn); reset_per_request_attributes(conn);
// If next request is not pipelined, read it in // If next request is not pipelined, read it in
...@@ -3558,6 +3597,7 @@ static void process_new_connection(struct mg_connection *conn) { ...@@ -3558,6 +3597,7 @@ static void process_new_connection(struct mg_connection *conn) {
log_access(conn); log_access(conn);
discard_current_request_from_buffer(conn); discard_current_request_from_buffer(conn);
} }
} while (keep_alive_enabled && should_keep_alive(conn));
} }
// Worker threads take accepted socket from the queue // Worker threads take accepted socket from the queue
...@@ -3788,15 +3828,6 @@ struct mg_context *mg_start(mg_callback_t user_callback, const char **options) { ...@@ -3788,15 +3828,6 @@ struct mg_context *mg_start(mg_callback_t user_callback, const char **options) {
ctx = calloc(1, sizeof(*ctx)); ctx = calloc(1, sizeof(*ctx));
ctx->user_callback = user_callback; ctx->user_callback = user_callback;
ctx->config[DOCUMENT_ROOT] = mg_strdup(".");
ctx->config[LISTENING_PORTS] = mg_strdup("8080");
ctx->config[ENABLE_DIRECTORY_LISTING] = mg_strdup("yes");
ctx->config[AUTHENTICATION_DOMAIN] = mg_strdup("mydomain.com");
ctx->config[INDEX_FILES] = mg_strdup("index.html,index.htm,index.cgi");
ctx->config[CGI_EXTENSIONS] = mg_strdup(".cgi,.pl,.php");
ctx->config[SSI_EXTENSIONS] = mg_strdup(".shtml,.shtm");
ctx->config[NUM_THREADS] = mg_strdup("10");
while ((name = *options++) != NULL) { while ((name = *options++) != NULL) {
if ((i = get_option_index(name)) == -1) { if ((i = get_option_index(name)) == -1) {
cry(fc(ctx), "Invalid option: %s", name); cry(fc(ctx), "Invalid option: %s", name);
...@@ -3807,13 +3838,19 @@ struct mg_context *mg_start(mg_callback_t user_callback, const char **options) { ...@@ -3807,13 +3838,19 @@ struct mg_context *mg_start(mg_callback_t user_callback, const char **options) {
free_context(ctx); free_context(ctx);
return NULL; return NULL;
} }
if (ctx->config[i] != NULL) {
free(ctx->config[i]);
}
ctx->config[i] = mg_strdup(value); ctx->config[i] = mg_strdup(value);
DEBUG_TRACE(("[%s] -> [%s]", name, value)); DEBUG_TRACE(("[%s] -> [%s]", name, value));
} }
// Set default value if needed
for (i = 0; config_options[i * 3] != NULL; i++) {
if (ctx->config[i] == NULL && config_options[i * 3 + 2] != NULL) {
ctx->config[i] = mg_strdup(config_options[i * 3 + 2]);
DEBUG_TRACE(("Setting default: [%s] -> [%s]",
config_options[i * 3 + 1], config_options[i * 3 + 2]));
}
}
// NOTE(lsm): order is important here. SSL certificates must // NOTE(lsm): order is important here. SSL certificates must
// be initialized before listening ports. UID must be set last. // be initialized before listening ports. UID must be set last.
if (!set_ssl_option(ctx) || if (!set_ssl_option(ctx) ||
......
...@@ -118,8 +118,9 @@ void mg_stop(struct mg_context *); ...@@ -118,8 +118,9 @@ void mg_stop(struct mg_context *);
const char *mg_get_option(const struct mg_context *ctx, const char *name); const char *mg_get_option(const struct mg_context *ctx, const char *name);
// Return array of valid configuration options. For each option, a short // Return array of strings that represent valid configuration options.
// version and a long version is returned. Array is NULL terminated. // For each option, a short name, long name, and default value is returned.
// Array is NULL terminated.
const char **mg_get_valid_option_names(void); const char **mg_get_valid_option_names(void);
......
...@@ -211,6 +211,9 @@ o("GET /$test_dir_uri/x/ HTTP/1.0\n\n", "Content-Type: text/html\r\n\r\n", ...@@ -211,6 +211,9 @@ o("GET /$test_dir_uri/x/ HTTP/1.0\n\n", "Content-Type: text/html\r\n\r\n",
'index.cgi execution'); 'index.cgi execution');
o("GET /ta/x/ HTTP/1.0\n\n", "SCRIPT_NAME=/ta/x/index.cgi", o("GET /ta/x/ HTTP/1.0\n\n", "SCRIPT_NAME=/ta/x/index.cgi",
'Aliases SCRIPT_NAME'); 'Aliases SCRIPT_NAME');
#o("GET /hello.txt HTTP/1.1\n\n GET /hello.txt HTTP/1.0\n\n",
# 'HTTP/1.1 200.+keep-alive.+HTTP/1.1 200.+close',
# 'Request pipelining', 2);
my $mime_types = { my $mime_types = {
html => 'text/html', html => 'text/html',
......
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