From: Alexei Kosut <akosut@nueva.pvt.k12.ca.us>
      Rob Hartill <hartill@lanl.gov>
Subject: HTTP/1.1 Keep-Alive persistent connection support
Affects: http_main.c, http_protocol.c, http_protocol.h, httpd.h
Changelog: Adds support for Keep-Alive persistent connections, as defined
           in the HTTP/1.1 draft and implemented by several existing browsers
           and servers. Use new KeepAliveTimeout keyword to set timeout between
           requests (15 sec default) or 0 to disable.

*** http_config.c.orig	Sun Dec 17 06:32:29 1995
--- http_config.c	Sun Feb  4 18:56:44 1996
***************
*** 713,718 ****
--- 713,719 ----
      s->srm_confname = RESOURCE_CONFIG_FILE;
      s->access_confname = ACCESS_CONFIG_FILE;
      s->timeout = DEFAULT_TIMEOUT;
+     s->keep_alive_timeout = DEFAULT_KEEPALIVE_TIMEOUT;
      s->do_rfc931 = DEFAULT_RFC931;
      s->next = NULL;
      s->host_addr.s_addr = htonl (INADDR_ANY); /* NOT virtual host;
*** http_core.c.orig	Sun Dec 17 06:32:29 1995
--- http_core.c	Sun Feb  4 18:56:45 1996
***************
*** 514,519 ****
--- 514,524 ----
      return NULL;
  }
  
+ char *set_keep_alive_timeout (cmd_parms *cmd, void *dummy, char *arg) {
+     cmd->server->keep_alive_timeout = atoi (arg);
+     return NULL;
+ }
+ 
  char *set_pidfile (cmd_parms *cmd, void *dummy, char *arg) {
      pid_fname = pstrdup (cmd->pool, arg);
      return NULL;
***************
*** 609,614 ****
--- 614,620 ----
    (void *)XtOffsetOf (server_rec, srm_confname), RSRC_CONF, TAKE1,
    "the filename of the resource config file" },
  { "Timeout", set_timeout, NULL, RSRC_CONF, TAKE1, "timeout duration (sec)"},
+ { "KeepAliveTimeout", set_keep_alive_timeout, NULL, RSRC_CONF, TAKE1, "keepalive timeout duration (sec)"},
  { "IdentityCheck", set_idcheck, NULL, RSRC_CONF, FLAG, NULL },
  { "CacheNegotiatedDocs", },
  { "StartServers", set_daemons_to_start, NULL, RSRC_CONF, TAKE1, NULL },
*** http_main.c.orig	Mon Jan 29 18:19:19 1996
--- http_main.c	Sun Feb  4 18:56:45 1996
***************
*** 239,245 ****
  	    timeout_name ? timeout_name : "request",
  	    current_conn->remote_name);
      
!     log_error(errstr, current_conn->server);
        
      if (timeout_req) {
  	/* Someone has asked for this transaction to just be aborted
--- 239,246 ----
  	    timeout_name ? timeout_name : "request",
  	    current_conn->remote_name);
      
!     if (!current_conn->keptalive) 
!        log_error(errstr, current_conn->server);
        
      if (timeout_req) {
  	/* Someone has asked for this transaction to just be aborted
***************
*** 254,260 ****
  	    else log_req = log_req->prev;
  	}
  	
! 	log_transaction(log_req);
  
  	pfclose (timeout_req->connection->pool,
  		 timeout_req->connection->client);
--- 255,262 ----
  	    else log_req = log_req->prev;
  	}
  	
! 	if (!current_conn->keptalive) 
!             log_transaction(log_req);
  
  	pfclose (timeout_req->connection->pool,
  		 timeout_req->connection->client);
***************
*** 297,303 ****
      timeout_name = name;
      
      signal(SIGALRM,(void (*)())timeout);
!     alarm (r->server->timeout);
  }
  
  void soft_timeout (char *name, request_rec *r)
--- 299,308 ----
      timeout_name = name;
      
      signal(SIGALRM,(void (*)())timeout);
!     if (r->connection->keptalive) 
!        alarm (r->server->keep_alive_timeout);
!     else
!        alarm (r->server->timeout);
  }
  
  void soft_timeout (char *name, request_rec *r)
***************
*** 812,820 ****
  		rfc931((struct sockaddr_in *)&sa_client,
  		       (struct sockaddr_in *)&sa_server);
  	
! 	r = read_request (current_conn);
  	if (r) process_request (r); /* else premature EOF --- ignore */
! 		
  	if (bytes_in_pool (ptrans) > 80000)
  	    log_printf(r->server,
  		       "Memory hog alert: allocated %ld bytes for %s",
--- 817,831 ----
  		rfc931((struct sockaddr_in *)&sa_client,
  		       (struct sockaddr_in *)&sa_server);
  	
! 	r = read_request (current_conn, NULL);
  	if (r) process_request (r); /* else premature EOF --- ignore */
! 
! 	while (r && current_conn->keepalive) {
! 	  fflush(conn_out);
! 	  r = read_request (current_conn, r);
! 	  if (r) process_request (r);
! 	}
! 	
  	if (bytes_in_pool (ptrans) > 80000)
  	    log_printf(r->server,
  		       "Memory hog alert: allocated %ld bytes for %s",
***************
*** 1034,1041 ****
  	server_conf->port =ntohs(((struct sockaddr_in *)&sa_server)->sin_port);
  	conn = new_connection (ptrans, server_conf, stdin, stdout,
  			       (struct sockaddr_in *)&sa_server);
! 	r = read_request (conn);
  	if (r) process_request (r); /* else premature EOF (ignore) */
      }
      exit (0);
  }
