+ /*
+ * This is the body of the browser's request,
+ * just read and write it.
+ *
+ * Receives data from browser and sends it to server
+ *
+ * XXX: Make sure the client doesn't use pipelining
+ * behind Privoxy's back.
+ */
+#ifdef HAVE_POLL
+ if ((poll_fds[0].revents & (POLLERR|POLLHUP|POLLNVAL)) != 0)
+ {
+ log_error(LOG_LEVEL_CONNECT,
+ "The client socket %d has become unusable while "
+ "the server socket %d is still open.",
+ csp->cfd, csp->server_connection.sfd);
+ mark_server_socket_tainted(csp);
+ break;
+ }
+
+ if (poll_fds[0].revents != 0)
+#else
+ if (FD_ISSET(csp->cfd, &rfds))
+#endif /* def HAVE_POLL*/
+ {
+ int max_bytes_to_read = (int)csp->receive_buffer_size;
+
+#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,
+ "Stop watching client socket %d. "
+ "There's already another request waiting.",
+ csp->cfd);
+ 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->server_connection.sfd);
+ mark_server_socket_tainted(csp);
+ break;
+ }
+ if (csp->expected_client_content_length != 0)
+ {
+ if (csp->expected_client_content_length < csp->receive_buffer_size)
+ {
+ 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 <= csp->receive_buffer_size);
+#endif /* def FEATURE_CONNECTION_KEEP_ALIVE */
+
+#ifdef FEATURE_HTTPS_INSPECTION
+ if (client_use_ssl(csp))
+ {
+ log_error(LOG_LEVEL_CONNECT, "Breaking with TLS/SSL.");
+ break;
+ }
+ else
+#endif /* def FEATURE_HTTPS_INSPECTION */
+ {
+ len = read_socket(csp->cfd, csp->receive_buffer, max_bytes_to_read);
+
+ if (len <= 0)
+ {
+ /* XXX: not sure if this is necessary. */
+ mark_server_socket_tainted(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->server_connection.sfd, csp->receive_buffer, (size_t)len))
+ {
+ log_error(LOG_LEVEL_ERROR, "write to: %s failed: %E", http->host);
+ mark_server_socket_tainted(csp);
+ return;
+ }
+ }
+ continue;
+ }
+
+ /*
+ * 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.
+ */
+#ifdef HAVE_POLL
+ if (poll_fds[1].revents != 0)
+#else
+ if (FD_ISSET(csp->server_connection.sfd, &rfds))
+#endif /* HAVE_POLL */
+ {
+#ifdef FEATURE_CONNECTION_KEEP_ALIVE
+ /*
+ * If we are buffering content, we don't want to eat up to
+ * buffer-limit bytes if the client no longer cares about them.
+ * If we aren't buffering, however, a dead client socket will be
+ * noticed pretty much right away anyway, so we can reduce the
+ * overhead by skipping the check.
+ */
+ if (buffer_and_filter_content && !socket_is_still_alive(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);
+#ifdef FEATURE_HTTPS_INSPECTION
+ close_client_and_server_ssl_connections(csp);
+#endif
+ return;
+#endif /* def _WIN32 */
+ }
+#endif /* def FEATURE_CONNECTION_KEEP_ALIVE */
+
+#ifdef FEATURE_HTTPS_INSPECTION
+ /*
+ * Reading data from standard or secured connection (HTTP/HTTPS)
+ */
+ if (server_use_ssl(csp))
+ {
+ len = ssl_recv_data(&(csp->ssl_server_attr),
+ (unsigned char *)csp->receive_buffer, csp->receive_buffer_size);
+ }
+ else
+#endif
+ {
+ len = read_socket(csp->server_connection.sfd, csp->receive_buffer,
+ (int)csp->receive_buffer_size);
+ }
+
+ if (len < 0)
+ {
+ log_error(LOG_LEVEL_ERROR, "read from: %s failed: %E", http->host);
+
+ if ((http->ssl && (csp->fwd == NULL))
+#ifdef FEATURE_HTTPS_INSPECTION
+ && use_ssl_tunnel
+#endif
+ )
+ {
+ /*
+ * Just hang up. We already confirmed the client's CONNECT
+ * request with status code 200 and unencrypted content is
+ * no longer welcome.
+ */
+ log_error(LOG_LEVEL_ERROR,
+ "CONNECT already confirmed. Unable to tell the client about the problem.");
+ return;
+ }
+ else if (byte_count)
+ {
+ /*
+ * Just hang up. We already transmitted the original headers
+ * and parts of the original content and therefore missed the
+ * chance to send an error message (without risking data corruption).
+ *
+ * XXX: we could retry with a fancy range request here.
+ */
+ log_error(LOG_LEVEL_ERROR, "Already forwarded the original headers. "
+ "Unable to tell the client about the problem.");
+ mark_server_socket_tainted(csp);
+#ifdef FEATURE_HTTPS_INSPECTION
+ close_client_and_server_ssl_connections(csp);
+#endif
+ return;
+ }
+ /*
+ * XXX: Consider handling the cases above the same.
+ */
+ mark_server_socket_tainted(csp);
+ len = 0;
+ }
+
+#ifdef FEATURE_CONNECTION_KEEP_ALIVE
+ if (csp->flags & CSP_FLAG_CHUNKED)
+ {
+ if ((len >= 5) && !memcmp(csp->receive_buffer+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 + (unsigned long long)len;
+ csp->flags |= CSP_FLAG_CONTENT_LENGTH_SET;
+ }
+ }
+ reading_done:
+#endif /* FEATURE_CONNECTION_KEEP_ALIVE */
+
+ /*
+ * This is guaranteed by allocating with zalloc_or_die()
+ * and never (intentionally) writing to the last byte.
+ *
+ * csp->receive_buffer_size is the size of the part of the
+ * buffer we intentionally write to, but we actually
+ * allocated csp->receive_buffer_size+1 bytes so the assertion
+ * stays within the allocated range.
+ */
+ assert(csp->receive_buffer[csp->receive_buffer_size] == '\0');
+
+ /*
+ * Add a trailing zero to let be able to use string operations.
+ * XXX: do we still need this with filter_popups gone?
+ */
+ assert(len <= csp->receive_buffer_size);
+ csp->receive_buffer[len] = '\0';
+
+ /*
+ * 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
+ * prevents it from sending the trailing \r\n in
+ * a 302 redirect header (and possibly other headers).
+ * To work around this if we've haven't parsed
+ * a full header we'll append a trailing \r\n
+ * and see if this now generates a valid one.
+ *
+ * This hack shouldn't have any impacts. If we've
+ * already transmitted the header or if this is a
+ * SSL connection, then we won't bother with this
+ * hack. So we only work on partially received
+ * headers. If we append a \r\n and this still
+ * doesn't generate a valid header, then we won't
+ * transmit anything to the client.
+ */
+ if (len == 0)
+ {
+
+ if (server_body || (http->ssl
+#ifdef FEATURE_HTTPS_INSPECTION
+ && use_ssl_tunnel
+#endif
+ ))
+ {
+ /*
+ * If we have been buffering up the document,
+ * now is the time to apply content modification
+ * and send the result to the client.
+ */
+ if (buffer_and_filter_content)
+ {
+ p = execute_content_filters(csp);
+ /*
+ * If content filtering fails, use the original
+ * buffer and length.
+ * (see p != NULL ? p : csp->iob->cur below)
+ */
+ if (NULL == p)
+ {
+ csp->content_length = (size_t)(csp->iob->eod - csp->iob->cur);
+ }
+#ifdef FEATURE_COMPRESSION
+ else if ((csp->flags & CSP_FLAG_CLIENT_SUPPORTS_DEFLATE)
+ && (csp->content_length > LOWER_LENGTH_LIMIT_FOR_COMPRESSION))
+ {
+ char *compressed_content = compress_buffer(p,
+ (size_t *)&csp->content_length, csp->config->compression_level);
+ if (compressed_content != NULL)
+ {
+ freez(p);
+ p = compressed_content;
+ csp->flags |= CSP_FLAG_BUFFERED_CONTENT_DEFLATED;
+ }
+ }
+#endif
+
+ if (JB_ERR_OK != update_server_headers(csp))
+ {
+ log_error(LOG_LEVEL_FATAL,
+ "Failed to update server headers. after filtering.");
+ }
+
+ hdr = list_to_text(csp->headers);
+ if (hdr == NULL)
+ {
+ /* FIXME Should handle error properly */
+ log_error(LOG_LEVEL_FATAL, "Out of memory parsing server header");
+ }
+
+#ifdef FEATURE_HTTPS_INSPECTION
+ /*
+ * Sending data with standard or secured connection (HTTP/HTTPS)
+ */
+ if (client_use_ssl(csp))
+ {
+ if ((ssl_send_data_delayed(&(csp->ssl_client_attr),
+ (const unsigned char *)hdr, strlen(hdr),
+ get_write_delay(csp)) < 0)
+ || (ssl_send_data_delayed(&(csp->ssl_client_attr),
+ (const unsigned char *) ((p != NULL) ? p : csp->iob->cur),
+ csp->content_length, get_write_delay(csp)) < 0))
+ {
+ log_error(LOG_LEVEL_ERROR, "write modified content to "
+ "client over TLS/SSL failed");
+ freez(hdr);
+ freez(p);
+ mark_server_socket_tainted(csp);
+ close_client_and_server_ssl_connections(csp);
+ return;
+ }
+ }
+ else
+#endif /* def FEATURE_HTTPS_INSPECTION */
+ {
+ if (write_socket_delayed(csp->cfd, hdr, strlen(hdr), write_delay)
+ || write_socket_delayed(csp->cfd, ((p != NULL) ? p : csp->iob->cur),
+ (size_t)csp->content_length, write_delay))
+ {
+ log_error(LOG_LEVEL_ERROR, "write modified content to client failed: %E");
+ freez(hdr);
+ freez(p);
+ mark_server_socket_tainted(csp);
+ return;
+ }
+ }
+
+ freez(hdr);
+ freez(p);
+ }
+
+ break; /* "game over, man" */
+ }
+
+ /*
+ * This is NOT the body, so
+ * Let's pretend the server just sent us a blank line.
+ */
+ snprintf(csp->receive_buffer, csp->receive_buffer_size, "\r\n");
+ len = (int)strlen(csp->receive_buffer);
+
+ /*
+ * Now, let the normal header parsing algorithm below do its
+ * job. If it fails, we'll exit instead of continuing.
+ */
+
+ ms_iis5_hack = 1;
+ }
+
+ /*
+ * If we're in the body 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
+#ifdef FEATURE_HTTPS_INSPECTION
+ && use_ssl_tunnel
+#endif
+ ))
+ {
+ if (buffer_and_filter_content)
+ {
+ /*
+ * If there is no memory left for buffering the content, or the buffer limit
+ * has been reached, switch to non-filtering mode, i.e. make & write the
+ * header, flush the iob and buf, and get out of the way.
+ */
+ if (add_to_iob(csp->iob, csp->config->buffer_limit, csp->receive_buffer, len))
+ {
+ size_t hdrlen;
+ long flushed;
+
+ log_error(LOG_LEVEL_INFO,
+ "Flushing header and buffers. Stepping back from filtering.");
+
+ hdr = list_to_text(csp->headers);
+ if (hdr == NULL)
+ {
+ /*
+ * Memory is too tight to even generate the header.
+ * Send our static "Out-of-memory" page.
+ */
+ log_error(LOG_LEVEL_ERROR, "Out of memory while trying to flush.");
+ rsp = cgi_error_memory();
+ send_crunch_response(csp, rsp);
+ mark_server_socket_tainted(csp);
+#ifdef FEATURE_HTTPS_INSPECTION
+ close_client_and_server_ssl_connections(csp);
+#endif
+ return;
+ }
+ hdrlen = strlen(hdr);
+
+#ifdef FEATURE_HTTPS_INSPECTION
+ /*
+ * Sending data with standard or secured connection (HTTP/HTTPS)
+ */
+ if (client_use_ssl(csp))
+ {
+ if ((ssl_send_data_delayed(&(csp->ssl_client_attr),
+ (const unsigned char *)hdr, hdrlen, get_write_delay(csp)) < 0)
+ || ((flushed = ssl_flush_socket(&(csp->ssl_client_attr),
+ csp->iob)) < 0)
+ || (ssl_send_data_delayed(&(csp->ssl_client_attr),
+ (const unsigned char *)csp->receive_buffer, (size_t)len,
+ get_write_delay(csp)) < 0))
+ {
+ log_error(LOG_LEVEL_CONNECT,
+ "Flush header and buffers to client failed");
+ freez(hdr);
+ mark_server_socket_tainted(csp);
+ close_client_and_server_ssl_connections(csp);
+ return;
+ }
+ }
+ else
+#endif /* def FEATURE_HTTPS_INSPECTION */
+ {
+ if (write_socket_delayed(csp->cfd, hdr, hdrlen, write_delay)
+ || ((flushed = flush_iob(csp->cfd, csp->iob, write_delay)) < 0)
+ || write_socket_delayed(csp->cfd, csp->receive_buffer, (size_t)len,
+ write_delay))
+ {
+ log_error(LOG_LEVEL_CONNECT,
+ "Flush header and buffers to client failed: %E");
+ freez(hdr);
+ mark_server_socket_tainted(csp);
+ return;
+ }
+ }
+
+ /*
+ * Reset the byte_count to the amount of bytes
+ * we just flushed. len will be added a few lines below,
+ * hdrlen doesn't matter for LOG_LEVEL_CLF.
+ */
+ byte_count = (unsigned long long)flushed;
+ freez(hdr);
+ buffer_and_filter_content = 0;
+ server_body = 1;
+ }
+ }
+ else
+ {
+#ifdef FEATURE_HTTPS_INSPECTION
+ /*
+ * Sending data with standard or secured connection (HTTP/HTTPS)
+ */
+ if (client_use_ssl(csp))
+ {
+ ret = ssl_send_data_delayed(&(csp->ssl_client_attr),
+ (const unsigned char *)csp->receive_buffer, (size_t)len,
+ get_write_delay(csp));
+ if (ret < 0)
+ {
+ log_error(LOG_LEVEL_ERROR,
+ "Sending data to client failed");
+ mark_server_socket_tainted(csp);
+ close_client_and_server_ssl_connections(csp);
+ return;
+ }
+ }
+ else
+#endif /* def FEATURE_HTTPS_INSPECTION */
+ {
+ if (write_socket_delayed(csp->cfd, csp->receive_buffer,
+ (size_t)len, write_delay))
+ {
+ log_error(LOG_LEVEL_ERROR, "write to client failed: %E");
+ mark_server_socket_tainted(csp);
+ return;
+ }
+ }
+ }
+ byte_count += (unsigned long long)len;
+ continue;
+ }
+ else
+ {
+ /*
+ * 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->iob, csp->config->buffer_limit, csp->receive_buffer, 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);
+ mark_server_socket_tainted(csp);
+#ifdef FEATURE_HTTPS_INSPECTION
+ close_client_and_server_ssl_connections(csp);
+#endif
+ return;
+ }
+
+ /* 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
+ * we can do about it.
+ */
+ log_error(LOG_LEVEL_ERROR, "Invalid server headers. "
+ "Applying the MS IIS5 hack didn't help.");
+ log_error(LOG_LEVEL_CLF,
+ "%s - - [%T] \"%s\" 502 0", csp->ip_addr_str, http->cmd);
+#ifdef FEATURE_HTTPS_INSPECTION
+ /*
+ * Sending data with standard or secured connection (HTTP/HTTPS)
+ */
+ if (client_use_ssl(csp))
+ {
+ ssl_send_data_delayed(&(csp->ssl_client_attr),
+ (const unsigned char *)INVALID_SERVER_HEADERS_RESPONSE,
+ strlen(INVALID_SERVER_HEADERS_RESPONSE), get_write_delay(csp));
+ }
+ else
+#endif /* def FEATURE_HTTPS_INSPECTION */
+ {
+ write_socket_delayed(csp->cfd,
+ INVALID_SERVER_HEADERS_RESPONSE,
+ strlen(INVALID_SERVER_HEADERS_RESPONSE), write_delay);
+ }
+ mark_server_socket_tainted(csp);
+#ifdef FEATURE_HTTPS_INSPECTION
+ close_client_and_server_ssl_connections(csp);
+#endif
+ return;
+ }
+ else
+ {
+ /*
+ * Since we have to wait for more from the server before
+ * we can parse the headers we just continue here.
+ */
+ log_error(LOG_LEVEL_CONNECT,
+ "Continuing buffering server headers from socket %d. "
+ "Bytes most recently read: %d.", csp->cfd, 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)
+ {
+ if ((csp->flags & CSP_FLAG_REUSED_CLIENT_CONNECTION))
+ {
+ log_error(LOG_LEVEL_ERROR,
+ "No server or forwarder response received on socket %d. "
+ "Closing client socket %d without sending data.",
+ csp->server_connection.sfd, csp->cfd);
+ log_error(LOG_LEVEL_CLF,
+ "%s - - [%T] \"%s\" 502 0", csp->ip_addr_str, http->cmd);
+ }
+ else
+ {
+ log_error(LOG_LEVEL_ERROR,
+ "No server or forwarder response received on socket %d.",
+ csp->server_connection.sfd);
+ send_crunch_response(csp, error_response(csp, "no-server-data"));
+ }
+ free_http_request(http);
+ mark_server_socket_tainted(csp);
+#ifdef FEATURE_HTTPS_INSPECTION
+ close_client_and_server_ssl_connections(csp);
+#endif
+ return;
+ }
+
+ if (!csp->headers->first->str)
+ {
+ log_error(LOG_LEVEL_ERROR, "header search: csp->headers->first->str == NULL, assert will be called");
+ }
+ assert(csp->headers->first->str);
+
+ if (strncmpic(csp->headers->first->str, "HTTP", 4) &&
+ strncmpic(csp->headers->first->str, "ICY", 3))
+ {
+ /*
+ * It doesn't look like a HTTP (or Shoutcast) response:
+ * tell the client and log the problem.
+ */
+ if (strlen(csp->headers->first->str) > 30)
+ {
+ csp->headers->first->str[30] = '\0';
+ }
+ log_error(LOG_LEVEL_ERROR,
+ "Invalid server or forwarder response. Starts with: %s",
+ csp->headers->first->str);
+ log_error(LOG_LEVEL_CLF,
+ "%s - - [%T] \"%s\" 502 0", csp->ip_addr_str, http->cmd);
+#ifdef FEATURE_HTTPS_INSPECTION
+ /*
+ * Sending data with standard or secured connection (HTTP/HTTPS)
+ */
+ if (client_use_ssl(csp))
+ {
+ ssl_send_data_delayed(&(csp->ssl_client_attr),
+ (const unsigned char *)INVALID_SERVER_HEADERS_RESPONSE,
+ strlen(INVALID_SERVER_HEADERS_RESPONSE),
+ get_write_delay(csp));
+ }
+ else
+#endif /* def FEATURE_HTTPS_INSPECTION */
+ {
+ write_socket_delayed(csp->cfd, INVALID_SERVER_HEADERS_RESPONSE,
+ strlen(INVALID_SERVER_HEADERS_RESPONSE), write_delay);
+ }
+ free_http_request(http);
+ mark_server_socket_tainted(csp);
+#ifdef FEATURE_HTTPS_INSPECTION
+ close_client_and_server_ssl_connections(csp);
+#endif
+ return;
+ }
+
+ /*
+ * We have now received the entire server header,
+ * filter it and send the result to the client
+ */
+ if (JB_ERR_OK != sed(csp, FILTER_SERVER_HEADERS))
+ {
+ log_error(LOG_LEVEL_CLF,
+ "%s - - [%T] \"%s\" 502 0", csp->ip_addr_str, http->cmd);
+#ifdef FEATURE_HTTPS_INSPECTION
+ /*
+ * Sending data with standard or secured connection (HTTP/HTTPS)
+ */
+ if (client_use_ssl(csp))
+ {
+ ssl_send_data_delayed(&(csp->ssl_client_attr),
+ (const unsigned char *)INVALID_SERVER_HEADERS_RESPONSE,
+ strlen(INVALID_SERVER_HEADERS_RESPONSE),
+ get_write_delay(csp));
+ }
+ else
+#endif
+ {
+ write_socket_delayed(csp->cfd, INVALID_SERVER_HEADERS_RESPONSE,
+ strlen(INVALID_SERVER_HEADERS_RESPONSE), write_delay);
+ }
+ free_http_request(http);
+ mark_server_socket_tainted(csp);
+#ifdef FEATURE_HTTPS_INSPECTION
+ close_client_and_server_ssl_connections(csp);
+#endif
+ return;
+ }
+ hdr = list_to_text(csp->headers);
+ if (hdr == NULL)
+ {
+ /* FIXME Should handle error properly */
+ log_error(LOG_LEVEL_FATAL, "Out of memory parsing server header");
+ }
+
+ if ((csp->flags & CSP_FLAG_CHUNKED)
+ && !(csp->flags & CSP_FLAG_CONTENT_LENGTH_SET)
+ && ((csp->iob->eod - csp->iob->cur) >= 5)
+ && !memcmp(csp->iob->eod-5, "0\r\n\r\n", 5))
+ {
+ log_error(LOG_LEVEL_CONNECT,
+ "Looks like we got the last chunk together with "
+ "the server headers. We better stop reading.");
+ byte_count = (unsigned long long)(csp->iob->eod - csp->iob->cur);
+ csp->expected_content_length = byte_count;
+ csp->flags |= CSP_FLAG_CONTENT_LENGTH_SET;
+ }
+
+ csp->server_connection.response_received = time(NULL);
+
+ if (crunch_response_triggered(csp, crunchers_light))
+ {
+ /*
+ * One of the tags created by a server-header
+ * tagger triggered a crunch. We already
+ * delivered the crunch response to the client
+ * and are done here after cleaning up.
+ */
+ freez(hdr);
+ mark_server_socket_tainted(csp);
+#ifdef FEATURE_HTTPS_INSPECTION
+ close_client_and_server_ssl_connections(csp);
+#endif
+ return;
+ }
+
+ /* Buffer and pcrs filter this if appropriate. */
+ buffer_and_filter_content = content_requires_filtering(csp);
+
+ if (!buffer_and_filter_content)