X-Git-Url: http://www.privoxy.org/gitweb/?a=blobdiff_plain;f=jcc.c;h=f6e1c9be17a833ee79932d3ae2da7d184fb21b49;hb=0f043956ec83b1a007cf219a30be4b857d051ee4;hp=58fe9a031904ed1a3eb660d6c614d6886bb39cc5;hpb=d59c368d0ecf1514f0e177bee5ba62a665a367c0;p=privoxy.git diff --git a/jcc.c b/jcc.c index 58fe9a03..f6e1c9be 100644 --- a/jcc.c +++ b/jcc.c @@ -1,4 +1,4 @@ -const char jcc_rcs[] = "$Id: jcc.c,v 1.260 2009/07/05 12:00:09 fabiankeil Exp $"; +const char jcc_rcs[] = "$Id: jcc.c,v 1.291 2009/09/12 12:35:14 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 */ /********************************************************************* @@ -1077,6 +1079,8 @@ void save_connection_destination(jb_socket sfd, { assert(sfd != JB_INVALID_SOCKET); assert(NULL != http->host); + + server_connection->sfd = sfd; server_connection->host = strdup(http->host); if (NULL == server_connection->host) { @@ -1120,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 */ @@ -1139,10 +1231,11 @@ 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 connection to the server tainted."); + "Marking the server socket %d tainted.", csp->sfd); csp->flags |= CSP_FLAG_SERVER_SOCKET_TAINTED; } } @@ -1415,6 +1508,16 @@ static jb_err parse_client_request(struct client_state *csp) struct http_request *http = csp->http; jb_err err; +#ifdef FEATURE_CONNECTION_KEEP_ALIVE + if ((csp->config->feature_flags & RUNTIME_FEATURE_CONNECTION_KEEP_ALIVE) + && (!strcmpic(csp->http->ver, "HTTP/1.1")) + && (csp->http->ssl == 0)) + { + /* Assume persistence until further notice */ + csp->flags |= CSP_FLAG_CLIENT_CONNECTION_KEEP_ALIVE; + } +#endif /* def FEATURE_CONNECTION_KEEP_ALIVE */ + err = sed(csp, FILTER_CLIENT_HEADERS); if (JB_ERR_OK != err) { @@ -1444,35 +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->flags |= CSP_FLAG_SERVER_SOCKET_TAINTED; - if (!strcmpic(csp->http->gpc, "POST")) - { - /* XXX: this is an incomplete hack */ - csp->flags &= ~CSP_FLAG_CLIENT_REQUEST_COMPLETELY_READ; - log_error(LOG_LEVEL_CONNECT, - "POST request detected. 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 */ @@ -1523,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)); @@ -1632,7 +1710,6 @@ static void chat(struct client_state *csp) #ifdef FEATURE_CONNECTION_KEEP_ALIVE if ((csp->sfd != JB_INVALID_SOCKET) - && !(csp->flags & CSP_FLAG_SERVER_SOCKET_TAINTED) && socket_is_still_usable(csp->sfd) && connection_destination_matches(&csp->server_connection, http, fwd)) { @@ -1740,6 +1817,8 @@ static void chat(struct client_state *csp) log_error(LOG_LEVEL_CONNECT, "to %s successful", http->hostport); + csp->server_connection.request_sent = time(NULL); + /* we're finished with the client's header */ freez(hdr); @@ -1763,7 +1842,7 @@ 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; } @@ -1790,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, @@ -1834,7 +1923,49 @@ static void chat(struct client_state *csp) */ if (FD_ISSET(csp->cfd, &rfds)) { - len = read_socket(csp->cfd, buf, 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 = (int)csp->expected_client_content_length; + } + log_error(LOG_LEVEL_CONNECT, + "Waiting for up to %d bytes from the client.", + max_bytes_to_read); + } + assert(max_bytes_to_read < sizeof(buf)); +#endif /* def FEATURE_CONNECTION_KEEP_ALIVE */ + + len = read_socket(csp->cfd, buf, max_bytes_to_read); if (len <= 0) { @@ -1843,6 +1974,24 @@ static void chat(struct client_state *csp) break; /* "game over, man" */ } +#ifdef FEATURE_CONNECTION_KEEP_ALIVE + if (csp->expected_client_content_length != 0) + { + assert(len <= max_bytes_to_read); + 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 */ + if (write_socket(csp->sfd, buf, (size_t)len)) { log_error(LOG_LEVEL_ERROR, "write to: %s failed: %E", http->host); @@ -1859,6 +2008,21 @@ static void chat(struct client_state *csp) */ if (FD_ISSET(csp->sfd, &rfds)) { +#ifdef FEATURE_CONNECTION_KEEP_ALIVE + if (!socket_is_still_usable(csp->cfd)) + { +#ifdef _WIN32 + log_error(LOG_LEVEL_CONNECT, + "The server still wants to talk, but the client may already have hung up on us."); +#else + log_error(LOG_LEVEL_CONNECT, + "The server still wants to talk, but the client hung up on us."); + mark_server_socket_tainted(csp); + return; +#endif /* def _WIN32 */ + } +#endif /* def FEATURE_CONNECTION_KEEP_ALIVE */ + fflush(NULL); len = read_socket(csp->sfd, buf, sizeof(buf) - 1); @@ -2081,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 @@ -2096,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)) { @@ -2123,20 +2284,27 @@ 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) { - log_error(LOG_LEVEL_ERROR, "Empty server or forwarder response."); + log_error(LOG_LEVEL_ERROR, + "Empty server or forwarder response received on socket %d.", csp->sfd); 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); @@ -2184,6 +2352,8 @@ static void chat(struct client_state *csp) log_error(LOG_LEVEL_FATAL, "Out of memory parsing server header"); } + csp->server_connection.response_received = time(NULL); + if (crunch_response_triggered(csp, crunchers_light)) { /* @@ -2226,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 */ @@ -2291,6 +2449,8 @@ static void chat(struct client_state *csp) log_error(LOG_LEVEL_CLF, "%s - - [%T] \"%s\" 200 %llu", csp->ip_addr_str, http->ocmd, csp->content_length); + + csp->server_connection.timestamp = time(NULL); } @@ -2315,8 +2475,12 @@ 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 { chat(csp); @@ -2327,17 +2491,29 @@ static void serve(struct client_state *csp) && !(csp->flags & CSP_FLAG_SERVER_SOCKET_TAINTED) && (csp->cfd != JB_INVALID_SOCKET) && (csp->sfd != JB_INVALID_SOCKET) - && socket_is_still_usable(csp->sfd); + && socket_is_still_usable(csp->sfd) + && (latency < csp->server_connection.keep_alive_timeout); if (continue_chatting) { + 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.", csp->sfd, csp->server_connection.host); if ((csp->flags & CSP_FLAG_CLIENT_CONNECTION_KEEP_ALIVE) - && data_is_available(csp->cfd, (int)csp->server_connection.keep_alive_timeout) + && data_is_available(csp->cfd, (int)client_timeout) && socket_is_still_usable(csp->cfd)) { log_error(LOG_LEVEL_CONNECT, "Client request arrived in " @@ -2349,6 +2525,7 @@ static void serve(struct client_state *csp) csp->content_type = 0; csp->content_length = 0; csp->expected_content_length = 0; + csp->expected_client_content_length = 0; list_remove_all(csp->headers); freez(csp->iob->buf); memset(csp->iob, 0, sizeof(csp->iob)); @@ -2370,11 +2547,11 @@ static void serve(struct client_state *csp) { log_error(LOG_LEVEL_CONNECT, "No additional client request received in time."); - if ((csp->config->feature_flags & RUNTIME_FEATURE_CONNECTION_SHARING)) +#ifdef FEATURE_CONNECTION_SHARING + if ((csp->config->feature_flags & RUNTIME_FEATURE_CONNECTION_SHARING) + && (socket_is_still_usable(csp->sfd))) { - remember_connection(csp->sfd, csp->http, - forward_url(csp, csp->http), - csp->server_connection.keep_alive_timeout); + remember_connection(csp, forward_url(csp, csp->http)); csp->sfd = JB_INVALID_SOCKET; close_socket(csp->cfd); csp->cfd = JB_INVALID_SOCKET; @@ -2389,6 +2566,7 @@ static void serve(struct client_state *csp) } privoxy_mutex_unlock(&connection_reuse_mutex); } +#endif /* def FEATURE_CONNECTION_SHARING */ break; } } @@ -2407,9 +2585,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); } @@ -2643,9 +2821,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; @@ -3159,13 +3337,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);