Initial keep-alive support for the client socket.
authorFabian Keil <fk@fabiankeil.de>
Sun, 10 May 2009 10:12:30 +0000 (10:12 +0000)
committerFabian Keil <fk@fabiankeil.de>
Sun, 10 May 2009 10:12:30 +0000 (10:12 +0000)
Temporarily disable the server-side-only keep-alive code.

gateway.c
gateway.h
jbsockets.c
jbsockets.h
jcc.c
loadcfg.c
parsers.c
project.h

index bdca2b5..3181609 100644 (file)
--- a/gateway.c
+++ b/gateway.c
@@ -1,4 +1,4 @@
-const char gateway_rcs[] = "$Id: gateway.c,v 1.47 2008/12/24 17:06:19 fabiankeil Exp $";
+const char gateway_rcs[] = "$Id: gateway.c,v 1.48 2009/02/13 17:20:36 fabiankeil Exp $";
 /*********************************************************************
  *
  * File        :  $Source: /cvsroot/ijbswa/current/gateway.c,v $
@@ -34,6 +34,10 @@ const char gateway_rcs[] = "$Id: gateway.c,v 1.47 2008/12/24 17:06:19 fabiankeil
  *
  * Revisions   :
  *    $Log: gateway.c,v $
+ *    Revision 1.48  2009/02/13 17:20:36  fabiankeil
+ *    Reword keep-alive support warning and only show
+ *    it #if !defined(HAVE_POLL) && !defined(_WIN32).
+ *
  *    Revision 1.47  2008/12/24 17:06:19  fabiankeil
  *    Keep a thread around to timeout alive connections
  *    even if no new requests are coming in.
@@ -335,27 +339,8 @@ static const char socks_userid[] = "anonymous";
 #define MAX_REUSABLE_CONNECTIONS 100
 static int keep_alive_timeout = DEFAULT_KEEP_ALIVE_TIMEOUT;
 
-struct reusable_connection
-{
-   jb_socket sfd;
-   int       in_use;
-   char      *host;
-   int       port;
-   time_t    timestamp;
-
-   int       forwarder_type;
-   char      *gateway_host;
-   int       gateway_port;
-   char      *forward_host;
-   int       forward_port;
-};
-
 static struct reusable_connection reusable_connection[MAX_REUSABLE_CONNECTIONS];
-
 static int mark_connection_unused(jb_socket sfd);
-static void mark_connection_closed(struct reusable_connection *closed_connection);
-static int socket_is_still_usable(jb_socket sfd);
-
 
 /*********************************************************************
  *
@@ -498,7 +483,6 @@ void remember_connection(jb_socket sfd, const struct http_request *http,
  * Function    :  mark_connection_closed
  *
  * Description : Marks a reused connection closed.
- *               Must be called with connection_reuse_mutex locked.
  *
  * Parameters  :
  *          1  :  closed_connection = The connection to mark as closed.
@@ -506,7 +490,7 @@ void remember_connection(jb_socket sfd, const struct http_request *http,
  * Returns     : void
  *
  *********************************************************************/
