Browse Source

Merge pull request #282 from thewaterymoon/master

Fix the checking of 'keep-alive' connection flag.
bel2125 9 years ago
parent
commit
4e58f156d2
2 changed files with 50 additions and 5 deletions
  1. 4 0
      .gitignore
  2. 46 5
      src/civetweb.c

+ 4 - 0
.gitignore

@@ -64,6 +64,10 @@ local.properties
 *.user
 *.sln.docstates
 
+# Text-mode IDE tools
+cscope.out
+tags
+
 # Build results
 
 [Dd]ebug/

+ 46 - 5
src/civetweb.c

@@ -2052,10 +2052,17 @@ mg_get_header(const struct mg_connection *conn, const char *name)
 static const char *
 next_option(const char *list, struct vec *val, struct vec *eq_val)
 {
+	int end;
+
+reparse:
 	if (val == NULL || list == NULL || *list == '\0') {
 		/* End of the list */
 		list = NULL;
 	} else {
+		/* Skip over leading LWS */
+		while (*list == ' ' || *list == '\t')
+			list++;
+
 		val->ptr = list;
 		if ((list = strchr(val->ptr, ',')) != NULL) {
 			/* Comma found. Store length and shift the list ptr */
@@ -2067,6 +2074,17 @@ next_option(const char *list, struct vec *val, struct vec *eq_val)
 			val->len = ((size_t)(list - val->ptr));
 		}
 
+		/* Adjust length for trailing LWS */
+		end = (int)val->len - 1;
+		while (end >= 0 && (val->ptr[end] == ' ' || val->ptr[end] == '\t'))
+			end--;
+		val->len = (size_t)(end + 1);
+
+		if (val->len == 0) {
+			/* Ignore any empty entries. */
+			goto reparse;
+		}
+
 		if (eq_val != NULL) {
 			/* Value has form "x=y", adjust pointers and lengths
 			 * so that val points to "x", and eq_val points to "y". */
@@ -2083,6 +2101,24 @@ next_option(const char *list, struct vec *val, struct vec *eq_val)
 	return list;
 }
 
+/* A helper function for checking if a comma separated list of values contains
+ * the given option (case insensitvely).
+ * 'header' can be NULL, in which case false is returned. */
+static int header_has_option(const char *header, const char *option)
+{
+	struct vec opt_vec;
+	struct vec eq_vec;
+
+	assert(option != NULL);
+	assert(option[0] != '\0');
+
+	while ((header = next_option(header, &opt_vec, &eq_vec)) != NULL) {
+		if (mg_strncasecmp(option, opt_vec.ptr, opt_vec.len) == 0)
+			return 1;
+	}
+
+	return 0;
+}
 
 /* Perform case-insensitive match of string against pattern */
 static int
@@ -2139,7 +2175,7 @@ should_keep_alive(const struct mg_connection *conn)
 		const char *header = mg_get_header(conn, "Connection");
 		if (conn->must_close || conn->internal_error || conn->status_code == 401
 		    || mg_strcasecmp(conn->ctx->config[ENABLE_KEEP_ALIVE], "yes") != 0
-		    || (header != NULL && mg_strcasecmp(header, "keep-alive") != 0)
+		    || (header != NULL && !header_has_option(header, "keep-alive"))
 		    || (header == NULL && http_version
 		        && 0 != strcmp(http_version, "1.1"))) {
 			return 0;
@@ -3822,11 +3858,17 @@ pull(FILE *fp, struct mg_connection *conn, char *buf, int len, double timeout)
 			 * blocking in close_socket_gracefully, so we can not distinguish
 			 * here. We have to wait for the timeout in both cases for now.
 			 */
-			if (err == EAGAIN || err == EWOULDBLOCK) {
-				/* standard case if called from close_socket_gracefully
+			if (err == EAGAIN || err == EWOULDBLOCK || err == EINTR) {
+				/* EAGAIN/EWOULDBLOCK:
+				 * standard case if called from close_socket_gracefully
 				 * => should return -1 */
 				/* or timeout occured
 				 * => the code must stay in the while loop */
+
+				/* EINTR can be generated on a socket with a timeout set even
+				 * when SA_RESTART is effective for all relevant signals
+				 * (see signal(7)).
+				 * => stay in the while loop */
 			} else {
 				DEBUG_TRACE("recv() failed, error %d", err);
 				return -1;
@@ -7472,8 +7514,7 @@ handle_cgi_request(struct mg_connection *conn, const char *prog)
 		conn->status_code = 200;
 	}
 	connection_state = get_header(&ri, "Connection");
-	if (connection_state == NULL
-	    || mg_strcasecmp(connection_state, "keep-alive")) {
+	if (!header_has_option(connection_state, "keep-alive")) {
 		conn->must_close = 1;
 	}
 	(void)mg_printf(conn, "HTTP/1.1 %d %s\r\n", conn->status_code, status_text);