X-Git-Url: http://www.privoxy.org/gitweb/?a=blobdiff_plain;f=jcc.c;h=ea6f99d5bfb092db75f7a85d7d6f05b239e0ba12;hb=29eb66c766d9e2168d5fd67f4823e8f87a574af9;hp=c5a8a4a817dba0f420dddc6597ff3af77c7f036d;hpb=07b1101cc1ed28618e07eda179eaab84715a9399;p=privoxy.git diff --git a/jcc.c b/jcc.c index c5a8a4a8..ea6f99d5 100644 --- a/jcc.c +++ b/jcc.c @@ -1,4 +1,4 @@ -const char jcc_rcs[] = "$Id: jcc.c,v 1.250 2009/05/16 13:27:20 fabiankeil Exp $"; +const char jcc_rcs[] = "$Id: jcc.c,v 1.266 2009/07/11 14:39:34 fabiankeil Exp $"; /********************************************************************* * * File : $Source: /cvsroot/ijbswa/current/jcc.c,v $ @@ -236,15 +236,6 @@ static const char MISSING_DESTINATION_RESPONSE[] = "Connection: close\r\n\r\n" "Bad request. Privoxy was unable to extract the destination.\r\n"; -/* XXX: should be a template */ -static const char NO_SERVER_DATA_RESPONSE[] = - "HTTP/1.0 502 Server or forwarder response empty\r\n" - "Proxy-Agent: Privoxy " VERSION "\r\n" - "Content-Type: text/plain\r\n" - "Connection: close\r\n\r\n" - "Empty server or forwarder response.\r\n" - "The connection has been closed but Privoxy didn't receive any data.\r\n"; - /* XXX: should be a template */ static const char INVALID_SERVER_HEADERS_RESPONSE[] = "HTTP/1.0 502 Server or forwarder response invalid\r\n" @@ -278,13 +269,12 @@ static const char TOO_MANY_CONNECTIONS_RESPONSE[] = "Connection: close\r\n\r\n" "Maximum number of open connections reached.\r\n"; -/* XXX: should be a template */ -static const char CONNECTION_TIMEOUT_RESPONSE[] = +static const char CLIENT_CONNECTION_TIMEOUT_RESPONSE[] = "HTTP/1.0 504 Connection timeout\r\n" "Proxy-Agent: Privoxy " VERSION "\r\n" "Content-Type: text/plain\r\n" "Connection: close\r\n\r\n" - "The connection timed out.\r\n"; + "The connection timed out because the client request didn't arrive in time.\r\n"; /* A function to crunch a response */ typedef struct http_response *(*crunch_func_ptr)(struct client_state *); @@ -664,6 +654,12 @@ static const char *crunch_reason(const struct http_response *rsp) case RSP_REASON_OUT_OF_MEMORY: reason = "Out of memory (may mask other reasons)"; break; + case RSP_REASON_CONNECTION_TIMEOUT: + reason = "Connection timeout"; + break; + case RSP_REASON_NO_SERVER_DATA: + reason = "No server data received"; + break; default: reason = "No reason recorded"; break; @@ -1081,6 +1077,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) { @@ -1145,8 +1143,9 @@ static void mark_server_socket_tainted(struct client_state *csp) { if ((csp->flags & CSP_FLAG_SERVER_CONNECTION_KEEP_ALIVE)) { - log_error(LOG_LEVEL_CONNECT, "Unsetting keep-alive flag."); - csp->flags &= ~CSP_FLAG_SERVER_CONNECTION_KEEP_ALIVE; + log_error(LOG_LEVEL_CONNECT, + "Marking the server socket %d tainted.", csp->sfd); + csp->flags |= CSP_FLAG_SERVER_SOCKET_TAINTED; } } @@ -1176,8 +1175,8 @@ static char *get_request_line(struct client_state *csp) { log_error(LOG_LEVEL_ERROR, "Stopped waiting for the request line."); - write_socket(csp->cfd, CONNECTION_TIMEOUT_RESPONSE, - strlen(CONNECTION_TIMEOUT_RESPONSE)); + write_socket(csp->cfd, CLIENT_CONNECTION_TIMEOUT_RESPONSE, + strlen(CLIENT_CONNECTION_TIMEOUT_RESPONSE)); return NULL; } @@ -1307,6 +1306,7 @@ static jb_err receive_client_request(struct client_state *csp) { log_error(LOG_LEVEL_ERROR, "Stopped grabbing the client headers."); + destroy_list(headers); return JB_ERR_PARSE; } @@ -1417,6 +1417,15 @@ 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 ((!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) { @@ -1445,6 +1454,39 @@ static jb_err parse_client_request(struct client_state *csp) return JB_ERR_PARSE; } +#ifdef FEATURE_CONNECTION_KEEP_ALIVE + if ((csp->flags & CSP_FLAG_CLIENT_CONNECTION_KEEP_ALIVE)) + { + 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."); + } + } +#endif /* def FEATURE_CONNECTION_KEEP_ALIVE */ + return JB_ERR_OK; } @@ -1634,15 +1676,15 @@ static void chat(struct client_state *csp) if (fwd->type != SOCKS_NONE) { /* Socks error. */ - rsp = error_response(csp, "forwarding-failed", errno); + rsp = error_response(csp, "forwarding-failed"); } else if (errno == EINVAL) { - rsp = error_response(csp, "no-such-domain", errno); + rsp = error_response(csp, "no-such-domain"); } else { - rsp = error_response(csp, "connect-failed", errno); + rsp = error_response(csp, "connect-failed"); log_error(LOG_LEVEL_CONNECT, "connect to: %s failed: %E", http->hostport); } @@ -1681,7 +1723,7 @@ static void chat(struct client_state *csp) log_error(LOG_LEVEL_CONNECT, "write header to: %s failed: %E", http->hostport); - rsp = error_response(csp, "connect-failed", errno); + rsp = error_response(csp, "connect-failed"); if (rsp) { send_crunch_response(csp, rsp); @@ -1730,7 +1772,17 @@ static void chat(struct client_state *csp) #else FD_ZERO(&rfds); #endif - FD_SET(csp->cfd, &rfds); +#ifdef FEATURE_CONNECTION_KEEP_ALIVE + if ((csp->flags & CSP_FLAG_CLIENT_REQUEST_COMPLETELY_READ)) + { + maxfd = csp->sfd; + } + else +#endif /* def FEATURE_CONNECTION_KEEP_ALIVE */ + { + FD_SET(csp->cfd, &rfds); + } + FD_SET(csp->sfd, &rfds); #ifdef FEATURE_CONNECTION_KEEP_ALIVE @@ -1771,8 +1823,7 @@ static void chat(struct client_state *csp) "Didn't receive data in time: %s", http->url); if ((byte_count == 0) && (http->ssl == 0)) { - write_socket(csp->cfd, CONNECTION_TIMEOUT_RESPONSE, - strlen(CONNECTION_TIMEOUT_RESPONSE)); + send_crunch_response(csp, error_response(csp, "connection-timeout")); } mark_server_socket_tainted(csp); return; @@ -1818,7 +1869,17 @@ static void chat(struct client_state *csp) */ if (FD_ISSET(csp->sfd, &rfds)) { - fflush(0); +#ifdef FEATURE_CONNECTION_KEEP_ALIVE + if (!socket_is_still_usable(csp->cfd)) + { + 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 FEATURE_CONNECTION_KEEP_ALIVE */ + + fflush(NULL); len = read_socket(csp->sfd, buf, sizeof(buf) - 1); if (len < 0) @@ -2095,9 +2156,10 @@ 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."); + 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); - write_socket(csp->cfd, NO_SERVER_DATA_RESPONSE, strlen(NO_SERVER_DATA_RESPONSE)); + send_crunch_response(csp, error_response(csp, "no-server-data")); free_http_request(http); mark_server_socket_tainted(csp); return; @@ -2128,6 +2190,8 @@ static void chat(struct client_state *csp) return; } + csp->server_connection.timestamp = time(NULL); + /* * We have now received the entire server header, * filter it and send the result to the client @@ -2283,6 +2347,7 @@ static void serve(struct client_state *csp) 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); @@ -2330,9 +2395,7 @@ static void serve(struct client_state *csp) "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->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; @@ -3072,7 +3135,7 @@ static jb_socket bind_port_helper(struct configuration_spec * config) (NULL != config->haddr) ? config->haddr : "INADDR_ANY", config->hport); default : - log_error(LOG_LEVEL_FATAL, "can't bind to %s:%d: because %E", + log_error(LOG_LEVEL_FATAL, "can't bind to %s:%d: %E", (NULL != config->haddr) ? config->haddr : "INADDR_ANY", config->hport); }