diff --git a/build/src/core.c b/build/src/core.c index 833be865ad4780a5ecf8bde0e3117fd735d73b72..e25e79e591548738404a148f5f3f0831ad4e2243 100644 --- a/build/src/core.c +++ b/build/src/core.c @@ -66,6 +66,9 @@ typedef __int64 int64_t; #ifndef va_copy #define va_copy(x,y) x = y #endif // MINGW #defines va_copy +#ifndef __func__ +#define __func__ "" +#endif #else #include <inttypes.h> #include <unistd.h> @@ -98,7 +101,7 @@ struct linked_list_link { struct linked_list_link *prev, *next; }; #define MAX_REQUEST_SIZE 16384 #define IOBUF_SIZE 8192 #define MAX_PATH_SIZE 8192 -#define DBG(x) do { printf("%s:%d: ", __FILE__, __LINE__); \ +#define DBG(x) do { printf("%s::%s() ", __FILE__, __func__); \ printf x; putchar('\n'); fflush(stdout); } while(0) //#define DBG(x) @@ -131,6 +134,7 @@ struct mg_server { char *config_options[NUM_OPTIONS]; mg_event_handler_t event_handler; void *user_data; + sock_t ctl[2]; // Control socketpair. Used to wake up from select() call }; struct iobuf { @@ -534,13 +538,13 @@ static int is_valid_http_method(const char *method) { // This function modifies the buffer by NUL-terminating // HTTP request components, header names and header values. static int parse_http_message(char *buf, int len, struct mg_request_info *ri) { - int is_request, request_length = get_request_len((unsigned char *) buf, len); - if (request_length > 0) { + int is_request, request_len = get_request_len((unsigned char *) buf, len); + if (request_len > 0) { // Reset attributes. DO NOT TOUCH is_ssl, remote_ip, remote_port ri->remote_user = ri->request_method = ri->uri = ri->http_version = NULL; ri->num_headers = 0; - buf[request_length - 1] = '\0'; + buf[request_len - 1] = '\0'; // RFC says that all initial whitespaces should be ingored while (*buf != '\0' && isspace(* (unsigned char *) buf)) { @@ -555,7 +559,7 @@ static int parse_http_message(char *buf, int len, struct mg_request_info *ri) { is_request = is_valid_http_method(ri->request_method); if ((is_request && memcmp(ri->http_version, "HTTP/", 5) != 0) || (!is_request && memcmp(ri->request_method, "HTTP/", 5) != 0)) { - request_length = -1; + request_len = -1; } else { if (is_request) { ri->http_version += 5; @@ -563,7 +567,7 @@ static int parse_http_message(char *buf, int len, struct mg_request_info *ri) { parse_http_headers(&buf, ri); } } - return request_length; + return request_len; } static int parse_range_header(const char *header, int64_t *a, int64_t *b) { @@ -658,6 +662,21 @@ int mg_url_decode(const char *src, int src_len, char *dst, return i >= src_len ? j : -1; } +// Return HTTP header value, or NULL if not found. +static const char *get_header(const struct mg_request_info *ri, const char *s) { + int i; + + for (i = 0; i < ri->num_headers; i++) + if (!strcmp(s, ri->http_headers[i].name)) + return ri->http_headers[i].value; + + return NULL; +} + +const char *mg_get_header(const struct mg_connection *conn, const char *name) { + return get_header(&conn->request_info, name); +} + // Return 1 if real file has been found, 0 otherwise static int convert_uri_to_file_name(struct mg_connection *conn, char *buf, size_t buf_len, struct stat *st) { @@ -721,7 +740,6 @@ static int vspool(struct iobuf *io, const char *fmt, va_list ap) { va_copy(ap_copy, ap); len = vsnprintf(NULL, 0, fmt, ap_copy); - DBG(("len = %d", len)); if (len <= 0) { } else if (len < io->size - io->len || @@ -753,7 +771,7 @@ static void write_to_client(struct mg_connection *conn) { struct iobuf *io = &conn->remote_iobuf; int n = send(conn->client_sock, io->buf, io->len, 0); - DBG(("Written %d of %d(%d): [%.*s]", n, io->len, io->size, 0, io->buf)); + //DBG(("Written %d of %d(%d): [%.*s]", n, io->len, io->size, 0, io->buf)); if (is_error(n)) { conn->flags |= CONN_CLOSE; @@ -791,6 +809,29 @@ static void open_local_endpoint(struct mg_connection *conn) { } } +static int io_space(const struct iobuf *io) { + return io->size - io->len; +} + +static void process_request(struct mg_connection *conn) { + struct iobuf *io = &conn->local_iobuf; + + DBG(("parse_http_message(%d [%.*s])", io->len, io->len, io->buf)); + if (conn->request_len == 0) { + conn->request_len = parse_http_message(io->buf, io->len, + &conn->request_info); + } + DBG(("parse_http_message() -> %d", conn->request_len)); + + if (conn->request_len < 0 || + (conn->request_len == 0 && io_space(io) <= 0)) { + // Invalid request, or request is too big: close the connection + conn->flags |= CONN_CLOSE; + } else if (conn->request_len > 0 && conn->endpoint_type == EP_NONE) { + open_local_endpoint(conn); + } +} + static void read_from_client(struct mg_connection *conn) { struct iobuf *io = &conn->local_iobuf; int n = recv(conn->client_sock, io->buf + io->len, io->size - io->len, 0); @@ -803,76 +844,90 @@ static void read_from_client(struct mg_connection *conn) { conn->flags |= CONN_CLOSE; } else if (n > 0) { io->len += n; - if (conn->request_len == 0) { - conn->request_len = parse_http_message(io->buf, io->len, - &conn->request_info); - } - if (conn->request_len < 0 || - (conn->request_len == 0 || io->len >= io->size)) { - conn->flags |= CONN_CLOSE; - } else if (conn->endpoint_type == EP_NONE) { - open_local_endpoint(conn); - } + process_request(conn); } } -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; - } +static int should_keep_alive(const struct mg_connection *conn) { + const char *method = conn->request_info.request_method; + const char *http_version = conn->request_info.http_version; + const char *header = mg_get_header(conn, "Connection"); + return method != NULL && !strcmp(method, "GET") && + ((header != NULL && !strcmp(header, "keep-alive")) || + (header == NULL && http_version && !strcmp(http_version, "1.1"))); } -static int io_space(const struct iobuf *io) { - return io->size - io->len; +static void close_local_endpoint(struct mg_connection *conn) { + struct iobuf *io = &conn->local_iobuf; + int keep_alive = should_keep_alive(conn); // Must be done before memmove! + + // Close file descriptor + switch (conn->endpoint_type) { + case EP_FILE: close(conn->endpoint.fd); break; + default: assert(1); 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->request_len = 0; + + if (keep_alive) { + DBG(("keep alive!")); + process_request(conn); // Can call us recursively! + } else { + DBG(("closing!")); + conn->flags |= conn->remote_iobuf.len == 0 ? CONN_CLOSE : CONN_SPOOL_DONE; + } } static void transfer_file_data(struct mg_connection *conn) { - struct iobuf *io = &conn->remote_iobuf, *iol = &conn->local_iobuf; - int n = read(conn->endpoint.fd, io->buf + io->len, io->size - io->len); - //DBG(("%s: %d", __func__, n)); + struct iobuf *io = &conn->remote_iobuf; + int n, rem_space = io_space(io); + + if (rem_space <= 0) return; + n = read(conn->endpoint.fd, io->buf + io->len, rem_space); + if (is_error(n)) { - close(conn->endpoint.fd); - conn->endpoint.fd = 0; - conn->endpoint_type = EP_NONE; - memmove(iol->buf, iol->buf + conn->request_len, - iol->len - conn->request_len); - iol->len -= conn->request_len; - conn->request_len = 0; - if (iol->len <= 0) { - conn->flags |= io->len > 0 ? CONN_SPOOL_DONE : CONN_CLOSE; - } else { - DBG(("%s", "more req!")); - } - //DBG(("%s: %s", __func__, "YEEEEEE")); - //conn->flags |= io->len > 0 ? CONN_SPOOL_DONE : CONN_CLOSE; + close_local_endpoint(conn); } else if (n > 0) { io->len += n; } } +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; + } +} + void mg_poll_server(struct mg_server *server, unsigned int milliseconds) { struct linked_list_link *lp, *tmp; struct mg_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 + 3; + time_t current_time = time(NULL), expire_time = current_time + + atoi(server->config_options[IDLE_TIMEOUT_MS]) / 1000; FD_ZERO(&read_set); FD_ZERO(&write_set); add_to_set(server->listening_sock, &read_set, &max_fd); + add_to_set(server->ctl[0], &read_set, &max_fd); LINKED_LIST_FOREACH(&server->active_connections, lp, tmp) { conn = LINKED_LIST_ENTRY(lp, struct mg_connection, link); add_to_set(conn->client_sock, &read_set, &max_fd); - if (conn->endpoint_type == EP_FILE && io_space(&conn->remote_iobuf) > 0) { + if (conn->endpoint_type == EP_FILE) { transfer_file_data(conn); - // read can return 0, meaning EOF. Need to signal the writer to close. - add_to_set(conn->client_sock, &write_set, &max_fd); } if (conn->remote_iobuf.len > 0) { add_to_set(conn->client_sock, &write_set, &max_fd); + } else if (conn->flags & CONN_CLOSE) { + close_conn(conn); } } @@ -895,7 +950,6 @@ void mg_poll_server(struct mg_server *server, unsigned int milliseconds) { read_from_client(conn); } if (FD_ISSET(conn->client_sock, &write_set)) { - assert(conn->remote_iobuf.len >= 0); conn->expire_time = expire_time; write_to_client(conn); } @@ -942,6 +996,7 @@ struct mg_server *mg_create_server(const char *opts[], mg_event_handler_t func, server->event_handler = func; server->user_data = user_data; LINKED_LIST_INIT(&server->active_connections); + mg_socketpair(server->ctl); while (opts != NULL && (name = *opts++) != NULL) { if ((i = get_option_index(name)) == -1) {