X-Git-Url: http://www.privoxy.org/gitweb/?p=privoxy.git;a=blobdiff_plain;f=jcc.c;h=a98df8ba98fb6a1a3e0edd6942154ab8081f0600;hp=c8e9ea758b5c88df46b35b09709d09cc205f7cec;hb=2277ae3f6ddc205e1f93a9187a2fdd467cd473c5;hpb=df9616f1dacac74ba9469d6c334d0105a6effe15 diff --git a/jcc.c b/jcc.c index c8e9ea75..a98df8ba 100644 --- a/jcc.c +++ b/jcc.c @@ -1,4 +1,4 @@ -const char jcc_rcs[] = "$Id: jcc.c,v 1.242 2009/04/11 10:44:47 fabiankeil Exp $"; +const char jcc_rcs[] = "$Id: jcc.c,v 1.247 2009/05/10 10:19:23 fabiankeil Exp $"; /********************************************************************* * * File : $Source: /cvsroot/ijbswa/current/jcc.c,v $ @@ -33,6 +33,23 @@ const char jcc_rcs[] = "$Id: jcc.c,v 1.242 2009/04/11 10:44:47 fabiankeil Exp $" * * Revisions : * $Log: jcc.c,v $ + * Revision 1.247 2009/05/10 10:19:23 fabiankeil + * Reenable server-side-only keep-alive support, but only share + * outgoing connections if the connection-sharing option is set. + * + * Revision 1.246 2009/05/10 10:12:30 fabiankeil + * Initial keep-alive support for the client socket. + * Temporarily disable the server-side-only keep-alive code. + * + * 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. + * + * Revision 1.243 2009/04/17 11:27:49 fabiankeil + * Petr Pisar's privoxy-3.0.12-ipv6-3.diff. + * * Revision 1.242 2009/04/11 10:44:47 fabiankeil * Update a comment. We're not in Kansas anymore. * @@ -2266,7 +2283,7 @@ static int server_response_is_complete(struct client_state *csp, * Returns : N/A * *********************************************************************/ -static void wait_for_alive_connections() +static void wait_for_alive_connections(void) { int connections_alive = close_unusable_connections(); @@ -2282,6 +2299,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 */ @@ -2757,41 +2841,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) @@ -2919,6 +3028,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)) { @@ -3402,38 +3514,101 @@ 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; + 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)) + if (continue_chatting) { - 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) + 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)) + { + log_error(LOG_LEVEL_CONNECT, "Client request arrived in " + "time or the client closed the connection."); + /* + * 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) + { + 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); + } + else { - 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, + "No additional client request received in time."); + if ((csp->config->feature_flags & RUNTIME_FEATURE_CONNECTION_SHARING)) + { + remember_connection(csp->sfd, csp->http, + forward_url(csp, csp->http)); + csp->sfd = JB_INVALID_SOCKET; + close_socket(csp->cfd); + csp->cfd = JB_INVALID_SOCKET; + privoxy_mutex_lock(&connection_reuse_mutex); + if (!monitor_thread_running) + { + monitor_thread_running = 1; + privoxy_mutex_unlock(&connection_reuse_mutex); + wait_for_alive_connections(); + privoxy_mutex_lock(&connection_reuse_mutex); + monitor_thread_running = 0; + } + privoxy_mutex_unlock(&connection_reuse_mutex); + } + 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) + { +#ifdef FEATURE_CONNECTION_KEEP_ALIVE + forget_connection(csp->sfd); #endif /* def FEATURE_CONNECTION_KEEP_ALIVE */ + close_socket(csp->sfd); } if (csp->cfd != JB_INVALID_SOCKET) @@ -4177,7 +4352,8 @@ static void listen_loop(void) { struct client_state *csp = NULL; jb_socket bfd; - struct configuration_spec * config; + struct configuration_spec *config; + unsigned int active_threads = 0; config = load_config(); @@ -4207,7 +4383,7 @@ static void listen_loop(void) /* * Free data that was used by died threads */ - sweep(); + active_threads = sweep(); #if defined(unix) /* @@ -4298,6 +4474,20 @@ static void listen_loop(void) } #endif /* def FEATURE_ACL */ + if ((0 != config->max_client_connections) + && (active_threads >= config->max_client_connections)) + { + log_error(LOG_LEVEL_CONNECT, + "Rejecting connection from %s. Maximum number of connections reached.", + csp->ip_addr_str); + write_socket(csp->cfd, TOO_MANY_CONNECTIONS_RESPONSE, + strlen(TOO_MANY_CONNECTIONS_RESPONSE)); + close_socket(csp->cfd); + freez(csp->ip_addr_str); + freez(csp); + continue; + } + /* add it to the list of clients */ csp->next = clients->next; clients->next = csp; @@ -4536,6 +4726,4 @@ static void listen_loop(void) Local Variables: tab-width: 3 end: - - vim:softtabstop=3 shiftwidth=3 */