summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--net/base/force_tls_state.cc99
-rw-r--r--net/base/force_tls_state.h10
-rw-r--r--net/base/force_tls_state_unittest.cc121
-rw-r--r--net/net.gyp1
4 files changed, 230 insertions, 1 deletions
diff --git a/net/base/force_tls_state.cc b/net/base/force_tls_state.cc
index 4be33f5..ea2e2f8 100644
--- a/net/base/force_tls_state.cc
+++ b/net/base/force_tls_state.cc
@@ -5,6 +5,8 @@
#include "net/base/force_tls_state.h"
#include "base/logging.h"
+#include "base/string_tokenizer.h"
+#include "base/string_util.h"
#include "googleurl/src/gurl.h"
#include "net/base/registry_controlled_domain.h"
@@ -31,4 +33,101 @@ bool ForceTLSState::IsEnabledForHost(const std::string& host) {
return enabled_hosts_.find(host) != enabled_hosts_.end();
}
+// "X-Force-TLS" ":" "max-age" "=" delta-seconds *1INCLUDESUBDOMAINS
+// INCLUDESUBDOMAINS = [ " includeSubDomains" ]
+bool ForceTLSState::ParseHeader(const std::string& value,
+ int* max_age,
+ bool* include_subdomains) {
+ DCHECK(max_age);
+ DCHECK(include_subdomains);
+
+ int max_age_candidate;
+
+ enum ParserState {
+ START,
+ AFTER_MAX_AGE_LABEL,
+ AFTER_MAX_AGE_EQUALS,
+ AFTER_MAX_AGE,
+ AFTER_MAX_AGE_INCLUDE_SUB_DOMAINS_DELIMITER,
+ AFTER_INCLUDE_SUBDOMAINS,
+ } state = START;
+
+ StringTokenizer tokenizer(value, " =");
+ tokenizer.set_options(StringTokenizer::RETURN_DELIMS);
+ while (tokenizer.GetNext()) {
+ DCHECK(!tokenizer.token_is_delim() || tokenizer.token().length() == 1);
+ DCHECK(tokenizer.token_is_delim() || *tokenizer.token_begin() != ' ');
+ switch (state) {
+ case START:
+ if (*tokenizer.token_begin() == ' ')
+ continue;
+ if (!LowerCaseEqualsASCII(tokenizer.token(), "max-age"))
+ return false;
+ state = AFTER_MAX_AGE_LABEL;
+ break;
+
+ case AFTER_MAX_AGE_LABEL:
+ if (*tokenizer.token_begin() == ' ')
+ continue;
+ if (*tokenizer.token_begin() != '=')
+ return false;
+ DCHECK(tokenizer.token().length() == 1);
+ state = AFTER_MAX_AGE_EQUALS;
+ break;
+
+ case AFTER_MAX_AGE_EQUALS:
+ if (*tokenizer.token_begin() == ' ')
+ continue;
+ if (!StringToInt(tokenizer.token(), &max_age_candidate))
+ return false;
+ if (max_age_candidate < 0)
+ return false;
+ state = AFTER_MAX_AGE;
+ break;
+
+ case AFTER_MAX_AGE:
+ if (*tokenizer.token_begin() != ' ')
+ return false;
+ state = AFTER_MAX_AGE_INCLUDE_SUB_DOMAINS_DELIMITER;
+ break;
+
+ case AFTER_MAX_AGE_INCLUDE_SUB_DOMAINS_DELIMITER:
+ if (*tokenizer.token_begin() == ' ')
+ continue;
+ if (!LowerCaseEqualsASCII(tokenizer.token(), "includesubdomains"))
+ return false;
+ state = AFTER_INCLUDE_SUBDOMAINS;
+ break;
+
+ case AFTER_INCLUDE_SUBDOMAINS:
+ if (*tokenizer.token_begin() != ' ')
+ return false;
+ break;
+
+ default:
+ NOTREACHED();
+ }
+ }
+
+ // We've consumed all the input. Let's see what state we ended up in.
+ switch (state) {
+ case START:
+ case AFTER_MAX_AGE_LABEL:
+ case AFTER_MAX_AGE_EQUALS:
+ return false;
+ case AFTER_MAX_AGE:
+ case AFTER_MAX_AGE_INCLUDE_SUB_DOMAINS_DELIMITER:
+ *max_age = max_age_candidate;
+ *include_subdomains = false;
+ return true;
+ case AFTER_INCLUDE_SUBDOMAINS:
+ *max_age = max_age_candidate;
+ *include_subdomains = true;
+ return true;
+ default:
+ NOTREACHED();
+ return false;
+ }
+}
+
} // namespace
diff --git a/net/base/force_tls_state.h b/net/base/force_tls_state.h
index 988e9c07..e52adb9 100644
--- a/net/base/force_tls_state.h
+++ b/net/base/force_tls_state.h
@@ -19,7 +19,7 @@ namespace net {
//
// Tracks which hosts have enabled ForceTLS. After a host enables ForceTLS,
// then we refuse to talk to the host over HTTP, treat all certificate errors as
-// fatal, and refuses to load any mixed content.
+// fatal, and refuse to load any mixed content.
//
class ForceTLSState {
public:
@@ -35,6 +35,14 @@ class ForceTLSState {
// Returns whether |host| has had ForceTLS enabled.
bool IsEnabledForHost(const std::string& host);
+ // Returns |true| if |value| parses as a valid X-Force-TLS header value.
+ // The values of max-age and and includeSubDomains are returned in |max_age|
+ // and |include_subdomains|, respectively. The out parameters are not
+ // modified if the function returns |false|.
+ static bool ParseHeader(const std::string& value,
+ int* max_age,
+ bool* include_subdomains);
+
private:
// The set of hosts that have enabled ForceTLS.
std::set<std::string> enabled_hosts_;
diff --git a/net/base/force_tls_state_unittest.cc b/net/base/force_tls_state_unittest.cc
new file mode 100644
index 0000000..c1f12c1
--- /dev/null
+++ b/net/base/force_tls_state_unittest.cc
@@ -0,0 +1,121 @@
+// Copyright (c) 2009 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 "net/base/force_tls_state.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+class ForceTLSStateTest : public testing::Test {
+};
+
+TEST_F(ForceTLSStateTest, BogusHeaders) {
+ int max_age = 42;
+ bool include_subdomains = false;
+
+ EXPECT_FALSE(net::ForceTLSState::ParseHeader(
+ "", &max_age, &include_subdomains));
+ EXPECT_FALSE(net::ForceTLSState::ParseHeader(
+ " ", &max_age, &include_subdomains));
+ EXPECT_FALSE(net::ForceTLSState::ParseHeader(
+ "abc", &max_age, &include_subdomains));
+ EXPECT_FALSE(net::ForceTLSState::ParseHeader(
+ " abc", &max_age, &include_subdomains));
+ EXPECT_FALSE(net::ForceTLSState::ParseHeader(
+ " abc ", &max_age, &include_subdomains));
+ EXPECT_FALSE(net::ForceTLSState::ParseHeader(
+ "max-age", &max_age, &include_subdomains));
+ EXPECT_FALSE(net::ForceTLSState::ParseHeader(
+ " max-age", &max_age, &include_subdomains));
+ EXPECT_FALSE(net::ForceTLSState::ParseHeader(
+ " max-age ", &max_age, &include_subdomains));
+ EXPECT_FALSE(net::ForceTLSState::ParseHeader(
+ "max-age=", &max_age, &include_subdomains));
+ EXPECT_FALSE(net::ForceTLSState::ParseHeader(
+ " max-age=", &max_age, &include_subdomains));
+ EXPECT_FALSE(net::ForceTLSState::ParseHeader(
+ " max-age =", &max_age, &include_subdomains));
+ EXPECT_FALSE(net::ForceTLSState::ParseHeader(
+ " max-age= ", &max_age, &include_subdomains));
+ EXPECT_FALSE(net::ForceTLSState::ParseHeader(
+ " max-age = ", &max_age, &include_subdomains));
+ EXPECT_FALSE(net::ForceTLSState::ParseHeader(
+ " max-age = xy", &max_age, &include_subdomains));
+ EXPECT_FALSE(net::ForceTLSState::ParseHeader(
+ " max-age = 3488a923", &max_age, &include_subdomains));
+ EXPECT_FALSE(net::ForceTLSState::ParseHeader(
+ "max-age=3488a923 ", &max_age, &include_subdomains));
+ EXPECT_FALSE(net::ForceTLSState::ParseHeader(
+ "max-ag=3488923", &max_age, &include_subdomains));
+ EXPECT_FALSE(net::ForceTLSState::ParseHeader(
+ "max-aged=3488923", &max_age, &include_subdomains));
+ EXPECT_FALSE(net::ForceTLSState::ParseHeader(
+ "max-age==3488923", &max_age, &include_subdomains));
+ EXPECT_FALSE(net::ForceTLSState::ParseHeader(
+ "amax-age=3488923", &max_age, &include_subdomains));
+ EXPECT_FALSE(net::ForceTLSState::ParseHeader(
+ "max-age=-3488923", &max_age, &include_subdomains));
+ EXPECT_FALSE(net::ForceTLSState::ParseHeader(
+ "max-age=3488923;", &max_age, &include_subdomains));
+ EXPECT_FALSE(net::ForceTLSState::ParseHeader(
+ "max-age=3488923 e", &max_age, &include_subdomains));
+ EXPECT_FALSE(net::ForceTLSState::ParseHeader(
+ "max-age=3488923 includesubdomain", &max_age, &include_subdomains));
+ EXPECT_FALSE(net::ForceTLSState::ParseHeader(
+ "max-age=3488923includesubdomains", &max_age, &include_subdomains));
+ EXPECT_FALSE(net::ForceTLSState::ParseHeader(
+ "max-age=3488923=includesubdomains", &max_age, &include_subdomains));
+ EXPECT_FALSE(net::ForceTLSState::ParseHeader(
+ "max-age=3488923 includesubdomainx", &max_age, &include_subdomains));
+ EXPECT_FALSE(net::ForceTLSState::ParseHeader(
+ "max-age=3488923 includesubdomain=", &max_age, &include_subdomains));
+ EXPECT_FALSE(net::ForceTLSState::ParseHeader(
+ "max-age=3488923 includesubdomain=true", &max_age, &include_subdomains));
+ EXPECT_FALSE(net::ForceTLSState::ParseHeader(
+ "max-age=3488923 includesubdomainsx", &max_age, &include_subdomains));
+ EXPECT_FALSE(net::ForceTLSState::ParseHeader(
+ "max-age=3488923 includesubdomains x", &max_age, &include_subdomains));
+ EXPECT_FALSE(net::ForceTLSState::ParseHeader(
+ "max-age=34889.23 includesubdomains", &max_age, &include_subdomains));
+
+ EXPECT_EQ(max_age, 42);
+ EXPECT_FALSE(include_subdomains);
+}
+
+TEST_F(ForceTLSStateTest, ValidHeaders) {
+ int max_age = 42;
+ bool include_subdomains = true;
+
+ EXPECT_TRUE(net::ForceTLSState::ParseHeader(
+ "max-age=243", &max_age, &include_subdomains));
+ EXPECT_EQ(max_age, 243);
+ EXPECT_FALSE(include_subdomains);
+
+ EXPECT_TRUE(net::ForceTLSState::ParseHeader(
+ " Max-agE = 567", &max_age, &include_subdomains));
+ EXPECT_EQ(max_age, 567);
+ EXPECT_FALSE(include_subdomains);
+
+ EXPECT_TRUE(net::ForceTLSState::ParseHeader(
+ " mAx-aGe = 890 ", &max_age, &include_subdomains));
+ EXPECT_EQ(max_age, 890);
+ EXPECT_FALSE(include_subdomains);
+
+ EXPECT_TRUE(net::ForceTLSState::ParseHeader(
+ "max-age=123 incLudesUbdOmains", &max_age, &include_subdomains));
+ EXPECT_EQ(max_age, 123);
+ EXPECT_TRUE(include_subdomains);
+
+ EXPECT_TRUE(net::ForceTLSState::ParseHeader(
+ "max-age=394082038 incLudesUbdOmains", &max_age, &include_subdomains));
+ EXPECT_EQ(max_age, 394082038);
+ EXPECT_TRUE(include_subdomains);
+
+ EXPECT_TRUE(net::ForceTLSState::ParseHeader(
+ " max-age=0 incLudesUbdOmains ", &max_age, &include_subdomains));
+ EXPECT_EQ(max_age, 0);
+ EXPECT_TRUE(include_subdomains);
+}
+
+} // namespace
diff --git a/net/net.gyp b/net/net.gyp
index 73eded7..3490d7c 100644
--- a/net/net.gyp
+++ b/net/net.gyp
@@ -404,6 +404,7 @@
'base/file_stream_unittest.cc',
'base/filter_unittest.cc',
'base/filter_unittest.h',
+ 'base/force_tls_state_unittest.cc',
'base/gzip_filter_unittest.cc',
'base/host_resolver_unittest.cc',
'base/listen_socket_unittest.cc',