From b21a8b2193bc5fbab3086440c325868a399c8162 Mon Sep 17 00:00:00 2001
From: Sergey Lyubka <valenok@gmail.com>
Date: Tue, 29 Jan 2013 11:21:23 +0000
Subject: [PATCH] Removed wait_until_socket_is_readable(). Rely on socket
 timeout.

---
 mongoose.c | 143 ++++++++++++++++++-----------------------------------
 1 file changed, 48 insertions(+), 95 deletions(-)

diff --git a/mongoose.c b/mongoose.c
index b5834c6b5..f824dfb76 100644
--- a/mongoose.c
+++ b/mongoose.c
@@ -100,7 +100,7 @@ typedef long off_t;
 #if defined(_MSC_VER) && _MSC_VER < 1300
 #define STRX(x) #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 strtoll(x, y, z) strtol(x, y, z)
 #else
@@ -460,7 +460,7 @@ static const char *config_options[] = {
   "u", "run_as_user", NULL,
   "w", "url_rewrite_patterns", NULL,
   "x", "hide_files_patterns", NULL,
-  "z", "request_timeout", NULL,
+  "z", "request_timeout_ms", "30000",
   NULL
 };
 #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;
 }
 
-// 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.
 // 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) {
@@ -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
     // to block and must pass all read bytes immediately to the client.
     nread = read(fileno(fp), buf, (size_t) len);
-  } else if (!conn->must_close && !wait_until_socket_is_readable(conn)) {
-    nread = -1;
 #ifndef NO_SSL
   } else if (conn->ssl != NULL) {
     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.
 static int read_request(FILE *fp, struct mg_connection *conn,
                         char *buf, int bufsiz, int *nread) {
-  int request_len, n = 1;
+  int request_len, n = 0;
 
   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.
@@ -3838,9 +3806,6 @@ static void read_websocket(struct mg_connection *conn) {
       conn->data_len -= discard_len;
       conn->content_len = conn->consumed_content = 0;
     } else {
-      if (wait_until_socket_is_readable(conn) == 0) {
-        break;
-      }
       n = pull(NULL, conn, conn->buf + conn->data_len,
                conn->buf_size - conn->data_len);
       if (n <= 0) {
@@ -4352,23 +4317,6 @@ static int parse_port_string(const struct vec *vec, struct socket *so) {
   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) {
   const char *list = ctx->config[LISTENING_PORTS];
   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",
           __func__, (int) vec.len, vec.ptr, "[IP_ADDRESS:]PORT[s|p]");
       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?");
       success = 0;
     } else if ((so.sock = socket(so.lsa.sa.sa_family, SOCK_STREAM, 6)) ==
                INVALID_SOCKET ||
-               set_timeout(ctx, so.sock) ||
                // On Windows, SO_REUSEADDR is recommended only for
                // 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 ||
                listen(so.sock, SOMAXCONN) != 0) {
-      closesocket(so.sock);
       cry(fc(ctx), "%s: cannot bind to %.*s: %s", __func__,
           (int) vec.len, vec.ptr, strerror(ERRNO));
+      closesocket(so.sock);
       success = 0;
     } else {
       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);
 }
 
+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,
                                   struct mg_context *ctx) {
-  struct socket accepted;
+  struct socket so;
   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);
   }
 }
 
-- 
GitLab