+ }
+
+ log_error(LOG_LEVEL_RE_FILTER,
+ "Compressed content from %lu to %lu bytes. Compression level: %d",
+ *buffer_length, new_length, compression_level);
+
+ *buffer_length = (size_t)new_length;
+
+ return compressed_buffer;
+
+}
+#endif
+
+
+/*********************************************************************
+ *
+ * Function : finish_http_response
+ *
+ * Description : Fill in the missing headers in an http response,
+ * and flatten the headers to an http head.
+ * For HEAD requests the body is freed once
+ * the Content-Length header is set.
+ *
+ * Parameters :
+ * 1 : rsp = pointer to http_response to be processed
+ *
+ * Returns : A http_response, usually the rsp parameter.
+ * On error, free()s rsp and returns cgi_error_memory()
+ *
+ *********************************************************************/
+struct http_response *finish_http_response(struct client_state *csp, struct http_response *rsp)
+{
+ char buf[BUFFER_SIZE];
+ jb_err err;
+
+ /* Special case - do NOT change this statically allocated response,
+ * which is ready for output anyway.
+ */
+ if (rsp == cgi_error_memory_response)
+ {
+ return rsp;
+ }
+
+ /*
+ * Add "Cross-origin resource sharing" (CORS) headers if enabled
+ */
+ if (NULL != csp->config->cors_allowed_origin)
+ {
+ enlist_unique_header(rsp->headers, "Access-Control-Allow-Origin",
+ csp->config->cors_allowed_origin);
+ enlist_unique_header(rsp->headers, "Access-Control-Allow-Methods", "GET,POST");
+ enlist_unique_header(rsp->headers, "Access-Control-Allow-Headers", "X-Requested-With");
+ enlist_unique_header(rsp->headers, "Access-Control-Max-Age", "86400");
+ }
+
+ /*
+ * Fill in the HTTP Status, using HTTP/1.1
+ * unless the client asked for HTTP/1.0.
+ */
+ snprintf(buf, sizeof(buf), "%s %s",
+ strcmpic(csp->http->version, "HTTP/1.0") ? "HTTP/1.1" : "HTTP/1.0",
+ rsp->status ? rsp->status : "200 OK");
+ err = enlist_first(rsp->headers, buf);
+
+ /*
+ * Set the Content-Length
+ */
+ if (rsp->content_length == 0)
+ {
+ rsp->content_length = rsp->body ? strlen(rsp->body) : 0;
+ }
+
+#ifdef FEATURE_COMPRESSION
+ if (!err && (csp->flags & CSP_FLAG_CLIENT_SUPPORTS_DEFLATE)
+ && (rsp->content_length > LOWER_LENGTH_LIMIT_FOR_COMPRESSION))
+ {
+ char *compressed_content;
+
+ compressed_content = compress_buffer(rsp->body, &rsp->content_length,
+ csp->config->compression_level);
+ if (NULL != compressed_content)
+ {
+ freez(rsp->body);
+ rsp->body = compressed_content;
+ err = enlist_unique_header(rsp->headers, "Content-Encoding", "deflate");
+ }
+ }
+#endif
+
+ if (!err)
+ {
+ snprintf(buf, sizeof(buf), "Content-Length: %d", (int)rsp->content_length);
+ /*
+ * Signal serve() that the client will be able to figure out
+ * the end of the response without having to close the connection.
+ */
+ csp->flags |= CSP_FLAG_SERVER_CONTENT_LENGTH_SET;
+ err = enlist(rsp->headers, buf);
+ }
+
+ if (0 == strcmpic(csp->http->gpc, "head"))
+ {
+ /*
+ * The client only asked for the head. Dispose
+ * the body and log an offensive message.
+ *
+ * While it may seem to be a bit inefficient to
+ * prepare the body if it isn't needed, it's the
+ * only way to get the Content-Length right for
+ * dynamic pages. We could have disposed the body
+ * earlier, but not without duplicating the
+ * Content-Length setting code above.
+ */
+ log_error(LOG_LEVEL_CGI, "Preparing to give head to %s.", csp->ip_addr_str);
+ freez(rsp->body);
+ rsp->content_length = 0;
+ }
+
+ if (strncmpic(rsp->status, "302", 3))
+ {
+ /*
+ * If it's not a redirect without any content,
+ * set the Content-Type to text/html if it's
+ * not already specified.
+ */
+ if (!err) err = enlist_unique(rsp->headers, "Content-Type: text/html", 13);
+ }
+
+ /*
+ * Fill in the rest of the default headers:
+ *
+ * Date: set to current date/time.
+ * Last-Modified: set to date/time the page was last changed.
+ * Expires: set to date/time page next needs reloading.
+ * Cache-Control: set to "no-cache" if applicable.
+ *
+ * See http://www.w3.org/Protocols/rfc2068/rfc2068
+ */
+ if (rsp->is_static)
+ {
+ /*
+ * Set Expires to about 10 min into the future so it'll get reloaded
+ * occasionally, e.g. if Privoxy gets upgraded.
+ */
+
+ if (!err)
+ {
+ get_http_time(0, buf, sizeof(buf));
+ err = enlist_unique_header(rsp->headers, "Date", buf);
+ }