diff --git a/gateway.c b/gateway.c index 1e4ffd0..8850895 100644 --- a/gateway.c +++ b/gateway.c @@ -336,173 +336,11 @@ static const char socks_userid[] = "anonymous"; #ifdef FEATURE_CONNECTION_KEEP_ALIVE -#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); - - -/********************************************************************* - * - * Function : initialize_reusable_connections - * - * Description : Initializes the reusable_connection structures. - * Must be called with connection_reuse_mutex locked. - * - * Parameters : N/A - * - * Returns : void - * - *********************************************************************/ -extern void initialize_reusable_connections(void) -{ - unsigned int slot = 0; - -#if !defined(HAVE_POLL) && !defined(_WIN32) - log_error(LOG_LEVEL_INFO, - "Detecting already dead connections might not work " - "correctly on your platform. In case of problems, " - "unset the keep-alive-timeout option."); -#endif - - for (slot = 0; slot < SZ(reusable_connection); slot++) - { - mark_connection_closed(&reusable_connection[slot]); - } - - log_error(LOG_LEVEL_CONNECT, "Initialized %d socket slots.", slot); -} - - -/********************************************************************* - * - * Function : remember_connection - * - * 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. - * - * Returns : void - * - *********************************************************************/ -void remember_connection(jb_socket sfd, const struct http_request *http, - const struct forward_spec *fwd) -{ - unsigned int slot = 0; - int free_slot_found = FALSE; - - assert(sfd != JB_INVALID_SOCKET); - - if (mark_connection_unused(sfd)) - { - return; - } - - privoxy_mutex_lock(&connection_reuse_mutex); - - /* Find free socket slot. */ - for (slot = 0; slot < SZ(reusable_connection); slot++) - { - if (reusable_connection[slot].sfd == JB_INVALID_SOCKET) - { - assert(reusable_connection[slot].in_use == 0); - log_error(LOG_LEVEL_CONNECT, - "Remembering socket %d for %s:%d in slot %d.", - sfd, http->host, http->port, slot); - free_slot_found = TRUE; - break; - } - } - - if (!free_slot_found) - { - log_error(LOG_LEVEL_CONNECT, - "No free slots found to remembering socket for %s:%d. Last slot %d.", - http->host, http->port, slot); - privoxy_mutex_unlock(&connection_reuse_mutex); - close_socket(sfd); - return; - } - - assert(NULL != http->host); - reusable_connection[slot].host = strdup(http->host); - if (NULL == reusable_connection[slot].host) - { - log_error(LOG_LEVEL_FATAL, "Out of memory saving socket."); - } - reusable_connection[slot].sfd = sfd; - reusable_connection[slot].port = http->port; - reusable_connection[slot].in_use = 0; - reusable_connection[slot].timestamp = time(NULL); - - assert(NULL != fwd); - assert(reusable_connection[slot].gateway_host == NULL); - assert(reusable_connection[slot].gateway_port == 0); - assert(reusable_connection[slot].forwarder_type == SOCKS_NONE); - assert(reusable_connection[slot].forward_host == NULL); - assert(reusable_connection[slot].forward_port == 0); - - reusable_connection[slot].forwarder_type = fwd->type; - if (NULL != fwd->gateway_host) - { - reusable_connection[slot].gateway_host = strdup(fwd->gateway_host); - if (NULL == reusable_connection[slot].gateway_host) - { - log_error(LOG_LEVEL_FATAL, "Out of memory saving gateway_host."); - } - } - else - { - reusable_connection[slot].gateway_host = NULL; - } - reusable_connection[slot].gateway_port = fwd->gateway_port; - - if (NULL != fwd->forward_host) - { - reusable_connection[slot].forward_host = strdup(fwd->forward_host); - if (NULL == reusable_connection[slot].forward_host) - { - log_error(LOG_LEVEL_FATAL, "Out of memory saving forward_host."); - } - } - else - { - reusable_connection[slot].forward_host = NULL; - } - reusable_connection[slot].forward_port = fwd->forward_port; - - privoxy_mutex_unlock(&connection_reuse_mutex); -} - - /********************************************************************* * * 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. @@ -510,13 +348,10 @@ 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; freez(closed_connection->host); closed_connection->port = 0; - closed_connection->timestamp = 0; closed_connection->forwarder_type = SOCKS_NONE; freez(closed_connection->gateway_host); closed_connection->gateway_port = 0; @@ -527,51 +362,6 @@ static void mark_connection_closed(struct reusable_connection *closed_connection /********************************************************************* * - * Function : forget_connection - * - * Description : Removes a previously remembered connection from - * the list of reusable connections. - * - * Parameters : - * 1 : sfd = The socket belonging to the connection in question. - * - * Returns : void - * - *********************************************************************/ -void forget_connection(jb_socket sfd) -{ - unsigned int slot = 0; - - assert(sfd != JB_INVALID_SOCKET); - - privoxy_mutex_lock(&connection_reuse_mutex); - - for (slot = 0; slot < SZ(reusable_connection); slot++) - { - if (reusable_connection[slot].sfd == sfd) - { - assert(reusable_connection[slot].in_use); - - log_error(LOG_LEVEL_CONNECT, - "Forgetting socket %d for %s:%d in slot %d.", - sfd, reusable_connection[slot].host, - reusable_connection[slot].port, slot); - mark_connection_closed(&reusable_connection[slot]); - privoxy_mutex_unlock(&connection_reuse_mutex); - - return; - } - } - - log_error(LOG_LEVEL_CONNECT, - "Socket %d already forgotten or never remembered.", sfd); - - privoxy_mutex_unlock(&connection_reuse_mutex); -} - - -/********************************************************************* - * * Function : connection_destination_matches * * Description : Determines whether a remembered connection can @@ -586,9 +376,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) @@ -619,240 +409,6 @@ static int connection_destination_matches(const struct reusable_connection *conn return (!strcmpic(connection->host, http->host)); } - - -/********************************************************************* - * - * Function : close_unusable_connections - * - * Description : Closes remembered connections that have timed - * out or have been closed on the other side. - * - * Parameters : none - * - * Returns : Number of connections that are still alive. - * - *********************************************************************/ -int close_unusable_connections(void) -{ - unsigned int slot = 0; - int connections_alive = 0; - - privoxy_mutex_lock(&connection_reuse_mutex); - - for (slot = 0; slot < SZ(reusable_connection); slot++) - { - if (!reusable_connection[slot].in_use - && (JB_INVALID_SOCKET != reusable_connection[slot].sfd)) - { - time_t time_open = time(NULL) - reusable_connection[slot].timestamp; - - if (keep_alive_timeout < time_open) - { - log_error(LOG_LEVEL_CONNECT, - "The connection to %s:%d in slot %d timed out. " - "Closing socket %d. Timeout is: %d.", - reusable_connection[slot].host, - reusable_connection[slot].port, slot, - reusable_connection[slot].sfd, keep_alive_timeout); - close_socket(reusable_connection[slot].sfd); - mark_connection_closed(&reusable_connection[slot]); - } - else if (!socket_is_still_usable(reusable_connection[slot].sfd)) - { - log_error(LOG_LEVEL_CONNECT, - "The connection to %s:%d in slot %d is no longer usable. " - "Closing socket %d.", reusable_connection[slot].host, - reusable_connection[slot].port, slot, - reusable_connection[slot].sfd); - close_socket(reusable_connection[slot].sfd); - mark_connection_closed(&reusable_connection[slot]); - } - else - { - connections_alive++; - } - } - } - - privoxy_mutex_unlock(&connection_reuse_mutex); - - return connections_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. - * - *********************************************************************/ -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 - * - * Description : Returns an open socket to a previously remembered - * open connection (if there is one). - * - * Parameters : - * 1 : http = The destination for the connection. - * 2 : fwd = The forwarder settings. - * - * Returns : JB_INVALID_SOCKET => No reusable connection found, - * otherwise a usable socket. - * - *********************************************************************/ -static jb_socket get_reusable_connection(const struct http_request *http, - const struct forward_spec *fwd) -{ - jb_socket sfd = JB_INVALID_SOCKET; - unsigned int slot = 0; - - close_unusable_connections(); - - privoxy_mutex_lock(&connection_reuse_mutex); - - for (slot = 0; slot < SZ(reusable_connection); slot++) - { - if (!reusable_connection[slot].in_use - && (JB_INVALID_SOCKET != reusable_connection[slot].sfd)) - { - if (connection_destination_matches(&reusable_connection[slot], http, fwd)) - { - reusable_connection[slot].in_use = TRUE; - sfd = reusable_connection[slot].sfd; - log_error(LOG_LEVEL_CONNECT, - "Found reusable socket %d for %s:%d in slot %d.", - sfd, reusable_connection[slot].host, reusable_connection[slot].port, slot); - break; - } - } - } - - privoxy_mutex_unlock(&connection_reuse_mutex); - - return sfd; - -} - - -/********************************************************************* - * - * Function : mark_connection_unused - * - * Description : Gives a remembered connection free for reuse. - * - * Parameters : - * 1 : sfd = The socket belonging to the connection in question. - * - * Returns : TRUE => Socket found and marked as unused. - * FALSE => Socket not found. - * - *********************************************************************/ -static int mark_connection_unused(jb_socket sfd) -{ - unsigned int slot = 0; - int socket_found = FALSE; - - assert(sfd != JB_INVALID_SOCKET); - - privoxy_mutex_lock(&connection_reuse_mutex); - - for (slot = 0; slot < SZ(reusable_connection); slot++) - { - if (reusable_connection[slot].sfd == sfd) - { - assert(reusable_connection[slot].in_use); - socket_found = TRUE; - log_error(LOG_LEVEL_CONNECT, - "Marking open socket %d for %s:%d in slot %d as unused.", - sfd, reusable_connection[slot].host, - reusable_connection[slot].port, slot); - reusable_connection[slot].in_use = 0; - reusable_connection[slot].timestamp = time(NULL); - break; - } - } - - privoxy_mutex_unlock(&connection_reuse_mutex); - - return socket_found; - -} - - -/********************************************************************* - * - * Function : set_keep_alive_timeout - * - * Description : Sets the timeout after which open - * connections will no longer be reused. - * - * Parameters : - * 1 : timeout = The timeout in seconds. - * - * Returns : void - * - *********************************************************************/ -void set_keep_alive_timeout(int timeout) -{ - keep_alive_timeout = timeout; -} #endif /* def FEATURE_CONNECTION_KEEP_ALIVE */ @@ -879,14 +435,6 @@ jb_socket forwarded_connect(const struct forward_spec * fwd, int dest_port; jb_socket sfd = JB_INVALID_SOCKET; -#ifdef FEATURE_CONNECTION_KEEP_ALIVE - sfd = get_reusable_connection(http, fwd); - if (JB_INVALID_SOCKET != sfd) - { - return sfd; - } -#endif /* def FEATURE_CONNECTION_KEEP_ALIVE */ - /* Figure out if we need to connect to the web server or a HTTP proxy. */ if (fwd->forward_host) { diff --git a/gateway.h b/gateway.h index 94115f7..46c899e 100644 --- a/gateway.h +++ b/gateway.h @@ -116,19 +116,10 @@ extern jb_socket forwarded_connect(const struct forward_spec * fwd, struct client_state *csp); #ifdef FEATURE_CONNECTION_KEEP_ALIVE -/* - * Default number of seconds after which an - * open connection will no longer be reused. - */ -#define DEFAULT_KEEP_ALIVE_TIMEOUT 180 - -extern void set_keep_alive_timeout(int timeout); -extern void initialize_reusable_connections(void); -extern void forget_connection(jb_socket sfd); -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 */ diff --git a/jbsockets.c b/jbsockets.c index 4548ff2..053f5ec 100644 --- a/jbsockets.c +++ b/jbsockets.c @@ -8,7 +8,7 @@ const char jbsockets_rcs[] = "$Id: jbsockets.c,v 1.50 2008/12/20 14:53:55 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 @@ -315,6 +315,16 @@ const char jbsockets_rcs[] = "$Id: jbsockets.c,v 1.50 2008/12/20 14:53:55 fabian #endif +#ifdef FEATURE_CONNECTION_KEEP_ALIVE +#ifdef HAVE_POLL +#ifdef __GLIBC__ +#include +#else +#include +#endif /* def __GLIBC__ */ +#endif /* HAVE_POLL */ +#endif /* def FEATURE_CONNECTION_KEEP_ALIVE */ + #include "project.h" #ifdef FEATURE_PTHREAD @@ -1047,6 +1057,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 diff --git a/jbsockets.h b/jbsockets.h index 115cb6a..92c4350 100644 --- a/jbsockets.h +++ b/jbsockets.h @@ -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 @@ -132,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 5e97702..98f37ef 100644 --- a/jcc.c +++ b/jcc.c @@ -1389,7 +1389,6 @@ static int32 server_thread(void *data); */ privoxy_mutex_t log_mutex; privoxy_mutex_t log_init_mutex; -privoxy_mutex_t connection_reuse_mutex; #if !defined(HAVE_GETHOSTBYADDR_R) || !defined(HAVE_GETHOSTBYNAME_R) privoxy_mutex_t resolver_mutex; @@ -2241,30 +2240,68 @@ static int server_response_is_complete(struct client_state *csp, /********************************************************************* * - * Function : wait_for_alive_connections + * Function : save_connection_destination * - * Description : Waits for alive connections to timeout. + * Description : Remembers a connection for reuse later on. * - * Parameters : N/A + * 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 : N/A + * Returns : void * *********************************************************************/ -static void wait_for_alive_connections() +void save_connection_destination(jb_socket sfd, + const struct http_request *http, + const struct forward_spec *fwd, + struct reusable_connection *server_connection) { - int connections_alive = close_unusable_connections(); - - while (0 < connections_alive) + assert(sfd != JB_INVALID_SOCKET); + assert(NULL != http->host); + server_connection->host = strdup(http->host); + if (NULL == server_connection->host) { - log_error(LOG_LEVEL_CONNECT, - "Waiting for %d connections to timeout.", - connections_alive); - sleep(60); - connections_alive = close_unusable_connections(); + log_error(LOG_LEVEL_FATAL, "Out of memory saving socket."); } + server_connection->port = http->port; - log_error(LOG_LEVEL_CONNECT, "No connections to wait for left."); + 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 */ @@ -2741,41 +2778,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) @@ -2903,6 +2965,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)) { @@ -3378,38 +3443,79 @@ 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 (data_is_available(csp->cfd, csp->config->keep_alive_timeout)) { - 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); + mark_connection_closed(&csp->server_connection); #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) @@ -3586,7 +3692,6 @@ static void initialize_mutexes(void) */ privoxy_mutex_init(&log_mutex); privoxy_mutex_init(&log_init_mutex); - privoxy_mutex_init(&connection_reuse_mutex); /* * XXX: The assumptions below are a bit naive @@ -4157,14 +4262,6 @@ static void listen_loop(void) config = load_config(); -#ifdef FEATURE_CONNECTION_KEEP_ALIVE - /* - * XXX: Should be relocated once it no - * longer needs to emit log messages. - */ - initialize_reusable_connections(); -#endif /* def FEATURE_CONNECTION_KEEP_ALIVE */ - bfd = bind_port_helper(config); #ifdef FEATURE_GRACEFUL_TERMINATION diff --git a/jcc.h b/jcc.h index add854c..55d1bf4 100644 --- a/jcc.h +++ b/jcc.h @@ -216,7 +216,6 @@ extern void privoxy_mutex_unlock(privoxy_mutex_t *mutex); extern privoxy_mutex_t log_mutex; extern privoxy_mutex_t log_init_mutex; -extern privoxy_mutex_t connection_reuse_mutex; #ifndef HAVE_GMTIME_R extern privoxy_mutex_t gmtime_mutex; diff --git a/loadcfg.c b/loadcfg.c index c2e3bd1..12a57f1 100644 --- a/loadcfg.c +++ b/loadcfg.c @@ -586,6 +586,12 @@ const char loadcfg_h_rcs[] = LOADCFG_H_VERSION; int global_toggle_state = 1; #endif /* def FEATURE_TOGGLE */ +/* + * Default number of seconds after which an + * open connection will no longer be reused. + */ +#define DEFAULT_KEEP_ALIVE_TIMEOUT 180 + /* The filename of the configfile */ const char *configfile = NULL; @@ -785,9 +791,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)) { @@ -841,6 +844,10 @@ struct configuration_spec * load_config(void) config->proxy_args = strdup(""); config->forwarded_connect_retries = 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; @@ -1410,7 +1417,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 { @@ -1800,28 +1807,6 @@ struct configuration_spec * load_config(void) } } -#ifdef FEATURE_CONNECTION_KEEP_ALIVE - if (config->feature_flags & RUNTIME_FEATURE_CONNECTION_KEEP_ALIVE) - { - if (config->multi_threaded) - { - set_keep_alive_timeout(keep_alive_timeout); - } - else - { - /* - * While we could use keep-alive without multiple threads - * 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. - */ - config->feature_flags &= ~RUNTIME_FEATURE_CONNECTION_KEEP_ALIVE; - log_error(LOG_LEVEL_ERROR, - "Config option single-threaded disables connection keep-alive."); - } - } -#endif - if (NULL == config->proxy_args) { log_error(LOG_LEVEL_FATAL, "Out of memory loading config - insufficient memory for config->proxy_args"); diff --git a/parsers.c b/parsers.c index 021762e..24cac5c 100644 --- a/parsers.c +++ b/parsers.c @@ -992,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); @@ -1026,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 }, @@ -1050,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 }, @@ -1070,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 }; @@ -2391,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")) { @@ -2404,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) @@ -2413,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; } @@ -2442,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) { @@ -2455,9 +2473,19 @@ 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); } +#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; @@ -4090,14 +4118,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...) * @@ -4105,13 +4130,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; } @@ -4129,12 +4155,36 @@ 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, "Connection: close"); + return enlist(csp->headers, wanted_header); } +#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 */ + + /********************************************************************* * * Function : client_connection_header_adder diff --git a/project.h b/project.h index 300d9d6..e1039ad 100644 --- a/project.h +++ b/project.h @@ -1292,6 +1292,25 @@ struct url_actions /* + * Structure to make sure we only reuse the server socket + * if the host and forwarding settings are the same. + * + * XXX: The name will make sense as soon as + * csp->sfd is moved into it. + */ +struct reusable_connection +{ + 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 */ @@ -1330,15 +1349,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 @@ -1418,6 +1437,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; @@ -1808,6 +1830,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; diff --git a/tools/privoxy-log-parser.pl b/tools/privoxy-log-parser.pl index 29839a4..19ab1e0 100755 --- a/tools/privoxy-log-parser.pl +++ b/tools/privoxy-log-parser.pl @@ -8,7 +8,7 @@ # # http://www.fabiankeil.de/sourcecode/privoxy-log-parser/ # -# $Id: privoxy-log-parser.pl,v 1.23 2009/03/14 15:31:58 fabiankeil Exp $ +# $Id: privoxy-log-parser.pl,v 1.140 2009/04/05 15:57:53 fk Exp $ # # TODO: # - LOG_LEVEL_CGI, LOG_LEVEL_ERROR, LOG_LEVEL_WRITE content highlighting @@ -912,6 +912,7 @@ sub handle_loglevel_header ($) { or $c =~ m/^Converting tab to space in / or $c =~ m/A HTTP\/1\.1 response without/ or $c =~ m/Disabled filter mode on behalf of the client/ + or $c =~ m/Keeping the (?:server|client) header / ) { # XXX: Some of these may need highlighting @@ -950,6 +951,9 @@ sub handle_loglevel_header ($) { # this again is not' # A HTTP/1.1 response without Connection header implies keep-alive. # Disabled filter mode on behalf of the client. + # Keeping the server header 'Connection: keep-alive' around. + # Keeping the client header 'Connection: close' around. The connection will not be kept alive. + # Keeping the client header 'Connection: keep-alive' around. The connection will be kept alive if possible. } elsif ($c =~ m/^scanning headers for:/) { @@ -1490,7 +1494,7 @@ sub handle_loglevel_connect ($) { $c =~ s@(?<=Closing socket )(\d+)@$h{'Number'}$1$h{'Standard'}@; $c =~ s@(?<=Timeout is: )(\d+)@$h{'Number'}$1$h{'Standard'}@; - } elsif ($c =~ m/^Waiting for/) { + } elsif ($c =~ m/^Waiting for \d/) { # Waiting for 1 connections to timeout. $c =~ s@(?<=^Waiting for )(\d+)@$h{'Number'}$1$h{'Standard'}@; @@ -1526,15 +1530,37 @@ sub handle_loglevel_connect ($) { # Connection from 81.163.28.218 dropped due to ACL $c =~ s@(?<=^Connection from )((?:\d+\.?){4})@$h{'Number'}$1$h{'Standard'}@; + } elsif ($c =~ m/^(?:Reusing|Closing) server socket \d./ or + $c =~ m/^No additional client request/) { + + # Reusing server socket 4. Opened for 10.0.0.1. + # Closing server socket 2. Opened for 10.0.0.1. + # No additional client request received in time. \ + # Closing server socket 4, initially opened for 10.0.0.1. + + $c =~ s@(?<=server socket )(\d+)@$h{'Number'}$1$h{'Standard'}@; + $c = highlight_matched_host($c, '(?<=for )[^\s]+(?=\.$)'); + + } elsif ($c =~ m/^Waiting for the next client request/ or + $c =~ m/^The connection on server socket/ ) { + + # Waiting for the next client request. Keeping the server socket 5 to 10.0.0.1 open. + # The connection on server socket 6 to upload.wikimedia.org isn't reusable. Closing. + + $c =~ s@(?<=server socket )(\d+)@$h{'Number'}$1$h{'Standard'}@; + $c = highlight_matched_host($c, '(?<=to )[^\s]+'); + } elsif ($c =~ m/^Looks like we rea/ or $c =~ m/^Unsetting keep-alive flag/ or - $c =~ m/^No connections to wait/) { + $c =~ m/^No connections to wait/ or + $c =~ m/^Client request arrived in time or the client closed the connection/) { # Looks like we reached the end of the last chunk. We better stop reading. # Looks like we read the end of the last chunk together with the server \ # headers. We better stop reading. # Unsetting keep-alive flag. # No connections to wait for left. + # Client request arrived in time or the client closed the connection. } else {