-const char jcc_rcs[] = "$Id: jcc.c,v 1.252 2009/06/08 16:48:50 fabiankeil Exp $";
+const char jcc_rcs[] = "$Id: jcc.c,v 1.273 2009/07/19 10:04:55 fabiankeil Exp $";
/*********************************************************************
*
* File : $Source: /cvsroot/ijbswa/current/jcc.c,v $
"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"
"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 *);
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;
{
assert(sfd != JB_INVALID_SOCKET);
assert(NULL != http->host);
+
+ server_connection->sfd = sfd;
server_connection->host = strdup(http->host);
if (NULL == server_connection->host)
{
{
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;
}
}
{
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;
}
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)
{
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, "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 /* def FEATURE_CONNECTION_KEEP_ALIVE */
+
return JB_ERR_OK;
}
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);
}
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);
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);
#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
"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;
*/
if (FD_ISSET(csp->sfd, &rfds))
{
+#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);
/* 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;
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))
{
/*
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);
}
#ifdef FEATURE_CONNECTION_KEEP_ALIVE
static int monitor_thread_running = 0;
int continue_chatting = 0;
+ unsigned int latency = 0;
+
do
{
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;
+ }
+
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);
+ && socket_is_still_usable(csp->sfd)
+ && (latency < csp->server_connection.keep_alive_timeout);
if (continue_chatting)
{
+ unsigned int 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 "
{
log_error(LOG_LEVEL_CONNECT,
"No additional client request received in time.");
- if ((csp->config->feature_flags & RUNTIME_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;
(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);
}