OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 // End-to-end tests for WebSocket. Usage guidelines: | |
6 // There are at least 4 sets of end-to-end tests in the source tree: | |
7 // 1) Blink layout tests. These are easy to write, and efficient because the | |
8 // client and server processes are both re-used. They are easy to write as | |
9 // they are just Javascript. They should be used for the majority of | |
10 // functional tests. | |
11 // 2) Browser tests in //chrome/browser/net/websocket_browsertest.cc. These | |
12 // are inefficient because both the server and client are restarted for each | |
13 // test. They are also hard to write and hard to debug. They should generally | |
14 // only be used for testing UI-related features. | |
15 // 3) PPAPI tests in //chrome/test/ppapi/ppapi_browsertest.cc. These are | |
16 // very hard to write and debug, and should only be used for verifying the | |
17 // operation of the Pepper API. | |
18 // 4) These tests. They are somewhat inefficient because the server is restarted | |
19 // for each test. They are well-suited for tests which require special server | |
20 // configurations. | |
21 // | |
22 // Tests should generally not be duplicated in the different places, except for | |
23 // regression tests and basic smoke tests. | |
24 | |
tyoshino (SeeGerritForStatus)
2015/01/05 08:45:48
string
Adam Rice
2015/01/06 12:27:15
Done.
| |
25 #include "base/callback.h" | |
26 #include "base/memory/scoped_ptr.h" | |
27 #include "base/run_loop.h" | |
28 #include "net/base/auth.h" | |
29 #include "net/base/network_delegate.h" | |
30 #include "net/base/test_data_directory.h" | |
tyoshino (SeeGerritForStatus)
2015/01/05 08:47:29
net/proxy/proxy_service.h
Adam Rice
2015/01/06 12:27:15
Done.
| |
31 #include "net/test/spawned_test_server/spawned_test_server.h" | |
32 #include "net/url_request/url_request_test_util.h" | |
33 #include "net/websockets/websocket_channel.h" | |
34 #include "net/websockets/websocket_event_interface.h" | |
35 #include "testing/gtest/include/gtest/gtest.h" | |
36 #include "url/origin.h" | |
37 | |
38 namespace net { | |
39 | |
40 namespace { | |
41 | |
42 static const char kEchoServer[] = "echo-with-no-extension"; | |
43 | |
44 // An implementation of WebSocketEventInterface that waits for and records the | |
45 // results of the connect. | |
46 class ConnectTestingEventInterface : public WebSocketEventInterface { | |
47 public: | |
48 ConnectTestingEventInterface() : fail_(true) {} | |
49 | |
50 void WaitForResponse() { | |
51 quit_closure_ = run_loop_.QuitClosure(); | |
tyoshino (SeeGerritForStatus)
2015/01/05 08:45:48
quit_closure_ is initialized here to make DCHECK o
Adam Rice
2015/01/06 12:27:15
No, it was a mistake. I misunderstood the API of R
| |
52 run_loop_.Run(); | |
53 } | |
54 | |
tyoshino (SeeGerritForStatus)
2015/01/05 08:45:48
explain that this variable is used for storing sev
Adam Rice
2015/01/06 12:27:15
I decided that setting fail_ in OnDropChannel() wa
| |
55 bool fail() const { return fail_; } | |
56 | |
57 // Only set if the handshake failed, otherwise empty. | |
58 // Failure messages appear on the console and are checked by the layout tests, | |
59 // so they are expected to stay reasonably stable across versions. | |
60 std::string failure_message() const { return failure_message_; } | |
61 | |
62 std::string selected_subprotocol() const { return selected_subprotocol_; } | |
63 | |
64 std::string extensions() const { return extensions_; } | |
65 | |
66 ChannelState OnAddChannelResponse(bool fail, | |
67 const std::string& selected_subprotocol, | |
68 const std::string& extensions) override { | |
69 fail_ = fail; | |
70 selected_subprotocol_ = selected_subprotocol; | |
71 extensions_ = extensions; | |
72 QuitNestedEventLoop(); | |
73 return fail ? CHANNEL_DELETED : CHANNEL_ALIVE; | |
74 } | |
75 | |
76 ChannelState OnDataFrame(bool fin, | |
77 WebSocketMessageType type, | |
78 const std::vector<char>& data) override { | |
79 return CHANNEL_ALIVE; | |
80 } | |
81 | |
82 ChannelState OnFlowControl(int64 quota) override { return CHANNEL_ALIVE; } | |
83 | |
84 ChannelState OnClosingHandshake() override { return CHANNEL_ALIVE; } | |
85 | |
86 ChannelState OnDropChannel(bool was_clean, | |
87 uint16 code, | |
88 const std::string& reason) override { | |
89 fail_ = !was_clean; | |
90 QuitNestedEventLoop(); | |
91 return CHANNEL_DELETED; | |
92 } | |
93 | |
94 ChannelState OnFailChannel(const std::string& message) override { | |
95 fail_ = true; | |
96 failure_message_ = message; | |
97 QuitNestedEventLoop(); | |
98 return CHANNEL_DELETED; | |
99 } | |
100 | |
101 ChannelState OnStartOpeningHandshake( | |
102 scoped_ptr<WebSocketHandshakeRequestInfo> request) override { | |
103 return CHANNEL_ALIVE; | |
104 } | |
105 | |
106 ChannelState OnFinishOpeningHandshake( | |
107 scoped_ptr<WebSocketHandshakeResponseInfo> response) override { | |
108 return CHANNEL_ALIVE; | |
109 } | |
110 | |
111 ChannelState OnSSLCertificateError( | |
112 scoped_ptr<SSLErrorCallbacks> ssl_error_callbacks, | |
113 const GURL& url, | |
114 const SSLInfo& ssl_info, | |
115 bool fatal) override { | |
116 ssl_error_callbacks->CancelSSLRequest(ERR_SSL_PROTOCOL_ERROR, &ssl_info); | |
tyoshino (SeeGerritForStatus)
2015/01/05 08:45:48
I'm a little worried about CancelSSLRequest() bein
Adam Rice
2015/01/06 12:27:15
I'm not 100% sure. I decided to call it via the Po
| |
117 return CHANNEL_ALIVE; | |
118 } | |
119 | |
120 private: | |
121 void QuitNestedEventLoop() { | |
122 DCHECK(!quit_closure_.is_null()); | |
123 quit_closure_.Run(); | |
124 } | |
125 bool fail_; | |
126 std::string selected_subprotocol_; | |
127 std::string extensions_; | |
128 std::string failure_message_; | |
129 base::RunLoop run_loop_; | |
130 base::Closure quit_closure_; | |
131 | |
132 DISALLOW_COPY_AND_ASSIGN(ConnectTestingEventInterface); | |
133 }; | |
134 | |
135 class WebSocketEndToEndTest : public ::testing::Test { | |
136 protected: | |
137 WebSocketEndToEndTest() | |
138 : event_interface_(new ConnectTestingEventInterface), | |
139 network_delegate_(new TestNetworkDelegate), | |
140 context_(true), | |
141 channel_(make_scoped_ptr(event_interface_), &context_), | |
142 origin_("http://localhost"), | |
143 initialised_context_(false) {} | |
144 | |
145 void InitialiseContext() { | |
146 context_.set_network_delegate(network_delegate_.get()); | |
147 context_.Init(); | |
148 initialised_context_ = true; | |
149 } | |
150 | |
151 bool ConnectAndWait(const GURL& socket_url) { | |
152 if (!initialised_context_) { | |
153 InitialiseContext(); | |
154 } | |
155 channel_.SendAddChannelRequest(GURL(socket_url), sub_protocols_, origin_); | |
156 event_interface_->WaitForResponse(); | |
157 return !event_interface_->fail(); | |
158 } | |
159 | |
160 ConnectTestingEventInterface* event_interface_; // owned by channel_ | |
161 scoped_ptr<TestNetworkDelegate> network_delegate_; | |
162 TestURLRequestContext context_; | |
163 WebSocketChannel channel_; | |
164 std::vector<std::string> sub_protocols_; | |
165 url::Origin origin_; | |
tyoshino (SeeGerritForStatus)
2015/01/05 08:45:48
how about making sub_protocols_ and origin_ local
Adam Rice
2015/01/06 12:27:15
Done.
| |
166 bool initialised_context_; | |
167 }; | |
168 | |
169 // None of these tests work on Android. | |
170 // TODO(ricea): Make these tests work on Android. See crbug.com/441711. | |
171 #if defined(OS_ANDROID) | |
172 #define DISABLED_ON_ANDROID(test) DISABLED_##test | |
173 #else | |
174 #define DISABLED_ON_ANDROID(test) test | |
175 #endif | |
176 | |
177 // Basic test of connectivity. If this test fails, nothing else can be expected | |
178 // to work. | |
179 TEST_F(WebSocketEndToEndTest, DISABLED_ON_ANDROID(BasicSmokeTest)) { | |
180 SpawnedTestServer ws_server(SpawnedTestServer::TYPE_WS, | |
181 SpawnedTestServer::kLocalhost, | |
182 GetWebSocketTestDataDirectory()); | |
183 ASSERT_TRUE(ws_server.Start()); | |
184 EXPECT_TRUE(ConnectAndWait(ws_server.GetURL(kEchoServer))); | |
185 } | |
186 | |
187 // Test for issue crbug.com/433695 "Unencrypted WebSocket connection via | |
188 // authenticated proxy times out" | |
189 // TODO(ricea): Enable this when the issue is fixed. | |
190 TEST_F(WebSocketEndToEndTest, DISABLED_HttpsProxyUnauthedFails) { | |
191 SpawnedTestServer proxy_server(SpawnedTestServer::TYPE_BASIC_AUTH_PROXY, | |
192 SpawnedTestServer::kLocalhost, | |
193 base::FilePath()); | |
194 SpawnedTestServer ws_server(SpawnedTestServer::TYPE_WS, | |
195 SpawnedTestServer::kLocalhost, | |
196 GetWebSocketTestDataDirectory()); | |
197 ASSERT_TRUE(proxy_server.StartInBackground()); | |
198 ASSERT_TRUE(ws_server.StartInBackground()); | |
199 ASSERT_TRUE(proxy_server.BlockUntilStarted()); | |
200 ASSERT_TRUE(ws_server.BlockUntilStarted()); | |
201 std::string proxy_config = | |
202 "https=" + proxy_server.host_port_pair().ToString(); | |
203 scoped_ptr<ProxyService> proxy_service( | |
204 ProxyService::CreateFixed(proxy_config)); | |
205 ASSERT_TRUE(proxy_service); | |
206 context_.set_proxy_service(proxy_service.get()); | |
207 EXPECT_FALSE(ConnectAndWait(ws_server.GetURL(kEchoServer))); | |
208 EXPECT_EQ("Proxy authentication failed", event_interface_->failure_message()); | |
209 } | |
210 | |
211 TEST_F(WebSocketEndToEndTest, DISABLED_ON_ANDROID(HttpsWssProxyUnauthedFails)) { | |
212 SpawnedTestServer proxy_server(SpawnedTestServer::TYPE_BASIC_AUTH_PROXY, | |
213 SpawnedTestServer::kLocalhost, | |
214 base::FilePath()); | |
215 SpawnedTestServer wss_server(SpawnedTestServer::TYPE_WSS, | |
216 SpawnedTestServer::kLocalhost, | |
217 GetWebSocketTestDataDirectory()); | |
218 ASSERT_TRUE(proxy_server.StartInBackground()); | |
219 ASSERT_TRUE(wss_server.StartInBackground()); | |
220 ASSERT_TRUE(proxy_server.BlockUntilStarted()); | |
221 ASSERT_TRUE(wss_server.BlockUntilStarted()); | |
222 std::string proxy_config = | |
223 "https=" + proxy_server.host_port_pair().ToString(); | |
224 scoped_ptr<ProxyService> proxy_service( | |
225 ProxyService::CreateFixed(proxy_config)); | |
226 ASSERT_TRUE(proxy_service); | |
227 context_.set_proxy_service(proxy_service.get()); | |
228 EXPECT_FALSE(ConnectAndWait(wss_server.GetURL(kEchoServer))); | |
229 EXPECT_EQ("Proxy authentication failed", event_interface_->failure_message()); | |
230 } | |
231 | |
232 // Regression test for crbug/426736 "WebSocket connections not using configured | |
233 // system HTTPS Proxy". | |
234 TEST_F(WebSocketEndToEndTest, DISABLED_ON_ANDROID(HttpsProxyUsed)) { | |
235 SpawnedTestServer proxy_server(SpawnedTestServer::TYPE_BASIC_AUTH_PROXY, | |
236 SpawnedTestServer::kLocalhost, | |
237 base::FilePath()); | |
238 SpawnedTestServer ws_server(SpawnedTestServer::TYPE_WS, | |
239 SpawnedTestServer::kLocalhost, | |
240 GetWebSocketTestDataDirectory()); | |
241 ASSERT_TRUE(proxy_server.StartInBackground()); | |
242 ASSERT_TRUE(ws_server.StartInBackground()); | |
243 ASSERT_TRUE(proxy_server.BlockUntilStarted()); | |
244 ASSERT_TRUE(ws_server.BlockUntilStarted()); | |
245 std::string proxy_config = "https=" + | |
246 proxy_server.host_port_pair().ToString() + ";" + | |
247 "http=" + proxy_server.host_port_pair().ToString(); | |
248 scoped_ptr<ProxyService> proxy_service( | |
249 ProxyService::CreateFixed(proxy_config)); | |
250 context_.set_proxy_service(proxy_service.get()); | |
251 InitialiseContext(); | |
252 | |
253 // The test server doesn't have an unauthenticated proxy mode. WebSockets | |
254 // cannot provide auth information that isn't already cached, so it's | |
255 // necessary to preflight an HTTP request to authenticate against the proxy. | |
256 std::string scheme("http"); | |
257 GURL::Replacements replacements; | |
258 replacements.SetSchemeStr(scheme); | |
259 // It doesn't matter what the URL is, as long as it is an HTTP navigation. | |
260 GURL http_page = | |
261 ws_server.GetURL("connect_check.html").ReplaceComponents(replacements); | |
262 TestDelegate delegate; | |
263 delegate.set_credentials( | |
264 AuthCredentials(base::ASCIIToUTF16("foo"), base::ASCIIToUTF16("bar"))); | |
265 { | |
266 scoped_ptr<URLRequest> request( | |
267 context_.CreateRequest(http_page, DEFAULT_PRIORITY, &delegate, NULL)); | |
268 request->Start(); | |
269 // TestDelegate exits the message loop when the request completes by | |
270 // default. | |
271 base::RunLoop().Run(); | |
272 EXPECT_TRUE(delegate.auth_required_called()); | |
273 } | |
274 | |
275 int previous_observed_before_proxy_headers_sent_callbacks = | |
276 network_delegate_->observed_before_proxy_headers_sent_callbacks(); | |
277 EXPECT_TRUE(ConnectAndWait(ws_server.GetURL(kEchoServer))); | |
278 EXPECT_EQ(previous_observed_before_proxy_headers_sent_callbacks + 1, | |
279 network_delegate_->observed_before_proxy_headers_sent_callbacks()); | |
tyoshino (SeeGerritForStatus)
2015/01/05 08:45:48
what does this expectations add? reading the varia
Adam Rice
2015/01/06 12:27:15
It works because of a bug. I'm still looking for a
Adam Rice
2015/01/06 15:48:43
I found a way to do it. PTAL.
| |
280 } | |
281 | |
282 } // namespace | |
283 | |
284 } // namespace net | |
OLD | NEW |