-static void mark_connection_closed(struct reusable_connection *closed_connection)
+void mark_connection_closed(struct reusable_connection *closed_connection)
 {
    closed_connection->in_use = FALSE;
    closed_connection->sfd = JB_INVALID_SOCKET;
@@ -582,9 +566,9 @@ void forget_connection(jb_socket sfd)
  * Returns     :  TRUE for yes, FALSE otherwise.
  *
  *********************************************************************/
-static int connection_destination_matches(const struct reusable_connection *connection,
-                                          const struct http_request *http,
-                                          const struct forward_spec *fwd)
+int connection_destination_matches(const struct reusable_connection *connection,
+                                   const struct http_request *http,
+                                   const struct forward_spec *fwd)
 {
    if ((connection->forwarder_type != fwd->type)
     || (connection->gateway_port   != fwd->gateway_port)
@@ -678,66 +662,6 @@ int close_unusable_connections(void)
 }
 
 
-/*********************************************************************
- *
- * Function    :  socket_is_still_usable
- *
- * Description :  Decides whether or not an open socket is still usable.
- *
- * Parameters  :
- *          1  :  sfd = The socket to check.
- *
- * Returns     :  TRUE for yes, otherwise FALSE.
- *
- *********************************************************************/
-static int socket_is_still_usable(jb_socket sfd)
-{
-#ifdef HAVE_POLL
-   int poll_result;
-   struct pollfd poll_fd[1];
-
-   memset(poll_fd, 0, sizeof(poll_fd));
-   poll_fd[0].fd = sfd;
-   poll_fd[0].events = POLLIN;
-
-   poll_result = poll(poll_fd, 1, 0);
-
-   if (-1 != poll_result)
-   {
-      return !(poll_fd[0].revents & POLLIN);
-   }
-   else
-   {
-      log_error(LOG_LEVEL_CONNECT, "Polling socket %d failed.", sfd);
-      return FALSE;
-   }
-#else
-   fd_set readable_fds;
-   struct timeval timeout;
-   int ret;
-   int socket_is_alive = 0;
-
-   memset(&timeout, '\0', sizeof(timeout));
-   FD_ZERO(&readable_fds);
-   FD_SET(sfd, &readable_fds);
-
-   ret = select((int)sfd+1, &readable_fds, NULL, NULL, &timeout);
-   if (ret < 0)
-   {
-      log_error(LOG_LEVEL_ERROR, "select() failed!: %E");
-   }
-
-   /*
-    * XXX: I'm not sure why !FD_ISSET() works,
-    * but apparently it does.
-    */
-   socket_is_alive = !FD_ISSET(sfd, &readable_fds);
-
-   return socket_is_alive;
-#endif /* def HAVE_POLL */
-}
-
-
 /*********************************************************************
  *
  * Function    :  get_reusable_connection
index 73a60bc..eb439ca 100644 (file)
--- a/gateway.h
+++ b/gateway.h
@@ -1,6 +1,6 @@
 #ifndef GATEWAY_H_INCLUDED
 #define GATEWAY_H_INCLUDED
-#define GATEWAY_H_VERSION "$Id: gateway.h,v 1.11 2008/11/13 09:08:42 fabiankeil Exp $"
+#define GATEWAY_H_VERSION "$Id: gateway.h,v 1.12 2008/12/24 17:06:19 fabiankeil Exp $"
 /*********************************************************************
  *
  * File        :  $Source: /cvsroot/ijbswa/current/gateway.h,v $
  *
  * Revisions   :
  *    $Log: gateway.h,v $
+ *    Revision 1.12  2008/12/24 17:06:19  fabiankeil
+ *    Keep a thread around to timeout alive connections
+ *    even if no new requests are coming in.
+ *
  *    Revision 1.11  2008/11/13 09:08:42  fabiankeil
  *    Add new config option: keep-alive-timeout.
  *
@@ -125,6 +129,10 @@ extern void remember_connection(jb_socket sfd,
                                 const struct http_request *http,
                                 const struct forward_spec *fwd);
 extern int close_unusable_connections(void);
+extern void mark_connection_closed(struct reusable_connection *closed_connection);
+extern int connection_destination_matches(const struct reusable_connection *connection,
+                                          const struct http_request *http,
+                                          const struct forward_spec *fwd);
 #endif /* FEATURE_CONNECTION_KEEP_ALIVE */
 
 
index 990c8e4..18e4246 100644 (file)
@@ -1,4 +1,4 @@
-const char jbsockets_rcs[] = "$Id: jbsockets.c,v 1.53 2009/04/17 11:39:52 fabiankeil Exp $";
+const char jbsockets_rcs[] = "$Id: jbsockets.c,v 1.54 2009/04/17 11:45:19 fabiankeil Exp $";
 /*********************************************************************
  *
  * File        :  $Source: /cvsroot/ijbswa/current/jbsockets.c,v $
@@ -8,7 +8,7 @@ const char jbsockets_rcs[] = "$Id: jbsockets.c,v 1.53 2009/04/17 11:39:52 fabian
  *                OS-independent.  Contains #ifdefs to make this work
  *                on many platforms.
  *
- * Copyright   :  Written by and Copyright (C) 2001-2007 the SourceForge
+ * Copyright   :  Written by and Copyright (C) 2001-2009 the
  *                Privoxy team. http://www.privoxy.org/
  *
  *                Based on the Internet Junkbuster originally written
@@ -35,6 +35,10 @@ const char jbsockets_rcs[] = "$Id: jbsockets.c,v 1.53 2009/04/17 11:39:52 fabian
  *
  * Revisions   :
  *    $Log: jbsockets.c,v $
+ *    Revision 1.54  2009/04/17 11:45:19  fabiankeil
+ *    Replace HAVE_GETADDRINFO and HAVE_GETNAMEINFO macros
+ *    with HAVE_RFC2553 macro. Original patch by Petr Pisar.
+ *
  *    Revision 1.53  2009/04/17 11:39:52  fabiankeil
  *    If the hostname is 'localhost' or not specified, request an AF_INET address.
  *
@@ -324,6 +328,16 @@ const char jbsockets_rcs[] = "$Id: jbsockets.c,v 1.53 2009/04/17 11:39:52 fabian
 
 #endif
 
+#ifdef FEATURE_CONNECTION_KEEP_ALIVE
+#ifdef HAVE_POLL
+#ifdef __GLIBC__
+#include <sys/poll.h>
+#else
+#include <poll.h>
+#endif /* def __GLIBC__ */
+#endif /* HAVE_POLL */
+#endif /* def FEATURE_CONNECTION_KEEP_ALIVE */
+
 #include "project.h"
 
 #ifdef FEATURE_PTHREAD
@@ -1381,6 +1395,68 @@ unsigned long resolve_hostname_to_ip(const char *host)
 }
 
 
+#ifdef FEATURE_CONNECTION_KEEP_ALIVE
+/*********************************************************************
+ *
+ * Function    :  socket_is_still_usable
+ *
+ * Description :  Decides whether or not an open socket is still usable.
+ *
+ * Parameters  :
+ *          1  :  sfd = The socket to check.
+ *
+ * Returns     :  TRUE for yes, otherwise FALSE.
+ *
+ *********************************************************************/
+int socket_is_still_usable(jb_socket sfd)
+{
+#ifdef HAVE_POLL
+   int poll_result;
+   struct pollfd poll_fd[1];
+
+   memset(poll_fd, 0, sizeof(poll_fd));
+   poll_fd[0].fd = sfd;
+   poll_fd[0].events = POLLIN;
+
+   poll_result = poll(poll_fd, 1, 0);
+
+   if (-1 != poll_result)
+   {
+      return !(poll_fd[0].revents & POLLIN);
+   }
+   else
+   {
+      log_error(LOG_LEVEL_CONNECT, "Polling socket %d failed.", sfd);
+      return FALSE;
+   }
+#else
+   fd_set readable_fds;
+   struct timeval timeout;
+   int ret;
+   int socket_is_alive = 0;
+
+   memset(&timeout, '\0', sizeof(timeout));
+   FD_ZERO(&readable_fds);
+   FD_SET(sfd, &readable_fds);
+
+   ret = select((int)sfd+1, &readable_fds, NULL, NULL, &timeout);
+   if (ret < 0)
+   {
+      log_error(LOG_LEVEL_ERROR, "select() failed!: %E");
+   }
+
+   /*
+    * XXX: I'm not sure why !FD_ISSET() works,
+    * but apparently it does.
+    */
+   socket_is_alive = !FD_ISSET(sfd, &readable_fds);
+
+   return socket_is_alive;
+#endif /* def HAVE_POLL */
+}
+#endif /* def FEATURE_CONNECTION_KEEP_ALIVE */
+
+
 /*
   Local Variables:
   tab-width: 3
index 8a58066..92c4350 100644 (file)
@@ -1,6 +1,6 @@
 #ifndef JBSOCKETS_H_INCLUDED
 #define JBSOCKETS_H_INCLUDED
-#define JBSOCKETS_H_VERSION "$Id: jbsockets.h,v 1.13 2008/03/21 11:13:59 fabiankeil Exp $"
+#define JBSOCKETS_H_VERSION "$Id: jbsockets.h,v 1.14 2008/12/20 14:53:55 fabiankeil Exp $"
 /*********************************************************************
  *
  * File        :  $Source: /cvsroot/ijbswa/current/jbsockets.h,v $
@@ -10,7 +10,7 @@
  *                OS-independent.  Contains #ifdefs to make this work
  *                on many platforms.
  *
- * Copyright   :  Written by and Copyright (C) 2001 the SourceForge
+ * Copyright   :  Written by and Copyright (C) 2001-2009 the
  *                Privoxy team. http://www.privoxy.org/
  *
  *                Based on the Internet Junkbuster originally written
  *
  * Revisions   :
  *    $Log: jbsockets.h,v $
+ *    Revision 1.14  2008/12/20 14:53:55  fabiankeil
+ *    Add config option socket-timeout to control the time
+ *    Privoxy waits for data to arrive on a socket. Useful
+ *    in case of stale ssh tunnels or when fuzz-testing.
+ *
  *    Revision 1.13  2008/03/21 11:13:59  fabiankeil
  *    Only gather host information if it's actually needed.
  *    Also move the code out of accept_connection() so it's less likely
@@ -127,6 +132,8 @@ extern void get_host_information(jb_socket afd, char **ip_address, char **hostna
 
 extern unsigned long resolve_hostname_to_ip(const char *host);
 
+extern int socket_is_still_usable(jb_socket sfd);
+
 /* Revision control strings from this header and associated .c file */
 extern const char jbsockets_rcs[];
 extern const char jbsockets_h_rcs[];
diff --git a/jcc.c b/jcc.c
index 7ca0f46..5991a72 100644 (file)
--- a/jcc.c
+++ b/jcc.c
@@ -1,4 +1,4 @@
-const char jcc_rcs[] = "$Id: jcc.c,v 1.244 2009/04/17 11:34:34 fabiankeil Exp $";
+const char jcc_rcs[] = "$Id: jcc.c,v 1.245 2009/04/24 15:29:43 fabiankeil Exp $";
 /*********************************************************************
  *
  * File        :  $Source: /cvsroot/ijbswa/current/jcc.c,v $
@@ -33,6 +33,9 @@ const char jcc_rcs[] = "$Id: jcc.c,v 1.244 2009/04/17 11:34:34 fabiankeil Exp $"
  *
  * Revisions   :
  *    $Log: jcc.c,v $
+ *    Revision 1.245  2009/04/24 15:29:43  fabiankeil
+ *    Allow to limit the number of of client connections.
+ *
  *    Revision 1.244  2009/04/17 11:34:34  fabiankeil
  *    Style cosmetics for the IPv6 code.
  *
@@ -2288,6 +2291,73 @@ static void wait_for_alive_connections()
    log_error(LOG_LEVEL_CONNECT, "No connections to wait for left.");
 
 }
+
+
+/*********************************************************************
+ *
+ * Function    :  save_connection_destination
+ *
+ * Description :  Remembers a connection for reuse later on.
+ *
+ * Parameters  :
+ *          1  :  sfd  = Open socket to remember.
+ *          2  :  http = The destination for the connection.
+ *          3  :  fwd  = The forwarder settings used.
+ *          3  :  server_connection  = storage.
+ *
+ * Returns     : void
+ *
+ *********************************************************************/
+void save_connection_destination(jb_socket sfd,
+                                 const struct http_request *http,
+                                 const struct forward_spec *fwd,
+                                 struct reusable_connection *server_connection)
+{
+   assert(sfd != JB_INVALID_SOCKET);
+   assert(NULL != http->host);
+   server_connection->host = strdup(http->host);
+   if (NULL == server_connection->host)
+   {
+      log_error(LOG_LEVEL_FATAL, "Out of memory saving socket.");
+   }
+   server_connection->port = http->port;
+
+   assert(NULL != fwd);
+   assert(server_connection->gateway_host == NULL);
+   assert(server_connection->gateway_port == 0);
+   assert(server_connection->forwarder_type == 0);
+   assert(server_connection->forward_host == NULL);
+   assert(server_connection->forward_port == 0);
+
+   server_connection->forwarder_type = fwd->type;
+   if (NULL != fwd->gateway_host)
+   {
+      server_connection->gateway_host = strdup(fwd->gateway_host);
+      if (NULL == server_connection->gateway_host)
+      {
+         log_error(LOG_LEVEL_FATAL, "Out of memory saving gateway_host.");
+      }
+   }
+   else
+   {
+      server_connection->gateway_host = NULL;
+   }
+   server_connection->gateway_port = fwd->gateway_port;
+
+   if (NULL != fwd->forward_host)
+   {
+      server_connection->forward_host = strdup(fwd->forward_host);
+      if (NULL == server_connection->forward_host)
+      {
+         log_error(LOG_LEVEL_FATAL, "Out of memory saving forward_host.");
+      }
+   }
+   else
+   {
+      server_connection->forward_host = NULL;
+   }
+   server_connection->forward_port = fwd->forward_port;
+}
 #endif /* FEATURE_CONNECTION_KEEP_ALIVE */
 
 
@@ -2763,41 +2833,66 @@ static void chat(struct client_state *csp)
 
    /* here we connect to the server, gateway, or the forwarder */
 
-   while ((csp->sfd = forwarded_connect(fwd, http, csp))
-      && (errno == EINVAL)
-      && (forwarded_connect_retries++ < max_forwarded_connect_retries))
+#ifdef FEATURE_CONNECTION_KEEP_ALIVE
+   if ((csp->sfd != JB_INVALID_SOCKET)
+      && socket_is_still_usable(csp->sfd)
+      && connection_destination_matches(&csp->server_connection, http, fwd))
    {
-      log_error(LOG_LEVEL_ERROR,
-         "failed request #%u to connect to %s. Trying again.",
-         forwarded_connect_retries, http->hostport);
+      log_error(LOG_LEVEL_CONNECT,
+         "Reusing server socket %u. Opened for %s.",
+         csp->sfd, csp->server_connection.host);
    }
-
-   if (csp->sfd == JB_INVALID_SOCKET)
+   else
    {
-      if (fwd->type != SOCKS_NONE)
-      {
-         /* Socks error. */
-         rsp = error_response(csp, "forwarding-failed", errno);
-      }
-      else if (errno == EINVAL)
+      if (csp->sfd != JB_INVALID_SOCKET)
       {
-         rsp = error_response(csp, "no-such-domain", errno);
+         log_error(LOG_LEVEL_CONNECT,
+            "Closing server socket %u. Opened for %s.",
+            csp->sfd, csp->server_connection.host);
+         close_socket(csp->sfd);
+         mark_connection_closed(&csp->server_connection);
       }
-      else
+#endif /* def FEATURE_CONNECTION_KEEP_ALIVE */
+
+      while ((csp->sfd = forwarded_connect(fwd, http, csp))
+         && (errno == EINVAL)
+         && (forwarded_connect_retries++ < max_forwarded_connect_retries))
       {
-         rsp = error_response(csp, "connect-failed", errno);
-         log_error(LOG_LEVEL_CONNECT, "connect to: %s failed: %E",
-            http->hostport);
+         log_error(LOG_LEVEL_ERROR,
+            "failed request #%u to connect to %s. Trying again.",
+            forwarded_connect_retries, http->hostport);
       }
 
-      /* Write the answer to the client */
-      if (rsp != NULL)
+      if (csp->sfd == JB_INVALID_SOCKET)
       {
-         send_crunch_response(csp, rsp);
-      }
+         if (fwd->type != SOCKS_NONE)
+         {
+            /* Socks error. */
+            rsp = error_response(csp, "forwarding-failed", errno);
+         }
+         else if (errno == EINVAL)
+         {
+            rsp = error_response(csp, "no-such-domain", errno);
+         }
+         else
+         {
+            rsp = error_response(csp, "connect-failed", errno);
+            log_error(LOG_LEVEL_CONNECT, "connect to: %s failed: %E",
+               http->hostport);
+         }
 
-      return;
+         /* Write the answer to the client */
+         if (rsp != NULL)
+         {
+            send_crunch_response(csp, rsp);
+         }
+
+         return;
+      }
+#ifdef FEATURE_CONNECTION_KEEP_ALIVE
+      save_connection_destination(csp->sfd, http, fwd, &csp->server_connection);
    }
