From 375950f6d7819f584ae464d8d0f73ef6d8c9b8a0 Mon Sep 17 00:00:00 2001 From: Sergey Lyubka Date: Thu, 16 Aug 2012 00:05:46 +0100 Subject: [PATCH] Fixed hang for keep-alive connections --- mongoose.c | 42 +++++++++++++++++++++++++++++++----------- 1 file changed, 31 insertions(+), 11 deletions(-) diff --git a/mongoose.c b/mongoose.c index 96561c10..ddb83d39 100644 --- a/mongoose.c +++ b/mongoose.c @@ -250,6 +250,7 @@ static pthread_t pthread_self(void) { #ifdef NO_SOCKLEN_T typedef int socklen_t; #endif // NO_SOCKLEN_T +#define _DARWIN_UNLIMITED_SELECT #if !defined(MSG_NOSIGNAL) #define MSG_NOSIGNAL 0 @@ -1370,25 +1371,47 @@ static int64_t push(FILE *fp, SOCKET sock, SSL *ssl, const char *buf, 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 timeval tv; + fd_set set; + + do { + tv.tv_sec = 0; + tv.tv_usec = 300 * 1000; + FD_ZERO(&set); + FD_SET(conn->client.sock, &set); + result = select(conn->client.sock + 1, &set, NULL, NULL, &tv); + } 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. // Return number of bytes read. static int pull(FILE *fp, struct mg_connection *conn, char *buf, int len) { int nread; - if (conn->ssl != NULL) { - nread = SSL_read(conn->ssl, buf, len); - } else if (fp != NULL) { + if (fp != NULL) { // Use read() instead of fread(), because if we're reading from the CGI // 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. nread = read(fileno(fp), buf, (size_t) len); - if (ferror(fp)) - nread = -1; + } else if (!wait_until_socket_is_readable(conn)) { + nread = -1; + } else if (conn->ssl != NULL) { + nread = SSL_read(conn->ssl, buf, len); } else { nread = recv(conn->client.sock, buf, (size_t) len, 0); } - return nread; + return conn->ctx->stop_flag ? -1 : nread; } int mg_read(struct mg_connection *conn, void *buf, size_t len) { @@ -2697,7 +2720,7 @@ static int read_request(FILE *fp, struct mg_connection *conn, } } while (*nread <= bufsiz && request_len == 0 && n > 0); - return request_len; + return n < 0 ? -1 : request_len; } // For given directory path, substitute it to valid index file. @@ -3033,10 +3056,7 @@ static void handle_cgi_request(struct mg_connection *conn, const char *prog) { // Do not send anything back to client, until we buffer in all // HTTP headers. data_len = 0; - struct mg_connection fake; - fake.client.sock = INVALID_SOCKET; - fake.ssl = NULL; - headers_len = read_request(out, &fake, buf, sizeof(buf), &data_len); + headers_len = read_request(out, fc(conn->ctx), buf, sizeof(buf), &data_len); if (headers_len <= 0) { send_http_error(conn, 500, http_500_error, "CGI program sent malformed HTTP headers: [%.*s]",