/* * Copyright 2012 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: jefftk@google.com (Jeff Kaufman) #include "ngx_rewrite_driver_factory.h" #include #include "log_message_handler.h" #include "ngx_message_handler.h" #include "ngx_rewrite_options.h" #include "ngx_server_context.h" #include "ngx_url_async_fetcher.h" #include "net/instaweb/http/public/rate_controller.h" #include "net/instaweb/http/public/rate_controlling_url_async_fetcher.h" #include "net/instaweb/http/public/wget_url_fetcher.h" #include "net/instaweb/rewriter/public/rewrite_driver.h" #include "net/instaweb/rewriter/public/rewrite_driver_factory.h" #include "net/instaweb/rewriter/public/server_context.h" #include "net/instaweb/util/public/property_cache.h" #include "pagespeed/kernel/base/google_message_handler.h" #include "pagespeed/kernel/base/null_shared_mem.h" #include "pagespeed/kernel/base/posix_timer.h" #include "pagespeed/kernel/base/stdio_file_system.h" #include "pagespeed/kernel/base/string.h" #include "pagespeed/kernel/base/string_util.h" #include "pagespeed/kernel/base/thread_system.h" #include "pagespeed/kernel/http/content_type.h" #include "pagespeed/kernel/sharedmem/shared_circular_buffer.h" #include "pagespeed/kernel/sharedmem/shared_mem_statistics.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/in_place_resource_recorder.h" #include "pagespeed/system/serf_url_async_fetcher.h" #include "pagespeed/system/system_caches.h" #include "pagespeed/system/system_rewrite_options.h" namespace net_instaweb { class FileSystem; class Hasher; class MessageHandler; class Statistics; class Timer; class UrlAsyncFetcher; class UrlFetcher; class Writer; class SharedCircularBuffer; NgxRewriteDriverFactory::NgxRewriteDriverFactory( const ProcessContext& process_context, SystemThreadSystem* system_thread_system, StringPiece hostname, int port) : SystemRewriteDriverFactory(process_context, system_thread_system, NULL /* default shared memory runtime */, hostname, port), threads_started_(false), ngx_message_handler_( new NgxMessageHandler(timer(), thread_system()->NewMutex())), ngx_html_parse_message_handler_( new NgxMessageHandler(timer(), thread_system()->NewMutex())), log_(NULL), resolver_timeout_(NGX_CONF_UNSET_MSEC), use_native_fetcher_(false), // 100 Aligns to nginx's server-side default. native_fetcher_max_keepalive_requests_(100), ngx_shared_circular_buffer_(NULL), hostname_(hostname.as_string()), port_(port), process_script_variables_mode_(ProcessScriptVariablesMode::kOff), process_script_variables_set_(false), shut_down_(false) { InitializeDefaultOptions(); default_options()->set_beacon_url("/ngx_pagespeed_beacon"); SystemRewriteOptions* system_options = dynamic_cast( default_options()); system_options->set_file_cache_clean_inode_limit(500000); system_options->set_avoid_renaming_introspective_javascript(true); set_message_handler(ngx_message_handler_); set_html_parse_message_handler(ngx_html_parse_message_handler_); } NgxRewriteDriverFactory::~NgxRewriteDriverFactory() { ShutDown(); ngx_shared_circular_buffer_ = NULL; STLDeleteElements(&uninitialized_server_contexts_); } Hasher* NgxRewriteDriverFactory::NewHasher() { return new MD5Hasher; } UrlAsyncFetcher* NgxRewriteDriverFactory::AllocateFetcher( SystemRewriteOptions* config) { if (use_native_fetcher_) { NgxUrlAsyncFetcher* fetcher = new NgxUrlAsyncFetcher( config->fetcher_proxy().c_str(), log_, resolver_timeout_, config->blocking_fetch_timeout_ms(), resolver_, native_fetcher_max_keepalive_requests_, thread_system(), message_handler()); ngx_url_async_fetchers_.push_back(fetcher); return fetcher; } else { return SystemRewriteDriverFactory::AllocateFetcher(config); } } MessageHandler* NgxRewriteDriverFactory::DefaultHtmlParseMessageHandler() { return ngx_html_parse_message_handler_; } MessageHandler* NgxRewriteDriverFactory::DefaultMessageHandler() { return ngx_message_handler_; } FileSystem* NgxRewriteDriverFactory::DefaultFileSystem() { return new StdioFileSystem(); } Timer* NgxRewriteDriverFactory::DefaultTimer() { return new PosixTimer; } NamedLockManager* NgxRewriteDriverFactory::DefaultLockManager() { CHECK(false); return NULL; } RewriteOptions* NgxRewriteDriverFactory::NewRewriteOptions() { NgxRewriteOptions* options = new NgxRewriteOptions(thread_system()); // TODO(jefftk): figure out why using SetDefaultRewriteLevel like // mod_pagespeed does in mod_instaweb.cc:create_dir_config() isn't enough here // -- if you use that instead then ngx_pagespeed doesn't actually end up // defaulting CoreFilters. // See: https://github.com/pagespeed/ngx_pagespeed/issues/1190 options->SetRewriteLevel(RewriteOptions::kCoreFilters); return options; } RewriteOptions* NgxRewriteDriverFactory::NewRewriteOptionsForQuery() { return new NgxRewriteOptions(thread_system()); } bool NgxRewriteDriverFactory::CheckResolver() { if (use_native_fetcher_ && resolver_ == NULL) { return false; } return true; } NgxServerContext* NgxRewriteDriverFactory::MakeNgxServerContext( StringPiece hostname, int port) { NgxServerContext* server_context = new NgxServerContext(this, hostname, port); uninitialized_server_contexts_.insert(server_context); return server_context; } ServerContext* NgxRewriteDriverFactory::NewDecodingServerContext() { ServerContext* sc = new NgxServerContext(this, hostname_, port_); InitStubDecodingServerContext(sc); return sc; } ServerContext* NgxRewriteDriverFactory::NewServerContext() { LOG(DFATAL) << "MakeNgxServerContext should be used instead"; return NULL; } void NgxRewriteDriverFactory::ShutDown() { if (!shut_down_) { shut_down_ = true; SystemRewriteDriverFactory::ShutDown(); } } void NgxRewriteDriverFactory::ShutDownMessageHandlers() { ngx_message_handler_->set_buffer(NULL); ngx_html_parse_message_handler_->set_buffer(NULL); for (NgxMessageHandlerSet::iterator p = server_context_message_handlers_.begin(); p != server_context_message_handlers_.end(); ++p) { (*p)->set_buffer(NULL); } server_context_message_handlers_.clear(); } void NgxRewriteDriverFactory::StartThreads() { if (threads_started_) { return; } // TODO(jefftk): use a native nginx timer instead of running our own thread. // See issue #111. SchedulerThread* thread = new SchedulerThread(thread_system(), scheduler()); bool ok = thread->Start(); CHECK(ok) << "Unable to start scheduler thread"; defer_cleanup(thread->MakeDeleter()); threads_started_ = true; } void NgxRewriteDriverFactory::SetMainConf(NgxRewriteOptions* main_options) { // Propagate process-scope options from the copy we had during nginx option // parsing to our own. if (main_options != NULL) { default_options()->MergeOnlyProcessScopeOptions(*main_options); } } void NgxRewriteDriverFactory::LoggingInit( ngx_log_t* log, bool may_install_crash_handler) { log_ = log; net_instaweb::log_message_handler::Install(log); if (may_install_crash_handler && install_crash_handler()) { NgxMessageHandler::InstallCrashHandler(log); } ngx_message_handler_->set_log(log); ngx_html_parse_message_handler_->set_log(log); } void NgxRewriteDriverFactory::SetCircularBuffer( SharedCircularBuffer* buffer) { ngx_shared_circular_buffer_ = buffer; ngx_message_handler_->set_buffer(buffer); ngx_html_parse_message_handler_->set_buffer(buffer); } void NgxRewriteDriverFactory::SetServerContextMessageHandler( ServerContext* server_context, ngx_log_t* log) { NgxMessageHandler* handler = new NgxMessageHandler( timer(), thread_system()->NewMutex()); handler->set_log(log); // The ngx_shared_circular_buffer_ will be NULL if MessageBufferSize hasn't // been raised from its default of 0. handler->set_buffer(ngx_shared_circular_buffer_); server_context_message_handlers_.insert(handler); defer_cleanup(new Deleter(handler)); server_context->set_message_handler(handler); } void NgxRewriteDriverFactory::InitStats(Statistics* statistics) { // Init standard PSOL stats. SystemRewriteDriverFactory::InitStats(statistics); RewriteDriverFactory::InitStats(statistics); RateController::InitStats(statistics); // Init Ngx-specific stats. NgxServerContext::InitStats(statistics); InPlaceResourceRecorder::InitStats(statistics); } void NgxRewriteDriverFactory::PrepareForkedProcess(const char* name) { ngx_pid = ngx_getpid(); // Needed for logging to have the right PIDs. SystemRewriteDriverFactory::PrepareForkedProcess(name); } void NgxRewriteDriverFactory::NameProcess(const char* name) { SystemRewriteDriverFactory::NameProcess(name); // Superclass set status with prctl. Nginx has a helper function for setting // argv[0] as well, so let's use that. We'll show up as: // // nginx: pagespeed $name char name_for_setproctitle[32]; snprintf(name_for_setproctitle, sizeof(name_for_setproctitle), "pagespeed %s", name); ngx_setproctitle(name_for_setproctitle); } } // namespace net_instaweb