httpd-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Roy T. Fielding" <field...@kiwi.ICS.UCI.EDU>
Subject [PATCH] Handle timeouts in buffers as well as connection
Date Fri, 18 Apr 1997 11:03:46 GMT
If a soft timeout (or lingerout) occurs while trying to flush a
buffer or write inside buff.c or fread'ing from a CGI's output,
then the timeout would be ignored.  This fix is rather nasty,
since what we would really like to do is flush the output if we
timeout on a read, but we can't do that without differentiating
between read and write timeouts, and we can't do that without
rewriting most of the server.

What we really need is a global (per-thread) timeout that sets a
single flag, and then have all of our code check for that flag and
recover gracefully based upon what it was trying to do at the time.
Something to remember for 1.2.

.....Roy

Index: buff.c
===================================================================
RCS file: /export/home/cvs/apache/src/buff.c,v
retrieving revision 1.23
diff -c -r1.23 buff.c
*** buff.c	1997/03/04 21:44:38	1.23
--- buff.c	1997/04/18 10:40:29
***************
*** 191,201 ****
      char chunksize[16];	/* Big enough for practically anything */
      int chunk_header_size;
  
!     if( fb->outchunk != -1 ) {
  	/* already chunking */
  	return;
      }
!     if( !(fb->flags & B_WR) ) {
  	/* unbuffered writes */
  	return;
      }
--- 191,201 ----
      char chunksize[16];	/* Big enough for practically anything */
      int chunk_header_size;
  
!     if (fb->outchunk != -1) {
  	/* already chunking */
  	return;
      }
!     if (!(fb->flags & B_WR) || (fb->flags & (B_WRERR|B_EOUT))) {
  	/* unbuffered writes */
  	return;
      }
***************
*** 609,630 ****
   * This is *seriously broken* if used on a non-blocking fd.  It will poll.
   */
  static int
! write_it_all( int fd, const void *buf, int nbyte ) {
! 
      int i;
  
!     while( nbyte > 0 ) {
! 	i = write( fd, buf, nbyte );
! 	if( i == -1 ) {
! 	    if( errno != EAGAIN && errno != EINTR ) {
! 		return( -1 );
  	    }
! 	} else {
  	    nbyte -= i;
  	    buf = i + (const char *)buf;
  	}
      }
!     return( 0 );
  }
  
  
--- 609,636 ----
   * This is *seriously broken* if used on a non-blocking fd.  It will poll.
   */
  static int
! write_it_all(BUFF *fb, const void *buf, int nbyte)
! {
      int i;
  
!     if (fb->flags & (B_WRERR|B_EOUT))
! 	return -1;
! 
!     while (nbyte > 0) {
! 	i = write(fb->fd, buf, nbyte);
! 	if (i < 0) {
! 	    if (errno != EAGAIN && errno != EINTR) {
! 		return -1;
  	    }
! 	}
! 	else {
  	    nbyte -= i;
  	    buf = i + (const char *)buf;
  	}
+ 	if (fb->flags & B_EOUT)
+ 	    return -1;
      }
!     return 0;
  }
  
  
***************
*** 635,665 ****
   * 2.0, using something like sfio stacked disciplines or BSD's funopen().
   */
  static int
