Sfoglia il codice sorgente

Fix multiple CGI interpreters and allow unbuffered CGI responses

bel2125 3 anni fa
parent
commit
b69c6933ed
2 ha cambiato i file con 28 aggiunte e 11 eliminazioni
  1. 26 10
      src/civetweb.c
  2. 2 1
      test/flush.php

+ 26 - 10
src/civetweb.c

@@ -1961,6 +1961,7 @@ enum {
 #if defined(USE_TIMERS)
 	CGI_TIMEOUT,
 #endif
+	CGI_BUFFERING,
 
 	CGI2_EXTENSIONS,
 	CGI2_ENVIRONMENT,
@@ -1969,6 +1970,7 @@ enum {
 #if defined(USE_TIMERS)
 	CGI2_TIMEOUT,
 #endif
+	CGI2_BUFFERING,
 
 #if defined(USE_4_CGI)
 	CGI3_EXTENSIONS,
@@ -1978,6 +1980,7 @@ enum {
 #if defined(USE_TIMERS)
 	CGI3_TIMEOUT,
 #endif
+	CGI3_BUFFERING,
 
 	CGI4_EXTENSIONS,
 	CGI4_ENVIRONMENT,
@@ -1986,6 +1989,7 @@ enum {
 #if defined(USE_TIMERS)
 	CGI4_TIMEOUT,
 #endif
+	CGI4_BUFFERING,
 #endif
 
 	PUT_DELETE_PASSWORDS_FILE, /* must follow CGI_* */
@@ -2100,6 +2104,7 @@ static const struct mg_option config_options[] = {
 #if defined(USE_TIMERS)
     {"cgi_timeout_ms", MG_CONFIG_TYPE_NUMBER, NULL},
 #endif
+    {"cgi_buffering", MG_CONFIG_TYPE_BOOLEAN, "yes"},
 
     {"cgi2_pattern", MG_CONFIG_TYPE_EXT_PATTERN, NULL},
     {"cgi2_environment", MG_CONFIG_TYPE_STRING_LIST, NULL},
@@ -2108,6 +2113,7 @@ static const struct mg_option config_options[] = {
 #if defined(USE_TIMERS)
     {"cgi2_timeout_ms", MG_CONFIG_TYPE_NUMBER, NULL},
 #endif
+    {"cgi2_buffering", MG_CONFIG_TYPE_BOOLEAN, "yes"},
 
 #if defined(USE_4_CGI)
     {"cgi3_pattern", MG_CONFIG_TYPE_EXT_PATTERN, NULL},
@@ -2117,6 +2123,7 @@ static const struct mg_option config_options[] = {
 #if defined(USE_TIMERS)
     {"cgi3_timeout_ms", MG_CONFIG_TYPE_NUMBER, NULL},
 #endif
+    {"cgi3_buffering", MG_CONFIG_TYPE_BOOLEAN, "yes"},
 
     {"cgi4_pattern", MG_CONFIG_TYPE_EXT_PATTERN, NULL},
     {"cgi4_environment", MG_CONFIG_TYPE_STRING_LIST, NULL},
@@ -2125,6 +2132,8 @@ static const struct mg_option config_options[] = {
 #if defined(USE_TIMERS)
     {"cgi4_timeout_ms", MG_CONFIG_TYPE_NUMBER, NULL},
 #endif
+    {"cgi4_buffering", MG_CONFIG_TYPE_BOOLEAN, "yes"},
+
 #endif
 
     {"put_delete_auth_file", MG_CONFIG_TYPE_FILE, NULL},
@@ -9920,7 +9929,8 @@ static void
 send_file_data(struct mg_connection *conn,
                struct mg_file *filep,
                int64_t offset,
-               int64_t len)
+               int64_t len,
+               int no_buffering)
 {
 	char buf[MG_BUF_LEN];
 	int to_read, num_read, num_written;
@@ -9993,8 +10003,8 @@ send_file_data(struct mg_connection *conn,
 			    "Error: Unable to access file at requested position.");
 		} else {
 			while (len > 0) {
-				/* Calculate how much to read from the file in the buffer */
-				to_read = sizeof(buf);
+				/* Calculate how much to read from the file into the buffer */
+				to_read = no_buffering ? 1 : sizeof(buf);
 				if ((int64_t)to_read > len) {
 					to_read = (int)len;
 				}
@@ -10295,7 +10305,7 @@ handle_static_file_request(struct mg_connection *conn,
 #endif
 		{
 			/* Send file directly */
-			send_file_data(conn, filep, r1, cl);
+			send_file_data(conn, filep, r1, cl, 0); /* send static file */
 		}
 	}
 	(void)mg_fclose(&filep->access); /* ignore error on read only file */
@@ -10310,7 +10320,7 @@ mg_send_file_body(struct mg_connection *conn, const char *path)
 		return -1;
 	}
 	fclose_on_exec(&file.access, conn);
-	send_file_data(conn, &file, 0, INT64_MAX);
+	send_file_data(conn, &file, 0, INT64_MAX, 0); /* send static file */
 	(void)mg_fclose(&file.access); /* Ignore errors for readonly files */
 	return 0;                      /* >= 0 for OK */
 }
@@ -11452,6 +11462,8 @@ handle_cgi_request(struct mg_connection *conn,
 	struct mg_file fout = STRUCT_FILE_INITIALIZER;
 	pid_t pid = (pid_t)-1;
 	struct process_control_data *proc = NULL;
+	char *cfg_buffering = conn->dom_ctx->config[CGI_BUFFERING + cgi_config_idx];
+	int no_buffering = 0;
 
 #if defined(USE_TIMERS)
 	double cgi_timeout;
@@ -11463,8 +11475,12 @@ handle_cgi_request(struct mg_connection *conn,
 		cgi_timeout =
 		    atof(config_options[REQUEST_TIMEOUT].default_value) * 0.001;
 	}
-
 #endif
+	if (cfg_buffering != NULL) {
+		if (!mg_strcasecmp(cfg_buffering, "no")) {
+			no_buffering = 1;
+		}
+	}
 
 	buf = NULL;
 	buflen = conn->phys_ctx->max_request_size;
@@ -11713,7 +11729,7 @@ handle_cgi_request(struct mg_connection *conn,
 
 	/* Read the rest of CGI output and send to the client */
 	DEBUG_TRACE("CGI: %s", "forward all data");
-	send_file_data(conn, &fout, 0, INT64_MAX);
+	send_file_data(conn, &fout, 0, INT64_MAX, no_buffering); /* send CGI data */
 	DEBUG_TRACE("CGI: %s", "all data sent");
 
 done:
@@ -12257,7 +12273,7 @@ do_ssi_include(struct mg_connection *conn,
 		    > 0) {
 			send_ssi_file(conn, path, &file, include_level + 1);
 		} else {
-			send_file_data(conn, &file, 0, INT64_MAX);
+			send_file_data(conn, &file, 0, INT64_MAX, 0); /* send static file */
 		}
 		(void)mg_fclose(&file.access); /* Ignore errors for readonly files */
 	}
@@ -12281,7 +12297,7 @@ do_ssi_exec(struct mg_connection *conn, char *tag)
 			                cmd,
 			                strerror(ERRNO));
 		} else {
-			send_file_data(conn, &file, 0, INT64_MAX);
+			send_file_data(conn, &file, 0, INT64_MAX, 0); /* send static file */
 			pclose(file.access.fp);
 		}
 	}
@@ -15189,7 +15205,7 @@ handle_file_based_request(struct mg_connection *conn,
 			    > 0) {
 				if (is_in_script_path(conn, path)) {
 					/* CGI scripts may support all HTTP methods */
-					handle_cgi_request(conn, path, 0);
+					handle_cgi_request(conn, path, cgi_config_idx);
 				} else {
 					/* Script was in an illegal path */
 					mg_send_http_error(conn, 403, "%s", "Forbidden");

+ 2 - 1
test/flush.php

@@ -4,7 +4,8 @@ header('Content-Type: text/plain; charset=utf-8');
 echo "CivetWeb Auto Flush Test\n";
 for($i = 0; $i <10; $i++) {
 	echo "Auto flush $i\n";
-	@ob_flush();@flush();
+	@flush();
+	@ob_flush();
 	sleep(1);
 }
 echo "\nCurrently this test fails, only the first FLUSH is processed by the server.\n";