From e734bb1389aaa25ccc30da467aa439a9b00f9973 Mon Sep 17 00:00:00 2001 From: Fabian Keil Date: Sun, 10 May 2009 10:12:30 +0000 Subject: [PATCH] Initial keep-alive support for the client socket. Temporarily disable the server-side-only keep-alive code. --- gateway.c | 94 ++------------------- gateway.h | 10 ++- jbsockets.c | 80 +++++++++++++++++- jbsockets.h | 11 ++- jcc.c | 234 +++++++++++++++++++++++++++++++++++++++++----------- loadcfg.c | 18 ++-- parsers.c | 100 ++++++++++++++++------ project.h | 47 +++++++++-- 8 files changed, 423 insertions(+), 171 deletions(-) diff --git a/gateway.c b/gateway.c index bdca2b58..31816093 100644 --- 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 diff --git a/gateway.h b/gateway.h index 73a60bcd..eb439ca4 100644 --- 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 $ @@ -36,6 +36,10 @@ * * 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 */ diff --git a/jbsockets.c b/jbsockets.c index 990c8e42..18e42469 100644 --- a/jbsockets.c +++ b/jbsockets.c @@ -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 +#else +#include +#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 diff --git a/jbsockets.h b/jbsockets.h index 8a58066a..92c43505 100644 --- a/jbsockets.h +++ b/jbsockets.h @@ -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 @@ -37,6 +37,11 @@ * * 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 7ca0f46f..5991a72d 100644 --- 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) diff --git a/loadcfg.c b/loadcfg.c index fb804f8f..4942ca45 100644 --- 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, diff --git a/parsers.c b/parsers.c index cfe9307f..bef06eaf 100644 --- 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 */ /********************************************************************* diff --git a/project.h b/project.h index ccd29f30..0fab7ff0 100644 --- 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; -- 2.39.2