+#endif /* def FEATURE_CONNECTION_KEEP_ALIVE */
 
    hdr = list_to_text(csp->headers);
    if (hdr == NULL)
@@ -2925,6 +3020,9 @@ static void chat(struct client_state *csp)
       /*
        * This is the body of the browser's request,
        * just read and write it.
+       *
+       * XXX: Make sure the client doesn't use pipelining
+       * behind Privoxy's back.
        */
       if (FD_ISSET(csp->cfd, &rfds))
       {
@@ -3408,38 +3506,80 @@ void serve(struct client_state *csp)
 static void serve(struct client_state *csp)
 #endif /* def AMIGA */
 {
-   chat(csp);
-
-   if (csp->sfd != JB_INVALID_SOCKET)
-   {
 #ifdef FEATURE_CONNECTION_KEEP_ALIVE
-      static int monitor_thread_running = 0;
+   int continue_chatting = 0;
+   do
+   {
+      chat(csp);
+
+      continue_chatting = (csp->config->feature_flags
+         & RUNTIME_FEATURE_CONNECTION_KEEP_ALIVE)
+         && (csp->flags & CSP_FLAG_SERVER_CONNECTION_KEEP_ALIVE)
+         && (csp->cfd != JB_INVALID_SOCKET)
+         && (csp->sfd != JB_INVALID_SOCKET)
+         && socket_is_still_usable(csp->sfd);
 
-      if ((csp->config->feature_flags & RUNTIME_FEATURE_CONNECTION_KEEP_ALIVE)
-       && (csp->flags & CSP_FLAG_SERVER_CONNECTION_KEEP_ALIVE))
+      /*
+       * Get the csp in a mostly vergin state again.
+       * XXX: Should be done elsewhere.
+       */
+      csp->content_type = 0;
+      csp->content_length = 0;
+      csp->expected_content_length = 0;
+      list_remove_all(csp->headers);
+      freez(csp->iob->buf);
+      memset(csp->iob, 0, sizeof(csp->iob));
+      freez(csp->error_message);
+      free_http_request(csp->http);
+      destroy_list(csp->headers);
+      destroy_list(csp->tags);
+      free_current_action(csp->action);
+      if (NULL != csp->fwd)
       {
-         remember_connection(csp->sfd, csp->http, forward_url(csp, csp->http));
-         close_socket(csp->cfd);
-         csp->cfd = JB_INVALID_SOCKET;
-         privoxy_mutex_lock(&connection_reuse_mutex);
-         if (!monitor_thread_running)
+         unload_forward_spec(csp->fwd);
+         csp->fwd = NULL;
+      }
+
+      /* XXX: Store per-connection flags someplace else. */
+      csp->flags = CSP_FLAG_ACTIVE | (csp->flags & CSP_FLAG_TOGGLED_ON);
+
+      if (continue_chatting)
+      {
+         log_error(LOG_LEVEL_CONNECT,
+            "Waiting for the next client request. "
+            "Keeping the server socket %d to %s open.",
+            csp->sfd, csp->server_connection.host);
+
+         if ((csp->flags & CSP_FLAG_CLIENT_CONNECTION_KEEP_ALIVE)
+            && data_is_available(csp->cfd, csp->config->keep_alive_timeout)
+            && socket_is_still_usable(csp->cfd))
          {
-            monitor_thread_running = 1;
-            privoxy_mutex_unlock(&connection_reuse_mutex);
-            wait_for_alive_connections();
-            privoxy_mutex_lock(&connection_reuse_mutex);
-            monitor_thread_running = 0;
+            log_error(LOG_LEVEL_CONNECT, "Client request arrived in "
+               "time or the client closed the connection.");
+         }
+         else
+         {
+            log_error(LOG_LEVEL_CONNECT,
+               "No additional client request received in time. "
+               "Closing server socket %d, initially opened for %s.",
+               csp->sfd, csp->server_connection.host);
+            break;
          }
-         privoxy_mutex_unlock(&connection_reuse_mutex);
       }
-      else
+      else if (csp->sfd != JB_INVALID_SOCKET)
       {
-         forget_connection(csp->sfd);
-         close_socket(csp->sfd);
+         log_error(LOG_LEVEL_CONNECT,
+            "The connection on server socket %d to %s isn't reusable. "
+            "Closing.", csp->sfd, csp->server_connection.host);
       }
+   } while (continue_chatting);
 #else
-      close_socket(csp->sfd);
+   chat(csp);
 #endif /* def FEATURE_CONNECTION_KEEP_ALIVE */
+
+   if (csp->sfd != JB_INVALID_SOCKET)
+   {
+      close_socket(csp->sfd);
    }
 
    if (csp->cfd != JB_INVALID_SOCKET)
index fb804f8..4942ca4 100644 (file)
--- a/loadcfg.c
+++ b/loadcfg.c
@@ -1,4 +1,4 @@
-const char loadcfg_rcs[] = "$Id: loadcfg.c,v 1.97 2009/04/17 11:45:19 fabiankeil Exp $";
+const char loadcfg_rcs[] = "$Id: loadcfg.c,v 1.98 2009/04/24 15:29:43 fabiankeil Exp $";
 /*********************************************************************
  *
  * File        :  $Source: /cvsroot/ijbswa/current/loadcfg.c,v $
@@ -35,6 +35,9 @@ const char loadcfg_rcs[] = "$Id: loadcfg.c,v 1.97 2009/04/17 11:45:19 fabiankeil
  *
  * Revisions   :
  *    $Log: loadcfg.c,v $
+ *    Revision 1.98  2009/04/24 15:29:43  fabiankeil
+ *    Allow to limit the number of of client connections.
+ *
  *    Revision 1.97  2009/04/17 11:45:19  fabiankeil
  *    Replace HAVE_GETADDRINFO and HAVE_GETNAMEINFO macros
  *    with HAVE_RFC2553 macro. Original patch by Petr Pisar.
@@ -799,9 +802,6 @@ struct configuration_spec * load_config(void)
    unsigned long linenum = 0;
    int i;
    char *logfile = NULL;
-#ifdef FEATURE_CONNECTION_KEEP_ALIVE
-   int keep_alive_timeout = DEFAULT_KEEP_ALIVE_TIMEOUT;
-#endif
 
    if (!check_file_changed(current_configfile, configfile, &fs))
    {
@@ -855,6 +855,10 @@ struct configuration_spec * load_config(void)
    config->forwarded_connect_retries = 0;
    config->max_client_connections    = 0;
    config->socket_timeout            = 300; /* XXX: Should be a macro. */
+#ifdef FEATURE_CONNECTION_KEEP_ALIVE
+   config->keep_alive_timeout        = DEFAULT_KEEP_ALIVE_TIMEOUT;
+   config->feature_flags            &= ~RUNTIME_FEATURE_CONNECTION_KEEP_ALIVE;
+#endif
    config->feature_flags            &= ~RUNTIME_FEATURE_CGI_TOGGLE;
    config->feature_flags            &= ~RUNTIME_FEATURE_SPLIT_LARGE_FORMS;
    config->feature_flags            &= ~RUNTIME_FEATURE_ACCEPT_INTERCEPTED_REQUESTS;
@@ -1388,7 +1392,7 @@ struct configuration_spec * load_config(void)
                if (0 <= timeout)
                {
                   config->feature_flags |= RUNTIME_FEATURE_CONNECTION_KEEP_ALIVE;
-                  keep_alive_timeout = timeout;
+                  config->keep_alive_timeout = timeout;
                }
                else
                {
@@ -1803,7 +1807,7 @@ struct configuration_spec * load_config(void)
    {
       if (config->multi_threaded)
       {
-         set_keep_alive_timeout(keep_alive_timeout);
+         set_keep_alive_timeout(config->keep_alive_timeout);
       }
       else
       {
@@ -1812,6 +1816,8 @@ struct configuration_spec * load_config(void)
           * if we didn't bother with enforcing the connection timeout,
           * that might make Tor users sad, even though they shouldn't
           * enable the single-threaded option anyway.
+          *
+          * XXX: We could still use Proxy-Connection: keep-alive.
           */
          config->feature_flags &= ~RUNTIME_FEATURE_CONNECTION_KEEP_ALIVE;
          log_error(LOG_LEVEL_ERROR,
index cfe9307..bef06ea 100644 (file)
--- a/parsers.c
+++ b/parsers.c
@@ -1,4 +1,4 @@
-const char parsers_rcs[] = "$Id: parsers.c,v 1.153 2009/03/07 13:09:17 fabiankeil Exp $";
+const char parsers_rcs[] = "$Id: parsers.c,v 1.154 2009/03/13 14:10:07 fabiankeil Exp $";
 /*********************************************************************
  *
  * File        :  $Source: /cvsroot/ijbswa/current/parsers.c,v $
@@ -44,6 +44,9 @@ const char parsers_rcs[] = "$Id: parsers.c,v 1.153 2009/03/07 13:09:17 fabiankei
  *
  * Revisions   :
  *    $Log: parsers.c,v $
+ *    Revision 1.154  2009/03/13 14:10:07  fabiankeil
+ *    Fix some more harmless warnings on amd64.
+ *
  *    Revision 1.153  2009/03/07 13:09:17  fabiankeil
  *    Change csp->expected_content and_csp->expected_content_length from
  *    size_t to unsigned long long to reduce the likelihood of integer
@@ -989,7 +992,10 @@ static jb_err client_host_adder       (struct client_state *csp);
 static jb_err client_xtra_adder       (struct client_state *csp);
 static jb_err client_x_forwarded_for_adder(struct client_state *csp);
 static jb_err client_connection_header_adder(struct client_state *csp);
-static jb_err server_connection_close_adder(struct client_state *csp);
+static jb_err server_connection_adder(struct client_state *csp);
+#ifdef FEATURE_CONNECTION_KEEP_ALIVE
+static jb_err server_proxy_connection_adder(struct client_state *csp);
+#endif /* def FEATURE_CONNECTION_KEEP_ALIVE */
 
 static jb_err create_forged_referrer(char **header, const char *hostport);
 static jb_err create_fake_referrer(char **header, const char *fake_referrer);
@@ -1023,7 +1029,9 @@ static const struct parsers client_patterns[] = {
    { "TE:",                       3,   client_te },
    { "Host:",                     5,   client_host },
    { "if-modified-since:",       18,   client_if_modified_since },
+#ifndef FEATURE_CONNECTION_KEEP_ALIVE
    { "Keep-Alive:",              11,   crumble },
+#endif
    { "connection:",              11,   client_connection },
    { "proxy-connection:",        17,   crumble },
    { "max-forwards:",            13,   client_max_forwards },
@@ -1047,9 +1055,10 @@ static const struct parsers server_patterns[] = {
    { "Content-Encoding:",        17, server_content_encoding },
 #ifdef FEATURE_CONNECTION_KEEP_ALIVE
    { "Content-Length:",          15, server_save_content_length },
+#else
+   { "Keep-Alive:",              11, crumble },
 #endif /* def FEATURE_CONNECTION_KEEP_ALIVE */
    { "Transfer-Encoding:",       18, server_transfer_coding },
-   { "Keep-Alive:",              11, crumble },
    { "content-disposition:",     20, server_content_disposition },
    { "Last-Modified:",           14, server_last_modified },
    { "*",                         0, crunch_server_header },
@@ -1067,7 +1076,10 @@ static const add_header_func_ptr add_client_headers[] = {
 };
 
 static const add_header_func_ptr add_server_headers[] = {
-   server_connection_close_adder,
+   server_connection_adder,
+#ifdef FEATURE_CONNECTION_KEEP_ALIVE
+   server_proxy_connection_adder,
+#endif /* def FEATURE_CONNECTION_KEEP_ALIVE */
    NULL
 };
 
@@ -2371,9 +2383,9 @@ static jb_err filter_header(struct client_state *csp, char **header)
  *
  * Function    :  server_connection
  *
- * Description :  Makes sure that the value of the Connection: header
- *                is "close" and signals server_connection_close_adder 
- *                to do nothing.
+ * Description :  Makes sure a proper "Connection:" header is
+ *                set and signals connection_header_adder to
+ *                do nothing.
  *
  * Parameters  :
  *          1  :  csp = Current client state (buffers, headers, etc...)
@@ -2388,8 +2400,6 @@ static jb_err filter_header(struct client_state *csp, char **header)
  *********************************************************************/
 static jb_err server_connection(struct client_state *csp, char **header)
 {
-   char *old_header = *header;
-
    /* Do we have a 'Connection: close' header? */
    if (strcmpic(*header, "Connection: close"))
    {
@@ -2401,7 +2411,10 @@ static jb_err server_connection(struct client_state *csp, char **header)
          /* Remember to keep the connection alive. */
          csp->flags |= CSP_FLAG_SERVER_CONNECTION_KEEP_ALIVE;
       }
-#endif /* FEATURE_CONNECTION_KEEP_ALIVE */
+      log_error(LOG_LEVEL_HEADER,
+         "Keeping the server header '%s' around.", *header);
+#else
+      char *old_header = *header;
 
       *header = strdup("Connection: close");
       if (header == NULL)
@@ -2410,10 +2423,11 @@ static jb_err server_connection(struct client_state *csp, char **header)
       }
       log_error(LOG_LEVEL_HEADER, "Replaced: \'%s\' with \'%s\'", old_header, *header);
       freez(old_header);
+#endif /* FEATURE_CONNECTION_KEEP_ALIVE */
    }
 
-   /* Signal server_connection_close_adder() to return early. */
-   csp->flags |= CSP_FLAG_SERVER_CONNECTION_CLOSE_SET;
+   /* Signal server_connection_adder() to return early. */
+   csp->flags |= CSP_FLAG_SERVER_CONNECTION_HEADER_SET;
 
    return JB_ERR_OK;
 }
@@ -2439,11 +2453,18 @@ static jb_err server_connection(struct client_state *csp, char **header)
  *********************************************************************/
 static jb_err client_connection(struct client_state *csp, char **header)
 {
-   char *old_header = *header;
    const char *wanted_header = get_appropiate_connection_header(csp);
 
    if (strcmpic(*header, wanted_header))
    {
+#ifdef FEATURE_CONNECTION_KEEP_ALIVE
+      log_error(LOG_LEVEL_HEADER,
+         "Keeping the client header '%s' around. "
+         "The connection will not be kept alive.",
+         *header);
+#else
+      char *old_header = *header;
+
       *header = strdup(wanted_header);
       if (header == NULL)
       { 
@@ -2452,9 +2473,20 @@ static jb_err client_connection(struct client_state *csp, char **header)
       log_error(LOG_LEVEL_HEADER,
          "Replaced: \'%s\' with \'%s\'", old_header, *header);
       freez(old_header);
+#endif /* def FEATURE_CONNECTION_KEEP_ALIVE */
    }
+#ifdef FEATURE_CONNECTION_KEEP_ALIVE
+   else
+   {
+      log_error(LOG_LEVEL_HEADER,
+         "Keeping the client header '%s' around. "
+         "The server connection will be kept alive if possible.",
+         *header);
+      csp->flags |= CSP_FLAG_CLIENT_CONNECTION_KEEP_ALIVE;
+   }
+#endif  /* def FEATURE_CONNECTION_KEEP_ALIVE */
 
-   /* Signal client_connection_close_adder() to return early. */
+   /* Signal client_connection_adder() to return early. */
    csp->flags |= CSP_FLAG_CLIENT_CONNECTION_HEADER_SET;
 
    return JB_ERR_OK;
@@ -4087,14 +4119,11 @@ static jb_err client_x_forwarded_for_adder(struct client_state *csp)
 
 /*********************************************************************
  *
- * Function    :  server_connection_close_adder
+ * Function    :  server_connection_adder
  *
- * Description :  "Temporary" fix for the needed but missing HTTP/1.1
- *                support. Adds a "Connection: close" header to csp->headers
+ * Description :  Adds an appropiate "Connection:" header to csp->headers
  *                unless the header was already present. Called from `sed'.
  *
- *                FIXME: This whole function shouldn't be neccessary!
- *
  * Parameters  :
  *          1  :  csp = Current client state (buffers, headers, etc...)
  *
@@ -4102,13 +4131,14 @@ static jb_err client_x_forwarded_for_adder(struct client_state *csp)
  *                JB_ERR_MEMORY on out-of-memory error.
  *
  *********************************************************************/
-static jb_err server_connection_close_adder(struct client_state *csp)
+static jb_err server_connection_adder(struct client_state *csp)
 {
    const unsigned int flags = csp->flags;
    const char *response_status_line = csp->headers->first->str;
+   const char *wanted_header = get_appropiate_connection_header(csp);
 
    if ((flags & CSP_FLAG_CLIENT_HEADER_PARSING_DONE)
-    && (flags & CSP_FLAG_SERVER_CONNECTION_CLOSE_SET))
+    && (flags & CSP_FLAG_SERVER_CONNECTION_HEADER_SET))
    {
       return JB_ERR_OK;
    }
@@ -4126,10 +4156,34 @@ static jb_err server_connection_close_adder(struct client_state *csp)
       csp->flags |= CSP_FLAG_SERVER_CONNECTION_KEEP_ALIVE;
    }
 
-   log_error(LOG_LEVEL_HEADER, "Adding: Connection: close");
+   log_error(LOG_LEVEL_HEADER, "Adding: %s", wanted_header);
+
+   return enlist(csp->headers, wanted_header);
+}
+
 
-   return enlist(csp->headers, "Connection: close");
+#ifdef FEATURE_CONNECTION_KEEP_ALIVE
+/*********************************************************************
+ *
+ * Function    :  server_proxy_connection_adder
+ *
+ * Description :  Adds a "Proxy-Connection: keep-alive" header to
+ *                csp->headers. XXX: We should reuse existant ones.
+ *
+ * Parameters  :
+ *          1  :  csp = Current client state (buffers, headers, etc...)
+ *
+ * Returns     :  JB_ERR_OK on success, or
+ *                JB_ERR_MEMORY on out-of-memory error.
+ *
+ *********************************************************************/
+static jb_err server_proxy_connection_adder(struct client_state *csp)
+{
+   static const char proxy_connection_header[] = "Proxy-Connection: keep-alive";
+   log_error(LOG_LEVEL_HEADER, "Adding: %s", proxy_connection_header);
+   return enlist(csp->headers, proxy_connection_header);
 }
+#endif /* FEATURE_CONNECTION_KEEP_ALIVE */
 
 
 /*********************************************************************
index ccd29f3..0fab7ff 100644 (file)
--- a/project.h
+++ b/project.h
@@ -1,7 +1,7 @@
 #ifndef PROJECT_H_INCLUDED
 #define PROJECT_H_INCLUDED
 /** Version string. */
-#define PROJECT_H_VERSION "$Id: project.h,v 1.132 2009/04/17 11:45:19 fabiankeil Exp $"
+#define PROJECT_H_VERSION "$Id: project.h,v 1.133 2009/04/24 15:29:43 fabiankeil Exp $"
 /*********************************************************************
  *
  * File        :  $Source: /cvsroot/ijbswa/current/project.h,v $
@@ -37,6 +37,9 @@
  *
  * Revisions   :
  *    $Log: project.h,v $
+ *    Revision 1.133  2009/04/24 15:29:43  fabiankeil
+ *    Allow to limit the number of of client connections.
+ *
  *    Revision 1.132  2009/04/17 11:45:19  fabiankeil
  *    Replace HAVE_GETADDRINFO and HAVE_GETNAMEINFO macros
  *    with HAVE_RFC2553 macro. Original patch by Petr Pisar.
@@ -1301,6 +1304,26 @@ struct url_actions
 };
 
 
+/*
+ * Structure to make sure we only reuse the server socket
+ * if the host and forwarding settings are the same.
+ */
+struct reusable_connection
+{
+   jb_socket sfd;
+   int       in_use;
+   time_t    timestamp;
+
+   char *host;
+   int  port;
+   int  forwarder_type;
+   char *gateway_host;
+   int  gateway_port;
+   char *forward_host;
+   int  forward_port;
+};
+
+
 /*
  * Flags for use in csp->flags
  */
@@ -1340,15 +1363,15 @@ struct url_actions
 
 /**
  * Flag for csp->flags: Set if an acceptable Connection header
- * is already set.
+ * has already been set by the client.
  */
 #define CSP_FLAG_CLIENT_CONNECTION_HEADER_SET  0x00000040U
 
 /**
- * Flag for csp->flags: Set if adding the 'Connection: close' header
- * for the server isn't necessary.
+ * Flag for csp->flags: Set if an acceptable Connection header
+ * has already been set by the server.
  */
-#define CSP_FLAG_SERVER_CONNECTION_CLOSE_SET   0x00000080U
+#define CSP_FLAG_SERVER_CONNECTION_HEADER_SET  0x00000080U
 
 /**
  * Flag for csp->flags: Signals header parsers whether they
@@ -1387,6 +1410,12 @@ struct url_actions
  * content length.
  */
 #define CSP_FLAG_CONTENT_LENGTH_SET            0x00002000U
+
+/**
+ * Flag for csp->flags: Set if the client wants to keep
+ * the connection alive.
+ */
+#define CSP_FLAG_CLIENT_CONNECTION_KEEP_ALIVE  0x00004000U
 #endif /* def FEATURE_CONNECTION_KEEP_ALIVE */
 
 /*
@@ -1428,6 +1457,9 @@ struct client_state
    /** socket to talk to server (web server or proxy) */
    jb_socket sfd;
 
+   /** current connection to the server (may go through a proxy) */
+   struct reusable_connection server_connection;
+
    /** Multi-purpose flag container, see CSP_FLAG_* above */
    unsigned int flags;
 
@@ -1835,6 +1867,11 @@ struct configuration_spec
    /* Timeout when waiting on sockets for data to become available. */
    int socket_timeout;
 
+#ifdef FEATURE_CONNECTION_KEEP_ALIVE
+   /* Number of seconds after which an open connection will no longer be reused. */
+   int keep_alive_timeout;
+#endif
+
    /** All options from the config file, HTML-formatted. */
    char *proxy_args;