blob: 828fffea7337dacce5361f99a9dca6966a399d23 [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 "pagespeed/apache/apache_rewrite_driver_factory.h"
#include <unistd.h>
#include "pagespeed/apache/apache_httpd_includes.h"
#include "apr_pools.h"
#include "ap_mpm.h"
#include "base/logging.h"
#include "pagespeed/apache/apache_config.h"
#include "pagespeed/apache/apache_message_handler.h"
#include "pagespeed/apache/apache_server_context.h"
#include "pagespeed/apache/apache_thread_system.h"
#include "pagespeed/apache/apr_timer.h"
#include "net/instaweb/rewriter/public/rewrite_driver_factory.h"
#include "net/instaweb/rewriter/public/server_context.h"
#include "pagespeed/kernel/base/message_handler.h"
#include "pagespeed/kernel/base/null_shared_mem.h"
#include "pagespeed/kernel/base/stl_util.h"
#include "pagespeed/kernel/base/string.h"
#include "pagespeed/kernel/base/string_util.h"
#include "pagespeed/kernel/base/thread_system.h"
#include "pagespeed/kernel/base/timer.h"
#include "pagespeed/kernel/sharedmem/shared_circular_buffer.h"
#include "pagespeed/kernel/thread/pthread_shared_mem.h"
#include "pagespeed/kernel/thread/scheduler_thread.h"
#include "pagespeed/kernel/thread/slow_worker.h"
#include "pagespeed/system/controller_manager.h"
namespace net_instaweb {
class ProcessContext;
ApacheRewriteDriverFactory::ApacheRewriteDriverFactory(
const ProcessContext& process_context,
server_rec* server, const StringPiece& version)
: SystemRewriteDriverFactory(
process_context,
new ApacheThreadSystem,
NULL, /* default shared memory runtime */
server->server_hostname,
server->port),
server_rec_(server),
scheduler_thread_(nullptr),
version_(version.data(), version.size()),
apache_message_handler_(new ApacheMessageHandler(
server_rec_, version_, timer(), thread_system()->NewMutex())),
apache_html_parse_message_handler_(new ApacheMessageHandler(
server_rec_, version_, timer(), thread_system()->NewMutex())),
inherit_vhost_config_(false) {
apr_pool_create(&pool_, NULL);
// Apache defaults UsePerVhostStatistics to false for historical reasons, but
// more recent implementations default it to true.
set_use_per_vhost_statistics(false);
// Make sure the ownership of apache_message_handler_ and
// apache_html_parse_message_handler_ is given to scoped pointer.
// Otherwise may result in leak error in test.
message_handler();
html_parse_message_handler();
InitializeDefaultOptions();
}
ApacheRewriteDriverFactory::~ApacheRewriteDriverFactory() {
// We free all the resources before destroying the pool, because some of the
// resource uses the sub-pool and will need that pool to be around to
// clean up properly.
ShutDown();
apr_pool_destroy(pool_);
// We still have registered a pool deleter here, right? This seems risky...
STLDeleteElements(&uninitialized_server_contexts_);
// Apache startup is pretty weird, in that it initializes twice:
// first to check configuration, then for real. In between the two runs,
// it cleans us up very thoroughly, including unloading our module, so if we
// are here at the end of run 1, we are about to forget all about the
// controller process hanging around, while the FD to it will be kept around
// (including accross daemonization), keeping it alive.
//
// So here we drop the FD, to get the controller to exit, letting us start
// it again (and we want it to exit on regular exit, too).
//
// This call is a no-op if nothing was started.
//
// This is done in Apache-specific code rather than System* because
// nginx has other challenges: it can create multiple
// SystemRewriteDriverFactory's at once when reloading config, and
// ~SystemRewriteDriverFactory for the old one happens too late to be useful,
// so there we are better off just using global state to keep track of
// the controller (as there are no pesky dlunload's making us forget all of
// it!).
ControllerManager::DetachFromControllerProcess();
}
Timer* ApacheRewriteDriverFactory::DefaultTimer() {
return new AprTimer();
}
MessageHandler* ApacheRewriteDriverFactory::DefaultHtmlParseMessageHandler() {
return apache_html_parse_message_handler_;
}
MessageHandler* ApacheRewriteDriverFactory::DefaultMessageHandler() {
return apache_message_handler_;
}
void ApacheRewriteDriverFactory::SetupCaches(ServerContext* server_context) {
SystemRewriteDriverFactory::SetupCaches(server_context);
// TODO(jmarantz): It would make more sense to have the base ServerContext
// own the ProxyFetchFactory, but that would create a cyclic directory
// dependency. This can be resolved minimally by moving proxy_fetch.cc
// from automatic/ to rewriter/. I think we should also think harder about
// separating out rewriting infrastructure from rewriters.
ApacheServerContext* apache_server_context =
dynamic_cast<ApacheServerContext*>(server_context);
CHECK(apache_server_context != NULL);
apache_server_context->InitProxyFetchFactory();
}
void ApacheRewriteDriverFactory::SetNeedSchedulerThread() {
if (scheduler_thread_ == nullptr) {
scheduler_thread_ = new SchedulerThread(thread_system(), scheduler());
defer_cleanup(scheduler_thread_->MakeDeleter());
scheduler_thread_->Start();
}
}
bool ApacheRewriteDriverFactory::IsServerThreaded() {
// Detect whether we're using a threaded MPM.
apr_status_t status;
int result = 0, threads = 1;
status = ap_mpm_query(AP_MPMQ_IS_THREADED, &result);
if (status == APR_SUCCESS &&
(result == AP_MPMQ_STATIC || result == AP_MPMQ_DYNAMIC)) {
// Number of configured threads.
status = ap_mpm_query(AP_MPMQ_MAX_THREADS, &threads);
if (status != APR_SUCCESS) {
return false; // Assume non-thready by default.
}
}
return threads > 1;
}
int ApacheRewriteDriverFactory::LookupThreadLimit() {
int thread_limit = 0;
// The compiled maximum number of threads.
ap_mpm_query(AP_MPMQ_HARD_LIMIT_THREADS, &thread_limit);
return thread_limit;
}
void ApacheRewriteDriverFactory::ParentOrChildInit() {
if (install_crash_handler()) {
ApacheMessageHandler::InstallCrashHandler(server_rec_);
}
SystemRewriteDriverFactory::ParentOrChildInit();
}
void ApacheRewriteDriverFactory::ShutDownMessageHandlers() {
// Reset SharedCircularBuffer to NULL, so that any shutdown warnings
// (e.g. in ServerContext::ShutDownDrivers) don't reference
// deleted objects as the base-class is deleted.
//
// TODO(jefftk): merge ApacheMessageHandler and NgxMessageHandler into
// SystemMessageHandler and then move this into System.
apache_message_handler_->set_buffer(NULL);
apache_html_parse_message_handler_->set_buffer(NULL);
}
void ApacheRewriteDriverFactory::SetupMessageHandlers() {
// TODO(jefftk): merge ApacheMessageHandler and NgxMessageHandler into
// SystemMessageHandler and then move this into System.
apache_message_handler_->SetPidString(static_cast<int64>(getpid()));
apache_html_parse_message_handler_->SetPidString(
static_cast<int64>(getpid()));
}
void ApacheRewriteDriverFactory::SetCircularBuffer(
SharedCircularBuffer* buffer) {
// TODO(jefftk): merge ApacheMessageHandler and NgxMessageHandler into
// SystemMessageHandler and then move this into System.
apache_message_handler_->set_buffer(buffer);
apache_html_parse_message_handler_->set_buffer(buffer);
}
void ApacheRewriteDriverFactory::Initialize() {
ApacheConfig::Initialize();
RewriteDriverFactory::Initialize();
}
void ApacheRewriteDriverFactory::InitStats(Statistics* statistics) {
// Init standard system stats.
SystemRewriteDriverFactory::InitStats(statistics);
// Init Apache-specific stats.
ApacheServerContext::InitStats(statistics);
}
void ApacheRewriteDriverFactory::Terminate() {
RewriteDriverFactory::Terminate();
ApacheConfig::Terminate();
PthreadSharedMem::Terminate();
}
ApacheServerContext* ApacheRewriteDriverFactory::MakeApacheServerContext(
server_rec* server) {
ApacheServerContext* server_context =
new ApacheServerContext(this, server, version_);
uninitialized_server_contexts_.insert(server_context);
return server_context;
}
ServerContext* ApacheRewriteDriverFactory::NewDecodingServerContext() {
ServerContext* sc = new ApacheServerContext(this, server_rec_, version_);
InitStubDecodingServerContext(sc);
return sc;
}
bool ApacheRewriteDriverFactory::PoolDestroyed(
ApacheServerContext* server_context) {
if (uninitialized_server_contexts_.erase(server_context) == 1) {
delete server_context;
}
// Returns true if all the ServerContexts known by the factory and its
// superclass are finished. Then it's time to destroy the factory. Note
// that ApacheRewriteDriverFactory keeps track of ServerContexts that
// are partially constructed. RewriteDriverFactory keeps track of
// ServerContexts that are already serving requests. We need to clean
// all of them out before we can terminate the driver.
bool no_active_server_contexts = TerminateServerContext(server_context);
return (no_active_server_contexts && uninitialized_server_contexts_.empty());
}
ApacheConfig* ApacheRewriteDriverFactory::NewRewriteOptions() {
return new ApacheConfig(hostname_identifier(), thread_system());
}
ApacheConfig* ApacheRewriteDriverFactory::NewRewriteOptionsForQuery() {
return new ApacheConfig("query", thread_system());
}
} // namespace net_instaweb