Browse Source

fix blocked CGI read

Sergey Lyubka 14 years ago
parent
commit
1de7fef760
3 changed files with 19 additions and 18 deletions
  1. 4 1
      mongoose.c
  2. 11 8
      test/test.pl
  3. 4 9
      test/timeout.cgi

+ 4 - 1
mongoose.c

@@ -1305,7 +1305,10 @@ static int pull(FILE *fp, SOCKET sock, SSL *ssl, char *buf, int len) {
   if (ssl != NULL) {
     nread = SSL_read(ssl, buf, len);
   } else if (fp != NULL) {
-    nread = fread(buf, 1, (size_t) len, fp);
+    // 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 {

+ 11 - 8
test/test.pl

@@ -59,13 +59,11 @@ sub req {
     last unless print $sock $byte;
     select undef, undef, undef, .001 if length($request) < 256;
   }
-  my $out = '';
+  my ($out, $buf) = ('', '');
   eval {
     alarm $timeout if $timeout;
-    foreach (<$sock>) {
-      $out .= $_;
-    }
-    alarm 0;
+    $out .= $buf while (sysread($sock, $buf, 1024) > 0);
+    alarm 0 if $timeout;
   };
   close $sock;
 
@@ -182,6 +180,14 @@ o("GET /hello.txt HTTP/1.0\n\n", 'Content-Length: 17\s',
 o("GET /%68%65%6c%6c%6f%2e%74%78%74 HTTP/1.0\n\n",
   'HTTP/1.1 200 OK', 'URL-decoding');
 
+# Break CGI reading after 1 second. We must get full output.
+# Since CGI script does sleep, we sleep as well and increase request count
+# manually.
+fail('Slow CGI output forward ') unless
+  req("GET /timeout.cgi HTTP/1.0\r\n\r\n", 0, 1) =~ /Some data/s;
+sleep 3;
+$num_requests++;
+
 # '+' in URI must not be URL-decoded to space
 write_file("$root/a+.txt", '');
 o("GET /a+.txt HTTP/1.0\n\n", 'HTTP/1.1 200 OK', 'URL-decoding, + in URI');
@@ -225,9 +231,6 @@ o("GET /ta/x/ HTTP/1.0\n\n", "SCRIPT_NAME=/ta/x/index.cgi",
 #  'HTTP/1.1 200.+keep-alive.+HTTP/1.1 200.+close',
 #  'Request pipelining', 2);
 
-fail('Slow CGI output forward ') unless
-  req("GET /timeout.cgi HTTP/1.0\r\n\r\n", 1, 1) =~ /Some data/gs;
-
 my $mime_types = {
   html => 'text/html',
   htm => 'text/html',

+ 4 - 9
test/timeout.cgi

@@ -1,16 +1,11 @@
 #!/usr/bin/env perl
 
-use Cwd;
-use CGI;
-
-use vars '%in';
-CGI::ReadParse();
-
-print "Content-Type: text/html\r\n\r\n";
+# Make stdout unbuffered
+$| = 1;
 
 # This script outputs some content, then sleeps for 5 seconds, then exits.
 # Web server should return the content immediately after it is sent,
 # not waiting until the script exits.
+print "Content-Type: text/html\r\n\r\n";
 print "Some data";
-flush STDOUT;
-sleep 5;
+sleep 3;