X-Git-Url: http://www.privoxy.org/gitweb/?a=blobdiff_plain;f=jcc.c;h=cb846ca86e8f71f6e45306b84ada9c73e3638fb9;hb=77134f9aac18e65f3d84341fc6c8237fd3829995;hp=950c4a27c0a9ed603e60f4c6c805560c95c1ba88;hpb=6d728d7cc958ee7f24e117f4adeccd4bd66dab5a;p=privoxy.git diff --git a/jcc.c b/jcc.c index 950c4a27..cb846ca8 100644 --- a/jcc.c +++ b/jcc.c @@ -1,4 +1,4 @@ -const char jcc_rcs[] = "$Id: jcc.c,v 1.285 2009/09/06 14:15:46 fabiankeil Exp $"; +const char jcc_rcs[] = "$Id: jcc.c,v 1.300 2009/10/04 15:46:25 fabiankeil Exp $"; /********************************************************************* * * File : $Source: /cvsroot/ijbswa/current/jcc.c,v $ @@ -1231,10 +1231,12 @@ static void verify_request_length(struct client_state *csp) *********************************************************************/ static void mark_server_socket_tainted(struct client_state *csp) { - if ((csp->flags & CSP_FLAG_SERVER_CONNECTION_KEEP_ALIVE)) + if ((csp->flags & CSP_FLAG_SERVER_CONNECTION_KEEP_ALIVE) + && !(csp->flags |= CSP_FLAG_SERVER_SOCKET_TAINTED)) { log_error(LOG_LEVEL_CONNECT, - "Marking the server socket %d tainted.", csp->sfd); + "Marking the server socket %d tainted.", + csp->server_connection.sfd); csp->flags |= CSP_FLAG_SERVER_SOCKET_TAINTED; } } @@ -1263,8 +1265,9 @@ static char *get_request_line(struct client_state *csp) { if (!data_is_available(csp->cfd, csp->config->socket_timeout)) { - log_error(LOG_LEVEL_ERROR, - "Stopped waiting for the request line."); + log_error(LOG_LEVEL_CONNECT, + "Stopped waiting for the request line. Timeout: %d.", + csp->config->socket_timeout); write_socket(csp->cfd, CLIENT_CONNECTION_TIMEOUT_RESPONSE, strlen(CLIENT_CONNECTION_TIMEOUT_RESPONSE)); return NULL; @@ -1329,6 +1332,7 @@ static jb_err receive_client_request(struct client_state *csp) req = get_request_line(csp); if (req == NULL) { + mark_server_socket_tainted(csp); return JB_ERR_PARSE; } assert(*req != '\0'); @@ -1599,6 +1603,7 @@ static void chat(struct client_state *csp) /* Skeleton for HTTP response, if we should intercept the request */ struct http_response *rsp; struct timeval timeout; + int watch_client_socket = 1; memset(buf, 0, sizeof(buf)); @@ -1707,27 +1712,27 @@ static void chat(struct client_state *csp) /* here we connect to the server, gateway, or the forwarder */ #ifdef FEATURE_CONNECTION_KEEP_ALIVE - if ((csp->sfd != JB_INVALID_SOCKET) - && socket_is_still_usable(csp->sfd) + if ((csp->server_connection.sfd != JB_INVALID_SOCKET) + && socket_is_still_usable(csp->server_connection.sfd) && connection_destination_matches(&csp->server_connection, http, fwd)) { log_error(LOG_LEVEL_CONNECT, "Reusing server socket %u. Opened for %s.", - csp->sfd, csp->server_connection.host); + csp->server_connection.sfd, csp->server_connection.host); } else { - if (csp->sfd != JB_INVALID_SOCKET) + if (csp->server_connection.sfd != JB_INVALID_SOCKET) { log_error(LOG_LEVEL_CONNECT, "Closing server socket %u. Opened for %s.", - csp->sfd, csp->server_connection.host); - close_socket(csp->sfd); + csp->server_connection.sfd, csp->server_connection.host); + close_socket(csp->server_connection.sfd); mark_connection_closed(&csp->server_connection); } #endif /* def FEATURE_CONNECTION_KEEP_ALIVE */ - while ((csp->sfd = forwarded_connect(fwd, http, csp)) + while ((csp->server_connection.sfd = forwarded_connect(fwd, http, csp)) && (errno == EINVAL) && (forwarded_connect_retries++ < max_forwarded_connect_retries)) { @@ -1736,7 +1741,7 @@ static void chat(struct client_state *csp) forwarded_connect_retries, http->hostport); } - if (csp->sfd == JB_INVALID_SOCKET) + if (csp->server_connection.sfd == JB_INVALID_SOCKET) { if (fwd->type != SOCKS_NONE) { @@ -1763,8 +1768,10 @@ static void chat(struct client_state *csp) return; } #ifdef FEATURE_CONNECTION_KEEP_ALIVE - save_connection_destination(csp->sfd, http, fwd, &csp->server_connection); - csp->server_connection.keep_alive_timeout = (unsigned)csp->config->keep_alive_timeout; + save_connection_destination(csp->server_connection.sfd, + http, fwd, &csp->server_connection); + csp->server_connection.keep_alive_timeout = + (unsigned)csp->config->keep_alive_timeout; } #endif /* def FEATURE_CONNECTION_KEEP_ALIVE */ @@ -1782,8 +1789,8 @@ static void chat(struct client_state *csp) * Write the client's (modified) header to the server * (along with anything else that may be in the buffer) */ - if (write_socket(csp->sfd, hdr, strlen(hdr)) - || (flush_socket(csp->sfd, csp->iob) < 0)) + if (write_socket(csp->server_connection.sfd, hdr, strlen(hdr)) + || (flush_socket(csp->server_connection.sfd, csp->iob) < 0)) { log_error(LOG_LEVEL_CONNECT, "write header to: %s failed: %E", http->hostport); @@ -1820,7 +1827,8 @@ static void chat(struct client_state *csp) /* we're finished with the client's header */ freez(hdr); - maxfd = (csp->cfd > csp->sfd) ? csp->cfd : csp->sfd; + maxfd = (csp->cfd > csp->server_connection.sfd) ? + csp->cfd : csp->server_connection.sfd; /* pass data between the client and server * until one or the other shuts down the connection. @@ -1840,9 +1848,9 @@ static void chat(struct client_state *csp) FD_ZERO(&rfds); #endif #ifdef FEATURE_CONNECTION_KEEP_ALIVE - if ((csp->flags & CSP_FLAG_CLIENT_REQUEST_COMPLETELY_READ)) + if (!watch_client_socket) { - maxfd = csp->sfd; + maxfd = csp->server_connection.sfd; } else #endif /* def FEATURE_CONNECTION_KEEP_ALIVE */ @@ -1850,7 +1858,7 @@ static void chat(struct client_state *csp) FD_SET(csp->cfd, &rfds); } - FD_SET(csp->sfd, &rfds); + FD_SET(csp->server_connection.sfd, &rfds); #ifdef FEATURE_CONNECTION_KEEP_ALIVE if ((csp->flags & CSP_FLAG_CHUNKED) @@ -1867,10 +1875,20 @@ static void chat(struct client_state *csp) } if (server_body && server_response_is_complete(csp, byte_count)) { - log_error(LOG_LEVEL_CONNECT, - "Done reading from server. Expected content length: %llu. " - "Actual content length: %llu. Most recently received: %d.", - csp->expected_content_length, byte_count, len); + if (csp->expected_content_length == byte_count) + { + log_error(LOG_LEVEL_CONNECT, + "Done reading from server. Content length: %llu as expected. " + "Bytes most recently read: %d.", + byte_count, len); + } + else + { + log_error(LOG_LEVEL_CONNECT, + "Done reading from server. Expected content length: %llu. " + "Actual content length: %llu. Bytes most recently read: %d.", + csp->expected_content_length, byte_count, len); + } len = 0; /* * XXX: should not jump around, @@ -1911,14 +1929,42 @@ static void chat(struct client_state *csp) */ if (FD_ISSET(csp->cfd, &rfds)) { - unsigned max_bytes_to_read = sizeof(buf) - 1; + int max_bytes_to_read = sizeof(buf) - 1; #ifdef FEATURE_CONNECTION_KEEP_ALIVE + if ((csp->flags & CSP_FLAG_CLIENT_REQUEST_COMPLETELY_READ)) + { + if (data_is_available(csp->cfd, 0)) + { + /* + * If the next request is already waiting, we have + * to stop select()ing the client socket. Otherwise + * we would always return right away and get nothing + * else done. + */ + watch_client_socket = 0; + log_error(LOG_LEVEL_CONNECT, + "Stopping to watch the client socket. " + "There's already another request waiting."); + continue; + } + /* + * If the client socket is set, but there's no data + * available on the socket, the client went fishing + * and continuing talking to the server makes no sense. + */ + log_error(LOG_LEVEL_CONNECT, + "The client closed socket %d while " + "the server socket %d is still open.", + csp->cfd, csp->server_connection.sfd); + mark_server_socket_tainted(csp); + break; + } if (csp->expected_client_content_length != 0) { if (csp->expected_client_content_length < (sizeof(buf) - 1)) { - max_bytes_to_read = csp->expected_client_content_length; + max_bytes_to_read = (int)csp->expected_client_content_length; } log_error(LOG_LEVEL_CONNECT, "Waiting for up to %d bytes from the client.", @@ -1940,7 +1986,7 @@ static void chat(struct client_state *csp) if (csp->expected_client_content_length != 0) { assert(len <= max_bytes_to_read); - csp->expected_client_content_length -= len; + csp->expected_client_content_length -= (unsigned)len; log_error(LOG_LEVEL_CONNECT, "Expected client content length set to %llu " "after reading %d bytes.", @@ -1954,7 +2000,7 @@ static void chat(struct client_state *csp) } #endif /* def FEATURE_CONNECTION_KEEP_ALIVE */ - if (write_socket(csp->sfd, buf, (size_t)len)) + if (write_socket(csp->server_connection.sfd, buf, (size_t)len)) { log_error(LOG_LEVEL_ERROR, "write to: %s failed: %E", http->host); mark_server_socket_tainted(csp); @@ -1968,7 +2014,7 @@ static void chat(struct client_state *csp) * If `hdr' is null, then it's the header otherwise it's the body. * FIXME: Does `hdr' really mean `host'? No. */ - if (FD_ISSET(csp->sfd, &rfds)) + if (FD_ISSET(csp->server_connection.sfd, &rfds)) { #ifdef FEATURE_CONNECTION_KEEP_ALIVE if (!socket_is_still_usable(csp->cfd)) @@ -1986,7 +2032,7 @@ static void chat(struct client_state *csp) #endif /* def FEATURE_CONNECTION_KEEP_ALIVE */ fflush(NULL); - len = read_socket(csp->sfd, buf, sizeof(buf) - 1); + len = read_socket(csp->server_connection.sfd, buf, sizeof(buf) - 1); if (len < 0) { @@ -2247,7 +2293,7 @@ static void chat(struct client_state *csp) * we can parse the headers we just continue here. */ log_error(LOG_LEVEL_CONNECT, - "Continuing buffering headers. Most recently received: %d", + "Continuing buffering headers. Bytes most recently read: %d.", len); continue; } @@ -2265,10 +2311,21 @@ static void chat(struct client_state *csp) /* Did we actually get anything? */ if (NULL == csp->headers->first) { - log_error(LOG_LEVEL_ERROR, - "Empty server or forwarder response received on socket %d.", csp->sfd); + if ((csp->flags & CSP_FLAG_REUSED_CLIENT_CONNECTION)) + { + log_error(LOG_LEVEL_ERROR, + "Empty server or forwarder response received on socket %d. " + "Closing client connection %d without sending data.", + csp->server_connection.sfd, csp->cfd); + } + else + { + log_error(LOG_LEVEL_ERROR, + "Empty server or forwarder response received on socket %d.", + csp->server_connection.sfd); + send_crunch_response(csp, error_response(csp, "no-server-data")); + } log_error(LOG_LEVEL_CLF, "%s - - [%T] \"%s\" 502 0", csp->ip_addr_str, http->cmd); - send_crunch_response(csp, error_response(csp, "no-server-data")); free_http_request(http); mark_server_socket_tainted(csp); return; @@ -2441,19 +2498,23 @@ static void serve(struct client_state *csp) static int monitor_thread_running = 0; #endif /* def FEATURE_CONNECTION_SHARING */ int continue_chatting = 0; - unsigned int latency = 0; do { + unsigned int latency; + chat(csp); + latency = (unsigned)(csp->server_connection.response_received - + csp->server_connection.request_sent) / 2; + continue_chatting = (csp->config->feature_flags & RUNTIME_FEATURE_CONNECTION_KEEP_ALIVE) && (csp->flags & CSP_FLAG_SERVER_CONNECTION_KEEP_ALIVE) && !(csp->flags & CSP_FLAG_SERVER_SOCKET_TAINTED) && (csp->cfd != JB_INVALID_SOCKET) - && (csp->sfd != JB_INVALID_SOCKET) - && socket_is_still_usable(csp->sfd) + && (csp->server_connection.sfd != JB_INVALID_SOCKET) + && socket_is_still_usable(csp->server_connection.sfd) && (latency < csp->server_connection.keep_alive_timeout); if (continue_chatting) @@ -2472,7 +2533,7 @@ static void serve(struct client_state *csp) 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); + csp->server_connection.sfd, csp->server_connection.host); if ((csp->flags & CSP_FLAG_CLIENT_CONNECTION_KEEP_ALIVE) && data_is_available(csp->cfd, (int)client_timeout) @@ -2503,7 +2564,8 @@ static void serve(struct client_state *csp) } /* XXX: Store per-connection flags someplace else. */ - csp->flags = CSP_FLAG_ACTIVE | (csp->flags & CSP_FLAG_TOGGLED_ON); + csp->flags = CSP_FLAG_ACTIVE | + (csp->flags & CSP_FLAG_TOGGLED_ON) | CSP_FLAG_REUSED_CLIENT_CONNECTION; } else { @@ -2511,10 +2573,17 @@ static void serve(struct client_state *csp) "No additional client request received in time."); #ifdef FEATURE_CONNECTION_SHARING if ((csp->config->feature_flags & RUNTIME_FEATURE_CONNECTION_SHARING) - && (socket_is_still_usable(csp->sfd))) + && (socket_is_still_usable(csp->server_connection.sfd))) { - remember_connection(csp, forward_url(csp, csp->http)); - csp->sfd = JB_INVALID_SOCKET; + time_t time_open = time(NULL) - csp->server_connection.timestamp; + + if (csp->server_connection.keep_alive_timeout < time_open + latency) + { + break; + } + + remember_connection(&csp->server_connection); + csp->server_connection.sfd = JB_INVALID_SOCKET; close_socket(csp->cfd); csp->cfd = JB_INVALID_SOCKET; privoxy_mutex_lock(&connection_reuse_mutex); @@ -2532,27 +2601,30 @@ static void serve(struct client_state *csp) break; } } - else if (csp->sfd != JB_INVALID_SOCKET) + else if (csp->server_connection.sfd != JB_INVALID_SOCKET) { log_error(LOG_LEVEL_CONNECT, "The connection on server socket %d to %s isn't reusable. " - "Closing.", csp->sfd, csp->server_connection.host); + "Closing.", csp->server_connection.sfd, csp->server_connection.host); } } while (continue_chatting); - mark_connection_closed(&csp->server_connection); #else chat(csp); #endif /* def FEATURE_CONNECTION_KEEP_ALIVE */ - if (csp->sfd != JB_INVALID_SOCKET) + if (csp->server_connection.sfd != JB_INVALID_SOCKET) { #ifdef FEATURE_CONNECTION_SHARING - forget_connection(csp->sfd); + forget_connection(csp->server_connection.sfd); #endif /* def FEATURE_CONNECTION_SHARING */ - close_socket(csp->sfd); + close_socket(csp->server_connection.sfd); } +#ifdef FEATURE_CONNECTION_KEEP_ALIVE + mark_connection_closed(&csp->server_connection); +#endif + if (csp->cfd != JB_INVALID_SOCKET) { close_socket(csp->cfd); @@ -2783,9 +2855,9 @@ static void initialize_mutexes(void) * *********************************************************************/ #ifdef __MINGW32__ -int real_main(int argc, const char *argv[]) +int real_main(int argc, char **argv) #else -int main(int argc, const char *argv[]) +int main(int argc, char **argv) #endif { int argc_pos = 0; @@ -3348,7 +3420,7 @@ static void listen_loop(void) } csp->flags |= CSP_FLAG_ACTIVE; - csp->sfd = JB_INVALID_SOCKET; + csp->server_connection.sfd = JB_INVALID_SOCKET; csp->config = config = load_config();