summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoragl@chromium.org <agl@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-09-03 22:01:37 +0000
committeragl@chromium.org <agl@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-09-03 22:01:37 +0000
commit673336b8ef5a8708d7d3fe0dc8c3daa8d12d4097 (patch)
tree8ebf30b2881b71786236ce0383e18ba52290a083
parent43eb2f8c2daa0a67a04fbfdf60554774b021ded4 (diff)
downloadchromium_src-673336b8ef5a8708d7d3fe0dc8c3daa8d12d4097.zip
chromium_src-673336b8ef5a8708d7d3fe0dc8c3daa8d12d4097.tar.gz
chromium_src-673336b8ef5a8708d7d3fe0dc8c3daa8d12d4097.tar.bz2
ForceTLS: persist to disk
With this patch, we'll persist ForceTLS state to disk. It's saved as a JSON file (ForceTLSState) in the user data directory for the moment. You still need the --force-https flag in order to trigger any ForceTLS behaviour, however. For the moment, this state isn't cleared when the rest of the browser state it. That's ok because it's still behind a flag. http://codereview.chromium.org/186014 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@25382 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/force_tls_persister.cc72
-rw-r--r--chrome/browser/force_tls_persister.h64
-rw-r--r--chrome/browser/profile.cc12
-rw-r--r--chrome/browser/profile.h6
-rw-r--r--chrome/chrome.gyp2
-rw-r--r--net/base/force_tls_state.cc110
-rw-r--r--net/base/force_tls_state.h35
7 files changed, 285 insertions, 16 deletions
diff --git a/chrome/browser/force_tls_persister.cc b/chrome/browser/force_tls_persister.cc
new file mode 100644
index 0000000..a07f523
--- /dev/null
+++ b/chrome/browser/force_tls_persister.cc
@@ -0,0 +1,72 @@
+// 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.
+
+#include "chrome/browser/force_tls_persister.h"
+
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "base/message_loop.h"
+#include "base/path_service.h"
+#include "base/thread.h"
+#include "chrome/common/chrome_paths.h"
+#include "net/base/force_tls_state.h"
+
+ForceTLSPersister::ForceTLSPersister(net::ForceTLSState* state,
+ base::Thread* file_thread)
+ : state_is_dirty_(false),
+ force_tls_state_(state),
+ file_thread_(file_thread) {
+ state->SetDelegate(this);
+
+ Task* task = NewRunnableMethod(this, &ForceTLSPersister::LoadState);
+ file_thread->message_loop()->PostDelayedTask(FROM_HERE, task,
+ 1000 /* 1 second */);
+}
+
+static FilePath GetStateFile() {
+ FilePath user_data_dir;
+ PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
+ return user_data_dir.Append("ForceTLSState");
+}
+
+void ForceTLSPersister::LoadState() {
+ // Runs on |file_thread_|
+ AutoLock locked_(lock_);
+ DCHECK(file_thread_->message_loop() == MessageLoop::current());
+
+ std::string state;
+ if (!file_util::ReadFileToString(GetStateFile(), &state))
+ return;
+
+ force_tls_state_->Deserialise(state);
+}
+
+void ForceTLSPersister::StateIsDirty(net::ForceTLSState* state) {
+ // Runs on arbitary thread, may not block nor reenter |force_tls_state_|
+ AutoLock locked_(lock_);
+ DCHECK(state == force_tls_state_);
+
+ if (state_is_dirty_)
+ return; // we already have a serialisation scheduled
+
+ Task* task = NewRunnableMethod(this, &ForceTLSPersister::SerialiseState);
+ file_thread_->message_loop()->PostDelayedTask(FROM_HERE, task,
+ 1000 /* 1 second */);
+ state_is_dirty_ = true;
+}
+
+void ForceTLSPersister::SerialiseState() {
+ // Runs on |file_thread_|
+ AutoLock locked_(lock_);
+ DCHECK(file_thread_->message_loop() == MessageLoop::current());
+
+ DCHECK(state_is_dirty_);
+ state_is_dirty_ = false;
+
+ std::string state;
+ if (!force_tls_state_->Serialise(&state))
+ return;
+
+ file_util::WriteFile(GetStateFile(), state.data(), state.size());
+}
diff --git a/chrome/browser/force_tls_persister.h b/chrome/browser/force_tls_persister.h
new file mode 100644
index 0000000..be4c17d
--- /dev/null
+++ b/chrome/browser/force_tls_persister.h
@@ -0,0 +1,64 @@
+// 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.
+
+// ForceTLSState maintains an in memory database containing the list of hosts
+// that currently have ForceTLS enabled. This singleton object deals with
+// writing that data out to disk as needed and loading it at startup.
+
+// At startup we need to load the ForceTLS state from the disk. For the moment,
+// we don't want to delay startup for this load, so we let the ForceTLSState
+// run for a while without being loaded. This means that it's possible for
+// pages opened very quickly not to get the correct ForceTLS information.
+//
+// To load the state, we schedule a Task on the file thread which loads,
+// deserialises and configures the ForceTLSState.
+//
+// The ForceTLSState object supports running a callback function when it
+// changes. This object registers the callback, pointing at itself.
+//
+// ForceTLSState calls...
+// ForceTLSPersister::StateIsDirty
+// since the callback isn't allowed to block or reenter, we schedule a Task
+// on |file_thread_| after some small amount of time
+//
+// ...
+//
+// ForceTLSPersister::SerialiseState
+// copies the current state of the ForceTLSState, serialises and writes to
+// disk.
+
+#include "base/lock.h"
+#include "base/ref_counted.h"
+#include "net/base/force_tls_state.h"
+
+namespace base {
+class Thread;
+}
+
+class ForceTLSPersister : public base::RefCountedThreadSafe<ForceTLSPersister>,
+ public net::ForceTLSState::Delegate {
+ public:
+ ForceTLSPersister(net::ForceTLSState* state, base::Thread* file_thread);
+
+ // Called by the ForceTLSState when it changes its state.
+ virtual void StateIsDirty(net::ForceTLSState*);
+
+ private:
+ // a Task callback for when the state needs to be written out.
+ void SerialiseState();
+
+ // a Task callback for when the state needs to be loaded from disk at startup.
+ void LoadState();
+
+ Lock lock_; // protects all the members
+
+ // true when the state object has signaled that we're dirty and we haven't
+ // serialised the state yet.
+ bool state_is_dirty_;
+
+ scoped_refptr<net::ForceTLSState> force_tls_state_;
+
+ // This is a thread which can perform file access.
+ base::Thread* const file_thread_;
+};
diff --git a/chrome/browser/profile.cc b/chrome/browser/profile.cc
index 1aa04e8..a8377d3 100644
--- a/chrome/browser/profile.cc
+++ b/chrome/browser/profile.cc
@@ -20,6 +20,7 @@
#include "chrome/browser/extensions/extensions_service.h"
#include "chrome/browser/extensions/user_script_master.h"
#include "chrome/browser/favicon_service.h"
+#include "chrome/browser/force_tls_persister.h"
#include "chrome/browser/history/history.h"
#include "chrome/browser/in_process_webkit/webkit_context.h"
#include "chrome/browser/net/chrome_url_request_context.h"
@@ -264,7 +265,7 @@ class OffTheRecordProfileImpl : public Profile,
virtual net::ForceTLSState* GetForceTLSState() {
if (!force_tls_state_.get())
- force_tls_state_.reset(new net::ForceTLSState());
+ force_tls_state_ = new net::ForceTLSState();
return force_tls_state_.get();
}
@@ -521,7 +522,7 @@ class OffTheRecordProfileImpl : public Profile,
scoped_ptr<SSLHostState> ssl_host_state_;
// The ForceTLSState that only stores enabled sites in memory.
- scoped_ptr<net::ForceTLSState> force_tls_state_;
+ scoped_refptr<net::ForceTLSState> force_tls_state_;
// Time we were started.
Time start_time_;
@@ -823,8 +824,11 @@ SSLHostState* ProfileImpl::GetSSLHostState() {
}
net::ForceTLSState* ProfileImpl::GetForceTLSState() {
- if (!force_tls_state_.get())
- force_tls_state_.reset(new net::ForceTLSState());
+ if (!force_tls_state_.get()) {
+ force_tls_state_ = new net::ForceTLSState();
+ force_tls_persister_ = new ForceTLSPersister(
+ force_tls_state_.get(), g_browser_process->file_thread());
+ }
return force_tls_state_.get();
}
diff --git a/chrome/browser/profile.h b/chrome/browser/profile.h
index 83ae64c..97e747b 100644
--- a/chrome/browser/profile.h
+++ b/chrome/browser/profile.h
@@ -34,6 +34,7 @@ class ExtensionProcessManager;
class ExtensionMessageService;
class ExtensionsService;
class FaviconService;
+class ForceTLSPersister;
class HistoryService;
class NavigationController;
class PasswordStore;
@@ -154,7 +155,7 @@ class Profile {
// called.
virtual SSLHostState* GetSSLHostState() = 0;
- // Retrieves a pointer to the ForceTLStSate associated with this profile.
+ // Retrieves a pointer to the ForceTLSState associated with this profile.
// The ForceTLSState is lazily created the first time that this method is
// called.
virtual net::ForceTLSState* GetForceTLSState() = 0;
@@ -460,7 +461,8 @@ class ProfileImpl : public Profile,
scoped_ptr<ExtensionProcessManager> extension_process_manager_;
scoped_refptr<ExtensionMessageService> extension_message_service_;
scoped_ptr<SSLHostState> ssl_host_state_;
- scoped_ptr<net::ForceTLSState> force_tls_state_;
+ scoped_refptr<net::ForceTLSState> force_tls_state_;
+ scoped_refptr<ForceTLSPersister> force_tls_persister_;
scoped_ptr<PrefService> prefs_;
scoped_refptr<ThumbnailStore> thumbnail_store_;
scoped_ptr<TemplateURLFetcher> template_url_fetcher_;
diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp
index 6563807..c65b15a 100644
--- a/chrome/chrome.gyp
+++ b/chrome/chrome.gyp
@@ -1163,6 +1163,8 @@
'browser/external_protocol_handler.h',
'browser/external_tab_container.cc',
'browser/external_tab_container.h',
+ 'browser/force_tls_persister.cc',
+ 'browser/force_tls_persister.h',
'browser/global_keyboard_shortcuts_mac.h',
'browser/global_keyboard_shortcuts_mac.mm',
'browser/fav_icon_helper.cc',
diff --git a/net/base/force_tls_state.cc b/net/base/force_tls_state.cc
index ea2e2f8..f92015a 100644
--- a/net/base/force_tls_state.cc
+++ b/net/base/force_tls_state.cc
@@ -4,33 +4,64 @@
#include "net/base/force_tls_state.h"
+#include "base/json_reader.h"
+#include "base/json_writer.h"
#include "base/logging.h"
+#include "base/scoped_ptr.h"
#include "base/string_tokenizer.h"
#include "base/string_util.h"
+#include "base/values.h"
#include "googleurl/src/gurl.h"
#include "net/base/registry_controlled_domain.h"
namespace net {
-ForceTLSState::ForceTLSState() {
+ForceTLSState::ForceTLSState()
+ : delegate_(NULL) {
}
void ForceTLSState::DidReceiveHeader(const GURL& url,
const std::string& value) {
- // TODO(abarth): Actually parse |value| once the spec settles down.
- EnableHost(url.host());
+ int max_age;
+ bool include_subdomains;
+
+ if (!ParseHeader(value, &max_age, &include_subdomains))
+ return;
+
+ base::Time current_time(base::Time::Now());
+ base::TimeDelta max_age_delta = base::TimeDelta::FromSeconds(max_age);
+ base::Time expiry = current_time + max_age_delta;
+
+ EnableHost(url.host(), expiry, include_subdomains);
}
-void ForceTLSState::EnableHost(const std::string& host) {
+void ForceTLSState::EnableHost(const std::string& host, base::Time expiry,
+ bool include_subdomains) {
// TODO(abarth): Canonicalize host.
AutoLock lock(lock_);
- enabled_hosts_.insert(host);
+
+ State state = {expiry, include_subdomains};
+ enabled_hosts_[host] = state;
+ DirtyNotify();
}
bool ForceTLSState::IsEnabledForHost(const std::string& host) {
// TODO(abarth): Canonicalize host.
+ // TODO: check for subdomains too.
+
AutoLock lock(lock_);
- return enabled_hosts_.find(host) != enabled_hosts_.end();
+ std::map<std::string, State>::iterator i = enabled_hosts_.find(host);
+ if (i == enabled_hosts_.end())
+ return false;
+
+ base::Time current_time(base::Time::Now());
+ if (current_time > i->second.expiry) {
+ enabled_hosts_.erase(i);
+ DirtyNotify();
+ return false;
+ }
+
+ return true;
}
// "X-Force-TLS" ":" "max-age" "=" delta-seconds *1INCLUDESUBDOMAINS
@@ -130,4 +161,71 @@ bool ForceTLSState::ParseHeader(const std::string& value,
}
}
+void ForceTLSState::SetDelegate(ForceTLSState::Delegate* delegate) {
+ AutoLock lock(lock_);
+
+ delegate_ = delegate;
+}
+
+bool ForceTLSState::Serialise(std::string* output) {
+ AutoLock lock(lock_);
+
+ DictionaryValue toplevel;
+ for (std::map<std::string, State>::const_iterator
+ i = enabled_hosts_.begin(); i != enabled_hosts_.end(); ++i) {
+ DictionaryValue* state = new DictionaryValue;
+ state->SetBoolean(L"include_subdomains", i->second.include_subdomains);
+ state->SetReal(L"expiry", i->second.expiry.ToDoubleT());
+
+ toplevel.Set(ASCIIToWide(i->first), state);
+ }
+
+ JSONWriter::Write(&toplevel, true /* pretty print */, output);
+ return true;
+}
+
+bool ForceTLSState::Deserialise(const std::string& input) {
+ AutoLock lock(lock_);
+
+ enabled_hosts_.clear();
+
+ scoped_ptr<Value> value(
+ JSONReader::Read(input, false /* do not allow trailing commas */));
+ if (!value.get() || !value->IsType(Value::TYPE_DICTIONARY))
+ return false;
+
+ DictionaryValue* dict_value = reinterpret_cast<DictionaryValue*>(value.get());
+ const base::Time current_time(base::Time::Now());
+
+ for (DictionaryValue::key_iterator
+ i = dict_value->begin_keys(); i != dict_value->end_keys(); ++i) {
+ DictionaryValue* state;
+ if (!dict_value->GetDictionary(*i, &state))
+ continue;
+
+ const std::string host = WideToASCII(*i);
+ bool include_subdomains;
+ double expiry;
+
+ if (!state->GetBoolean(L"include_subdomains", &include_subdomains) ||
+ !state->GetReal(L"expiry", &expiry)) {
+ continue;
+ }
+
+ base::Time expiry_time = base::Time::FromDoubleT(expiry);
+ if (expiry_time <= current_time)
+ continue;
+
+ State new_state = { expiry_time, include_subdomains };
+ enabled_hosts_[host] = new_state;
+ }
+
+ return enabled_hosts_.size();
+}
+
+void ForceTLSState::DirtyNotify() {
+ if (delegate_)
+ delegate_->StateIsDirty(this);
+}
+
} // namespace
diff --git a/net/base/force_tls_state.h b/net/base/force_tls_state.h
index e52adb9..068d73c 100644
--- a/net/base/force_tls_state.h
+++ b/net/base/force_tls_state.h
@@ -5,11 +5,13 @@
#ifndef NET_BASE_FORCE_TLS_STATE_H_
#define NET_BASE_FORCE_TLS_STATE_H_
-#include <set>
+#include <map>
#include <string>
#include "base/basictypes.h"
#include "base/lock.h"
+#include "base/ref_counted.h"
+#include "base/time.h"
class GURL;
@@ -21,7 +23,7 @@ namespace net {
// then we refuse to talk to the host over HTTP, treat all certificate errors as
// fatal, and refuse to load any mixed content.
//
-class ForceTLSState {
+class ForceTLSState : public base::RefCountedThreadSafe<ForceTLSState> {
public:
ForceTLSState();
@@ -30,7 +32,8 @@ class ForceTLSState {
void DidReceiveHeader(const GURL& url, const std::string& value);
// Enable ForceTLS for |host|.
- void EnableHost(const std::string& host);
+ void EnableHost(const std::string& host, base::Time expiry,
+ bool include_subdomains);
// Returns whether |host| has had ForceTLS enabled.
bool IsEnabledForHost(const std::string& host);
@@ -43,13 +46,37 @@ class ForceTLSState {
int* max_age,
bool* include_subdomains);
+ struct State {
+ base::Time expiry; // the absolute time (UTC) when this record expires
+ bool include_subdomains; // subdomains included?
+ };
+
+ class Delegate {
+ public:
+ // This function may not block and may be called with internal locks held.
+ // Thus it must not reenter the ForceTLSState object.
+ virtual void StateIsDirty(ForceTLSState* state) = 0;
+ };
+
+ void SetDelegate(Delegate*);
+
+ bool Serialise(std::string* output);
+ bool Deserialise(const std::string& state);
+
private:
+ // If we have a callback configured, call it to let our serialiser know that
+ // our state is dirty.
+ void DirtyNotify();
+
// The set of hosts that have enabled ForceTLS.
- std::set<std::string> enabled_hosts_;
+ std::map<std::string, State> enabled_hosts_;
// Protect access to our data members with this lock.
Lock lock_;
+ // Our delegate who gets notified when we are dirtied, or NULL.
+ Delegate* delegate_;
+
DISALLOW_COPY_AND_ASSIGN(ForceTLSState);
};