Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(31)

Side by Side Diff: chrome/browser/net/ssl_config_service_manager_pref_unittest.cc

Issue 1320533007: Componentize ssl_config_service_manager_pref.cc (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/net/ssl_config_service_manager.h" 5 #include "components/ssl_config/ssl_config_service_manager.h"
6 6
7 #include "base/command_line.h" 7 #include "base/command_line.h"
8 #include "base/memory/ref_counted.h" 8 #include "base/memory/ref_counted.h"
9 #include "base/message_loop/message_loop.h" 9 #include "base/message_loop/message_loop.h"
10 #include "base/prefs/pref_registry_simple.h" 10 #include "base/prefs/pref_registry_simple.h"
11 #include "base/prefs/testing_pref_store.h" 11 #include "base/prefs/testing_pref_store.h"
12 #include "base/thread_task_runner_handle.h"
12 #include "base/values.h" 13 #include "base/values.h"
13 #include "chrome/browser/prefs/pref_service_mock_factory.h" 14 #include "chrome/browser/prefs/pref_service_mock_factory.h"
14 #include "chrome/common/chrome_switches.h"
15 #include "chrome/common/pref_names.h"
16 #include "chrome/test/base/testing_pref_service_syncable.h"
17 #include "chrome/test/base/testing_profile.h"
18 #include "components/content_settings/core/browser/host_content_settings_map.h" 15 #include "components/content_settings/core/browser/host_content_settings_map.h"
19 #include "components/content_settings/core/common/content_settings.h" 16 #include "components/content_settings/core/common/content_settings.h"
20 #include "content/public/test/test_browser_thread.h" 17 #include "components/pref_registry/testing_pref_service_syncable.h"
18 #include "components/ssl_config/ssl_config_prefs.h"
19 #include "components/ssl_config/ssl_config_switches.h"
21 #include "net/socket/ssl_client_socket.h" 20 #include "net/socket/ssl_client_socket.h"
22 #include "net/ssl/ssl_config_service.h" 21 #include "net/ssl/ssl_config_service.h"
23 #include "testing/gtest/include/gtest/gtest.h" 22 #include "testing/gtest/include/gtest/gtest.h"
24 23
25 using base::ListValue; 24 using base::ListValue;
26 using base::Value; 25 using base::Value;
27 using content::BrowserThread;
28 using net::SSLConfig; 26 using net::SSLConfig;
29 using net::SSLConfigService; 27 using net::SSLConfigService;
30 28
31 class SSLConfigServiceManagerPrefTest : public testing::Test { 29 class SSLConfigServiceManagerPrefTest : public testing::Test {
32 public: 30 public:
33 SSLConfigServiceManagerPrefTest() 31 SSLConfigServiceManagerPrefTest() {}
34 : ui_thread_(BrowserThread::UI, &message_loop_),
35 io_thread_(BrowserThread::IO, &message_loop_) {}
36 32
37 protected: 33 protected:
38 base::MessageLoop message_loop_; 34 base::MessageLoop message_loop_;
39 content::TestBrowserThread ui_thread_;
40 content::TestBrowserThread io_thread_;
41 }; 35 };
42 36
43 // Test channel id with no user prefs. 37 // Test channel id with no user prefs.
44 TEST_F(SSLConfigServiceManagerPrefTest, ChannelIDWithoutUserPrefs) { 38 TEST_F(SSLConfigServiceManagerPrefTest, ChannelIDWithoutUserPrefs) {
45 TestingPrefServiceSimple local_state; 39 TestingPrefServiceSimple local_state;
46 SSLConfigServiceManager::RegisterPrefs(local_state.registry()); 40 SSLConfigServiceManager::RegisterPrefs(local_state.registry());
47 41
48 scoped_ptr<SSLConfigServiceManager> config_manager( 42 scoped_ptr<SSLConfigServiceManager> config_manager(
49 SSLConfigServiceManager::CreateDefaultManager(&local_state)); 43 SSLConfigServiceManager::CreateDefaultManager(
44 &local_state, base::ThreadTaskRunnerHandle::Get()));
50 ASSERT_TRUE(config_manager.get()); 45 ASSERT_TRUE(config_manager.get());
51 scoped_refptr<SSLConfigService> config_service(config_manager->Get()); 46 scoped_refptr<SSLConfigService> config_service(config_manager->Get());
52 ASSERT_TRUE(config_service.get()); 47 ASSERT_TRUE(config_service.get());
53 48
54 SSLConfig config; 49 SSLConfig config;
55 config_service->GetSSLConfig(&config); 50 config_service->GetSSLConfig(&config);
56 EXPECT_TRUE(config.channel_id_enabled); 51 EXPECT_TRUE(config.channel_id_enabled);
57 } 52 }
58 53
59 // Test that cipher suites can be disabled. "Good" refers to the fact that 54 // Test that cipher suites can be disabled. "Good" refers to the fact that
60 // every value is expected to be successfully parsed into a cipher suite. 55 // every value is expected to be successfully parsed into a cipher suite.
61 TEST_F(SSLConfigServiceManagerPrefTest, GoodDisabledCipherSuites) { 56 TEST_F(SSLConfigServiceManagerPrefTest, GoodDisabledCipherSuites) {
62 TestingPrefServiceSimple local_state; 57 TestingPrefServiceSimple local_state;
63 SSLConfigServiceManager::RegisterPrefs(local_state.registry()); 58 SSLConfigServiceManager::RegisterPrefs(local_state.registry());
64 59
65 scoped_ptr<SSLConfigServiceManager> config_manager( 60 scoped_ptr<SSLConfigServiceManager> config_manager(
66 SSLConfigServiceManager::CreateDefaultManager(&local_state)); 61 SSLConfigServiceManager::CreateDefaultManager(
62 &local_state, base::ThreadTaskRunnerHandle::Get()));
67 ASSERT_TRUE(config_manager.get()); 63 ASSERT_TRUE(config_manager.get());
68 scoped_refptr<SSLConfigService> config_service(config_manager->Get()); 64 scoped_refptr<SSLConfigService> config_service(config_manager->Get());
69 ASSERT_TRUE(config_service.get()); 65 ASSERT_TRUE(config_service.get());
70 66
71 SSLConfig old_config; 67 SSLConfig old_config;
72 config_service->GetSSLConfig(&old_config); 68 config_service->GetSSLConfig(&old_config);
73 EXPECT_TRUE(old_config.disabled_cipher_suites.empty()); 69 EXPECT_TRUE(old_config.disabled_cipher_suites.empty());
74 70
75 base::ListValue* list_value = new base::ListValue(); 71 base::ListValue* list_value = new base::ListValue();
76 list_value->Append(new base::StringValue("0x0004")); 72 list_value->Append(new base::StringValue("0x0004"));
77 list_value->Append(new base::StringValue("0x0005")); 73 list_value->Append(new base::StringValue("0x0005"));
78 local_state.SetUserPref(prefs::kCipherSuiteBlacklist, list_value); 74 local_state.SetUserPref(ssl_config::prefs::kCipherSuiteBlacklist, list_value);
79 75
80 // Pump the message loop to notify the SSLConfigServiceManagerPref that the 76 // Pump the message loop to notify the SSLConfigServiceManagerPref that the
81 // preferences changed. 77 // preferences changed.
82 message_loop_.RunUntilIdle(); 78 message_loop_.RunUntilIdle();
83 79
84 SSLConfig config; 80 SSLConfig config;
85 config_service->GetSSLConfig(&config); 81 config_service->GetSSLConfig(&config);
86 82
87 EXPECT_NE(old_config.disabled_cipher_suites, config.disabled_cipher_suites); 83 EXPECT_NE(old_config.disabled_cipher_suites, config.disabled_cipher_suites);
88 ASSERT_EQ(2u, config.disabled_cipher_suites.size()); 84 ASSERT_EQ(2u, config.disabled_cipher_suites.size());
89 EXPECT_EQ(0x0004, config.disabled_cipher_suites[0]); 85 EXPECT_EQ(0x0004, config.disabled_cipher_suites[0]);
90 EXPECT_EQ(0x0005, config.disabled_cipher_suites[1]); 86 EXPECT_EQ(0x0005, config.disabled_cipher_suites[1]);
91 } 87 }
92 88
93 // Test that cipher suites can be disabled. "Bad" refers to the fact that 89 // Test that cipher suites can be disabled. "Bad" refers to the fact that
94 // there are one or more non-cipher suite strings in the preference. They 90 // there are one or more non-cipher suite strings in the preference. They
95 // should be ignored. 91 // should be ignored.
96 TEST_F(SSLConfigServiceManagerPrefTest, BadDisabledCipherSuites) { 92 TEST_F(SSLConfigServiceManagerPrefTest, BadDisabledCipherSuites) {
97 TestingPrefServiceSimple local_state; 93 TestingPrefServiceSimple local_state;
98 SSLConfigServiceManager::RegisterPrefs(local_state.registry()); 94 SSLConfigServiceManager::RegisterPrefs(local_state.registry());
99 95
100 scoped_ptr<SSLConfigServiceManager> config_manager( 96 scoped_ptr<SSLConfigServiceManager> config_manager(
101 SSLConfigServiceManager::CreateDefaultManager(&local_state)); 97 SSLConfigServiceManager::CreateDefaultManager(
98 &local_state, base::ThreadTaskRunnerHandle::Get()));
102 ASSERT_TRUE(config_manager.get()); 99 ASSERT_TRUE(config_manager.get());
103 scoped_refptr<SSLConfigService> config_service(config_manager->Get()); 100 scoped_refptr<SSLConfigService> config_service(config_manager->Get());
104 ASSERT_TRUE(config_service.get()); 101 ASSERT_TRUE(config_service.get());
105 102
106 SSLConfig old_config; 103 SSLConfig old_config;
107 config_service->GetSSLConfig(&old_config); 104 config_service->GetSSLConfig(&old_config);
108 EXPECT_TRUE(old_config.disabled_cipher_suites.empty()); 105 EXPECT_TRUE(old_config.disabled_cipher_suites.empty());
109 106
110 base::ListValue* list_value = new base::ListValue(); 107 base::ListValue* list_value = new base::ListValue();
111 list_value->Append(new base::StringValue("0x0004")); 108 list_value->Append(new base::StringValue("0x0004"));
112 list_value->Append(new base::StringValue("TLS_NOT_WITH_A_CIPHER_SUITE")); 109 list_value->Append(new base::StringValue("TLS_NOT_WITH_A_CIPHER_SUITE"));
113 list_value->Append(new base::StringValue("0x0005")); 110 list_value->Append(new base::StringValue("0x0005"));
114 list_value->Append(new base::StringValue("0xBEEFY")); 111 list_value->Append(new base::StringValue("0xBEEFY"));
115 local_state.SetUserPref(prefs::kCipherSuiteBlacklist, list_value); 112 local_state.SetUserPref(ssl_config::prefs::kCipherSuiteBlacklist, list_value);
116 113
117 // Pump the message loop to notify the SSLConfigServiceManagerPref that the 114 // Pump the message loop to notify the SSLConfigServiceManagerPref that the
118 // preferences changed. 115 // preferences changed.
119 message_loop_.RunUntilIdle(); 116 message_loop_.RunUntilIdle();
120 117
121 SSLConfig config; 118 SSLConfig config;
122 config_service->GetSSLConfig(&config); 119 config_service->GetSSLConfig(&config);
123 120
124 EXPECT_NE(old_config.disabled_cipher_suites, config.disabled_cipher_suites); 121 EXPECT_NE(old_config.disabled_cipher_suites, config.disabled_cipher_suites);
125 ASSERT_EQ(2u, config.disabled_cipher_suites.size()); 122 ASSERT_EQ(2u, config.disabled_cipher_suites.size());
126 EXPECT_EQ(0x0004, config.disabled_cipher_suites[0]); 123 EXPECT_EQ(0x0004, config.disabled_cipher_suites[0]);
127 EXPECT_EQ(0x0005, config.disabled_cipher_suites[1]); 124 EXPECT_EQ(0x0005, config.disabled_cipher_suites[1]);
128 } 125 }
129 126
130 // Test that without command-line settings for minimum and maximum SSL versions, 127 // Test that without command-line settings for minimum and maximum SSL versions,
131 // TLS versions from 1.0 up to 1.1 or 1.2 are enabled. 128 // TLS versions from 1.0 up to 1.1 or 1.2 are enabled.
132 TEST_F(SSLConfigServiceManagerPrefTest, NoCommandLinePrefs) { 129 TEST_F(SSLConfigServiceManagerPrefTest, NoCommandLinePrefs) {
133 scoped_refptr<TestingPrefStore> local_state_store(new TestingPrefStore()); 130 scoped_refptr<TestingPrefStore> local_state_store(new TestingPrefStore());
134 131
135 PrefServiceMockFactory factory; 132 PrefServiceMockFactory factory;
136 factory.set_user_prefs(local_state_store); 133 factory.set_user_prefs(local_state_store);
137 scoped_refptr<PrefRegistrySimple> registry = new PrefRegistrySimple; 134 scoped_refptr<PrefRegistrySimple> registry = new PrefRegistrySimple;
138 scoped_ptr<PrefService> local_state(factory.Create(registry.get())); 135 scoped_ptr<PrefService> local_state(factory.Create(registry.get()));
139 136
140 SSLConfigServiceManager::RegisterPrefs(registry.get()); 137 SSLConfigServiceManager::RegisterPrefs(registry.get());
141 138
142 scoped_ptr<SSLConfigServiceManager> config_manager( 139 scoped_ptr<SSLConfigServiceManager> config_manager(
143 SSLConfigServiceManager::CreateDefaultManager(local_state.get())); 140 SSLConfigServiceManager::CreateDefaultManager(
141 local_state.get(), base::ThreadTaskRunnerHandle::Get()));
144 ASSERT_TRUE(config_manager.get()); 142 ASSERT_TRUE(config_manager.get());
145 scoped_refptr<SSLConfigService> config_service(config_manager->Get()); 143 scoped_refptr<SSLConfigService> config_service(config_manager->Get());
146 ASSERT_TRUE(config_service.get()); 144 ASSERT_TRUE(config_service.get());
147 145
148 SSLConfig ssl_config; 146 SSLConfig ssl_config;
149 config_service->GetSSLConfig(&ssl_config); 147 config_service->GetSSLConfig(&ssl_config);
150 // In the absence of command-line options, TLS versions from 1.0 up to 1.1 or 148 // In the absence of command-line options, TLS versions from 1.0 up to 1.1 or
151 // 1.2 (depending on the underlying library and cryptographic implementation) 149 // 1.2 (depending on the underlying library and cryptographic implementation)
152 // are enabled. 150 // are enabled.
153 EXPECT_EQ(net::SSL_PROTOCOL_VERSION_TLS1, ssl_config.version_min); 151 EXPECT_EQ(net::SSL_PROTOCOL_VERSION_TLS1, ssl_config.version_min);
154 EXPECT_EQ(net::SSLClientSocket::GetMaxSupportedSSLVersion(), 152 EXPECT_EQ(net::SSLClientSocket::GetMaxSupportedSSLVersion(),
155 ssl_config.version_max); 153 ssl_config.version_max);
156 154
157 // The settings should not be added to the local_state. 155 // The settings should not be added to the local_state.
158 EXPECT_FALSE(local_state->HasPrefPath(prefs::kSSLVersionMin)); 156 EXPECT_FALSE(local_state->HasPrefPath(ssl_config::prefs::kSSLVersionMin));
159 EXPECT_FALSE(local_state->HasPrefPath(prefs::kSSLVersionMax)); 157 EXPECT_FALSE(local_state->HasPrefPath(ssl_config::prefs::kSSLVersionMax));
160 158
161 // Explicitly double-check the settings are not in the preference store. 159 // Explicitly double-check the settings are not in the preference store.
162 std::string version_min_str; 160 std::string version_min_str;
163 std::string version_max_str; 161 std::string version_max_str;
164 EXPECT_FALSE(local_state_store->GetString(prefs::kSSLVersionMin, 162 EXPECT_FALSE(local_state_store->GetString(ssl_config::prefs::kSSLVersionMin,
165 &version_min_str)); 163 &version_min_str));
166 EXPECT_FALSE(local_state_store->GetString(prefs::kSSLVersionMax, 164 EXPECT_FALSE(local_state_store->GetString(ssl_config::prefs::kSSLVersionMax,
167 &version_max_str)); 165 &version_max_str));
168 } 166 }
169 167
170 // Test that command-line settings for minimum and maximum SSL versions are 168 // Test that command-line settings for minimum and maximum SSL versions are
171 // respected and that they do not persist to the preferences files. 169 // respected and that they do not persist to the preferences files.
172 TEST_F(SSLConfigServiceManagerPrefTest, CommandLinePrefs) { 170 TEST_F(SSLConfigServiceManagerPrefTest, CommandLinePrefs) {
173 scoped_refptr<TestingPrefStore> local_state_store(new TestingPrefStore()); 171 scoped_refptr<TestingPrefStore> local_state_store(new TestingPrefStore());
174 172
175 base::CommandLine command_line(base::CommandLine::NO_PROGRAM); 173 base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
176 command_line.AppendSwitchASCII(switches::kSSLVersionMin, "tls1.1"); 174 command_line.AppendSwitchASCII(switches::kSSLVersionMin, "tls1.1");
177 command_line.AppendSwitchASCII(switches::kSSLVersionMax, "tls1"); 175 command_line.AppendSwitchASCII(switches::kSSLVersionMax, "tls1");
178 176
179 PrefServiceMockFactory factory; 177 PrefServiceMockFactory factory;
180 factory.set_user_prefs(local_state_store); 178 factory.set_user_prefs(local_state_store);
181 factory.SetCommandLine(&command_line); 179 factory.SetCommandLine(&command_line);
182 scoped_refptr<PrefRegistrySimple> registry = new PrefRegistrySimple; 180 scoped_refptr<PrefRegistrySimple> registry = new PrefRegistrySimple;
183 scoped_ptr<PrefService> local_state(factory.Create(registry.get())); 181 scoped_ptr<PrefService> local_state(factory.Create(registry.get()));
184 182
185 SSLConfigServiceManager::RegisterPrefs(registry.get()); 183 SSLConfigServiceManager::RegisterPrefs(registry.get());
186 184
187 scoped_ptr<SSLConfigServiceManager> config_manager( 185 scoped_ptr<SSLConfigServiceManager> config_manager(
188 SSLConfigServiceManager::CreateDefaultManager(local_state.get())); 186 SSLConfigServiceManager::CreateDefaultManager(
187 local_state.get(), base::ThreadTaskRunnerHandle::Get()));
189 ASSERT_TRUE(config_manager.get()); 188 ASSERT_TRUE(config_manager.get());
190 scoped_refptr<SSLConfigService> config_service(config_manager->Get()); 189 scoped_refptr<SSLConfigService> config_service(config_manager->Get());
191 ASSERT_TRUE(config_service.get()); 190 ASSERT_TRUE(config_service.get());
192 191
193 SSLConfig ssl_config; 192 SSLConfig ssl_config;
194 config_service->GetSSLConfig(&ssl_config); 193 config_service->GetSSLConfig(&ssl_config);
195 // Command-line flags should be respected. 194 // Command-line flags should be respected.
196 EXPECT_EQ(net::SSL_PROTOCOL_VERSION_TLS1_1, ssl_config.version_min); 195 EXPECT_EQ(net::SSL_PROTOCOL_VERSION_TLS1_1, ssl_config.version_min);
197 EXPECT_EQ(net::SSL_PROTOCOL_VERSION_TLS1, ssl_config.version_max); 196 EXPECT_EQ(net::SSL_PROTOCOL_VERSION_TLS1, ssl_config.version_max);
198 197
199 // Explicitly double-check the settings are not in the preference store. 198 // Explicitly double-check the settings are not in the preference store.
200 const PrefService::Preference* version_min_pref = 199 const PrefService::Preference* version_min_pref =
201 local_state->FindPreference(prefs::kSSLVersionMin); 200 local_state->FindPreference(ssl_config::prefs::kSSLVersionMin);
202 EXPECT_FALSE(version_min_pref->IsUserModifiable()); 201 EXPECT_FALSE(version_min_pref->IsUserModifiable());
203 202
204 const PrefService::Preference* version_max_pref = 203 const PrefService::Preference* version_max_pref =
205 local_state->FindPreference(prefs::kSSLVersionMax); 204 local_state->FindPreference(ssl_config::prefs::kSSLVersionMax);
206 EXPECT_FALSE(version_max_pref->IsUserModifiable()); 205 EXPECT_FALSE(version_max_pref->IsUserModifiable());
207 206
208 std::string version_min_str; 207 std::string version_min_str;
209 std::string version_max_str; 208 std::string version_max_str;
210 EXPECT_FALSE(local_state_store->GetString(prefs::kSSLVersionMin, 209 EXPECT_FALSE(local_state_store->GetString(ssl_config::prefs::kSSLVersionMin,
211 &version_min_str)); 210 &version_min_str));
212 EXPECT_FALSE(local_state_store->GetString(prefs::kSSLVersionMax, 211 EXPECT_FALSE(local_state_store->GetString(ssl_config::prefs::kSSLVersionMax,
213 &version_max_str)); 212 &version_max_str));
214 } 213 }
215 214
216 // Tests that "ssl3" is not treated as a valid minimum version. 215 // Tests that "ssl3" is not treated as a valid minimum version.
217 TEST_F(SSLConfigServiceManagerPrefTest, NoSSL3) { 216 TEST_F(SSLConfigServiceManagerPrefTest, NoSSL3) {
218 scoped_refptr<TestingPrefStore> local_state_store(new TestingPrefStore()); 217 scoped_refptr<TestingPrefStore> local_state_store(new TestingPrefStore());
219 218
220 base::CommandLine command_line(base::CommandLine::NO_PROGRAM); 219 base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
221 command_line.AppendSwitchASCII(switches::kSSLVersionMin, "ssl3"); 220 command_line.AppendSwitchASCII(switches::kSSLVersionMin, "ssl3");
222 221
223 PrefServiceMockFactory factory; 222 PrefServiceMockFactory factory;
224 factory.set_user_prefs(local_state_store); 223 factory.set_user_prefs(local_state_store);
225 factory.SetCommandLine(&command_line); 224 factory.SetCommandLine(&command_line);
226 scoped_refptr<PrefRegistrySimple> registry = new PrefRegistrySimple; 225 scoped_refptr<PrefRegistrySimple> registry = new PrefRegistrySimple;
227 scoped_ptr<PrefService> local_state(factory.Create(registry.get())); 226 scoped_ptr<PrefService> local_state(factory.Create(registry.get()));
228 227
229 SSLConfigServiceManager::RegisterPrefs(registry.get()); 228 SSLConfigServiceManager::RegisterPrefs(registry.get());
230 229
231 scoped_ptr<SSLConfigServiceManager> config_manager( 230 scoped_ptr<SSLConfigServiceManager> config_manager(
232 SSLConfigServiceManager::CreateDefaultManager(local_state.get())); 231 SSLConfigServiceManager::CreateDefaultManager(
232 local_state.get(), base::ThreadTaskRunnerHandle::Get()));
233 ASSERT_TRUE(config_manager.get()); 233 ASSERT_TRUE(config_manager.get());
234 scoped_refptr<SSLConfigService> config_service(config_manager->Get()); 234 scoped_refptr<SSLConfigService> config_service(config_manager->Get());
235 ASSERT_TRUE(config_service.get()); 235 ASSERT_TRUE(config_service.get());
236 236
237 SSLConfig ssl_config; 237 SSLConfig ssl_config;
238 config_service->GetSSLConfig(&ssl_config); 238 config_service->GetSSLConfig(&ssl_config);
239 // The command-line option must not have been honored. 239 // The command-line option must not have been honored.
240 EXPECT_LE(net::SSL_PROTOCOL_VERSION_TLS1, ssl_config.version_min); 240 EXPECT_LE(net::SSL_PROTOCOL_VERSION_TLS1, ssl_config.version_min);
241 } 241 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698