Merge pull request #190 from pagespeed/oschaaf-inflate-upstream
Adds suport for silently inflating compressed upstreams. This is good for when the link to the upstream is low bandwidth This should fix https://github.com/pagespeed/ngx_pagespeed/issues/140
This commit is contained in:
+97
-4
@@ -53,11 +53,13 @@ extern "C" {
|
||||
#include "net/instaweb/public/version.h"
|
||||
#include "net/instaweb/util/public/google_message_handler.h"
|
||||
#include "net/instaweb/util/public/google_url.h"
|
||||
#include "net/instaweb/util/public/gzip_inflater.h"
|
||||
#include "pthread_shared_mem.h"
|
||||
#include "net/instaweb/util/public/query_params.h"
|
||||
#include "net/instaweb/util/public/string.h"
|
||||
#include "net/instaweb/util/public/string_writer.h"
|
||||
#include "net/instaweb/util/public/time_util.h"
|
||||
#include "net/instaweb/util/stack_buffer.h"
|
||||
|
||||
extern ngx_module_t ngx_pagespeed;
|
||||
|
||||
@@ -291,6 +293,7 @@ typedef struct {
|
||||
bool is_resource_fetch;
|
||||
bool sent_headers;
|
||||
bool write_pending;
|
||||
net_instaweb::GzipInflater* inflater_;
|
||||
} ps_request_ctx_t;
|
||||
|
||||
ngx_int_t ps_body_filter(ngx_http_request_t* r, ngx_chain_t* in);
|
||||
@@ -722,6 +725,11 @@ void ps_release_request_context(void* data) {
|
||||
ctx->base_fetch = NULL;
|
||||
}
|
||||
|
||||
if (ctx->inflater_ != NULL) {
|
||||
delete ctx->inflater_;
|
||||
ctx->inflater_ = NULL;
|
||||
}
|
||||
|
||||
// Close the connection, delete the events attached with it, and free it to
|
||||
// Nginx's connection pool
|
||||
if (ctx->pagespeed_connection != NULL) {
|
||||
@@ -1329,10 +1337,26 @@ void ps_send_to_pagespeed(ngx_http_request_t* r,
|
||||
cur->buf->last_buf = 0;
|
||||
|
||||
CHECK(ctx->proxy_fetch != NULL);
|
||||
ctx->proxy_fetch->Write(StringPiece(reinterpret_cast<char*>(cur->buf->pos),
|
||||
cur->buf->last - cur->buf->pos),
|
||||
cfg_s->handler);
|
||||
|
||||
if (ctx->inflater_ == NULL) {
|
||||
ctx->proxy_fetch->Write(
|
||||
StringPiece(reinterpret_cast<char*>(cur->buf->pos),
|
||||
cur->buf->last - cur->buf->pos), cfg_s->handler);
|
||||
} else {
|
||||
char buf[net_instaweb::kStackBufferSize];
|
||||
ctx->inflater_->SetInput(reinterpret_cast<char*>(cur->buf->pos),
|
||||
cur->buf->last - cur->buf->pos);
|
||||
while (ctx->inflater_->HasUnconsumedInput()) {
|
||||
int num_inflated_bytes = ctx->inflater_->InflateBytes(
|
||||
buf, net_instaweb::kStackBufferSize);
|
||||
if (num_inflated_bytes < 0) {
|
||||
cfg_s->handler->Message(net_instaweb::kWarning,
|
||||
"Corrupted inflation");
|
||||
} else if (num_inflated_bytes > 0) {
|
||||
ctx->proxy_fetch->Write(StringPiece(buf, num_inflated_bytes),
|
||||
cfg_s->handler);
|
||||
}
|
||||
}
|
||||
}
|
||||
// We're done with buffers as we pass them through, so mark them as sent as
|
||||
// we go.
|
||||
cur->buf->pos = cur->buf->last;
|
||||
@@ -1468,6 +1492,46 @@ void ps_strip_html_headers(ngx_http_request_t* r) {
|
||||
}
|
||||
}
|
||||
|
||||
// Returns true, if the the response headers indicate there are multiple
|
||||
// content encodings.
|
||||
bool ps_has_stacked_content_encoding(ngx_http_request_t* r) {
|
||||
ngx_uint_t i;
|
||||
ngx_list_part_t* part = &(r->headers_out.headers.part);
|
||||
ngx_table_elt_t* header = static_cast<ngx_table_elt_t*>(part->elts);
|
||||
int field_count = 0;
|
||||
|
||||
for (i = 0 ; /* void */; i++) {
|
||||
if (i >= part->nelts) {
|
||||
if (part->next == NULL) {
|
||||
break;
|
||||
}
|
||||
|
||||
part = part->next;
|
||||
header = static_cast<ngx_table_elt_t*>(part->elts);
|
||||
i = 0;
|
||||
}
|
||||
|
||||
// Inspect Content-Encoding headers, checking all value fields
|
||||
// If an origin returns gzip,foo, that is what we will get here.
|
||||
if (STR_CASE_EQ_LITERAL(header[i].key, "Content-Encoding")) {
|
||||
if (header[i].value.data != NULL && header[i].value.len > 0) {
|
||||
char* p = reinterpret_cast<char*>(header[i].value.data);
|
||||
ngx_uint_t j;
|
||||
for (j = 0; j < header[i].value.len; j++) {
|
||||
if (p[j] == ',' || j == header[i].value.len - 1) {
|
||||
field_count++;
|
||||
}
|
||||
}
|
||||
if (field_count > 1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
ngx_int_t ps_header_filter(ngx_http_request_t* r) {
|
||||
ps_srv_conf_t* cfg_s = ps_get_srv_config(r);
|
||||
if (cfg_s->server_context == NULL) {
|
||||
@@ -1521,6 +1585,35 @@ ngx_int_t ps_header_filter(ngx_http_request_t* r) {
|
||||
}
|
||||
ctx = ps_get_request_context(r);
|
||||
CHECK(ctx->driver != NULL); // Not a resource fetch, so driver is defined.
|
||||
|
||||
if (r->headers_out.content_encoding &&
|
||||
r->headers_out.content_encoding->value.len) {
|
||||
// headers_out.content_encoding will be set to the exact last
|
||||
// Content-Encoding response header value that nginx receives. To
|
||||
// check if there were multiple (aka stacked) encodings in the
|
||||
// response headers, we must iterate them all.
|
||||
if (!ps_has_stacked_content_encoding(r)) {
|
||||
StringPiece content_encoding =
|
||||
str_to_string_piece(r->headers_out.content_encoding->value);
|
||||
net_instaweb::GzipInflater::InflateType inflate_type;
|
||||
bool is_encoded = false;
|
||||
if (net_instaweb::StringCaseEqual(content_encoding, "deflate")) {
|
||||
is_encoded = true;
|
||||
inflate_type = net_instaweb::GzipInflater::kDeflate;
|
||||
} else if (net_instaweb::StringCaseEqual(content_encoding, "gzip")) {
|
||||
is_encoded = true;
|
||||
inflate_type = net_instaweb::GzipInflater::kGzip;
|
||||
}
|
||||
|
||||
if (is_encoded) {
|
||||
r->headers_out.content_encoding->hash = 0;
|
||||
r->headers_out.content_encoding = NULL;
|
||||
ctx->inflater_ = new net_instaweb::GzipInflater(inflate_type);
|
||||
ctx->inflater_->Init();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const net_instaweb::RewriteOptions* options = ctx->driver->options();
|
||||
|
||||
ps_strip_html_headers(r);
|
||||
|
||||
Reference in New Issue
Block a user