blob: b019febb085cb1db11cfc98085b417ad90b9b182 [file] [log] [blame]
// Copyright 2010 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Author: jmarantz@google.com (Joshua Marantz)
// lsong@google.com (Libo Song)
#include "net/instaweb/apache/instaweb_context.h"
#include "net/instaweb/util/public/gzip_inflater.h"
#include "net/instaweb/util/stack_buffer.h"
#include "http_config.h"
extern "C" {
extern module AP_MODULE_DECLARE_DATA pagespeed_module;
}
namespace net_instaweb {
InstawebContext::InstawebContext(request_rec* request,
ApacheRewriteDriverFactory* factory,
const std::string& absolute_url,
bool use_custom_options,
const RewriteOptions& custom_options)
: content_encoding_(kNone),
factory_(factory),
string_writer_(&output_),
inflater_(NULL) {
if (use_custom_options) {
custom_rewriter_.reset(factory->NewCustomRewriteDriver(custom_options));
rewrite_driver_ = custom_rewriter_.get();
} else {
rewrite_driver_ = factory->GetRewriteDriver();
}
ComputeContentEncoding(request);
apr_pool_cleanup_register(request->pool, this, Cleanup,
apr_pool_cleanup_null);
bucket_brigade_ = apr_brigade_create(request->pool,
request->connection->bucket_alloc);
if (content_encoding_ == kGzip || content_encoding_ == kDeflate) {
// TODO(jmarantz): consider keeping a pool of these if they are expensive
// to initialize.
if (content_encoding_ == kGzip) {
inflater_.reset(new GzipInflater(GzipInflater::kGzip));
} else {
inflater_.reset(new GzipInflater(GzipInflater::kDeflate));
}
inflater_->Init();
}
const char* user_agent = apr_table_get(request->headers_in,
HttpAttributes::kUserAgent);
rewrite_driver_->SetUserAgent(user_agent);
// TODO(lsong): Bypass the string buffer, writer data directly to the next
// apache bucket.
rewrite_driver_->SetWriter(&string_writer_);
rewrite_driver_->html_parse()->StartParse(absolute_url);
}
InstawebContext::~InstawebContext() {
if (custom_rewriter_ == NULL) {
factory_->ReleaseRewriteDriver(rewrite_driver_);
}
}
void InstawebContext::Rewrite(const char* input, int size) {
if (inflater_.get() != NULL) {
char buf[kStackBufferSize];
inflater_->SetInput(input, size);
while (inflater_->HasUnconsumedInput()) {
int num_inflated_bytes = inflater_->InflateBytes(buf, kStackBufferSize);
rewrite_driver_->html_parse()->ParseText(buf, num_inflated_bytes);
}
} else {
rewrite_driver_->html_parse()->ParseText(input, size);
}
}
apr_status_t InstawebContext::Cleanup(void* object) {
InstawebContext* ic = static_cast<InstawebContext*>(object);
delete ic;
return APR_SUCCESS;
}
void InstawebContext::ComputeContentEncoding(request_rec* request) {
// Check if the content is gzipped. Steal from mod_deflate.
const char* encoding = apr_table_get(
request->headers_out, HttpAttributes::kContentEncoding);
if (encoding) {
const char* err_enc = apr_table_get(request->err_headers_out,
HttpAttributes::kContentEncoding);
if (err_enc) {
// We don't properly handle stacked encodings now.
content_encoding_ = kOther;
}
} else {
encoding = apr_table_get(request->err_headers_out,
HttpAttributes::kContentEncoding);
}
if (encoding) {
if (strcasecmp(encoding, HttpAttributes::kGzip) == 0) {
content_encoding_ = kGzip;
} else if (strcasecmp(encoding, HttpAttributes::kDeflate) == 0) {
content_encoding_ = kDeflate;
} else {
content_encoding_ = kOther;
}
}
}
ApacheRewriteDriverFactory* InstawebContext::Factory(server_rec* server) {
return static_cast<ApacheRewriteDriverFactory*>
ap_get_module_config(server->module_config, &pagespeed_module);
}
} // namespace net_instaweb