! bcwrite(BUFF *fb, const void *buf, int nbyte) {
! 
      char chunksize[16];	/* Big enough for practically anything */
  #ifndef NO_WRITEV
      struct iovec vec[3];
      int i, rv;
  #endif
  
!     if( !(fb->flags & B_CHUNK) ) return write( fb->fd, buf, nbyte );
  
  #ifdef NO_WRITEV
      /* without writev() this has poor performance, too bad */
  
      ap_snprintf(chunksize, sizeof(chunksize), "%x\015\012", nbyte);
!     if( write_it_all(fb->fd, chunksize, strlen(chunksize)) == -1 ) {
! 	return( -1 );
!     }
!     if( write_it_all(fb->fd, buf, nbyte) == -1 ) {
! 	return( -1 );
!     }
!     if( write_it_all(fb->fd, "\015\012", 2) == -1 ) {
! 	return( -1 );
!     }
!     return( nbyte );
  #else
  #define NVEC	(sizeof(vec)/sizeof(vec[0]))
  
      vec[0].iov_base = chunksize;
--- 641,673 ----
   * 2.0, using something like sfio stacked disciplines or BSD's funopen().
   */
  static int
! bcwrite(BUFF *fb, const void *buf, int nbyte)
! {
      char chunksize[16];	/* Big enough for practically anything */
  #ifndef NO_WRITEV
      struct iovec vec[3];
      int i, rv;
  #endif
  
!     if (fb->flags & (B_WRERR|B_EOUT))
! 	return -1;
! 
!     if (!(fb->flags & B_CHUNK))
! 	return write(fb->fd, buf, nbyte);
  
  #ifdef NO_WRITEV
      /* without writev() this has poor performance, too bad */
  
      ap_snprintf(chunksize, sizeof(chunksize), "%x\015\012", nbyte);
!     if (write_it_all(fb, chunksize, strlen(chunksize)) == -1)
! 	return -1;
!     if (write_it_all(fb, buf, nbyte) == -1)
! 	return -1;
!     if (write_it_all(fb, "\015\012", 2) == -1)
! 	return -1;
!     return nbyte;
  #else
+ 
  #define NVEC	(sizeof(vec)/sizeof(vec[0]))
  
      vec[0].iov_base = chunksize;
***************
*** 674,685 ****
       */
      for( i = 0; i < NVEC; ) {
  	do rv = writev( fb->fd, &vec[i], NVEC - i );
! 	while ( rv == -1 && errno == EINTR );
! 	if( rv == -1 ) {
! 	    return( -1 );
! 	}
  	/* recalculate vec to deal with partial writes */
! 	while( rv > 0 ) {
  	    if( rv <= vec[i].iov_len ) {
  		vec[i].iov_base = (char *)vec[i].iov_base + rv;
  		vec[i].iov_len -= rv;
--- 682,692 ----
       */
      for( i = 0; i < NVEC; ) {
  	do rv = writev( fb->fd, &vec[i], NVEC - i );
! 	while (rv == -1 && errno == EINTR && !(fb->flags & B_EOUT));
! 	if (rv == -1)
! 	    return -1;
  	/* recalculate vec to deal with partial writes */
! 	while (rv > 0) {
  	    if( rv <= vec[i].iov_len ) {
  		vec[i].iov_base = (char *)vec[i].iov_base + rv;
  		vec[i].iov_len -= rv;
***************
*** 692,700 ****
  		++i;
  	    }
  	}
      }
      /* if we got here, we wrote it all */
!     return( nbyte );
  #undef NVEC
  #endif
  }
--- 699,709 ----
  		++i;
  	    }
  	}
+ 	if (fb->flags & B_EOUT)
+ 	    return -1;
      }
      /* if we got here, we wrote it all */
!     return nbyte;
  #undef NVEC
  #endif
  }
***************
*** 720,734 ****
  /* unbuffered write -- have to use bcwrite since we aren't taking care
   * of chunking any other way */
  	do i = bcwrite(fb, buf, nbyte);
! 	while (i == -1 && errno == EINTR);
! 	if (i > 0) fb->bytes_sent += i;
! 	if (i == 0)
! 	{
! 	    i = -1;  /* return of 0 means non-blocking */
  	    errno = EAGAIN;
  	}
! 	if (i == -1 && errno != EAGAIN) doerror(fb, B_WR);
! 	return i;
      }
  
  /*
--- 729,749 ----
  /* unbuffered write -- have to use bcwrite since we aren't taking care
   * of chunking any other way */
  	do i = bcwrite(fb, buf, nbyte);
! 	while (i == -1 && errno == EINTR && !(fb->flags & B_EOUT));
! 	if (i == 0) {  /* return of 0 means non-blocking */
  	    errno = EAGAIN;
+ 	    return -1;
  	}
! 	else if (i < 0) {
! 	    if (errno != EAGAIN)
! 	        doerror(fb, B_WR);
! 	    return -1;
! 	}
! 	fb->bytes_sent += i;
! 	if (fb->flags & B_EOUT)
! 	    return -1;
! 	else
! 	    return i;
      }
  
  /*
***************
*** 752,783 ****
  	}
  
  /* the buffer must be full */
