X-Git-Url: http://www.privoxy.org/gitweb/?a=blobdiff_plain;f=jcc.c;h=12dc1db12efdc2e633161a88a3369d7f36e17a8d;hb=840f97d551a676a4b50815dc29de569298d081b6;hp=fad64da1e22f1f8df9663063eed1cc6b05bb7940;hpb=3043c7216d75378d643cc81ba5e17e197e33284f;p=privoxy.git diff --git a/jcc.c b/jcc.c index fad64da1..12dc1db1 100644 --- a/jcc.c +++ b/jcc.c @@ -1,4 +1,4 @@ -const char jcc_rcs[] = "$Id: jcc.c,v 1.276 2009/08/19 15:57:13 fabiankeil Exp $"; +const char jcc_rcs[] = "$Id: jcc.c,v 1.293 2009/09/18 18:52:33 fabiankeil Exp $"; /********************************************************************* * * File : $Source: /cvsroot/ijbswa/current/jcc.c,v $ @@ -1026,6 +1026,7 @@ static int server_response_is_complete(struct client_state *csp, } +#ifdef FEATURE_CONNECTION_SHARING /********************************************************************* * * Function : wait_for_alive_connections @@ -1053,6 +1054,7 @@ static void wait_for_alive_connections(void) log_error(LOG_LEVEL_CONNECT, "No connections to wait for left."); } +#endif /* def FEATURE_CONNECTION_SHARING */ /********************************************************************* @@ -1122,6 +1124,94 @@ void save_connection_destination(jb_socket sfd, } server_connection->forward_port = fwd->forward_port; } + + +/********************************************************************* + * + * Function : verify_request_length + * + * Description : Checks if we already got the whole client requests + * and sets CSP_FLAG_CLIENT_REQUEST_COMPLETELY_READ if + * we do. + * + * Data that doesn't belong to the current request is + * thrown away to let the client retry on a clean socket. + * + * XXX: This is a hack until we can deal with multiple + * pipelined requests at the same time. + * + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * + * Returns : void + * + *********************************************************************/ +static void verify_request_length(struct client_state *csp) +{ + unsigned long long buffered_request_bytes = + (unsigned long long)(csp->iob->eod - csp->iob->cur); + + if ((csp->expected_client_content_length != 0) + && (buffered_request_bytes != 0)) + { + if (csp->expected_client_content_length >= buffered_request_bytes) + { + csp->expected_client_content_length -= buffered_request_bytes; + log_error(LOG_LEVEL_CONNECT, "Reduced expected bytes to %llu " + "to account for the %llu ones we already got.", + csp->expected_client_content_length, buffered_request_bytes); + } + else + { + assert(csp->iob->eod > csp->iob->cur + csp->expected_client_content_length); + csp->iob->eod = csp->iob->cur + csp->expected_client_content_length; + log_error(LOG_LEVEL_CONNECT, "Reducing expected bytes to 0. " + "Marking the server socket tainted after throwing %llu bytes away.", + buffered_request_bytes - csp->expected_client_content_length); + csp->expected_client_content_length = 0; + csp->flags |= CSP_FLAG_SERVER_SOCKET_TAINTED; + } + + if (csp->expected_client_content_length == 0) + { + csp->flags |= CSP_FLAG_CLIENT_REQUEST_COMPLETELY_READ; + } + } + + if (!(csp->flags & CSP_FLAG_CLIENT_REQUEST_COMPLETELY_READ) + && ((csp->iob->cur[0] != '\0') || (csp->expected_client_content_length != 0))) + { + csp->flags |= CSP_FLAG_SERVER_SOCKET_TAINTED; + if (strcmpic(csp->http->gpc, "GET") + && strcmpic(csp->http->gpc, "HEAD") + && strcmpic(csp->http->gpc, "TRACE") + && strcmpic(csp->http->gpc, "OPTIONS") + && strcmpic(csp->http->gpc, "DELETE")) + { + /* XXX: this is an incomplete hack */ + csp->flags &= ~CSP_FLAG_CLIENT_REQUEST_COMPLETELY_READ; + log_error(LOG_LEVEL_CONNECT, + "There might be a request body. The connection will not be kept alive."); + } + else + { + /* XXX: and so is this */ + csp->flags |= CSP_FLAG_CLIENT_REQUEST_COMPLETELY_READ; + log_error(LOG_LEVEL_CONNECT, + "Possible pipeline attempt detected. The connection will not " + "be kept alive and we will only serve the first request."); + /* Nuke the pipelined requests from orbit, just to be sure. */ + csp->iob->buf[0] = '\0'; + csp->iob->eod = csp->iob->cur = csp->iob->buf; + } + } + else + { + csp->flags |= CSP_FLAG_CLIENT_REQUEST_COMPLETELY_READ; + log_error(LOG_LEVEL_CONNECT, "Complete client request received."); + } +} #endif /* FEATURE_CONNECTION_KEEP_ALIVE */ @@ -1141,7 +1231,8 @@ void save_connection_destination(jb_socket sfd, *********************************************************************/ 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); @@ -1456,40 +1547,9 @@ static jb_err parse_client_request(struct client_state *csp) } #ifdef FEATURE_CONNECTION_KEEP_ALIVE - if ((csp->flags & CSP_FLAG_CLIENT_CONNECTION_KEEP_ALIVE)) + if (csp->http->ssl == 0) { - if ((csp->iob->cur[0] != '\0') - || (csp->expected_client_content_length != 0)) - { - csp->flags |= CSP_FLAG_SERVER_SOCKET_TAINTED; - if (strcmpic(csp->http->gpc, "GET") - && strcmpic(csp->http->gpc, "HEAD") - && strcmpic(csp->http->gpc, "TRACE") - && strcmpic(csp->http->gpc, "OPTIONS") - && strcmpic(csp->http->gpc, "DELETE")) - { - /* XXX: this is an incomplete hack */ - csp->flags &= ~CSP_FLAG_CLIENT_REQUEST_COMPLETELY_READ; - log_error(LOG_LEVEL_CONNECT, - "There might be a request body. The connection will not be kept alive."); - } - else - { - /* XXX: and so is this */ - csp->flags |= CSP_FLAG_CLIENT_REQUEST_COMPLETELY_READ; - log_error(LOG_LEVEL_CONNECT, - "Possible pipeline attempt detected. The connection will not " - "be kept alive and we will only serve the first request."); - /* Nuke the pipelined requests from orbit, just to be sure. */ - csp->iob->buf[0] = '\0'; - csp->iob->eod = csp->iob->cur = csp->iob->buf; - } - } - else - { - csp->flags |= CSP_FLAG_CLIENT_REQUEST_COMPLETELY_READ; - log_error(LOG_LEVEL_CONNECT, "Complete client request received."); - } + verify_request_length(csp); } #endif /* def FEATURE_CONNECTION_KEEP_ALIVE */ @@ -1540,6 +1600,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)); @@ -1781,21 +1842,13 @@ 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; } else #endif /* def FEATURE_CONNECTION_KEEP_ALIVE */ { -#ifdef FEATURE_CONNECTION_KEEP_ALIVE - if (http->ssl == 0) - { - log_error(LOG_LEVEL_CONNECT, - "Allowing the client to continue talking. " - "Expecting %llu bytes.", csp->expected_client_content_length); - } -#endif /* def FEATURE_CONNECTION_KEEP_ALIVE */ FD_SET(csp->cfd, &rfds); } @@ -1816,10 +1869,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, @@ -1860,14 +1923,40 @@ 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->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.", @@ -1889,11 +1978,17 @@ 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.", csp->expected_client_content_length, len); + if (csp->expected_client_content_length == 0) + { + log_error(LOG_LEVEL_CONNECT, + "Done reading from the client."); + csp->flags |= CSP_FLAG_CLIENT_REQUEST_COMPLETELY_READ; + } } #endif /* def FEATURE_CONNECTION_KEEP_ALIVE */ @@ -2150,7 +2245,6 @@ static void chat(struct client_state *csp) } else { - const char *header_start; /* * We're still looking for the end of the server's header. * Buffer up the data we just read. If that fails, there's @@ -2165,8 +2259,6 @@ static void chat(struct client_state *csp) return; } - header_start = csp->iob->cur; - /* Convert iob into something sed() can digest */ if (JB_ERR_PARSE == get_server_headers(csp)) { @@ -2192,15 +2284,21 @@ static void chat(struct client_state *csp) * Since we have to wait for more from the server before * we can parse the headers we just continue here. */ - long header_offset = csp->iob->cur - header_start; - assert(csp->iob->cur >= header_start); - byte_count += (unsigned long long)(len - header_offset); - log_error(LOG_LEVEL_CONNECT, "Continuing buffering headers. " - "byte_count: %llu. header_offset: %d. len: %d.", - byte_count, header_offset, len); + log_error(LOG_LEVEL_CONNECT, + "Continuing buffering headers. Bytes most recently read: %d.", + len); continue; } } + else + { + /* + * Account for the content bytes we + * might have gotten with the headers. + */ + assert(csp->iob->eod >= csp->iob->cur); + byte_count = (unsigned long long)(csp->iob->eod - csp->iob->cur); + } /* Did we actually get anything? */ if (NULL == csp->headers->first) @@ -2298,18 +2396,6 @@ static void chat(struct client_state *csp) mark_server_socket_tainted(csp); return; } - - byte_count += (unsigned long long)len; - } - else - { - /* - * XXX: the header lenght should probably - * be calculated by get_server_headers(). - */ - long header_length = csp->iob->cur - header_start; - assert(csp->iob->cur > header_start); - byte_count += (unsigned long long)(len - header_length); } /* we're finished with the server's header */ @@ -2389,21 +2475,19 @@ static void serve(struct client_state *csp) #endif /* def AMIGA */ { #ifdef FEATURE_CONNECTION_KEEP_ALIVE +#ifdef FEATURE_CONNECTION_SHARING 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); - if ((csp->flags & CSP_FLAG_SERVER_CONNECTION_KEEP_ALIVE) - && !(csp->flags & CSP_FLAG_SERVER_KEEP_ALIVE_TIMEOUT_SET)) - { - log_error(LOG_LEVEL_CONNECT, "The server didn't specify how long " - "the connection will stay open. Assume it's only a second."); - csp->server_connection.keep_alive_timeout = 1; - } + latency = (unsigned)(csp->server_connection.response_received - + csp->server_connection.request_sent); continue_chatting = (csp->config->feature_flags & RUNTIME_FEATURE_CONNECTION_KEEP_ALIVE) @@ -2416,7 +2500,17 @@ static void serve(struct client_state *csp) if (continue_chatting) { - unsigned int client_timeout = (unsigned)csp->server_connection.keep_alive_timeout - latency; + unsigned int client_timeout; + + if (!(csp->flags & CSP_FLAG_SERVER_KEEP_ALIVE_TIMEOUT_SET)) + { + log_error(LOG_LEVEL_CONNECT, "The server didn't specify how long " + "the connection will stay open. Assume it's only a second."); + csp->server_connection.keep_alive_timeout = 1; + } + + client_timeout = (unsigned)csp->server_connection.keep_alive_timeout - latency; + log_error(LOG_LEVEL_CONNECT, "Waiting for the next client request. " "Keeping the server socket %d to %s open.", @@ -2457,9 +2551,17 @@ static void serve(struct client_state *csp) { log_error(LOG_LEVEL_CONNECT, "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))) { + time_t time_open = time(NULL) - csp->server_connection.timestamp; + + if (csp->server_connection.keep_alive_timeout < time_open + latency) + { + break; + } + remember_connection(csp, forward_url(csp, csp->http)); csp->sfd = JB_INVALID_SOCKET; close_socket(csp->cfd); @@ -2475,6 +2577,7 @@ static void serve(struct client_state *csp) } privoxy_mutex_unlock(&connection_reuse_mutex); } +#endif /* def FEATURE_CONNECTION_SHARING */ break; } } @@ -2493,9 +2596,9 @@ static void serve(struct client_state *csp) if (csp->sfd != JB_INVALID_SOCKET) { -#ifdef FEATURE_CONNECTION_KEEP_ALIVE +#ifdef FEATURE_CONNECTION_SHARING forget_connection(csp->sfd); -#endif /* def FEATURE_CONNECTION_KEEP_ALIVE */ +#endif /* def FEATURE_CONNECTION_SHARING */ close_socket(csp->sfd); } @@ -2729,9 +2832,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; @@ -3245,13 +3348,13 @@ static void listen_loop(void) config = load_config(); -#ifdef FEATURE_CONNECTION_KEEP_ALIVE +#ifdef FEATURE_CONNECTION_SHARING /* * XXX: Should be relocated once it no * longer needs to emit log messages. */ initialize_reusable_connections(); -#endif /* def FEATURE_CONNECTION_KEEP_ALIVE */ +#endif /* def FEATURE_CONNECTION_SHARING */ bfd = bind_port_helper(config);