| // 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: jefftk@google.com (Jeff Kaufman) |
| |
| #include "pagespeed/system/system_rewrite_driver_factory.h" |
| |
| #include <algorithm> // for min |
| #include <cstdlib> |
| #include <map> |
| #include <set> |
| #include <utility> // for pair |
| |
| #include "apr_general.h" |
| #include "base/logging.h" |
| #include "net/instaweb/http/public/http_dump_url_async_writer.h" |
| #include "net/instaweb/http/public/http_dump_url_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/url_async_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/rewriter/public/static_asset_manager.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" |
| #include "pagespeed/system/system_server_context.h" |
| #include "pagespeed/system/system_thread_system.h" |
| #include "net/instaweb/util/public/property_cache.h" |
| #include "pagespeed/kernel/base/abstract_shared_mem.h" |
| #include "pagespeed/kernel/base/file_system.h" |
| #include "pagespeed/kernel/base/google_message_handler.h" |
| #include "pagespeed/kernel/base/md5_hasher.h" |
| #include "pagespeed/kernel/base/message_handler.h" |
| #include "pagespeed/kernel/base/null_shared_mem.h" |
| #include "pagespeed/kernel/base/posix_timer.h" |
| #include "pagespeed/kernel/base/scoped_ptr.h" |
| #include "pagespeed/kernel/base/statistics.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/base/timer.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/queued_worker_pool.h" |
| #include "pagespeed/kernel/util/input_file_nonce_generator.h" |
| #include "pagespeed/kernel/util/nonce_generator.h" |
| |
| namespace net_instaweb { |
| |
| class ProcessContext; |
| |
| namespace { |
| |
| const char kShutdownCount[] = "child_shutdown_count"; |
| |
| const char kStaticAssetPrefix[] = "StaticAssetPrefix"; |
| const char kUsePerVHostStatistics[] = "UsePerVHostStatistics"; |
| const char kInstallCrashHandler[] = "InstallCrashHandler"; |
| const char kNumRewriteThreads[] = "NumRewriteThreads"; |
| const char kNumExpensiveRewriteThreads[] = "NumExpensiveRewriteThreads"; |
| const char kForceCaching[] = "ForceCaching"; |
| const char kListOutstandingUrlsOnError[] = "ListOutstandingUrlsOnError"; |
| const char kMessageBufferSize[] = "MessageBufferSize"; |
| const char kTrackOriginalContentLength[] = "TrackOriginalContentLength"; |
| const char kCreateSharedMemoryMetadataCache[] = |
| "CreateSharedMemoryMetadataCache"; |
| |
| } // namespace |
| |
| SystemRewriteDriverFactory::SystemRewriteDriverFactory( |
| const ProcessContext& process_context, |
| SystemThreadSystem* thread_system, |
| AbstractSharedMem* shared_mem_runtime, /* may be null */ |
| StringPiece hostname, int port) |
| : RewriteDriverFactory(process_context, thread_system), |
| statistics_frozen_(false), |
| is_root_process_(true), |
| hostname_identifier_(StrCat(hostname, ":", IntegerToString(port))), |
| message_buffer_size_(0), |
| track_original_content_length_(false), |
| list_outstanding_urls_on_error_(false), |
| static_asset_prefix_("/pagespeed_static/"), |
| system_thread_system_(thread_system), |
| use_per_vhost_statistics_(true), |
| install_crash_handler_(false), |
| thread_counts_finalized_(false), |
| num_rewrite_threads_(-1), |
| num_expensive_rewrite_threads_(-1) { |
| if (shared_mem_runtime == NULL) { |
| #ifdef PAGESPEED_SUPPORT_POSIX_SHARED_MEM |
| shared_mem_runtime = new PthreadSharedMem(); |
| #else |
| shared_mem_runtime = new NullSharedMem(); |
| #endif |
| } |
| shared_mem_runtime_.reset(shared_mem_runtime); |
| } |
| |
| // We need an Init() method to finish construction because we want to call |
| // virtual methods that subclasses can override. |
| void SystemRewriteDriverFactory::Init() { |
| // Note: in Apache this must run after mod_pagespeed_register_hooks has |
| // completed. See http://httpd.apache.org/docs/2.4/developer/new_api_2_4.html |
| // and search for ap_mpm_query. |
| AutoDetectThreadCounts(); |
| |
| int thread_limit = LookupThreadLimit(); |
| thread_limit += num_rewrite_threads() + num_expensive_rewrite_threads(); |
| caches_.reset( |
| new SystemCaches(this, shared_mem_runtime_.get(), thread_limit)); |
| } |
| |
| SystemRewriteDriverFactory::~SystemRewriteDriverFactory() { |
| shared_mem_statistics_.reset(NULL); |
| } |
| |
| void SystemRewriteDriverFactory::InitApr() { |
| apr_initialize(); |
| atexit(apr_terminate); |
| } |
| |
| // Initializes global statistics object if needed, using factory to |
| // help with the settings if needed. |
| // Note: does not call set_statistics() on the factory. |
| Statistics* SystemRewriteDriverFactory::SetUpGlobalSharedMemStatistics( |
| const SystemRewriteOptions& options) { |
| if (shared_mem_statistics_.get() == NULL) { |
| shared_mem_statistics_.reset(AllocateAndInitSharedMemStatistics( |
| false /* not local */, "global", options)); |
| } |
| DCHECK(!statistics_frozen_); |
| statistics_frozen_ = true; |
| SetStatistics(shared_mem_statistics_.get()); |
| return shared_mem_statistics_.get(); |
| } |
| |
| SharedMemStatistics* SystemRewriteDriverFactory:: |
| AllocateAndInitSharedMemStatistics( |
| bool local, |
| const StringPiece& name, |
| const SystemRewriteOptions& options) { |
| // Note that we create the statistics object in the parent process, and |
| // it stays around in the kids but gets reinitialized for them |
| // inside ChildInit(), called from pagespeed_child_init. |
| GoogleString log_filename; |
| bool logging_enabled = false; |
| if (!options.log_dir().empty()) { |
| // Only enable statistics logging if a log_dir() is actually specified. |
| log_filename = StrCat(options.log_dir(), "/stats_log_", name); |
| logging_enabled = options.statistics_logging_enabled(); |
| } |
| SharedMemStatistics* stats = new SharedMemStatistics( |
| options.statistics_logging_interval_ms(), |
| options.statistics_logging_max_file_size_kb(), |
| log_filename, logging_enabled, |
| // TODO(jmarantz): it appears that filename_prefix() is not actually |
| // established at the time of this construction, calling into question |
| // whether we are naming our shared-memory segments correctly. |
| StrCat(filename_prefix(), name), shared_mem_runtime(), |
| message_handler(), file_system(), timer()); |
| NonStaticInitStats(stats); |
| bool init_ok = stats->Init(true, message_handler()); |
| if (local && init_ok) { |
| local_shm_stats_segment_names_.push_back(stats->SegmentName()); |
| } |
| return stats; |
| } |
| |
| void SystemRewriteDriverFactory::InitStats(Statistics* statistics) { |
| // Init standard PSOL stats. |
| RewriteDriverFactory::InitStats(statistics); |
| |
| // Init System-specific stats. |
| SerfUrlAsyncFetcher::InitStats(statistics); |
| StdioFileSystem::InitStats(statistics); |
| SystemCaches::InitStats(statistics); |
| PropertyCache::InitCohortStats(RewriteDriver::kBeaconCohort, statistics); |
| PropertyCache::InitCohortStats(RewriteDriver::kDomCohort, statistics); |
| InPlaceResourceRecorder::InitStats(statistics); |
| RateController::InitStats(statistics); |
| |
| statistics->AddVariable(kShutdownCount); |
| } |
| |
| NonceGenerator* SystemRewriteDriverFactory::DefaultNonceGenerator() { |
| MessageHandler* handler = message_handler(); |
| FileSystem::InputFile* random_file = |
| file_system()->OpenInputFile("/dev/urandom", handler); |
| CHECK(random_file != NULL) << "Couldn't open /dev/urandom"; |
| // Now use the key to construct an InputFileNonceGenerator. Passing in a NULL |
| // random_file here will create a generator that will fail on first access. |
| return new InputFileNonceGenerator(random_file, file_system(), |
| thread_system()->NewMutex(), handler); |
| } |
| |
| void SystemRewriteDriverFactory::SetupCaches(ServerContext* server_context) { |
| caches_->SetupCaches(server_context, enable_property_cache()); |
| } |
| |
| void SystemRewriteDriverFactory::InitStaticAssetManager( |
| StaticAssetManager* static_asset_manager) { |
| static_asset_manager->set_library_url_prefix(static_asset_prefix_); |
| } |
| |
| QueuedWorkerPool* SystemRewriteDriverFactory::CreateWorkerPool( |
| WorkerPoolCategory pool, StringPiece name) { |
| switch (pool) { |
| case kHtmlWorkers: |
| // In Apache this will effectively be 0, as it doesn't use HTML threads. |
| return new QueuedWorkerPool(1, name, thread_system()); |
| case kRewriteWorkers: |
| return new QueuedWorkerPool(num_rewrite_threads_, name, thread_system()); |
| case kLowPriorityRewriteWorkers: |
| return new QueuedWorkerPool(num_expensive_rewrite_threads_, |
| name, |
| thread_system()); |
| default: |
| return RewriteDriverFactory::CreateWorkerPool(pool, name); |
| } |
| } |
| |
| void SystemRewriteDriverFactory::ParentOrChildInit() { |
| SharedCircularBufferInit(is_root_process_); |
| } |
| |
| void SystemRewriteDriverFactory::RootInit() { |
| ParentOrChildInit(); |
| |
| // Let SystemCaches know about the various paths we have in configuration |
| // first, as well as the memcached instances. |
| for (SystemServerContextSet::iterator |
| p = uninitialized_server_contexts_.begin(), |
| e = uninitialized_server_contexts_.end(); p != e; ++p) { |
| SystemServerContext* server_context = *p; |
| caches_->RegisterConfig(server_context->global_system_rewrite_options()); |
| } |
| |
| caches_->RootInit(); |
| } |
| |
| void SystemRewriteDriverFactory::ChildInit() { |
| const SystemRewriteOptions* conf = |
| SystemRewriteOptions::DynamicCast(default_options()); |
| CHECK(conf != NULL); |
| |
| StdioFileSystem* fs = dynamic_cast<StdioFileSystem*>(file_system()); |
| DCHECK(fs != NULL) << "Expected StdioFileSystem so we can call TrackTiming"; |
| if (fs != NULL) { |
| fs->TrackTiming(conf->slow_file_latency_threshold_us(), |
| timer(), statistics(), |
| message_handler()); |
| } |
| |
| is_root_process_ = false; |
| system_thread_system_->PermitThreadStarting(); |
| |
| ParentOrChildInit(); |
| |
| SetupMessageHandlers(); |
| |
| if (shared_mem_statistics_.get() != NULL) { |
| shared_mem_statistics_->Init(false, message_handler()); |
| } |
| |
| caches_->ChildInit(); |
| |
| // Static asset config is process-global. |
| if (conf->has_static_assets_to_cdn()) { |
| StaticAssetConfig out_conf; |
| conf->FillInStaticAssetCDNConf(&out_conf); |
| static_asset_manager()->ServeAssetsFromGStatic( |
| conf->static_assets_cdn_base()); |
| static_asset_manager()->ApplyGStaticConfiguration( |
| out_conf, |
| StaticAssetManager::kInitialConfiguration); |
| } |
| |
| for (SystemServerContextSet::iterator |
| p = uninitialized_server_contexts_.begin(), |
| e = uninitialized_server_contexts_.end(); p != e; ++p) { |
| SystemServerContext* server_context = *p; |
| server_context->ChildInit(this); |
| } |
| uninitialized_server_contexts_.clear(); |
| } |
| |
| // TODO(jmarantz): make this per-vhost. |
| void SystemRewriteDriverFactory::SharedCircularBufferInit(bool is_root) { |
| // Set buffer size to 0 means turning it off |
| if (shared_mem_runtime() != NULL && (message_buffer_size_ != 0)) { |
| // TODO(jmarantz): it appears that filename_prefix() is not actually |
| // established at the time of this construction, calling into question |
| // whether we are naming our shared-memory segments correctly. |
| shared_circular_buffer_.reset(new SharedCircularBuffer( |
| shared_mem_runtime(), |
| message_buffer_size_, |
| filename_prefix().as_string(), |
| hostname_identifier())); |
| if (shared_circular_buffer_->InitSegment(is_root, message_handler())) { |
| SetCircularBuffer(shared_circular_buffer_.get()); |
| } |
| } |
| } |
| |
| RewriteOptions::OptionSettingResult |
| SystemRewriteDriverFactory::ParseAndSetOption1(StringPiece option, |
| StringPiece arg, |
| bool process_scope, |
| GoogleString* msg, |
| MessageHandler* handler) { |
| // First check the scope. |
| if (StringCaseEqual(option, kStaticAssetPrefix) || |
| StringCaseEqual(option, kUsePerVHostStatistics) || |
| StringCaseEqual(option, kInstallCrashHandler) || |
| StringCaseEqual(option, kNumRewriteThreads) || |
| StringCaseEqual(option, kNumExpensiveRewriteThreads)) { |
| if (!process_scope) { |
| *msg = StrCat("'", option, "' is global and can't be set at this scope."); |
| return RewriteOptions::kOptionValueInvalid; |
| } |
| } else if (StringCaseEqual(option, kForceCaching) || |
| StringCaseEqual(option, kListOutstandingUrlsOnError) || |
| StringCaseEqual(option, kMessageBufferSize) || |
| StringCaseEqual(option, kTrackOriginalContentLength)) { |
| if (!process_scope) { |
| // msg is only printed to the user on error, so warnings must be logged. |
| handler->Message( |
| kWarning, "'%s' is global and is ignored at this scope", |
| option.as_string().c_str()); |
| // OK here means "move on" not "accepted and applied". |
| return RewriteOptions::kOptionOk; |
| } |
| } else { |
| return RewriteOptions::kOptionNameUnknown; |
| } |
| |
| // Scope is ok and option is known. Parse and apply. |
| |
| if (StringCaseEqual(option, kStaticAssetPrefix)) { |
| set_static_asset_prefix(arg); |
| return RewriteOptions::kOptionOk; |
| } |
| |
| // Most of our options take booleans, so just parse once. |
| bool is_on = false; |
| RewriteOptions::OptionSettingResult parsed_as_bool = |
| RewriteOptions::ParseFromString(arg, &is_on) ? |
| RewriteOptions::kOptionOk : RewriteOptions::kOptionValueInvalid; |
| if (StringCaseEqual(option, kUsePerVHostStatistics)) { |
| set_use_per_vhost_statistics(is_on); |
| return parsed_as_bool; |
| } else if (StringCaseEqual(option, kForceCaching)) { |
| set_force_caching(is_on); |
| return parsed_as_bool; |
| } else if (StringCaseEqual(option, kInstallCrashHandler)) { |
| set_install_crash_handler(is_on); |
| return parsed_as_bool; |
| } else if (StringCaseEqual(option, kListOutstandingUrlsOnError)) { |
| list_outstanding_urls_on_error(is_on); |
| return parsed_as_bool; |
| } else if (StringCaseEqual(option, kTrackOriginalContentLength)) { |
| set_track_original_content_length(is_on); |
| return parsed_as_bool; |
| } |
| |
| // Others take an integer >= 0. |
| // |
| // Values of 0 have special meanings: |
| // Num(Expensive)RewriteThreads: autodetect (see AutoDetectThreadCounts()) |
| // MessageBufferSize: disable the message buffer |
| int int_value = 0; |
| RewriteOptions::OptionSettingResult parsed_as_int = |
| RewriteOptions::ParseFromString(arg, &int_value) ? |
| RewriteOptions::kOptionOk : RewriteOptions::kOptionValueInvalid; |
| if (StringCaseEqual(option, kNumRewriteThreads)) { |
| set_num_rewrite_threads(int_value); |
| return parsed_as_int; |
| } else if (StringCaseEqual(option, kNumExpensiveRewriteThreads)) { |
| set_num_expensive_rewrite_threads(int_value); |
| return parsed_as_int; |
| } else if (StringCaseEqual(option, kMessageBufferSize)) { |
| set_message_buffer_size(int_value); |
| return parsed_as_int; |
| } |
| |
| LOG(FATAL) << "Unknown options should have been handled in scope checking."; |
| return RewriteOptions::kOptionNameUnknown; |
| } |
| |
| RewriteOptions::OptionSettingResult |
| SystemRewriteDriverFactory::ParseAndSetOption2(StringPiece option, |
| StringPiece arg1, |
| StringPiece arg2, |
| bool process_scope, |
| GoogleString* msg, |
| MessageHandler* handler) { |
| if (StringCaseEqual(option, kCreateSharedMemoryMetadataCache)) { |
| if (!process_scope) { |
| // msg is only printed to the user on error, so warnings must be logged. |
| handler->Message( |
| kWarning, "'%s' is global and is ignored at this scope", |
| option.as_string().c_str()); |
| // OK here means "move on" not "accepted and applied". |
| return RewriteOptions::kOptionOk; |
| } |
| |
| int64 kb = 0; |
| if (!StringToInt64(arg2, &kb) || kb < 0) { |
| *msg = "size_kb must be a positive 64-bit integer"; |
| return RewriteOptions::kOptionValueInvalid; |
| } |
| bool ok = caches()->CreateShmMetadataCache(arg1, kb, msg); |
| return ok ? RewriteOptions::kOptionOk : RewriteOptions::kOptionValueInvalid; |
| } |
| return RewriteOptions::kOptionNameUnknown; |
| } |
| |
| void SystemRewriteDriverFactory::PostConfig( |
| const std::vector<SystemServerContext*>& server_contexts, |
| GoogleString* error_message, |
| int* error_index, |
| Statistics** global_statistics) { |
| for (int i = 0, n = server_contexts.size(); i < n; ++i) { |
| server_contexts[i]->CollapseConfigOverlaysAndComputeSignatures(); |
| SystemRewriteOptions* options = |
| server_contexts[i]->global_system_rewrite_options(); |
| if (options->unplugged()) { |
| continue; |
| } |
| |
| if (options->enabled()) { |
| GoogleString file_cache_path = options->file_cache_path(); |
| if (file_cache_path.empty()) { |
| *error_index = i; |
| *error_message = "FileCachePath must not be empty"; |
| return; |
| } |
| } |
| |
| if (options->statistics_enabled()) { |
| // Lazily create shared-memory statistics if enabled in any config, even |
| // when PageSpeed is totally disabled. This allows statistics to work if |
| // PageSpeed gets turned on via .htaccess or query param. |
| if (*global_statistics == NULL) { |
| *global_statistics = SetUpGlobalSharedMemStatistics(*options); |
| } |
| |
| // If we have per-vhost statistics on as well, then set it up. |
| if (use_per_vhost_statistics()) { |
| server_contexts[i]->CreateLocalStatistics(*global_statistics, this); |
| } |
| } |
| } |
| } |
| |
| void SystemRewriteDriverFactory::StopCacheActivity() { |
| RewriteDriverFactory::StopCacheActivity(); |
| caches_->StopCacheActivity(); |
| } |
| |
| void SystemRewriteDriverFactory::ShutDown() { |
| if (!is_root_process_) { |
| Variable* child_shutdown_count = statistics()->GetVariable(kShutdownCount); |
| child_shutdown_count->Add(1); |
| message_handler()->MessageS(kInfo, "Shutting down PageSpeed child"); |
| } |
| StopCacheActivity(); |
| |
| // Next, we shutdown the fetchers before killing the workers in |
| // RewriteDriverFactory::ShutDown; this is so any rewrite jobs in progress |
| // can quickly wrap up. |
| for (FetcherMap::iterator p = fetcher_map_.begin(), e = fetcher_map_.end(); |
| p != e; ++p) { |
| UrlAsyncFetcher* fetcher = p->second; |
| fetcher->ShutDown(); |
| defer_cleanup(new Deleter<UrlAsyncFetcher>(fetcher)); |
| } |
| fetcher_map_.clear(); |
| ShutDownFetchers(); |
| |
| RewriteDriverFactory::ShutDown(); |
| |
| caches_->ShutDown(message_handler()); |
| |
| ShutDownMessageHandlers(); |
| |
| if (is_root_process_) { |
| // Cleanup statistics. |
| // TODO(morlovich): This looks dangerous with async. |
| if (shared_mem_statistics_.get() != NULL) { |
| shared_mem_statistics_->GlobalCleanup(message_handler()); |
| } |
| |
| // Likewise for local ones. We no longer have the objects here |
| // (since SplitStats destroyed them), but we saved the segment names. |
| for (int i = 0, n = local_shm_stats_segment_names_.size(); i < n; ++i) { |
| SharedMemStatistics::GlobalCleanup(shared_mem_runtime_.get(), |
| local_shm_stats_segment_names_[i], |
| message_handler()); |
| } |
| |
| // Cleanup SharedCircularBuffer. |
| // Use GoogleMessageHandler instead of SystemMessageHandler. |
| // As we are cleaning SharedCircularBuffer, we do not want to write to its |
| // buffer and passing SystemMessageHandler here may cause infinite loop. |
| GoogleMessageHandler handler; |
| if (shared_circular_buffer_ != NULL) { |
| shared_circular_buffer_->GlobalCleanup(&handler); |
| } |
| } |
| } |
| |
| GoogleString SystemRewriteDriverFactory::GetFetcherKey( |
| bool include_slurping_config, const SystemRewriteOptions* config) { |
| // Include all the fetcher parameters in the fetcher key, one per line. |
| GoogleString key; |
| if (config->unplugged()) { |
| key = "unplugged"; |
| } else { |
| key = StrCat( |
| list_outstanding_urls_on_error_ ? "list_errors\n" : "no_errors\n", |
| config->fetcher_proxy(), "\n", |
| config->fetch_with_gzip() ? "fetch_with_gzip\n": "no_gzip\n", |
| track_original_content_length_ ? "track_content_length\n" : "no_track\n" |
| "timeout: ", Integer64ToString(config->blocking_fetch_timeout_ms()), |
| "\n"); |
| if (config->slurping_enabled() && include_slurping_config) { |
| if (config->slurp_read_only()) { |
| StrAppend(&key, "R", config->slurp_directory(), "\n"); |
| } else { |
| StrAppend(&key, "W", config->slurp_directory(), "\n"); |
| } |
| } |
| StrAppend(&key, |
| "\nhttps: ", config->https_options(), |
| "\ncert_dir: ", config->ssl_cert_directory(), |
| "\ncert_file: ", config->ssl_cert_file()); |
| } |
| |
| return key; |
| } |
| |
| UrlAsyncFetcher* SystemRewriteDriverFactory::GetFetcher( |
| SystemRewriteOptions* config) { |
| // Include all the fetcher parameters in the fetcher key, one per line. |
| GoogleString key = GetFetcherKey(true, config); |
| std::pair<FetcherMap::iterator, bool> result = fetcher_map_.insert( |
| std::make_pair(key, static_cast<UrlAsyncFetcher*>(NULL))); |
| FetcherMap::iterator iter = result.first; |
| if (result.second) { |
| UrlAsyncFetcher* fetcher = NULL; |
| if (config->slurping_enabled()) { |
| if (config->slurp_read_only()) { |
| HttpDumpUrlFetcher* dump_fetcher = new HttpDumpUrlFetcher( |
| config->slurp_directory(), file_system(), timer()); |
| fetcher = dump_fetcher; |
| } else { |
| UrlAsyncFetcher* base_fetcher = GetBaseFetcher(config); |
| HttpDumpUrlAsyncWriter* dump_writer = new HttpDumpUrlAsyncWriter( |
| config->slurp_directory(), base_fetcher, file_system(), timer()); |
| fetcher = dump_writer; |
| } |
| } else { |
| fetcher = GetBaseFetcher(config); |
| if (config->rate_limit_background_fetches()) { |
| // Unfortunately, we need stats for load-shedding. |
| if (config->statistics_enabled()) { |
| TakeOwnership(fetcher); |
| fetcher = new RateControllingUrlAsyncFetcher( |
| fetcher, max_queue_size(), requests_per_host(), queued_per_host(), |
| thread_system(), statistics()); |
| } else { |
| message_handler()->Message( |
| kError, "Can't enable fetch rate-limiting without statistics"); |
| } |
| } |
| } |
| iter->second = fetcher; |
| } |
| return iter->second; |
| } |
| |
| UrlAsyncFetcher* SystemRewriteDriverFactory::AllocateFetcher( |
| SystemRewriteOptions* config) { |
| SerfUrlAsyncFetcher* serf = new SerfUrlAsyncFetcher( |
| config->fetcher_proxy().c_str(), |
| NULL, // Do not use the Factory pool so we can control deletion. |
| thread_system(), statistics(), timer(), |
| config->blocking_fetch_timeout_ms(), |
| message_handler()); |
| serf->set_list_outstanding_urls_on_error(list_outstanding_urls_on_error_); |
| serf->set_fetch_with_gzip(config->fetch_with_gzip()); |
| serf->set_track_original_content_length(track_original_content_length_); |
| serf->SetHttpsOptions(config->https_options()); |
| serf->SetSslCertificatesDir(config->ssl_cert_directory()); |
| serf->SetSslCertificatesFile(config->ssl_cert_file()); |
| return serf; |
| } |
| |
| |
| UrlAsyncFetcher* SystemRewriteDriverFactory::GetBaseFetcher( |
| SystemRewriteOptions* config) { |
| GoogleString cache_key = GetFetcherKey(false, config); |
| std::pair<FetcherMap::iterator, bool> result = base_fetcher_map_.insert( |
| std::make_pair(cache_key, static_cast<UrlAsyncFetcher*>(NULL))); |
| FetcherMap::iterator iter = result.first; |
| if (result.second) { |
| iter->second = AllocateFetcher(config); |
| } |
| return iter->second; |
| } |
| |
| UrlAsyncFetcher* SystemRewriteDriverFactory::DefaultAsyncUrlFetcher() { |
| LOG(DFATAL) << "The fetchers are not global, but kept in a map."; |
| return NULL; |
| } |
| |
| FileSystem* SystemRewriteDriverFactory::DefaultFileSystem() { |
| return new StdioFileSystem(); |
| } |
| |
| Hasher* SystemRewriteDriverFactory::NewHasher() { |
| return new MD5Hasher(); |
| } |
| |
| Timer* SystemRewriteDriverFactory::DefaultTimer() { |
| return new PosixTimer(); |
| } |
| |
| NamedLockManager* SystemRewriteDriverFactory::DefaultLockManager() { |
| LOG(DFATAL) << "Locks are owned by SystemCachePath, not the factory"; |
| return NULL; |
| } |
| |
| ServerContext* SystemRewriteDriverFactory::NewServerContext() { |
| LOG(DFATAL) << "Use implementation-specific MakeXServerXContext() instead"; |
| return NULL; |
| } |
| |
| int SystemRewriteDriverFactory::requests_per_host() { |
| CHECK(thread_counts_finalized_); |
| return std::min(4, num_rewrite_threads_); |
| } |
| |
| void SystemRewriteDriverFactory::AutoDetectThreadCounts() { |
| if (thread_counts_finalized_) { |
| return; |
| } |
| |
| if (IsServerThreaded()) { |
| if (num_rewrite_threads_ <= 0) { |
| num_rewrite_threads_ = 4; |
| } |
| if (num_expensive_rewrite_threads_ <= 0) { |
| num_expensive_rewrite_threads_ = 4; |
| } |
| message_handler()->Message( |
| kInfo, "Detected threaded server." |
| " Own threads: %d Rewrite, %d Expensive Rewrite.", |
| num_rewrite_threads_, num_expensive_rewrite_threads_); |
| |
| } else { |
| if (num_rewrite_threads_ <= 0) { |
| num_rewrite_threads_ = 1; |
| } |
| if (num_expensive_rewrite_threads_ <= 0) { |
| num_expensive_rewrite_threads_ = 1; |
| } |
| message_handler()->Message( |
| kInfo, "No threading detected." |
| " Own threads: %d Rewrite, %d Expensive Rewrite.", |
| num_rewrite_threads_, num_expensive_rewrite_threads_); |
| } |
| |
| thread_counts_finalized_ = true; |
| } |
| |
| } // namespace net_instaweb |