! 	if( fb->flags & B_CHUNK ) {
  	    end_chunk(fb);
  	    /* it is just too painful to try to re-cram the buffer while
  	     * chunking
  	     */
! 	    i = write_it_all( fb->fd, fb->outbase, fb->outcnt )
! 		    ? -1 : fb->outcnt;
! 	} else {
! 	    do i = write(fb->fd, fb->outbase, fb->outcnt);
! 	    while (i == -1 && errno == EINTR);
  	}
! 	if (i > 0) fb->bytes_sent += i;
! 	if (i == 0)
! 	{
! 	    i = -1;  /* return of 0 means non-blocking */
! 	    errno = EAGAIN;
  	}
! 	if (i == -1)
! 	{
! 	    if (nwr == 0)
! 	    {
  		if (errno != EAGAIN) doerror(fb, B_WR);
  		return -1;
  	    }
  	    else return nwr;
  	}
  
  	/* deal with a partial write */
  	if (i < fb->outcnt)
--- 767,794 ----
  	}
  
  /* the buffer must be full */
! 	if (fb->flags & B_CHUNK) {
  	    end_chunk(fb);
  	    /* it is just too painful to try to re-cram the buffer while
  	     * chunking
  	     */
! 	    i = (write_it_all(fb, fb->outbase, fb->outcnt) == -1) ?
! 	            -1 : fb->outcnt;
  	}
! 	else {
! 	    do i = write(fb->fd, fb->outbase, fb->outcnt);
! 	    while (i == -1 && errno == EINTR && !(fb->flags & B_EOUT));
  	}
! 	if (i <= 0) {
! 	    if (i == 0) /* return of 0 means non-blocking */
! 	        errno = EAGAIN;
! 	    if (nwr == 0) {
  		if (errno != EAGAIN) doerror(fb, B_WR);
  		return -1;
  	    }
  	    else return nwr;
  	}
+ 	fb->bytes_sent += i;
  
  	/* deal with a partial write */
  	if (i < fb->outcnt)
***************
*** 786,793 ****
  	    unsigned char *x=fb->outbase;
  	    for (j=i; j < n; j++) x[j-i] = x[j];
  	    fb->outcnt -= i;
! 	} else
  	    fb->outcnt = 0;
      }
  /* we have emptied the file buffer. Now try to write the data from the
   * original buffer until there is less than bufsiz left.  Note that we
--- 797,808 ----
  	    unsigned char *x=fb->outbase;
  	    for (j=i; j < n; j++) x[j-i] = x[j];
  	    fb->outcnt -= i;
! 	}
! 	else
  	    fb->outcnt = 0;
+ 
+ 	if (fb->flags & B_EOUT)
+ 	    return -1;
      }
  /* we have emptied the file buffer. Now try to write the data from the
   * original buffer until there is less than bufsiz left.  Note that we
***************
*** 797,822 ****
      while (nbyte >= fb->bufsiz)
      {
  	do i = bcwrite(fb, buf, nbyte);
! 	while (i == -1 && errno == EINTR);
! 	if (i > 0) fb->bytes_sent += i;
! 	if (i == 0)
! 	{
! 	    i = -1;  /* return of 0 means non-blocking */
! 	    errno = EAGAIN;
! 	}
! 	if (i == -1)
! 	{
! 	    if (nwr == 0)
! 	    {
  		if (errno != EAGAIN) doerror(fb, B_WR);
  		return -1;
  	    }
  	    else return nwr;
  	}
  
  	buf = i + (const char *)buf;
  	nwr += i;
  	nbyte -= i;
      }
  /* copy what's left to the file buffer */
      fb->outcnt = 0;
--- 812,835 ----
      while (nbyte >= fb->bufsiz)
      {
  	do i = bcwrite(fb, buf, nbyte);
! 	while (i == -1 && errno == EINTR && !(fb->flags & B_EOUT));
! 	if (i <= 0) {
! 	    if (i == 0) /* return of 0 means non-blocking */
! 	        errno = EAGAIN;
! 	    if (nwr == 0) {
  		if (errno != EAGAIN) doerror(fb, B_WR);
  		return -1;
  	    }
  	    else return nwr;
  	}
+ 	fb->bytes_sent += i;
  
  	buf = i + (const char *)buf;
  	nwr += i;
  	nbyte -= i;
+ 
+ 	if (fb->flags & B_EOUT)
+ 	    return -1;
      }
  /* copy what's left to the file buffer */
      fb->outcnt = 0;