--- 1045,1058 ----
  	server_conf->port =ntohs(((struct sockaddr_in *)&sa_server)->sin_port);
  	conn = new_connection (ptrans, server_conf, stdin, stdout,
  			       (struct sockaddr_in *)&sa_server);
! 	r = read_request (conn, NULL);
  	if (r) process_request (r); /* else premature EOF (ignore) */
+ 
+         while (r && conn->keepalive) {
+ 	  fflush(stdout);
+           r = read_request (conn, r);
+           if (r) process_request (r);
+         }
      }
      exit (0);
  }
*** http_protocol.c.orig	Mon Jan 29 18:19:18 1996
--- http_protocol.c	Mon Feb  5 14:29:16 1996
***************
*** 151,156 ****
--- 151,173 ----
      return 0;
  }
  
+ int set_keepalive(request_rec *r)
+ {
+   char *conn = table_get (r->headers_in, "Connection");
+   char *length = table_get (r->headers_out, "Content-length");
+ 
+   if (!conn || !length)
+     return 0;
+ 
+   if (!strncasecmp(conn, "Keep-Alive", 10) &&
+       (atoi(length) > 100) && r->server->keep_alive_timeout) {
+     table_set (r->headers_out, "Connection", "Keep-Alive");
+     r->connection->keepalive = 1;
+   }
+ 
+   return 1;
+ }
+ 
  int set_last_modified(request_rec *r, time_t mtime)
  {
      struct tm *tms;
***************
*** 261,267 ****
      }
  }
  
! request_rec *read_request (conn_rec *conn)
  {
      request_rec *r = (request_rec *)pcalloc (conn->pool, sizeof(request_rec));
    
--- 278,284 ----
      }
  }
  
! request_rec *read_request (conn_rec *conn, request_rec *back)
  {
      request_rec *r = (request_rec *)pcalloc (conn->pool, sizeof(request_rec));
    
***************
*** 269,274 ****
--- 286,295 ----
      r->server = conn->server;
      r->pool = conn->pool;
  
+     r->back = back;
+     conn->keptalive = conn->keepalive;
+     conn->keepalive = 0;
+ 
      r->headers_in = make_table (r->pool, 50);
      r->subprocess_env = make_table (r->pool, 50);
      r->headers_out = make_table (r->pool, 5);
***************
*** 291,296 ****
--- 312,318 ----
      if (!read_request_line (r)) return NULL;
      if (!r->assbackwards) get_mime_headers(r);
      kill_timeout (r);
+     conn->keptalive = 0;   /* We now have a request - so no more short timeouts */
      
      if(!strcmp(r->method, "HEAD")) {
          r->header_only=1;
***************
*** 499,504 ****
--- 521,528 ----
      }
      
      basic_http_header (r);
+ 
+     set_keepalive (r);
      
      if (r->content_type)
          fprintf (fd, "Content-type: %s\015\012",
***************
*** 607,612 ****
--- 631,638 ----
  	 */
  	
  	if (status == USE_LOCAL_COPY) {
+ 	    if (set_keepalive(r))
+ 	      fprintf(c->client, "Connection: Keep-Alive\015\012");
  	    fprintf (c->client, "\015\012");
  	    return;
  	}
*** http_protocol.h.orig	Sun Dec 17 06:32:29 1995
--- http_protocol.h	Sun Feb  4 18:56:45 1996
***************
*** 59,65 ****
  
  /* Read a request and fill in the fields. */
  
! request_rec *read_request (conn_rec *c);     
  
  /* Send header for http response */
  
--- 59,65 ----
  
  /* Read a request and fill in the fields. */
  
! request_rec *read_request (conn_rec *c, request_rec *back);
  
  /* Send header for http response */
  
***************
*** 82,87 ****
--- 82,88 ----
   */
  
  int set_content_length (request_rec *r, long length);
+ int set_keepalive (request_rec *r);
  int set_last_modified (request_rec *r, time_t mtime);
  
  void add_env_var (array_header *env, char *header_name, char *val);
*** httpd.h.orig	Tue Jan 30 18:54:07 1996
--- httpd.h	Sun Feb  4 18:56:45 1996
***************
*** 144,149 ****
--- 144,152 ----
  /* The timeout for waiting for messages */
  #define DEFAULT_TIMEOUT 1200
  
+ /* The timeout for waiting for keepalive timeout until next request */
+ #define DEFAULT_KEEPALIVE_TIMEOUT 15
+ 
  /* The size of the server's internal read-write buffers */
  #define IOBUFSIZE 8192
  
***************
*** 255,260 ****
--- 258,268 ----
  				 * pointer back to the main request.
  				 */
  
+   request_rec *back;		/* If this is part of a persistent con-
+ 				 * nection (Keep-Alive), pointer to the
+ 				 * previous one.
+ 				 */
+ 
    /* Info about the request itself... we begin with stuff that only
     * protocol.c should ever touch...
     */
***************
*** 350,355 ****
--- 358,366 ----
  				 * that there's only one user per connection(!)
  				 */
    char *auth_type;		/* Ditto. */
+ 
+   int keepalive;		/* Are we using HTTP Keep-Alive? */
+   int keptalive;		/* Did we use HTTP Keep-Alive? */
  };
  
  /* Per-vhost config... */
***************
*** 386,391 ****
--- 397,403 ----
    short port;
    struct in_addr host_addr;	/* Specific address, if "virtual" server */
    int timeout;			/* Timeout, in seconds, before we give up */
+   int keep_alive_timeout;	/* Seconds we'll wait for another request */
    int do_rfc931;		/* See if client is advertising a username? */
    
  };
