-const char jcc_rcs[] = "$Id: jcc.c,v 1.186 2008/09/04 08:13:58 fabiankeil Exp $";
+const char jcc_rcs[] = "$Id: jcc.c,v 1.199 2008/10/26 15:36:10 fabiankeil Exp $";
/*********************************************************************
*
* File : $Source: /cvsroot/ijbswa/current/jcc.c,v $
*
* Revisions :
* $Log: jcc.c,v $
+ * Revision 1.199 2008/10/26 15:36:10 fabiankeil
+ * Remove two debug messages with LOG_LEVEL_INFO.
+ *
+ * Revision 1.198 2008/10/22 15:19:55 fabiankeil
+ * Once More, With Feeling: if there is no logfile
+ * because the user didn't specify one, we shouldn't
+ * call init_error_log() after receiving SIGHUP either.
+ *
+ * Revision 1.197 2008/10/20 17:02:40 fabiankeil
+ * If SIGHUP is received while we aren't running in daemon
+ * mode, calling init_error_log() would be a mistake.
+ *
+ * Revision 1.196 2008/10/16 09:16:41 fabiankeil
+ * - Fix two gcc44 conversion warnings.
+ * - Don't bother logging the last five bytes
+ * of the 0-chunk.
+ *
+ * Revision 1.195 2008/10/13 16:04:37 fabiankeil
+ * Make sure we don't try to reuse tainted server sockets.
+ *
+ * Revision 1.194 2008/10/12 18:35:18 fabiankeil
+ * The last commit was a bit too ambitious, apparently the content
+ * length adjustment is only necessary if we aren't buffering.
+ *
+ * Revision 1.193 2008/10/12 15:57:35 fabiankeil
+ * Fix content length calculation if we read headers
+ * and the start of the body at once. Now that we have
+ * FEATURE_CONNECTION_KEEP_ALIVE, it actually matters.
+ *
+ * Revision 1.192 2008/10/11 18:19:14 fabiankeil
+ * Even more chat() cosmetics.
+ *
+ * Revision 1.191 2008/10/11 18:00:14 fabiankeil
+ * Reformat some comments in chat().
+ *
+ * Revision 1.190 2008/10/11 14:58:00 fabiankeil
+ * In case of chunk-encoded content, stop reading if
+ * the buffer looks like it ends with the last chunk.
+ *
+ * Revision 1.189 2008/10/11 09:53:00 fabiankeil
+ * Let server_response_is_complete() deal properly with
+ * content that is neither buffered nor read all at once.
+ *
+ * Revision 1.188 2008/10/09 18:21:41 fabiankeil
+ * Flush work-in-progress changes to keep outgoing connections
+ * alive where possible. Incomplete and mostly #ifdef'd out.
+ *
+ * Revision 1.187 2008/09/07 12:35:05 fabiankeil
+ * Add mutex lock support for _WIN32.
+ *
* Revision 1.186 2008/09/04 08:13:58 fabiankeil
* Prepare for critical sections on Windows by adding a
* layer of indirection before the pthread mutex functions.
*/
privoxy_mutex_t log_mutex;
privoxy_mutex_t log_init_mutex;
+privoxy_mutex_t connection_reuse_mutex;
#if !defined(HAVE_GETHOSTBYADDR_R) || !defined(HAVE_GETHOSTBYNAME_R)
privoxy_mutex_t resolver_mutex;
}
+#ifdef FEATURE_CONNECTION_KEEP_ALIVE
+/*********************************************************************
+ *
+ * Function : server_response_is_complete
+ *
+ * Description : Determines whether we should stop reading
+ * from the server socket.
+ *
+ * Parameters :
+ * 1 : csp = Current client state (buffers, headers, etc...)
+ * 2 : content_length = Length of content received so far.
+ *
+ * Returns : TRUE if the response is complete,
+ * FALSE otherwise.
+ *
+ *********************************************************************/
+static int server_response_is_complete(struct client_state *csp, size_t content_length)
+{
+ int content_length_known = (csp->flags & CSP_FLAG_CONTENT_LENGTH_SET);
+
+ if (!strcmpic(csp->http->gpc, "HEAD"))
+ {
+ /*
+ * "HEAD" implies no body, we are thus expecting
+ * no content. XXX: incomplete "list" of methods?
+ */
+ csp->expected_content_length = 0;
+ content_length_known = TRUE;
+ }
+
+ if (csp->http->status == 304)
+ {
+ /*
+ * Expect no body. XXX: incomplete "list" of status codes?
+ */
+ csp->expected_content_length = 0;
+ content_length_known = TRUE;
+ }
+
+ return (content_length_known && ((0 == csp->expected_content_length)
+ || (csp->expected_content_length <= content_length)));
+}
+#endif /* FEATURE_CONNECTION_KEEP_ALIVE */
+
+
/*********************************************************************
*
* Function : chat
http->hostport);
}
-
/* Write the answer to the client */
if (rsp != NULL)
{
if (fwd->forward_host || (http->ssl == 0))
{
- /* write the client's (modified) header to the server
+ /*
+ * 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))
{
- log_error(LOG_LEVEL_CONNECT, "write header 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);
-
- if(rsp)
+ if (rsp)
{
send_crunch_response(csp, rsp);
}
/* we're finished with the client's header */
freez(hdr);
- maxfd = ( csp->cfd > csp->sfd ) ? csp->cfd : csp->sfd;
+ maxfd = (csp->cfd > csp->sfd) ? csp->cfd : csp->sfd;
/* pass data between the client and server
* until one or the other shuts down the connection.
FD_SET(csp->cfd, &rfds);
FD_SET(csp->sfd, &rfds);
+#ifdef FEATURE_CONNECTION_KEEP_ALIVE
+ if (server_body && server_response_is_complete(csp, byte_count))
+ {
+ log_error(LOG_LEVEL_CONNECT,
+ "Done reading from server. Expected content length: %d. "
+ "Actual content length: %d. Most recently received: %d.",
+ csp->expected_content_length, byte_count, len);
+ len = 0;
+ /*
+ * XXX: should not jump around,
+ * chat() is complicated enough already.
+ */
+ goto reading_done;
+ }
+#endif /* FEATURE_CONNECTION_KEEP_ALIVE */
+
n = select((int)maxfd+1, &rfds, NULL, NULL, NULL);
if (n < 0)
{
log_error(LOG_LEVEL_ERROR, "select() failed!: %E");
- return;
+ break;
}
- /* this is the body of the browser's request
- * just read it and write it.
+ /*
+ * This is the body of the browser's request,
+ * just read and write it.
*/
-
if (FD_ISSET(csp->cfd, &rfds))
{
len = read_socket(csp->cfd, buf, sizeof(buf) - 1);
if (write_socket(csp->sfd, buf, (size_t)len))
{
log_error(LOG_LEVEL_ERROR, "write to: %s failed: %E", http->host);
- return;
+ break;
}
continue;
}
/*
- * The server wants to talk. It could be the header or the body.
+ * The server wants to talk. It could be the header or the body.
* 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))
{
- fflush( 0 );
+ fflush(0);
len = read_socket(csp->sfd, buf, sizeof(buf) - 1);
if (len < 0)
*/
log_error(LOG_LEVEL_ERROR, "Already forwarded the original headers. "
"Unable to tell the client about the problem.");
- return;
+ break;
}
rsp = error_response(csp, "connect-failed", errno);
-
if (rsp)
{
send_crunch_response(csp, rsp);
return;
}
- /* Add a trailing zero. This lets filter_popups
- * use string operations.
+#ifdef FEATURE_CONNECTION_KEEP_ALIVE
+ if (csp->flags & CSP_FLAG_CHUNKED)
+ {
+ if ((len > 5) && !memcmp(buf+len-5, "0\r\n\r\n", 5))
+ {
+ /* XXX: this is a temporary hack */
+ log_error(LOG_LEVEL_CONNECT,
+ "Looks like we reached the end of the last chunk. "
+ "We better stop reading.");
+ csp->expected_content_length = byte_count + (size_t)len;
+ csp->flags |= CSP_FLAG_CONTENT_LENGTH_SET;
+ }
+ }
+ reading_done:
+#endif /* FEATURE_CONNECTION_KEEP_ALIVE */
+
+ /*
+ * Add a trailing zero to let be able to use string operations.
+ * XXX: do we still need this with filter_popups gone?
*/
buf[len] = '\0';
- /* Normally, this would indicate that we've read
+ /*
+ * Normally, this would indicate that we've read
* as much as the server has sent us and we can
* close the client connection. However, Microsoft
* in its wisdom has released IIS/5 with a bug that
log_error(LOG_LEVEL_ERROR, "write modified content to client failed: %E");
freez(hdr);
freez(p);
- return;
+ break;
}
freez(hdr);
* of the server document, just write it to the client,
* unless we need to buffer the body for later content-filtering
*/
-
if (server_body || http->ssl)
{
if (content_filter)
size_t hdrlen;
int flushed;
- log_error(LOG_LEVEL_INFO, "Flushing header and buffers. Stepping back from filtering.");
+ log_error(LOG_LEVEL_INFO,
+ "Flushing header and buffers. Stepping back from filtering.");
hdr = list_to_text(csp->headers);
if (hdr == NULL)
log_error(LOG_LEVEL_ERROR, "Out of memory while trying to flush.");
rsp = cgi_error_memory();
send_crunch_response(csp, rsp);
-
- return;
+ break;
}
hdrlen = strlen(hdr);
|| ((flushed = flush_socket(csp->cfd, csp->iob)) < 0)
|| (write_socket(csp->cfd, buf, (size_t)len)))
{
- log_error(LOG_LEVEL_CONNECT, "Flush header and buffers to client failed: %E");
-
+ log_error(LOG_LEVEL_CONNECT,
+ "Flush header and buffers to client failed: %E");
freez(hdr);
- return;
+ break;
}
/*
if (write_socket(csp->cfd, buf, (size_t)len))
{
log_error(LOG_LEVEL_ERROR, "write to client failed: %E");
- return;
+ break;
}
}
byte_count += (size_t)len;
}
else
{
- /* we're still looking for the end of the
- * server's header ... (does that make header
- * parsing an "out of body experience" ?
- */
-
- /*
- * buffer up the data we just read. If that fails,
- * there's little we can do but send our static
- * out-of-memory page.
+ 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
+ * little we can do but send our static out-of-memory page.
*/
if (add_to_iob(csp, buf, len))
{
log_error(LOG_LEVEL_ERROR, "Out of memory while looking for end of server headers.");
rsp = cgi_error_memory();
send_crunch_response(csp, rsp);
-
- return;
+ break;
}
+ header_start = csp->iob->cur;
+
/* Convert iob into something sed() can digest */
if (JB_ERR_PARSE == get_server_headers(csp))
{
if (ms_iis5_hack)
{
- /* Well, we tried our MS IIS/5
- * hack and it didn't work.
- * The header is incomplete
- * and there isn't anything
+ /*
+ * Well, we tried our MS IIS/5 hack and it didn't work.
+ * The header is incomplete and there isn't anything
* we can do about it.
*/
log_error(LOG_LEVEL_INFO,
}
else
{
- /* Since we have to wait for
- * more from the server before
- * we can parse the headers
- * we just continue here.
+ /*
+ * Since we have to wait for more from the server before
+ * we can parse the headers we just continue here.
*/
continue;
}
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));
free_http_request(http);
- return;
+ break;
}
assert(csp->headers->first->str);
write_socket(csp->cfd, INVALID_SERVER_HEADERS_RESPONSE,
strlen(INVALID_SERVER_HEADERS_RESPONSE));
free_http_request(http);
- return;
+ break;
}
/*
* and are done here after cleaning up.
*/
freez(hdr);
- return;
+ break;
}
/* Buffer and pcrs filter this if appropriate. */
*/
if (!content_filter)
{
- /* write the server's (modified) header to
+ /*
+ * Write the server's (modified) header to
* the client (along with anything else that
* may be in the buffer)
*/
{
log_error(LOG_LEVEL_CONNECT, "write header to client failed: %E");
- /* the write failed, so don't bother
- * mentioning it to the client...
- * it probably can't hear us anyway.
+ /*
+ * The write failed, so don't bother mentioning it
+ * to the client... it probably can't hear us anyway.
*/
freez(hdr);
- return;
+ break;
}
byte_count += (size_t)len;
}
+ else
+ {
+ /*
+ * XXX: the header lenght should probably
+ * be calculated by get_server_headers().
+ */
+ int header_length = csp->iob->cur - header_start;
+ assert(csp->iob->cur > header_start);
+ byte_count += (size_t)(len - header_length);
+ }
/* we're finished with the server's header */
freez(hdr);
server_body = 1;
- /* If this was a MS IIS/5 hack then it means
- * the server has already closed the
- * connection. Nothing more to read. Time
- * to bail.
+ /*
+ * If this was a MS IIS/5 hack then it means the server
+ * has already closed the connection. Nothing more to read.
+ * Time to bail.
*/
if (ms_iis5_hack)
{
}
continue;
}
-
- return; /* huh? we should never get here */
+ /*
+ * If we reach this point, the server socket is tainted
+ * (most likely because we didn't read everything the
+ * server sent us) and reusing it would lead to garbage.
+ */
+ 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;
+ }
+ return;
}
if (csp->content_length == 0)
{
/*
- * If Privoxy didn't recalculate the
- * Content-Lenght, byte_count is still
- * correct.
+ * If Privoxy didn't recalculate the Content-Lenght,
+ * byte_count is still correct.
*/
csp->content_length = byte_count;
}
if (csp->sfd != JB_INVALID_SOCKET)
{
+#ifdef FEATURE_CONNECTION_KEEP_ALIVE
+ if ((csp->flags & CSP_FLAG_SERVER_CONNECTION_KEEP_ALIVE))
+ {
+ remember_connection(csp->sfd, csp->http, forward_url(csp, csp->http));
+ }
+ else
+ {
+ forget_connection(csp->sfd);
+ close_socket(csp->sfd);
+ }
+#else
close_socket(csp->sfd);
+#endif /* def FEATURE_CONNECTION_KEEP_ALIVE */
}
csp->flags &= ~CSP_FLAG_ACTIVE;
* Prepare global mutex semaphores
*/
privoxy_mutex_init(&log_mutex);
-
privoxy_mutex_init(&log_init_mutex);
+ privoxy_mutex_init(&connection_reuse_mutex);
/*
* XXX: The assumptions below are a bit naive
config = load_config();
+#ifdef FEATURE_CONNECTION_KEEP_ALIVE
+ /*
+ * XXX: Should be relocated once it no
+ * longer needs to emit log messages.
+ */
+ initialize_reusable_connections();
+#endif /* def FEATURE_CONNECTION_KEEP_ALIVE */
+
bfd = bind_port_helper(config);
#ifdef FEATURE_GRACEFUL_TERMINATION
*/
if (received_hup_signal)
{
- init_error_log(Argv[0], config->logfile);
+ if (NULL != config->logfile)
+ {
+ init_error_log(Argv[0], config->logfile);
+ }
received_hup_signal = 0;
}
#endif