***************
*** 840,868 ****
  
      if (fb->flags & B_WRERR) return -1;
      
!     if( fb->flags & B_CHUNK ) end_chunk(fb);
  
      while (fb->outcnt > 0)
      {
! /* the buffer must be full */
  	do i = write(fb->fd, fb->outbase, fb->outcnt);
! 	while (i == -1 && errno == EINTR);
! 	if (i > 0) fb->bytes_sent += i;
! 	if (i == 0)
! 	{
  	    errno = EAGAIN;
  	    return -1;  /* return of 0 means non-blocking */
  	}
! 	if (i == -1)
! 	{
  	    if (errno != EAGAIN) doerror(fb, B_WR);
  	    return -1;
  	}
  
! /*
!  * we should have written all the data, however if the fd was in a
!  * strange (non-blocking) mode, then we might not have done so.
!  */
  	if (i < fb->outcnt)
  	{
  	    int j, n=fb->outcnt;
--- 853,879 ----
  
      if (fb->flags & B_WRERR) return -1;
      
!     if (fb->flags & B_CHUNK) end_chunk(fb);
  
      while (fb->outcnt > 0)
      {
! 	/* the buffer must be full */
  	do i = write(fb->fd, fb->outbase, fb->outcnt);
! 	while (i == -1 && errno == EINTR && !(fb->flags & B_EOUT));
! 	if (i == 0) {
  	    errno = EAGAIN;
  	    return -1;  /* return of 0 means non-blocking */
  	}
! 	else if (i < 0) {
  	    if (errno != EAGAIN) doerror(fb, B_WR);
  	    return -1;
  	}
+ 	fb->bytes_sent += i;
  
! 	/*
!  	 * We should have written all the data, but if the fd was in a
!  	 * strange (non-blocking) mode, then we might not have done so.
!  	 */
  	if (i < fb->outcnt)
  	{
  	    int j, n=fb->outcnt;
***************
*** 870,875 ****
--- 881,892 ----
  	    for (j=i; j < n; j++) x[j-i] = x[j];
  	}
  	fb->outcnt -= i;
+ 
+ 	/* If a soft timeout occurs while flushing, the handler should
+ 	 * have set the buffer flag B_EOUT.
+ 	 */
+ 	if (fb->flags & B_EOUT)
+ 	    return -1;
      }
      return 0;
  }
Index: http_main.c
===================================================================
RCS file: /export/home/cvs/apache/src/http_main.c,v
retrieving revision 1.137
diff -c -r1.137 http_main.c
*** http_main.c	1997/04/12 04:24:57	1.137
--- http_main.c	1997/04/18 10:40:30
***************
*** 376,383 ****
  	if (!current_conn->keptalive) 
              log_transaction(log_req);
  
!         if (sig == SIGPIPE)
!             bsetflag(timeout_req->connection->client, B_EOUT, 1);
  	bclose(timeout_req->connection->client);
      
  	if (!standalone) exit(0);
--- 376,382 ----
  	if (!current_conn->keptalive) 
              log_transaction(log_req);
  
! 	bsetflag(timeout_req->connection->client, B_EOUT, 1);
  	bclose(timeout_req->connection->client);
      
  	if (!standalone) exit(0);
***************
*** 388,397 ****
  #endif
      }
      else {   /* abort the connection */
!         if (sig == SIGPIPE)
!             bsetflag(current_conn->client, B_EOUT, 1);
!         else
!             bflush(current_conn->client);
          current_conn->aborted = 1;
      }
  }
--- 387,393 ----
  #endif
      }
      else {   /* abort the connection */
!         bsetflag(current_conn->client, B_EOUT, 1);
          current_conn->aborted = 1;
      }
  }
