فهرست منبع

Fix premature timeout for wss (secure websocket) connections

In "pull", the socket can receive bytes (leaving select), while
SSL_read still returns 0 bytes (not enough data to decrypt, only
TLS protocol data but no payload, ...).
In this case, we must repeat calling "pull".
bel 8 سال پیش
والد
کامیت
88d885d309
1فایلهای تغییر یافته به همراه89 افزوده شده و 42 حذف شده
  1. 89 42
      src/civetweb.c

+ 89 - 42
src/civetweb.c

@@ -2296,12 +2296,12 @@ open_file_in_memory(const struct mg_connection *conn,
 	return (buf != NULL);
 	return (buf != NULL);
 
 
 #else
 #else
-    (void)conn;
-    (void)path;
-    (void)filep;
-    (void)mode;
+	(void)conn;
+	(void)path;
+	(void)filep;
+	(void)mode;
 
 
-    return 0;
+	return 0;
 
 
 #endif
 #endif
 }
 }
@@ -4829,9 +4829,17 @@ push_all(struct mg_context *ctx,
 
 
 
 
 /* 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 value:
+ *  >=0 .. number of bytes successfully read
+ *   -1 .. timeout
+ *   -2 .. error
+ */
 static int
 static int
-pull(FILE *fp, struct mg_connection *conn, char *buf, int len, double timeout)
+pull_inner(FILE *fp,
+           struct mg_connection *conn,
+           char *buf,
+           int len,
+           double timeout)
 {
 {
 	int nread, err = 0;
 	int nread, err = 0;
 
 
@@ -4841,6 +4849,11 @@ pull(FILE *fp, struct mg_connection *conn, char *buf, int len, double timeout)
 	typedef size_t len_t;
 	typedef size_t len_t;
 #endif
 #endif
 
 
+	/* We need an additional wait loop around this, because in some cases
+	 * with TLSwe may get data from the socket but not from SSL_read.
+	 * In this case we need to repeat at least once.
+	 */
+
 	if (fp != NULL) {
 	if (fp != NULL) {
 #if !defined(_WIN32_WCE)
 #if !defined(_WIN32_WCE)
 		/* Use read() instead of fread(), because if we're reading from the
 		/* Use read() instead of fread(), because if we're reading from the
@@ -4865,7 +4878,7 @@ pull(FILE *fp, struct mg_connection *conn, char *buf, int len, double timeout)
 		pollres =
 		pollres =
 		    mg_poll(pfd, 1, (int)(timeout * 1000.0), &(conn->ctx->stop_flag));
 		    mg_poll(pfd, 1, (int)(timeout * 1000.0), &(conn->ctx->stop_flag));
 		if (conn->ctx->stop_flag) {
 		if (conn->ctx->stop_flag) {
-			return -1;
+			return -2;
 		}
 		}
 		if (pollres > 0) {
 		if (pollres > 0) {
 			nread = SSL_read(conn->ssl, buf, len);
 			nread = SSL_read(conn->ssl, buf, len);
@@ -4878,7 +4891,7 @@ pull(FILE *fp, struct mg_connection *conn, char *buf, int len, double timeout)
 					nread = 0;
 					nread = 0;
 				} else {
 				} else {
 					DEBUG_TRACE("SSL_read() failed, error %d", err);
 					DEBUG_TRACE("SSL_read() failed, error %d", err);
-					return -1;
+					return -2;
 				}
 				}
 			} else {
 			} else {
 				err = 0;
 				err = 0;
@@ -4886,7 +4899,7 @@ pull(FILE *fp, struct mg_connection *conn, char *buf, int len, double timeout)
 
 
 		} else if (pollres < 0) {
 		} else if (pollres < 0) {
 			/* Error */
 			/* Error */
-			return -1;
+			return -2;
 		} else {
 		} else {
 			/* pollres = 0 means timeout */
 			/* pollres = 0 means timeout */
 			nread = 0;
 			nread = 0;
@@ -4903,18 +4916,18 @@ pull(FILE *fp, struct mg_connection *conn, char *buf, int len, double timeout)
 		pollres =
 		pollres =
 		    mg_poll(pfd, 1, (int)(timeout * 1000.0), &(conn->ctx->stop_flag));
 		    mg_poll(pfd, 1, (int)(timeout * 1000.0), &(conn->ctx->stop_flag));
 		if (conn->ctx->stop_flag) {
 		if (conn->ctx->stop_flag) {
-			return -1;
+			return -2;
 		}
 		}
 		if (pollres > 0) {
 		if (pollres > 0) {
 			nread = (int)recv(conn->client.sock, buf, (len_t)len, 0);
 			nread = (int)recv(conn->client.sock, buf, (len_t)len, 0);
 			err = (nread < 0) ? ERRNO : 0;
 			err = (nread < 0) ? ERRNO : 0;
 			if (nread <= 0) {
 			if (nread <= 0) {
 				/* shutdown of the socket at client side */
 				/* shutdown of the socket at client side */
-				return -1;
+				return -2;
 			}
 			}
 		} else if (pollres < 0) {
 		} else if (pollres < 0) {
 			/* error callint poll */
 			/* error callint poll */
-			return -1;
+			return -2;
 		} else {
 		} else {
 			/* pollres = 0 means timeout */
 			/* pollres = 0 means timeout */
 			nread = 0;
 			nread = 0;
@@ -4922,7 +4935,7 @@ pull(FILE *fp, struct mg_connection *conn, char *buf, int len, double timeout)
 	}
 	}
 
 
 	if (conn->ctx->stop_flag) {
 	if (conn->ctx->stop_flag) {
-		return -1;
+		return -2;
 	}
 	}
 
 
 	if ((nread > 0) || (nread == 0 && len == 0)) {
 	if ((nread > 0) || (nread == 0 && len == 0)) {
@@ -4936,17 +4949,17 @@ pull(FILE *fp, struct mg_connection *conn, char *buf, int len, double timeout)
 		if (err == WSAEWOULDBLOCK) {
 		if (err == WSAEWOULDBLOCK) {
 			/* TODO (low): check if this is still required */
 			/* TODO (low): check if this is still required */
 			/* standard case if called from close_socket_gracefully */
 			/* standard case if called from close_socket_gracefully */
-			return -1;
+			return -2;
 		} else if (err == WSAETIMEDOUT) {
 		} else if (err == WSAETIMEDOUT) {
 			/* TODO (low): check if this is still required */
 			/* TODO (low): check if this is still required */
 			/* timeout is handled by the while loop  */
 			/* timeout is handled by the while loop  */
 			return 0;
 			return 0;
 		} else if (err == WSAECONNABORTED) {
 		} else if (err == WSAECONNABORTED) {
 			/* See https://www.chilkatsoft.com/p/p_299.asp */
 			/* See https://www.chilkatsoft.com/p/p_299.asp */
-			return -1;
+			return -2;
 		} else {
 		} else {
 			DEBUG_TRACE("recv() failed, error %d", err);
 			DEBUG_TRACE("recv() failed, error %d", err);
-			return -1;
+			return -2;
 		}
 		}
 #else
 #else
 		/* TODO: POSIX returns either EAGAIN or EWOULDBLOCK in both cases,
 		/* TODO: POSIX returns either EAGAIN or EWOULDBLOCK in both cases,
@@ -4968,7 +4981,7 @@ pull(FILE *fp, struct mg_connection *conn, char *buf, int len, double timeout)
 			 * => stay in the while loop */
 			 * => stay in the while loop */
 		} else {
 		} else {
 			DEBUG_TRACE("recv() failed, error %d", err);
 			DEBUG_TRACE("recv() failed, error %d", err);
-			return -1;
+			return -2;
 		}
 		}
 #endif
 #endif
 	}
 	}
@@ -4983,16 +4996,30 @@ pull_all(FILE *fp, struct mg_connection *conn, char *buf, int len)
 {
 {
 	int n, nread = 0;
 	int n, nread = 0;
 	double timeout = -1.0;
 	double timeout = -1.0;
+	double dt;
+	struct timespec start_time, now;
 
 
 	if (conn->ctx->config[REQUEST_TIMEOUT]) {
 	if (conn->ctx->config[REQUEST_TIMEOUT]) {
 		timeout = atoi(conn->ctx->config[REQUEST_TIMEOUT]) / 1000.0;
 		timeout = atoi(conn->ctx->config[REQUEST_TIMEOUT]) / 1000.0;
+		clock_gettime(CLOCK_MONOTONIC, &start_time);
 	}
 	}
 
 
 	while (len > 0 && conn->ctx->stop_flag == 0) {
 	while (len > 0 && conn->ctx->stop_flag == 0) {
-		n = pull(fp, conn, buf + nread, len, timeout);
-		if (n < 0) {
+		n = pull_inner(fp, conn, buf + nread, len, timeout);
+		if (n == -2) {
 			if (nread == 0) {
 			if (nread == 0) {
-				nread = n; /* Propagate the error */
+				nread = -1; /* Propagate the error */
+			}
+			break;
+		} else if (n == -1) {
+			/* timeout */
+			if (timeout > 0.0) {
+				clock_gettime(CLOCK_MONOTONIC, &now);
+				dt = (now.tv_sec - start_time.tv_sec)
+				     + 1.0E-9 * (now.tv_nsec - start_time.tv_nsec);
+				if (dt < timeout) {
+					continue;
+				}
 			}
 			}
 			break;
 			break;
 		} else if (n == 0) {
 		} else if (n == 0) {
@@ -8135,13 +8162,18 @@ read_request(FILE *fp,
 			return -2;
 			return -2;
 		}
 		}
 
 
-		n = pull(fp, conn, buf + *nread, bufsiz - *nread, request_timeout);
-		if (n < 0) {
+		n = pull_inner(
+		    fp, conn, buf + *nread, bufsiz - *nread, request_timeout);
+		if (n == -2) {
 			/* Receive error */
 			/* Receive error */
 			return -1;
 			return -1;
 		}
 		}
-		*nread += n;
-		request_len = get_request_len(buf, *nread);
+		if (n > 0) {
+			*nread += n;
+			request_len = get_request_len(buf, *nread);
+		} else {
+			request_len = 0;
+		}
 
 
 		if ((request_len == 0) && (request_timeout >= 0)) {
 		if ((request_len == 0) && (request_timeout >= 0)) {
 			if (mg_difftimespec(&last_action_time, &(conn->req_time))
 			if (mg_difftimespec(&last_action_time, &(conn->req_time))
@@ -8302,11 +8334,16 @@ forward_body_data(struct mg_connection *conn, FILE *fp, SOCKET sock, SSL *ssl)
 			if ((int64_t)to_read > conn->content_len - conn->consumed_content) {
 			if ((int64_t)to_read > conn->content_len - conn->consumed_content) {
 				to_read = (int)(conn->content_len - conn->consumed_content);
 				to_read = (int)(conn->content_len - conn->consumed_content);
 			}
 			}
-			nread = pull(NULL, conn, buf, to_read, timeout);
-			if (nread <= 0
-			    || push_all(conn->ctx, fp, sock, ssl, buf, nread) != nread) {
+			nread = pull_inner(NULL, conn, buf, to_read, timeout);
+			if (nread == -2) {
+				/* error */
 				break;
 				break;
 			}
 			}
+			if (nread > 0) {
+				if (push_all(conn->ctx, fp, sock, ssl, buf, nread) != nread) {
+					break;
+				}
+			}
 			conn->consumed_content += nread;
 			conn->consumed_content += nread;
 		}
 		}
 
 
@@ -9752,16 +9789,20 @@ read_websocket(struct mg_connection *conn,
 				memcpy(data, buf + header_len, len);
 				memcpy(data, buf + header_len, len);
 				error = 0;
 				error = 0;
 				while (len < data_len) {
 				while (len < data_len) {
-					n = pull(NULL,
-					         conn,
-					         (char *)(data + len),
-					         (int)(data_len - len),
-					         timeout);
-					if (n <= 0) {
+					n = pull_inner(NULL,
+					               conn,
+					               (char *)(data + len),
+					               (int)(data_len - len),
+					               timeout);
+					if (n <= -2) {
 						error = 1;
 						error = 1;
 						break;
 						break;
+					} else if (n > 0) {
+						len += (size_t)n;
+					} else {
+						/* Timeout: should retry */
+						/* TODO: retry condition */
 					}
 					}
-					len += (size_t)n;
 				}
 				}
 				if (error) {
 				if (error) {
 					mg_cry(conn, "Websocket pull failed; closing connection");
 					mg_cry(conn, "Websocket pull failed; closing connection");
@@ -9816,15 +9857,21 @@ read_websocket(struct mg_connection *conn,
 		} else {
 		} else {
 			/* Read from the socket into the next available location in the
 			/* Read from the socket into the next available location in the
 			 * message queue. */
 			 * message queue. */
-			if ((n = pull(NULL,
-			              conn,
-			              conn->buf + conn->data_len,
-			              conn->buf_size - conn->data_len,
-			              timeout)) <= 0) {
+			n = pull_inner(NULL,
+			               conn,
+			               conn->buf + conn->data_len,
+			               conn->buf_size - conn->data_len,
+			               timeout);
+			if (n <= -2) {
 				/* Error, no bytes read */
 				/* Error, no bytes read */
 				break;
 				break;
 			}
 			}
-			conn->data_len += n;
+			if (n > 0) {
+				conn->data_len += n;
+			} else {
+				/* Timeout: should retry */
+				/* TODO: get timeout def */
+			}
 		}
 		}
 	}
 	}
 
 
@@ -12742,7 +12789,7 @@ close_socket_gracefully(struct mg_connection *conn)
 	 * when server decides to close the connection; then when client
 	 * when server decides to close the connection; then when client
 	 * does recv() it gets no data back. */
 	 * does recv() it gets no data back. */
 	do {
 	do {
-		n = pull(NULL, conn, buf, sizeof(buf), /* Timeout in s: */ 1.0);
+		n = pull_inner(NULL, conn, buf, sizeof(buf), /* Timeout in s: */ 1.0);
 	} while (n > 0);
 	} while (n > 0);
 #endif
 #endif