blob: 130dbc7b1dd50d954a7619a95df57af994b195b9 [file] [log] [blame]
// Copyright 2013 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)
#include "base/logging.h"
#include "pagespeed/apache/apache_writer.h"
#include "pagespeed/apache/header_util.h"
#include "net/instaweb/http/public/async_fetch.h"
#include "pagespeed/kernel/base/string_util.h"
#include "pagespeed/kernel/http/http_names.h"
#include "pagespeed/kernel/http/response_headers.h"
#include "apr_strings.h" // for apr_pstrdup // NOLINT
#include "http_protocol.h" // NOLINT
namespace net_instaweb {
ApacheWriter::ApacheWriter(request_rec* r, ThreadSystem* thread_system)
: request_(r),
headers_out_(false),
disable_downstream_header_filters_(false),
strip_cookies_(false),
content_length_(AsyncFetch::kContentLengthUnknown),
thread_system_(thread_system) {
apache_request_thread_.reset(thread_system->GetThreadId());
}
ApacheWriter::~ApacheWriter() {
}
bool ApacheWriter::Write(const StringPiece& str, MessageHandler* handler) {
DCHECK(apache_request_thread_->IsCurrentThread());
DCHECK(headers_out_);
ap_rwrite(str.data(), str.size(), request_);
return true;
}
bool ApacheWriter::Flush(MessageHandler* handler) {
DCHECK(apache_request_thread_->IsCurrentThread());
DCHECK(headers_out_);
ap_rflush(request_);
return true;
}
void ApacheWriter::OutputHeaders(ResponseHeaders* response_headers) {
DCHECK(apache_request_thread_->IsCurrentThread());
DCHECK(!headers_out_);
if (headers_out_) {
return;
}
headers_out_ = true;
// Apache2 defaults to set the status line as HTTP/1.1. If the
// original content was HTTP/1.0, we need to force the server to use
// HTTP/1.0. I'm not sure why/whether we need to do this; it was in
// mod_static from the sdpy project, which is where I copied this
// code from.
if ((response_headers->major_version() == 1) &&
(response_headers->minor_version() == 0)) {
apr_table_set(request_->subprocess_env, "force-response-1.0", "1");
}
const char* content_type = response_headers->Lookup1(
HttpAttributes::kContentType);
// It doesn't matter how the origin transferred the request to us;
// Apache will fill this data in when it issues the response.
response_headers->RemoveAll(HttpAttributes::kTransferEncoding);
response_headers->RemoveAll(HttpAttributes::kContentLength);
if (content_length_ != AsyncFetch::kContentLengthUnknown) {
ap_set_content_length(request_, content_length_);
}
ResponseHeadersToApacheRequest(*response_headers, request_);
request_->status = response_headers->status_code();
if (disable_downstream_header_filters_) {
DisableDownstreamHeaderFilters(request_);
}
// TODO(jefftk): Sanitize strips cookies and a lot of other headers. It's
// being run after ResponseHeadersToApacheRequest(), however, which means it's
// not actually doing anything. This code is not doing what it says it does,
// and should be fixed, but it's possible some users like the mobilizing proxy
// are depending on the current behavior.
if (strip_cookies_ && response_headers->Sanitize()) {
response_headers->ComputeCaching();
}
if (content_type != NULL) {
// ap_set_content_type does not make a copy of the string, we need
// to duplicate it. Note that we will update the content type below,
// after transforming the headers.
ap_set_content_type(request_, apr_pstrdup(request_->pool, content_type));
}
// We don't set the content-length here, because we don't know it yet.
}
} // namespace net_instaweb