diff options
author | pmeenan <pmeenan@chromium.org> | 2015-01-30 13:59:59 -0800 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-01-30 22:00:53 +0000 |
commit | ed4b6f8b7392eb64fc664aa49f2627ecb5fdc073 (patch) | |
tree | c41b78ef68d5d37d5ec28ecaaa4037607524060d /components/network_hints/renderer | |
parent | 7a47c4214a06aa1778ced28dc6c57658326bbf3d (diff) | |
download | chromium_src-ed4b6f8b7392eb64fc664aa49f2627ecb5fdc073.zip chromium_src-ed4b6f8b7392eb64fc664aa49f2627ecb5fdc073.tar.gz chromium_src-ed4b6f8b7392eb64fc664aa49f2627ecb5fdc073.tar.bz2 |
Renamed the dns_prefetch component to network_predictor
This is in preparation for adding preconnect support which goes through
the same underlying plumbing. dns_prefetch was too restrictive of a
component name and was only exposing the single method.
There are no functionality changes in this CL, just the rename (the namespace was also renamed)
BUG=450682
Review URL: https://codereview.chromium.org/848303005
Cr-Commit-Position: refs/heads/master@{#314001}
Diffstat (limited to 'components/network_hints/renderer')
10 files changed, 906 insertions, 0 deletions
diff --git a/components/network_hints/renderer/BUILD.gn b/components/network_hints/renderer/BUILD.gn new file mode 100644 index 0000000..8c56754 --- /dev/null +++ b/components/network_hints/renderer/BUILD.gn @@ -0,0 +1,21 @@ +# Copyright 2014 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# GYP version: components/network_hints.gypi:predictor_renderer +source_set("renderer") { + sources = [ + "dns_prefetch_queue.cc", + "dns_prefetch_queue.h", + "prescient_networking_dispatcher.cc", + "prescient_networking_dispatcher.h", + "renderer_dns_prefetch.cc", + "renderer_dns_prefetch.h", + ] + + public_deps = [ + "//components/network_hints/common", + "//content/public/renderer", + "//third_party/WebKit/public:blink", + ] +} diff --git a/components/network_hints/renderer/DEPS b/components/network_hints/renderer/DEPS new file mode 100644 index 0000000..d6ea0c4 --- /dev/null +++ b/components/network_hints/renderer/DEPS @@ -0,0 +1,5 @@ +include_rules = [ + "+components/network_hints/common", + "+content/public/renderer", + "+third_party/WebKit/public", +] diff --git a/components/network_hints/renderer/dns_prefetch_queue.cc b/components/network_hints/renderer/dns_prefetch_queue.cc new file mode 100644 index 0000000..8328df9 --- /dev/null +++ b/components/network_hints/renderer/dns_prefetch_queue.cc @@ -0,0 +1,156 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// See header file for description of DnsQueue class + +#include "components/network_hints/renderer/dns_prefetch_queue.h" + +#include "base/logging.h" +#include "base/metrics/stats_counters.h" + +namespace network_hints { + +DnsQueue::DnsQueue(BufferSize size) + : buffer_(new char[size + 2]), + buffer_size_(size + 1), + buffer_sentinel_(size + 1), + size_(0) { + CHECK(0 < static_cast<BufferSize>(size + 3)); // Avoid overflow worries. + buffer_[buffer_sentinel_] = '\0'; // Guard byte to help reading data. + readable_ = writeable_ = 0; // Buffer starts empty. +} + +DnsQueue::~DnsQueue(void) { +} + +void DnsQueue::Clear() { + size_ = 0; + readable_ = writeable_; + DCHECK(Validate()); +} + +// Push takes an unterminated string plus its length. +// The string must not contain a null terminator. +// Exactly length chars are written, or nothing is written. +// Returns true for success, false there was no room to push. +DnsQueue::PushResult DnsQueue::Push(const char* source, + const size_t unsigned_length) { + BufferSize length = static_cast<BufferSize>(unsigned_length); + if (0 > length+1) // Avoid overflows in conversion to signed. + return OVERFLOW_PUSH; + + // To save on sites with a LOT of links to the SAME domain, we have a + // a compaction hack that removes duplicates when we try to push() a + // match with the last push. + if (0 < size_ && readable_ + length < buffer_sentinel_ && + 0 == strncmp(source, &buffer_[readable_], unsigned_length) && + '\0' == buffer_[readable_ + unsigned_length]) { + SIMPLE_STATS_COUNTER("DNS.PrefetchDnsRedundantPush"); + + // We already wrote this name to the queue, so we'll skip this repeat. + return REDUNDANT_PUSH; + } + + // Calling convention precludes nulls. + DCHECK(!length || '\0' != source[length - 1]); + + DCHECK(Validate()); + + BufferSize available_space = readable_ - writeable_; + + if (0 >= available_space) { + available_space += buffer_size_; + } + + if (length + 1 >= available_space) { + SIMPLE_STATS_COUNTER("DNS.PrefetchDnsQueueFull"); + return OVERFLOW_PUSH; // Not enough space to push. + } + + BufferSize dest = writeable_; + BufferSize space_till_wrap = buffer_sentinel_ - dest; + if (space_till_wrap < length + 1) { + // Copy until we run out of room at end of buffer. + std::memcpy(&buffer_[dest], source, space_till_wrap); + // Ensure caller didn't have embedded '\0' and also + // ensure trailing sentinel was in place. + // Relies on sentinel. + DCHECK(static_cast<size_t>(space_till_wrap) == strlen(&buffer_[dest])); + + length -= space_till_wrap; + source += space_till_wrap; + dest = 0; // Continue writing at start of buffer. + } + + // Copy any remaining portion of source. + std::memcpy(&buffer_[dest], source, length); + DCHECK(dest + length < buffer_sentinel_); + buffer_[dest + length] = '\0'; // We need termination in our buffer. + // Preclude embedded '\0'. + DCHECK(static_cast<size_t>(length) == strlen(&buffer_[dest])); + + dest += length + 1; + if (dest == buffer_sentinel_) + dest = 0; + + writeable_ = dest; + size_++; + DCHECK(Validate()); + return SUCCESSFUL_PUSH; +} + +// Extracts the next available string from the buffer. +// The returned string is null terminated, and hence has length +// that is exactly one greater than the written string. +// If the buffer is empty, then the Pop and returns false. +bool DnsQueue::Pop(std::string* out_string) { + DCHECK(Validate()); + // Sentinel will preclude memory reads beyond buffer's end. + DCHECK('\0' == buffer_[buffer_sentinel_]); + + if (readable_ == writeable_) { + return false; // buffer was empty + } + + // Constructor *may* rely on sentinel for null termination. + (*out_string) = &buffer_[readable_]; + // Our sentinel_ at end of buffer precludes an overflow in cast. + BufferSize first_fragment_size = static_cast<BufferSize> (out_string->size()); + + BufferSize terminal_null; + if (readable_ + first_fragment_size >= buffer_sentinel_) { + // Sentinel was used, so we need the portion after the wrap. + out_string->append(&buffer_[0]); // Fragment at start of buffer. + // Sentinel precludes overflow in cast to signed type. + terminal_null = static_cast<BufferSize>(out_string->size()) + - first_fragment_size; + } else { + terminal_null = readable_ + first_fragment_size; + } + DCHECK('\0' == buffer_[terminal_null]); + + BufferSize new_readable = terminal_null + 1; + if (buffer_sentinel_ == new_readable) + new_readable = 0; + + readable_ = new_readable; + size_--; + if (readable_ == writeable_ || 0 == size_) { + // Queue is empty, so reset to start of buffer to help with peeking. + readable_ = writeable_ = 0; + } + DCHECK(Validate()); + return true; +} + +bool DnsQueue::Validate() { + return (readable_ >= 0) && + readable_ < buffer_sentinel_ && + writeable_ >= 0 && + writeable_ < buffer_sentinel_ && + '\0' == buffer_[buffer_sentinel_] && + ((0 == size_) == (readable_ == writeable_)); +} + +} // namespace network_hints diff --git a/components/network_hints/renderer/dns_prefetch_queue.h b/components/network_hints/renderer/dns_prefetch_queue.h new file mode 100644 index 0000000..72b4fad --- /dev/null +++ b/components/network_hints/renderer/dns_prefetch_queue.h @@ -0,0 +1,93 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// DnsQueue is implemented as an almost FIFO circular buffer for text +// strings that don't have embedded nulls ('\0'). The "almost" element is that +// some duplicate strings may be removed (i.e., the string won't really be +// pushed *if* the class happens to notice that a duplicate is already in the +// queue). +// The buffers internal format is null terminated character strings +// (a.k.a., c_strings). +// It is written to be as fast as possible during push() operations, so +// that there will be minimal performance impact on a supplier thread. +// The push() operation will not block, and no memory allocation is involved +// (internally) during the push operations. +// The one caveat is that if there is insufficient space in the buffer to +// accept additional string via a push(), then the push() will fail, and +// the buffer will be unmodified. + +// This class was designed for use in DNS prefetch operations. During +// rendering, the supplier is the renderer (typically), and the consumer +// is a thread that sends messages to an async DNS resolver. + +#ifndef COMPONENTS_NETWORK_HINTS_RENDERER_DNS_PREFETCH_QUEUE_H__ +#define COMPONENTS_NETWORK_HINTS_RENDERER_DNS_PREFETCH_QUEUE_H__ + +#include <string> + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" + +namespace network_hints { + +// A queue of DNS lookup requests for internal use within the network_hints +// component. +class DnsQueue { + public: + // BufferSize is a signed type used for indexing into a buffer. + typedef int32 BufferSize; + + enum PushResult { SUCCESSFUL_PUSH, OVERFLOW_PUSH, REDUNDANT_PUSH }; + + // The size specified in the constructor creates a buffer large enough + // to hold at most one string of that length, or "many" + // strings of considerably shorter length. Note that strings + // are padded internally with a terminal '\0" while stored, + // so if you are trying to be precise and get N strings of + // length K to fit, you should actually construct a buffer with + // an internal size of N*(K+1). + explicit DnsQueue(BufferSize size); + ~DnsQueue(void); + + size_t Size() const { return size_; } + void Clear(); + + // Push takes an unterminated string of the given length + // and inserts it into the queue for later + // extraction by read. For each successful push(), there + // can later be a corresponding read() to extracted the text. + // The string must not contain an embedded null terminator + // Exactly length chars are written, or the push fails (where + // "fails" means nothing is written). + // Returns true for success, false for failure (nothing written). + PushResult Push(const char* source, const size_t length); + + PushResult Push(std::string source) { + return Push(source.c_str(), source.length()); + } + + // Extract the next available string from the buffer. + // If the buffer is empty, then return false. + bool Pop(std::string* out_string); + + private: + bool Validate(); // Checks that all internal data is valid. + + const scoped_ptr<char[]> buffer_; // Circular buffer, plus extra char ('\0'). + const BufferSize buffer_size_; // Size one smaller than allocated space. + const BufferSize buffer_sentinel_; // Index of extra '\0' at end of buffer_. + + // If writable_ == readable_, then the buffer is empty. + BufferSize readable_; // Next readable char in buffer_. + BufferSize writeable_; // The next space in buffer_ to push. + + // Number of queued strings + size_t size_; + + DISALLOW_COPY_AND_ASSIGN(DnsQueue); +}; // class DnsQueue + +} // namespace network_hints + +#endif // COMPONENTS_NETWORK_HINTS_RENDERER_DNS_PREFETCH_QUEUE_H__ diff --git a/components/network_hints/renderer/dns_prefetch_queue_unittest.cc b/components/network_hints/renderer/dns_prefetch_queue_unittest.cc new file mode 100644 index 0000000..cb808c1 --- /dev/null +++ b/components/network_hints/renderer/dns_prefetch_queue_unittest.cc @@ -0,0 +1,262 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <sstream> + +#include "components/network_hints/renderer/dns_prefetch_queue.h" +#include "testing/gtest/include/gtest/gtest.h" + +// Single threaded tests of DnsQueue functionality. + +namespace network_hints { + +class DnsQueueTest : public testing::Test { +}; + +// Define a helper class that does Push'es and Pop's of numbers. +// This makes it easy to test a LOT of reads, and keep the expected Pop +// value in sync with the Push value. +class DnsQueueSequentialTester { + public: + DnsQueueSequentialTester(DnsQueue& buffer, int32 read_counter = 0, + int32 write_counter = 0); + + // Return of false means buffer was full, or would not take entry. + bool Push(void); // Push the string value of next number. + + // Return of false means buffer returned wrong value. + bool Pop(void); // Validate string value of next read. + + private: + DnsQueue* buffer_; + int32 read_counter_; // expected value of next read string. + int32 write_counter_; // Numerical value to write next string. + DISALLOW_COPY_AND_ASSIGN(DnsQueueSequentialTester); +}; + + +DnsQueueSequentialTester::DnsQueueSequentialTester( + DnsQueue& buffer, int32 read_counter, int32 write_counter) + : buffer_(&buffer), + read_counter_(read_counter), + write_counter_(write_counter) { +} + +bool DnsQueueSequentialTester::Push(void) { + std::ostringstream value; + value << write_counter_; + + // Exercise both write methods intermittently. + DnsQueue::PushResult result = (write_counter_ % 2) ? + buffer_->Push(value.str().c_str(), value.str().size()) : + buffer_->Push(value.str()); + if (DnsQueue::SUCCESSFUL_PUSH == result) + write_counter_++; + return DnsQueue::OVERFLOW_PUSH != result; +} + +bool DnsQueueSequentialTester::Pop(void) { + std::string string; + if (buffer_->Pop(&string)) { + std::ostringstream expected_value; + expected_value << read_counter_++; + EXPECT_STREQ(expected_value.str().c_str(), string.c_str()) + << "Pop did not match write for value " << read_counter_; + return true; + } + return false; +} + + +TEST(DnsQueueTest, BufferUseCheck) { + // Use a small buffer so we can see that we can't write a string as soon as it + // gets longer than one less than the buffer size. The extra empty character + // is used to keep read and write pointers from overlapping when buffer is + // full. This shows the buffer size can constrain writes (and we're not + // scribbling all over memory). + const int buffer_size = 3; // Just room for 2 digts plus '\0' plus blank. + std::string string; + DnsQueue buffer(buffer_size); + DnsQueueSequentialTester tester(buffer); + + EXPECT_FALSE(tester.Pop()) << "Pop from empty buffer succeeded"; + + int i; + for (i = 0; i < 102; i++) { + if (!tester.Push()) + break; // String was too large. + EXPECT_TRUE(tester.Pop()) << "Unable to read back data " << i; + EXPECT_FALSE(buffer.Pop(&string)) + << "read from empty buffer not flagged"; + } + + EXPECT_GE(i, 100) << "Can't write 2 digit strings in 4 character buffer"; + EXPECT_LT(i, 101) << "We wrote 3 digit strings into a 4 character buffer"; +} + +TEST(DnsQueueTest, SubstringUseCheck) { + // Verify that only substring is written/read. + const int buffer_size = 100; + const char big_string[] = "123456789"; + std::string string; + DnsQueue buffer(buffer_size); + + EXPECT_FALSE(buffer.Pop(&string)) << "Initial buffer not empty"; + + EXPECT_EQ(DnsQueue::SUCCESSFUL_PUSH, buffer.Push(big_string, 3)) + << "Can't write string"; + EXPECT_EQ(DnsQueue::SUCCESSFUL_PUSH, buffer.Push(big_string, 0)) + << "Can't write null string"; + EXPECT_EQ(DnsQueue::SUCCESSFUL_PUSH, buffer.Push(big_string, 5)) + << "Can't write string"; + + EXPECT_TRUE(buffer.Pop(&string)) << "Filled buffer marked as empty"; + EXPECT_STREQ(string.c_str(), "123") << "Can't read actual data"; + EXPECT_TRUE(buffer.Pop(&string)) << "Filled buffer marked as empty"; + EXPECT_STREQ(string.c_str(), "") << "Can't read null string"; + EXPECT_TRUE(buffer.Pop(&string)) << "Filled buffer marked as empty"; + EXPECT_STREQ(string.c_str(), "12345") << "Can't read actual data"; + + EXPECT_FALSE(buffer.Pop(&string)) + << "read from empty buffer not flagged"; +} + +TEST(DnsQueueTest, SizeCheck) { + // Verify that size is correctly accounted for in buffer. + const int buffer_size = 100; + std::string input_string = "Hello"; + std::string string; + DnsQueue buffer(buffer_size); + + EXPECT_EQ(0U, buffer.Size()); + EXPECT_FALSE(buffer.Pop(&string)); + EXPECT_EQ(DnsQueue::SUCCESSFUL_PUSH, buffer.Push(input_string)); + EXPECT_EQ(1U, buffer.Size()); + EXPECT_EQ(DnsQueue::SUCCESSFUL_PUSH, buffer.Push("Hi There")); + EXPECT_EQ(2U, buffer.Size()); + EXPECT_TRUE(buffer.Pop(&string)); + EXPECT_EQ(1U, buffer.Size()); + EXPECT_TRUE(buffer.Pop(&string)); + EXPECT_EQ(0U, buffer.Size()); + EXPECT_EQ(DnsQueue::SUCCESSFUL_PUSH, buffer.Push(input_string)); + EXPECT_EQ(1U, buffer.Size()); + + // Check to see that the first string, if repeated, is discarded. + EXPECT_EQ(DnsQueue::REDUNDANT_PUSH, buffer.Push(input_string)); + EXPECT_EQ(1U, buffer.Size()); +} + +TEST(DnsQueueTest, FillThenEmptyCheck) { + // Use a big buffer so we'll get a bunch of writes in. + // This tests to be sure the buffer holds many strings. + // We also make sure they all come out intact. + const size_t buffer_size = 1000; + size_t byte_usage_counter = 1; // Separation character between pointer. + DnsQueue buffer(buffer_size); + DnsQueueSequentialTester tester(buffer); + + size_t write_success; + for (write_success = 0; write_success < buffer_size; write_success++) { + if (!tester.Push()) + break; + EXPECT_EQ(buffer.Size(), write_success + 1); + if (write_success > 99) + byte_usage_counter += 4; // 3 digit plus '\0'. + else if (write_success > 9) + byte_usage_counter += 3; // 2 digits plus '\0'. + else + byte_usage_counter += 2; // Digit plus '\0'. + } + EXPECT_LE(byte_usage_counter, buffer_size) + << "Written data exceeded buffer size"; + EXPECT_GE(byte_usage_counter, buffer_size - 4) + << "Buffer does not appear to have filled"; + + EXPECT_GE(write_success, 10U) << "Couldn't even write 10 one digit strings " + "in " << buffer_size << " byte buffer"; + + + while (1) { + if (!tester.Pop()) + break; + write_success--; + } + EXPECT_EQ(write_success, 0U) << "Push and Pop count were different"; + + EXPECT_FALSE(tester.Pop()) << "Read from empty buffer succeeded"; +} + +TEST(DnsQueueTest, ClearCheck) { + // Use a big buffer so we'll get a bunch of writes in. + const size_t buffer_size = 1000; + DnsQueue buffer(buffer_size); + std::string string("ABC"); + DnsQueueSequentialTester tester(buffer); + + size_t write_success; + for (write_success = 0; write_success < buffer_size; write_success++) { + if (!tester.Push()) + break; + EXPECT_EQ(buffer.Size(), write_success + 1); + } + + buffer.Clear(); + EXPECT_EQ(buffer.Size(), 0U); + + size_t write_success2; + for (write_success2 = 0; write_success2 < buffer_size; write_success2++) { + if (!tester.Push()) + break; + EXPECT_EQ(buffer.Size(), write_success2 + 1); + } + + for (; write_success2 > 0; write_success2--) { + EXPECT_EQ(buffer.Size(), write_success2); + EXPECT_TRUE(buffer.Pop(&string)); + } + + EXPECT_EQ(buffer.Size(), 0U); + buffer.Clear(); + EXPECT_EQ(buffer.Size(), 0U); +} + +TEST(DnsQueueTest, WrapOnVariousSubstrings) { + // Use a prime number for the allocated buffer size so that we tend + // to exercise all possible edge conditions (in circular text buffer). + // Once we're over 10 writes, all our strings are 2 digits long, + // with a '\0' terminator added making 3 characters per write. + // Since 3 is relatively prime to 23, we'll soon wrap (about + // every 6 writes). Hence after 18 writes, we'll have tested all + // edge conditions. We'll first do this where we empty the buffer + // after each write, and then again where there are some strings + // still in the buffer after each write. + const int prime_number = 23; + // Circular buffer needs an extra extra space to distinguish full from empty. + const int buffer_size = prime_number - 1; + DnsQueue buffer(buffer_size); + DnsQueueSequentialTester tester(buffer); + + // First test empties between each write. Second loop + // has writes for each pop. Third has three pushes per pop. + // Third has two items pending during each write. + for (int j = 0; j < 3; j++) { + // Each group does 30 tests, which is more than 10+18 + // which was needed to get into the thorough testing zone + // mentioned above. + for (int i = 0; i < 30; i++) { + EXPECT_TRUE(tester.Push()) << "write failed with only " << j + << " blocks in buffer"; + EXPECT_TRUE(tester.Pop()) << "Unable to read back data "; + } + EXPECT_TRUE(tester.Push()); + } + + // Read back the accumulated 3 extra blocks. + EXPECT_TRUE(tester.Pop()); + EXPECT_TRUE(tester.Pop()); + EXPECT_TRUE(tester.Pop()); + EXPECT_FALSE(tester.Pop()); +} + +}; // namespace network_hints diff --git a/components/network_hints/renderer/prescient_networking_dispatcher.cc b/components/network_hints/renderer/prescient_networking_dispatcher.cc new file mode 100644 index 0000000..ee02f019 --- /dev/null +++ b/components/network_hints/renderer/prescient_networking_dispatcher.cc @@ -0,0 +1,27 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/network_hints/renderer/prescient_networking_dispatcher.h" + +#include "base/logging.h" + +namespace network_hints { + +PrescientNetworkingDispatcher::PrescientNetworkingDispatcher() { +} + +PrescientNetworkingDispatcher::~PrescientNetworkingDispatcher() { +} + +void PrescientNetworkingDispatcher::prefetchDNS( + const blink::WebString& hostname) { + VLOG(2) << "Prefetch DNS: " << hostname.utf8(); + if (hostname.isEmpty()) + return; + + std::string hostname_utf8 = hostname.utf8(); + dns_prefetch_.Resolve(hostname_utf8.data(), hostname_utf8.length()); +} + +} // namespace network_hints diff --git a/components/network_hints/renderer/prescient_networking_dispatcher.h b/components/network_hints/renderer/prescient_networking_dispatcher.h new file mode 100644 index 0000000..9b10739 --- /dev/null +++ b/components/network_hints/renderer/prescient_networking_dispatcher.h @@ -0,0 +1,31 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_NETWORK_HINTS_RENDERER_PRESCIENT_NETWORKING_DISPATCHER_H_ +#define COMPONENTS_NETWORK_HINTS_RENDERER_PRESCIENT_NETWORKING_DISPATCHER_H_ + +#include "base/macros.h" +#include "components/network_hints/renderer/renderer_dns_prefetch.h" +#include "third_party/WebKit/public/platform/WebPrescientNetworking.h" + +namespace network_hints { + +// The main entry point from blink for sending DNS prefetch requests to the +// network stack. +class PrescientNetworkingDispatcher : public blink::WebPrescientNetworking { + public: + PrescientNetworkingDispatcher(); + ~PrescientNetworkingDispatcher() override; + + void prefetchDNS(const blink::WebString& hostname) override; + + private: + network_hints::RendererDnsPrefetch dns_prefetch_; + + DISALLOW_COPY_AND_ASSIGN(PrescientNetworkingDispatcher); +}; + +} // namespace network_hints + +#endif // COMPONENTS_NETWORK_HINTS_RENDERER_PRESCIENT_NETWORKING_DISPATCHER_H_ diff --git a/components/network_hints/renderer/renderer_dns_prefetch.cc b/components/network_hints/renderer/renderer_dns_prefetch.cc new file mode 100644 index 0000000..7098037 --- /dev/null +++ b/components/network_hints/renderer/renderer_dns_prefetch.cc @@ -0,0 +1,165 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// See header file for description of RendererDnsPrefetch class + +#include "components/network_hints/renderer/renderer_dns_prefetch.h" + +#include <ctype.h> + +#include "base/bind.h" +#include "base/logging.h" +#include "base/message_loop/message_loop.h" +#include "components/network_hints/common/network_hints_common.h" +#include "components/network_hints/common/network_hints_messages.h" +#include "components/network_hints/renderer/dns_prefetch_queue.h" +#include "content/public/renderer/render_thread.h" + +using content::RenderThread; + +namespace network_hints { + +RendererDnsPrefetch::RendererDnsPrefetch() + : c_string_queue_(1000), + weak_factory_(this) { + Reset(); +} + +RendererDnsPrefetch::~RendererDnsPrefetch() { +} + +void RendererDnsPrefetch::Reset() { + domain_map_.clear(); + c_string_queue_.Clear(); + buffer_full_discard_count_ = 0; + numeric_ip_discard_count_ = 0; + new_name_count_ = 0; +} + +// Push names into queue quickly! +void RendererDnsPrefetch::Resolve(const char* name, size_t length) { + if (!length) + return; // Don't store empty strings in buffer. + if (is_numeric_ip(name, length)) + return; // Numeric IPs have no DNS lookup significance. + + size_t old_size = c_string_queue_.Size(); + DnsQueue::PushResult result = c_string_queue_.Push(name, length); + if (DnsQueue::SUCCESSFUL_PUSH == result) { + if (1 == c_string_queue_.Size()) { + DCHECK_EQ(old_size, 0u); + if (0 != old_size) + return; // Overkill safety net: Don't send too many InvokeLater's. + weak_factory_.InvalidateWeakPtrs(); + RenderThread::Get()->GetTaskRunner()->PostDelayedTask( + FROM_HERE, base::Bind(&RendererDnsPrefetch::SubmitHostnames, + weak_factory_.GetWeakPtr()), + base::TimeDelta::FromMilliseconds(10)); + } + return; + } + if (DnsQueue::OVERFLOW_PUSH == result) { + ++buffer_full_discard_count_; + return; + } + DCHECK(DnsQueue::REDUNDANT_PUSH == result); +} + +// Extract data from the Queue, and then send it off the the Browser process +// to be resolved. +void RendererDnsPrefetch::SubmitHostnames() { + // Get all names out of the C_string_queue (into our map) + ExtractBufferedNames(); + // TBD: IT could be that we should only extract about as many names as we are + // going to send to the browser. That would cause a "silly" page with a TON + // of URLs to start to overrun the DnsQueue, which will cause the names to + // be dropped (not stored in the queue). By fetching ALL names, we are + // taking on a lot of work, which may take a long time to process... perhaps + // longer than the page may be visible!?!?! If we implement a better + // mechanism for doing domain_map.clear() (see end of this method), then + // we'd automatically flush such pending work from a ridiculously link-filled + // page. + + // Don't overload the browser DNS lookup facility, or take too long here, + // by only sending off kMaxDnsHostnamesPerRequest names to the Browser. + // This will help to avoid overloads when a page has a TON of links. + DnsPrefetchNames(kMaxDnsHostnamesPerRequest); + if (new_name_count_ > 0 || 0 < c_string_queue_.Size()) { + weak_factory_.InvalidateWeakPtrs(); + RenderThread::Get()->GetTaskRunner()->PostDelayedTask( + FROM_HERE, base::Bind(&RendererDnsPrefetch::SubmitHostnames, + weak_factory_.GetWeakPtr()), + base::TimeDelta::FromMilliseconds(10)); + } else { + // TODO(JAR): Should we only clear the map when we navigate, or reload? + domain_map_.clear(); + } +} + +// Pull some hostnames from the queue, and add them to our map. +void RendererDnsPrefetch::ExtractBufferedNames(size_t size_goal) { + size_t count(0); // Number of entries to find (0 means find all). + if (size_goal > 0) { + if (size_goal <= domain_map_.size()) + return; // Size goal was met. + count = size_goal - domain_map_.size(); + } + + std::string name; + while (c_string_queue_.Pop(&name)) { + DCHECK_NE(name.size(), 0u); + // We don't put numeric IP names into buffer. + DCHECK(!is_numeric_ip(name.c_str(), name.size())); + DomainUseMap::iterator it; + it = domain_map_.find(name); + if (domain_map_.end() == it) { + domain_map_[name] = kPending; + ++new_name_count_; + if (0 == count) continue; // Until buffer is empty. + if (1 == count) break; // We found size_goal. + DCHECK_GT(count, 1u); + --count; + } else { + DCHECK(kPending == it->second || kLookupRequested == it->second); + } + } +} + +void RendererDnsPrefetch::DnsPrefetchNames(size_t max_count) { + // We are on the renderer thread, and just need to send things to the browser. + NameList names; + for (DomainUseMap::iterator it = domain_map_.begin(); + it != domain_map_.end(); + ++it) { + if (0 == (it->second & kLookupRequested)) { + it->second |= kLookupRequested; + names.push_back(it->first); + if (0 == max_count) continue; // Get all, independent of count. + if (1 == max_count) break; + --max_count; + DCHECK_GE(max_count, 1u); + } + } + DCHECK_GE(new_name_count_, names.size()); + new_name_count_ -= names.size(); + + network_hints::LookupRequest request; + request.hostname_list = names; + RenderThread::Get()->Send(new DnsPrefetchMsg_RequestPrefetch(request)); +} + +// is_numeric_ip() checks to see if all characters in name are either numeric, +// or dots. Such a name will not actually be passed to DNS, as it is an IP +// address. +bool RendererDnsPrefetch::is_numeric_ip(const char* name, size_t length) { + // Scan for a character outside our lookup list. + while (length-- > 0) { + if (!isdigit(*name) && '.' != *name) + return false; + ++name; + } + return true; +} + +} // namespcae predictor diff --git a/components/network_hints/renderer/renderer_dns_prefetch.h b/components/network_hints/renderer/renderer_dns_prefetch.h new file mode 100644 index 0000000..9ee4f61 --- /dev/null +++ b/components/network_hints/renderer/renderer_dns_prefetch.h @@ -0,0 +1,109 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// A RendererDnsPrefetch instance is maintained for each RenderThread. +// URL strings are typically added to the embedded queue during rendering. +// The first addition to the queue (transitioning from empty to having +// some names) causes a processing task to be added to the Renderer Thread. +// The processing task gathers all buffered names, and send them via IPC +// to the browser, so that DNS lookups can be performed before the user attempts +// to traverse a link. +// This class removed some duplicates, and discards numeric IP addresss +// (which wouldn't looked up in DNS anyway). +// To limit the time during the processing task (and avoid stalling the Render +// thread), several limits are placed on how much of the queue to process. +// If the processing task is not able to completely empty the queue, it +// schedules a future continuation of the task, and keeps the map of already +// sent names. If the entire queue is processed, then the list of "sent names" +// is cleared so that future gatherings might again pass along the same names. + +#ifndef COMPONENTS_NETWORK_HINTS_RENDERER_RENDERER_DNS_PREFETCH_H_ +#define COMPONENTS_NETWORK_HINTS_RENDERER_RENDERER_DNS_PREFETCH_H_ + +#include <map> +#include <string> + +#include "base/basictypes.h" +#include "base/memory/weak_ptr.h" +#include "components/network_hints/renderer/dns_prefetch_queue.h" + +namespace network_hints { + +// An internal interface to the network_hints component for efficiently sending +// DNS prefetch requests to the net stack. +class RendererDnsPrefetch { + public: + RendererDnsPrefetch(); + ~RendererDnsPrefetch(); + + // Push a name into the queue to be resolved. + void Resolve(const char* name, size_t length); + + // SubmitHosts processes the buffered names, and submits them for DNS + // prefetching. + // Note that browser process may decide which names should be looked up (to + // pre-warm the cache) based on what has been (or not been) looked up + // recently. + // If sending for DNS lookup is incomplete (queue is not empty, or not all + // names in map are sent, or ...) then a task to continue processing is + // sent to our thread loop. + void SubmitHostnames(); + + // The following is private, but exposed for testing purposes only. + static bool is_numeric_ip(const char* name, size_t length); + + private: + // ExtractBufferedNames pulls names from queue into the map, reducing or + // eliminating a waiting queue. + // The size_goal argument can be used to reduce the amount of + // processing done in this method, and can leave some data + // in the buffer under some circumstances. + // If size_goal is zero, then extraction proceeds until + // the queue is empty. If size goal is positive, then + // extraction continues until the domain_map_ contains + // at least the specified number of names, or the buffer is empty. + void ExtractBufferedNames(size_t size_goal = 0); + + // DnsPrefetchNames does not check the buffer, and just sends names + // that are already collected in the domain_map_ for DNS lookup. + // If max_count is zero, then all available names are sent; and + // if positive, then at most max_count names will be sent. + void DnsPrefetchNames(size_t max_count = 0); + + // Reset() restores initial state provided after construction. + // This discards ALL queue entries, and map entries. + void Reset(); + + // We use c_string_queue_ to hold lists of names supplied typically) by the + // renderer. It queues the names, at minimal cost to the renderer's thread, + // and allows this class to process them when time permits (in a later task). + DnsQueue c_string_queue_; + + + // domain_map_ contains (for each domain) one of the next two constants, + // depending on whether we have asked the browser process to do the actual + // DNS lookup. + static const int kLookupRequested = 0x1; + static const int kPending = 0x0; + typedef std::map<std::string, int> DomainUseMap; + DomainUseMap domain_map_; + + // Cache a tally of the count of names that haven't yet been sent + // for DNS pre-fetching. Note that we *could* recalculate this + // count by iterating over domain_map_, looking for even values. + size_t new_name_count_; + + // We have some metrics to examine performance. We might use + // these metrics to modify buffer counts etc. some day. + int buffer_full_discard_count_; + int numeric_ip_discard_count_; + + base::WeakPtrFactory<RendererDnsPrefetch> weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(RendererDnsPrefetch); +}; // class RendererDnsPrefetch + +} // namespace network_hints + +#endif // COMPONENTS_NETWORK_HINTS_RENDERER_RENDERER_DNS_PREFETCH_H_ diff --git a/components/network_hints/renderer/renderer_dns_prefetch_unittest.cc b/components/network_hints/renderer/renderer_dns_prefetch_unittest.cc new file mode 100644 index 0000000..588383c --- /dev/null +++ b/components/network_hints/renderer/renderer_dns_prefetch_unittest.cc @@ -0,0 +1,37 @@ +// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Single threaded tests of RendererDnsPrefetch functionality. + +#include "components/network_hints/renderer/renderer_dns_prefetch.h" + +#include <algorithm> + +#include "testing/gtest/include/gtest/gtest.h" + +namespace network_hints { + +class RenderDnsMasterTest : public testing::Test { +}; + +TEST(RenderDnsMasterTest, NumericIpDiscardCheck) { + // Regular names. + const std::string A("a.com"), B("b.net"), C("www.other.uk"); + // Combination of digits plus dots. + const std::string N1("1.3."), N2("5.5.7.12"); + +#define TESTNAME(string) RendererDnsPrefetch::is_numeric_ip((string.data()), \ + (string).size()) + + EXPECT_TRUE(TESTNAME(N1)); + EXPECT_TRUE(TESTNAME(N2)); + + EXPECT_FALSE(TESTNAME(A)); + EXPECT_FALSE(TESTNAME(B)); + EXPECT_FALSE(TESTNAME(C)); + +#undef TESTNAME +} + +} // namespace network_hints |