***************
*** 483,488 ****
--- 479,488 ----
   * calls to shutdown only half of the connection.  You should define
   * NO_LINGCLOSE in conf.h if such is the case for your system.
   */
+ #ifndef MAX_SECS_TO_LINGER
+ #define MAX_SECS_TO_LINGER 30
+ #endif
+ 
  #ifdef USE_SO_LINGER
  #define NO_LINGCLOSE    /* The two lingering options are exclusive */
  
***************
*** 491,497 ****
      struct linger li;
  
      li.l_onoff = 1;
!     li.l_linger = 30;
  
      if (setsockopt(s, SOL_SOCKET, SO_LINGER,
                     (char *)&li, sizeof(struct linger)) < 0) {
--- 491,497 ----
      struct linger li;
  
      li.l_onoff = 1;
!     li.l_linger = MAX_SECS_TO_LINGER;
  
      if (setsockopt(s, SOL_SOCKET, SO_LINGER,
                     (char *)&li, sizeof(struct linger)) < 0) {
***************
*** 523,539 ****
  	siglongjmp(jmpbuffer,1);
  #endif
      }
      current_conn->aborted = 1;
  }
      
  static void linger_timeout ()
  {
-     const int max_secs_to_linger = 30;
- 
      timeout_name = "lingering close";
      
      signal(SIGALRM,(void (*)())lingerout);
!     alarm(max_secs_to_linger);
  }
  
  /* Since many clients will abort a connection instead of closing it,
--- 523,538 ----
  	siglongjmp(jmpbuffer,1);
  #endif
      }
+     bsetflag(current_conn->client, B_EOUT, 1);
      current_conn->aborted = 1;
  }
      
  static void linger_timeout ()
  {
      timeout_name = "lingering close";
      
      signal(SIGALRM,(void (*)())lingerout);
!     alarm(MAX_SECS_TO_LINGER);
  }
  
  /* Since many clients will abort a connection instead of closing it,
***************
*** 556,562 ****
  
      /* Send any leftover data to the client, but never try to again */
  
!     bflush(r->connection->client);
      bsetflag(r->connection->client, B_EOUT, 1);
  
      /* Close our half of the connection --- send the client a FIN */
--- 555,565 ----
  
      /* Send any leftover data to the client, but never try to again */
  
!     if (bflush(r->connection->client) == -1) {
!         kill_timeout(r);
!         bclose(r->connection->client);
!         return;
!     }
      bsetflag(r->connection->client, B_EOUT, 1);
  
      /* Close our half of the connection --- send the client a FIN */
Index: http_protocol.c
===================================================================
RCS file: /export/home/cvs/apache/src/http_protocol.c,v
retrieving revision 1.113
diff -c -r1.113 http_protocol.c
*** http_protocol.c	1997/04/12 04:24:57	1.113
--- http_protocol.c	1997/04/18 10:40:30
***************
*** 1489,1495 ****
      char buf[IOBUFSIZE];
      long total_bytes_sent = 0;
      register int n, w, o, len;
-     conn_rec *c = r->connection;
      
      if (length == 0) return 0;
  
--- 1489,1494 ----
***************
*** 1501,1507 ****
  	else len = IOBUFSIZE;
  
          while ((n= fread(buf, sizeof(char), len, f)) < 1
! 	       && ferror(f) && errno == EINTR)
  	    continue;
  	
  	if (n < 1) {
--- 1500,1506 ----
  	else len = IOBUFSIZE;
  
          while ((n= fread(buf, sizeof(char), len, f)) < 1
! 	       && ferror(f) && errno == EINTR && !r->connection->aborted)
  	    continue;
  	
  	if (n < 1) {
***************
*** 1511,1517 ****
  	total_bytes_sent += n;
  	
          while(n && !r->connection->aborted) {
!             w=bwrite(c->client, &buf[o], n);
  	    if(w <= 0)
  		break;
  	    reset_timeout(r); /* reset timeout after successful write */
--- 1510,1516 ----
  	total_bytes_sent += n;
  	
          while(n && !r->connection->aborted) {
!             w=bwrite(r->connection->client, &buf[o], n);
  	    if(w <= 0)
  		break;
  	    reset_timeout(r); /* reset timeout after successful write */

Mime
View raw message