|
@@ -100,7 +100,7 @@ typedef long off_t;
|
|
#if defined(_MSC_VER) && _MSC_VER < 1300
|
|
#if defined(_MSC_VER) && _MSC_VER < 1300
|
|
#define STRX(x) #x
|
|
#define STRX(x) #x
|
|
#define STR(x) STRX(x)
|
|
#define STR(x) STRX(x)
|
|
-#define __func__ "line " STR(__LINE__)
|
|
|
|
|
|
+#define __func__ __FILE__ ":" STR(__LINE__)
|
|
#define strtoull(x, y, z) strtoul(x, y, z)
|
|
#define strtoull(x, y, z) strtoul(x, y, z)
|
|
#define strtoll(x, y, z) strtol(x, y, z)
|
|
#define strtoll(x, y, z) strtol(x, y, z)
|
|
#else
|
|
#else
|
|
@@ -460,7 +460,7 @@ static const char *config_options[] = {
|
|
"u", "run_as_user", NULL,
|
|
"u", "run_as_user", NULL,
|
|
"w", "url_rewrite_patterns", NULL,
|
|
"w", "url_rewrite_patterns", NULL,
|
|
"x", "hide_files_patterns", NULL,
|
|
"x", "hide_files_patterns", NULL,
|
|
- "z", "request_timeout", NULL,
|
|
|
|
|
|
+ "z", "request_timeout_ms", "30000",
|
|
NULL
|
|
NULL
|
|
};
|
|
};
|
|
#define ENTRIES_PER_CONFIG_OPTION 3
|
|
#define ENTRIES_PER_CONFIG_OPTION 3
|
|
@@ -1478,30 +1478,6 @@ static int64_t push(FILE *fp, SOCKET sock, SSL *ssl, const char *buf,
|
|
return sent;
|
|
return sent;
|
|
}
|
|
}
|
|
|
|
|
|
-// This function is needed to prevent Mongoose to be stuck in a blocking
|
|
|
|
-// socket read when user requested exit. To do that, we sleep in select
|
|
|
|
-// with a timeout, and when returned, check the context for the stop flag.
|
|
|
|
-// If it is set, we return 0, and this means that we must not continue
|
|
|
|
-// reading, must give up and close the connection and exit serving thread.
|
|
|
|
-static int wait_until_socket_is_readable(struct mg_connection *conn) {
|
|
|
|
- int result;
|
|
|
|
- struct pollfd pfd;
|
|
|
|
-
|
|
|
|
- do {
|
|
|
|
- pfd.fd = conn->client.sock;
|
|
|
|
- pfd.events = POLLIN;
|
|
|
|
- result = poll(&pfd, 1, 200);
|
|
|
|
-#ifndef NO_SSL
|
|
|
|
- if (result == 0 && conn->ssl != NULL) {
|
|
|
|
- result = SSL_pending(conn->ssl);
|
|
|
|
- }
|
|
|
|
-#endif
|
|
|
|
- } while ((result == 0 || (result < 0 && ERRNO == EINTR)) &&
|
|
|
|
- conn->ctx->stop_flag == 0);
|
|
|
|
-
|
|
|
|
- return conn->ctx->stop_flag || result < 0 ? 0 : 1;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
// Read from IO channel - opened file descriptor, socket, or SSL descriptor.
|
|
// Read from IO channel - opened file descriptor, socket, or SSL descriptor.
|
|
// Return negative value on error, or number of bytes read on success.
|
|
// Return negative value on error, or number of bytes read on success.
|
|
static int pull(FILE *fp, struct mg_connection *conn, char *buf, int len) {
|
|
static int pull(FILE *fp, struct mg_connection *conn, char *buf, int len) {
|
|
@@ -1512,8 +1488,6 @@ static int pull(FILE *fp, struct mg_connection *conn, char *buf, int len) {
|
|
// pipe, fread() may block until IO buffer is filled up. We cannot afford
|
|
// pipe, fread() may block until IO buffer is filled up. We cannot afford
|
|
// to block and must pass all read bytes immediately to the client.
|
|
// to block and must pass all read bytes immediately to the client.
|
|
nread = read(fileno(fp), buf, (size_t) len);
|
|
nread = read(fileno(fp), buf, (size_t) len);
|
|
- } else if (!conn->must_close && !wait_until_socket_is_readable(conn)) {
|
|
|
|
- nread = -1;
|
|
|
|
#ifndef NO_SSL
|
|
#ifndef NO_SSL
|
|
} else if (conn->ssl != NULL) {
|
|
} else if (conn->ssl != NULL) {
|
|
nread = SSL_read(conn->ssl, buf, len);
|
|
nread = SSL_read(conn->ssl, buf, len);
|
|
@@ -2912,22 +2886,16 @@ static int parse_http_message(char *buf, int len, struct mg_request_info *ri) {
|
|
// Upon every read operation, increase nread by the number of bytes read.
|
|
// Upon every read operation, increase nread by the number of bytes read.
|
|
static int read_request(FILE *fp, struct mg_connection *conn,
|
|
static int read_request(FILE *fp, struct mg_connection *conn,
|
|
char *buf, int bufsiz, int *nread) {
|
|
char *buf, int bufsiz, int *nread) {
|
|
- int request_len, n = 1;
|
|
|
|
|
|
+ int request_len, n = 0;
|
|
|
|
|
|
request_len = get_request_len(buf, *nread);
|
|
request_len = get_request_len(buf, *nread);
|
|
- while (*nread < bufsiz && request_len == 0 && n > 0) {
|
|
|
|
- n = pull(fp, conn, buf + *nread, bufsiz - *nread);
|
|
|
|
- if (n > 0) {
|
|
|
|
- *nread += n;
|
|
|
|
- request_len = get_request_len(buf, *nread);
|
|
|
|
- }
|
|
|
|
|
|
+ while (*nread < bufsiz && request_len == 0 &&
|
|
|
|
+ (n = pull(fp, conn, buf + *nread, bufsiz - *nread)) > 0) {
|
|
|
|
+ *nread += n;
|
|
|
|
+ request_len = get_request_len(buf, *nread);
|
|
}
|
|
}
|
|
|
|
|
|
- if (n < 0) {
|
|
|
|
- // recv() error -> propagate error; do not process a b0rked-with-very-high-probability request
|
|
|
|
- return -1;
|
|
|
|
- }
|
|
|
|
- return request_len;
|
|
|
|
|
|
+ return request_len <= 0 && n <= 0 ? -1 : request_len;
|
|
}
|
|
}
|
|
|
|
|
|
// For given directory path, substitute it to valid index file.
|
|
// For given directory path, substitute it to valid index file.
|
|
@@ -3838,9 +3806,6 @@ static void read_websocket(struct mg_connection *conn) {
|
|
conn->data_len -= discard_len;
|
|
conn->data_len -= discard_len;
|
|
conn->content_len = conn->consumed_content = 0;
|
|
conn->content_len = conn->consumed_content = 0;
|
|
} else {
|
|
} else {
|
|
- if (wait_until_socket_is_readable(conn) == 0) {
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
n = pull(NULL, conn, conn->buf + conn->data_len,
|
|
n = pull(NULL, conn, conn->buf + conn->data_len,
|
|
conn->buf_size - conn->data_len);
|
|
conn->buf_size - conn->data_len);
|
|
if (n <= 0) {
|
|
if (n <= 0) {
|
|
@@ -4352,23 +4317,6 @@ static int parse_port_string(const struct vec *vec, struct socket *so) {
|
|
return 1;
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
|
|
-static int set_timeout(struct mg_context *ctx, SOCKET sock) {
|
|
|
|
-#ifndef _WIN32
|
|
|
|
- struct timeval timeout;
|
|
|
|
- if( !ctx->config[REQUEST_TIMEOUT] )
|
|
|
|
- return 0;
|
|
|
|
- timeout.tv_sec = 0;
|
|
|
|
- timeout.tv_usec = atoi(ctx->config[REQUEST_TIMEOUT]) * 1000;
|
|
|
|
-#else
|
|
|
|
- DWORD timeout;
|
|
|
|
- if( !ctx->config[REQUEST_TIMEOUT] )
|
|
|
|
- return 0;
|
|
|
|
- timeout = atoi(ctx->config[REQUEST_TIMEOUT]);
|
|
|
|
-#endif
|
|
|
|
- return setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (void *)&timeout, sizeof(timeout))
|
|
|
|
- || setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (void *)&timeout, sizeof(timeout));
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
static int set_ports_option(struct mg_context *ctx) {
|
|
static int set_ports_option(struct mg_context *ctx) {
|
|
const char *list = ctx->config[LISTENING_PORTS];
|
|
const char *list = ctx->config[LISTENING_PORTS];
|
|
int on = 1, success = 1;
|
|
int on = 1, success = 1;
|
|
@@ -4380,31 +4328,20 @@ static int set_ports_option(struct mg_context *ctx) {
|
|
cry(fc(ctx), "%s: %.*s: invalid port spec. Expecting list of: %s",
|
|
cry(fc(ctx), "%s: %.*s: invalid port spec. Expecting list of: %s",
|
|
__func__, (int) vec.len, vec.ptr, "[IP_ADDRESS:]PORT[s|p]");
|
|
__func__, (int) vec.len, vec.ptr, "[IP_ADDRESS:]PORT[s|p]");
|
|
success = 0;
|
|
success = 0;
|
|
- } else if (so.is_ssl &&
|
|
|
|
- (ctx->ssl_ctx == NULL || ctx->config[SSL_CERTIFICATE] == NULL)) {
|
|
|
|
|
|
+ } else if (so.is_ssl && ctx->ssl_ctx == NULL) {
|
|
cry(fc(ctx), "Cannot add SSL socket, is -ssl_certificate option set?");
|
|
cry(fc(ctx), "Cannot add SSL socket, is -ssl_certificate option set?");
|
|
success = 0;
|
|
success = 0;
|
|
} else if ((so.sock = socket(so.lsa.sa.sa_family, SOCK_STREAM, 6)) ==
|
|
} else if ((so.sock = socket(so.lsa.sa.sa_family, SOCK_STREAM, 6)) ==
|
|
INVALID_SOCKET ||
|
|
INVALID_SOCKET ||
|
|
- set_timeout(ctx, so.sock) ||
|
|
|
|
// On Windows, SO_REUSEADDR is recommended only for
|
|
// On Windows, SO_REUSEADDR is recommended only for
|
|
// broadcast UDP sockets
|
|
// broadcast UDP sockets
|
|
- setsockopt(so.sock, SOL_SOCKET, SO_REUSEADDR, (const char *) &on,
|
|
|
|
- sizeof(on)) != 0 ||
|
|
|
|
- // Set TCP keep-alive. This is needed because if HTTP-level
|
|
|
|
- // keep-alive is enabled, and client resets the connection,
|
|
|
|
- // server won't get TCP FIN or RST and will keep the connection
|
|
|
|
- // open forever. With TCP keep-alive, next keep-alive
|
|
|
|
- // handshake will figure out that the client is down and
|
|
|
|
- // will close the server end.
|
|
|
|
- // Thanks to Igor Klopov who suggested the patch.
|
|
|
|
- setsockopt(so.sock, SOL_SOCKET, SO_KEEPALIVE, (char *) &on,
|
|
|
|
- sizeof(on)) != 0 ||
|
|
|
|
|
|
+ setsockopt(so.sock, SOL_SOCKET, SO_REUSEADDR,
|
|
|
|
+ (void *) &on, sizeof(on)) != 0 ||
|
|
bind(so.sock, &so.lsa.sa, sizeof(so.lsa)) != 0 ||
|
|
bind(so.sock, &so.lsa.sa, sizeof(so.lsa)) != 0 ||
|
|
listen(so.sock, SOMAXCONN) != 0) {
|
|
listen(so.sock, SOMAXCONN) != 0) {
|
|
- closesocket(so.sock);
|
|
|
|
cry(fc(ctx), "%s: cannot bind to %.*s: %s", __func__,
|
|
cry(fc(ctx), "%s: cannot bind to %.*s: %s", __func__,
|
|
(int) vec.len, vec.ptr, strerror(ERRNO));
|
|
(int) vec.len, vec.ptr, strerror(ERRNO));
|
|
|
|
+ closesocket(so.sock);
|
|
success = 0;
|
|
success = 0;
|
|
} else {
|
|
} else {
|
|
set_close_on_exec(so.sock);
|
|
set_close_on_exec(so.sock);
|
|
@@ -5003,29 +4940,45 @@ static void produce_socket(struct mg_context *ctx, const struct socket *sp) {
|
|
(void) pthread_mutex_unlock(&ctx->mutex);
|
|
(void) pthread_mutex_unlock(&ctx->mutex);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static int set_sock_timeout(SOCKET sock, int milliseconds) {
|
|
|
|
+#ifdef _WIN32
|
|
|
|
+ DWORD t = milliseconds;
|
|
|
|
+#else
|
|
|
|
+ struct timeval t;
|
|
|
|
+ t.tv_sec = milliseconds / 1000;
|
|
|
|
+ t.tv_usec = (milliseconds * 1000) % 1000000;
|
|
|
|
+#endif
|
|
|
|
+ return setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (void *) &t, sizeof(t)) ||
|
|
|
|
+ setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (void *) &t, sizeof(t));
|
|
|
|
+}
|
|
|
|
+
|
|
static void accept_new_connection(const struct socket *listener,
|
|
static void accept_new_connection(const struct socket *listener,
|
|
struct mg_context *ctx) {
|
|
struct mg_context *ctx) {
|
|
- struct socket accepted;
|
|
|
|
|
|
+ struct socket so;
|
|
char src_addr[20];
|
|
char src_addr[20];
|
|
- socklen_t len;
|
|
|
|
- int allowed;
|
|
|
|
-
|
|
|
|
- len = sizeof(accepted.rsa);
|
|
|
|
- accepted.sock = accept(listener->sock, &accepted.rsa.sa, &len);
|
|
|
|
- if (accepted.sock != INVALID_SOCKET) {
|
|
|
|
- allowed = check_acl(ctx, ntohl(* (uint32_t *) &accepted.rsa.sin.sin_addr));
|
|
|
|
- if (allowed) {
|
|
|
|
- // Put accepted socket structure into the queue
|
|
|
|
- DEBUG_TRACE(("accepted socket %d", accepted.sock));
|
|
|
|
- accepted.is_ssl = listener->is_ssl;
|
|
|
|
- accepted.ssl_redir = listener->ssl_redir;
|
|
|
|
- getsockname(accepted.sock, &accepted.lsa.sa, &len);
|
|
|
|
- produce_socket(ctx, &accepted);
|
|
|
|
- } else {
|
|
|
|
- sockaddr_to_string(src_addr, sizeof(src_addr), &accepted.rsa);
|
|
|
|
- cry(fc(ctx), "%s: %s is not allowed to connect", __func__, src_addr);
|
|
|
|
- (void) closesocket(accepted.sock);
|
|
|
|
- }
|
|
|
|
|
|
+ socklen_t len = sizeof(so.rsa);
|
|
|
|
+ int on = 1;
|
|
|
|
+
|
|
|
|
+ if ((so.sock = accept(listener->sock, &so.rsa.sa, &len)) == INVALID_SOCKET) {
|
|
|
|
+ } else if (!check_acl(ctx, ntohl(* (uint32_t *) &so.rsa.sin.sin_addr))) {
|
|
|
|
+ sockaddr_to_string(src_addr, sizeof(src_addr), &so.rsa);
|
|
|
|
+ cry(fc(ctx), "%s: %s is not allowed to connect", __func__, src_addr);
|
|
|
|
+ closesocket(so.sock);
|
|
|
|
+ } else {
|
|
|
|
+ // Put so socket structure into the queue
|
|
|
|
+ DEBUG_TRACE(("Accepted socket %d", (int) so.sock));
|
|
|
|
+ so.is_ssl = listener->is_ssl;
|
|
|
|
+ so.ssl_redir = listener->ssl_redir;
|
|
|
|
+ getsockname(so.sock, &so.lsa.sa, &len);
|
|
|
|
+ // Set TCP keep-alive. This is needed because if HTTP-level keep-alive
|
|
|
|
+ // is enabled, and client resets the connection, server won't get
|
|
|
|
+ // TCP FIN or RST and will keep the connection open forever. With TCP
|
|
|
|
+ // keep-alive, next keep-alive handshake will figure out that the client
|
|
|
|
+ // is down and will close the server end.
|
|
|
|
+ // Thanks to Igor Klopov who suggested the patch.
|
|
|
|
+ setsockopt(so.sock, SOL_SOCKET, SO_KEEPALIVE, (void *) &on, sizeof(on));
|
|
|
|
+ set_sock_timeout(so.sock, atoi(ctx->config[REQUEST_TIMEOUT]));
|
|
|
|
+ produce_socket(ctx, &so);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|