blob: fef2809557028d433fb288faa2142c1467d33072 [file] [log] [blame]
ricea433bdab2015-01-26 07:25:371// 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.
6//
7// A python server is (re)started for each test, which is moderately
8// inefficient. However, it makes these tests a good fit for scenarios which
9// require special server configurations.
10
tfarina8a2c66c22015-10-13 19:14:4911#include <stdint.h>
ricea433bdab2015-01-26 07:25:3712#include <string>
13
14#include "base/bind.h"
15#include "base/bind_helpers.h"
16#include "base/callback.h"
skyostil4891b25b2015-06-11 11:43:4517#include "base/location.h"
ricea433bdab2015-01-26 07:25:3718#include "base/memory/scoped_ptr.h"
ricea433bdab2015-01-26 07:25:3719#include "base/run_loop.h"
skyostil4891b25b2015-06-11 11:43:4520#include "base/single_thread_task_runner.h"
Adam Ricecb76ac62015-02-20 05:33:2521#include "base/strings/string_piece.h"
skyostil4891b25b2015-06-11 11:43:4522#include "base/thread_task_runner_handle.h"
ricea433bdab2015-01-26 07:25:3723#include "net/base/auth.h"
24#include "net/base/network_delegate.h"
25#include "net/base/test_data_directory.h"
26#include "net/proxy/proxy_service.h"
27#include "net/test/spawned_test_server/spawned_test_server.h"
28#include "net/url_request/url_request_test_util.h"
29#include "net/websockets/websocket_channel.h"
30#include "net/websockets/websocket_event_interface.h"
31#include "testing/gtest/include/gtest/gtest.h"
mkwst4997ce82015-07-25 12:00:0532#include "url/origin.h"
ricea433bdab2015-01-26 07:25:3733
34namespace net {
35
36namespace {
37
38static const char kEchoServer[] = "echo-with-no-extension";
39
Adam Ricecb76ac62015-02-20 05:33:2540// Simplify changing URL schemes.
41GURL ReplaceUrlScheme(const GURL& in_url, const base::StringPiece& scheme) {
42 GURL::Replacements replacements;
43 replacements.SetSchemeStr(scheme);
44 return in_url.ReplaceComponents(replacements);
45}
46
ricea433bdab2015-01-26 07:25:3747// An implementation of WebSocketEventInterface that waits for and records the
48// results of the connect.
49class ConnectTestingEventInterface : public WebSocketEventInterface {
50 public:
51 ConnectTestingEventInterface();
52
53 void WaitForResponse();
54
55 bool failed() const { return failed_; }
56
57 // Only set if the handshake failed, otherwise empty.
58 std::string failure_message() const;
59
60 std::string selected_subprotocol() const;
61
62 std::string extensions() const;
63
64 // Implementation of WebSocketEventInterface.
tyoshinoc06da562015-03-06 06:02:4265 ChannelState OnAddChannelResponse(const std::string& selected_subprotocol,
ricea433bdab2015-01-26 07:25:3766 const std::string& extensions) override;
67
68 ChannelState OnDataFrame(bool fin,
69 WebSocketMessageType type,
70 const std::vector<char>& data) override;
71
tfarina8a2c66c22015-10-13 19:14:4972 ChannelState OnFlowControl(int64_t quota) override;
ricea433bdab2015-01-26 07:25:3773
74 ChannelState OnClosingHandshake() override;
75
76 ChannelState OnDropChannel(bool was_clean,
tfarina8a2c66c22015-10-13 19:14:4977 uint16_t code,
ricea433bdab2015-01-26 07:25:3778 const std::string& reason) override;
79
80 ChannelState OnFailChannel(const std::string& message) override;
81
82 ChannelState OnStartOpeningHandshake(
83 scoped_ptr<WebSocketHandshakeRequestInfo> request) override;
84
85 ChannelState OnFinishOpeningHandshake(
86 scoped_ptr<WebSocketHandshakeResponseInfo> response) override;
87
88 ChannelState OnSSLCertificateError(
89 scoped_ptr<SSLErrorCallbacks> ssl_error_callbacks,
90 const GURL& url,
91 const SSLInfo& ssl_info,
92 bool fatal) override;
93
94 private:
95 void QuitNestedEventLoop();
96
97 // failed_ is true if the handshake failed (ie. OnFailChannel was called).
98 bool failed_;
99 std::string selected_subprotocol_;
100 std::string extensions_;
101 std::string failure_message_;
102 base::RunLoop run_loop_;
103
104 DISALLOW_COPY_AND_ASSIGN(ConnectTestingEventInterface);
105};
106
tyoshinoc06da562015-03-06 06:02:42107ConnectTestingEventInterface::ConnectTestingEventInterface() : failed_(false) {
ricea433bdab2015-01-26 07:25:37108}
109
110void ConnectTestingEventInterface::WaitForResponse() {
111 run_loop_.Run();
112}
113
114std::string ConnectTestingEventInterface::failure_message() const {
115 return failure_message_;
116}
117
118std::string ConnectTestingEventInterface::selected_subprotocol() const {
119 return selected_subprotocol_;
120}
121
122std::string ConnectTestingEventInterface::extensions() const {
123 return extensions_;
124}
125
126// Make the function definitions below less verbose.
127typedef ConnectTestingEventInterface::ChannelState ChannelState;
128
129ChannelState ConnectTestingEventInterface::OnAddChannelResponse(
ricea433bdab2015-01-26 07:25:37130 const std::string& selected_subprotocol,
131 const std::string& extensions) {
ricea433bdab2015-01-26 07:25:37132 selected_subprotocol_ = selected_subprotocol;
133 extensions_ = extensions;
134 QuitNestedEventLoop();
tyoshinoc06da562015-03-06 06:02:42135 return CHANNEL_ALIVE;
ricea433bdab2015-01-26 07:25:37136}
137
138ChannelState ConnectTestingEventInterface::OnDataFrame(
139 bool fin,
140 WebSocketMessageType type,
141 const std::vector<char>& data) {
142 return CHANNEL_ALIVE;
143}
144
tfarina8a2c66c22015-10-13 19:14:49145ChannelState ConnectTestingEventInterface::OnFlowControl(int64_t quota) {
ricea433bdab2015-01-26 07:25:37146 return CHANNEL_ALIVE;
147}
148
149ChannelState ConnectTestingEventInterface::OnClosingHandshake() {
150 return CHANNEL_ALIVE;
151}
152
153ChannelState ConnectTestingEventInterface::OnDropChannel(
154 bool was_clean,
tfarina8a2c66c22015-10-13 19:14:49155 uint16_t code,
ricea433bdab2015-01-26 07:25:37156 const std::string& reason) {
157 return CHANNEL_DELETED;
158}
159
160ChannelState ConnectTestingEventInterface::OnFailChannel(
161 const std::string& message) {
162 failed_ = true;
163 failure_message_ = message;
164 QuitNestedEventLoop();
165 return CHANNEL_DELETED;
166}
167
168ChannelState ConnectTestingEventInterface::OnStartOpeningHandshake(
169 scoped_ptr<WebSocketHandshakeRequestInfo> request) {
170 return CHANNEL_ALIVE;
171}
172
173ChannelState ConnectTestingEventInterface::OnFinishOpeningHandshake(
174 scoped_ptr<WebSocketHandshakeResponseInfo> response) {
175 return CHANNEL_ALIVE;
176}
177
178ChannelState ConnectTestingEventInterface::OnSSLCertificateError(
179 scoped_ptr<SSLErrorCallbacks> ssl_error_callbacks,
180 const GURL& url,
181 const SSLInfo& ssl_info,
182 bool fatal) {
skyostil4891b25b2015-06-11 11:43:45183 base::ThreadTaskRunnerHandle::Get()->PostTask(
ricea433bdab2015-01-26 07:25:37184 FROM_HERE, base::Bind(&SSLErrorCallbacks::CancelSSLRequest,
185 base::Owned(ssl_error_callbacks.release()),
186 ERR_SSL_PROTOCOL_ERROR, &ssl_info));
187 return CHANNEL_ALIVE;
188}
189
190void ConnectTestingEventInterface::QuitNestedEventLoop() {
191 run_loop_.Quit();
192}
193
194// A subclass of TestNetworkDelegate that additionally implements the
195// OnResolveProxy callback and records the information passed to it.
196class TestNetworkDelegateWithProxyInfo : public TestNetworkDelegate {
197 public:
198 TestNetworkDelegateWithProxyInfo() {}
199
200 struct ResolvedProxyInfo {
201 GURL url;
202 ProxyInfo proxy_info;
203 };
204
205 const ResolvedProxyInfo& resolved_proxy_info() const {
206 return resolved_proxy_info_;
207 }
208
209 protected:
210 void OnResolveProxy(const GURL& url,
211 int load_flags,
212 const ProxyService& proxy_service,
213 ProxyInfo* result) override {
214 resolved_proxy_info_.url = url;
215 resolved_proxy_info_.proxy_info = *result;
216 }
217
218 private:
219 ResolvedProxyInfo resolved_proxy_info_;
220
221 DISALLOW_COPY_AND_ASSIGN(TestNetworkDelegateWithProxyInfo);
222};
223
224class WebSocketEndToEndTest : public ::testing::Test {
225 protected:
226 WebSocketEndToEndTest()
Adam Ricecb76ac62015-02-20 05:33:25227 : event_interface_(),
ricea433bdab2015-01-26 07:25:37228 network_delegate_(new TestNetworkDelegateWithProxyInfo),
229 context_(true),
Adam Ricecb76ac62015-02-20 05:33:25230 channel_(),
ricea433bdab2015-01-26 07:25:37231 initialised_context_(false) {}
232
233 // Initialise the URLRequestContext. Normally done automatically by
234 // ConnectAndWait(). This method is for the use of tests that need the
235 // URLRequestContext initialised before calling ConnectAndWait().
236 void InitialiseContext() {
237 context_.set_network_delegate(network_delegate_.get());
238 context_.Init();
239 initialised_context_ = true;
240 }
241
242 // Send the connect request to |socket_url| and wait for a response. Returns
243 // true if the handshake succeeded.
244 bool ConnectAndWait(const GURL& socket_url) {
245 if (!initialised_context_) {
246 InitialiseContext();
247 }
mkwst4997ce82015-07-25 12:00:05248 url::Origin origin(GURL("http://localhost"));
Adam Ricecb76ac62015-02-20 05:33:25249 event_interface_ = new ConnectTestingEventInterface;
250 channel_.reset(
251 new WebSocketChannel(make_scoped_ptr(event_interface_), &context_));
ricea5acb1faf72015-03-16 15:34:00252 channel_->SendAddChannelRequest(GURL(socket_url), sub_protocols_, origin);
ricea433bdab2015-01-26 07:25:37253 event_interface_->WaitForResponse();
254 return !event_interface_->failed();
255 }
256
257 ConnectTestingEventInterface* event_interface_; // owned by channel_
258 scoped_ptr<TestNetworkDelegateWithProxyInfo> network_delegate_;
259 TestURLRequestContext context_;
Adam Ricecb76ac62015-02-20 05:33:25260 scoped_ptr<WebSocketChannel> channel_;
ricea5acb1faf72015-03-16 15:34:00261 std::vector<std::string> sub_protocols_;
ricea433bdab2015-01-26 07:25:37262 bool initialised_context_;
263};
264
265// None of these tests work on Android.
266// TODO(ricea): Make these tests work on Android. See crbug.com/441711.
267#if defined(OS_ANDROID)
268#define DISABLED_ON_ANDROID(test) DISABLED_##test
269#else
270#define DISABLED_ON_ANDROID(test) test
271#endif
272
273// Basic test of connectivity. If this test fails, nothing else can be expected
274// to work.
275TEST_F(WebSocketEndToEndTest, DISABLED_ON_ANDROID(BasicSmokeTest)) {
276 SpawnedTestServer ws_server(SpawnedTestServer::TYPE_WS,
277 SpawnedTestServer::kLocalhost,
278 GetWebSocketTestDataDirectory());
279 ASSERT_TRUE(ws_server.Start());
280 EXPECT_TRUE(ConnectAndWait(ws_server.GetURL(kEchoServer)));
281}
282
283// Test for issue crbug.com/433695 "Unencrypted WebSocket connection via
284// authenticated proxy times out"
285// TODO(ricea): Enable this when the issue is fixed.
286TEST_F(WebSocketEndToEndTest, DISABLED_HttpsProxyUnauthedFails) {
287 SpawnedTestServer proxy_server(SpawnedTestServer::TYPE_BASIC_AUTH_PROXY,
288 SpawnedTestServer::kLocalhost,
289 base::FilePath());
290 SpawnedTestServer ws_server(SpawnedTestServer::TYPE_WS,
291 SpawnedTestServer::kLocalhost,
292 GetWebSocketTestDataDirectory());
293 ASSERT_TRUE(proxy_server.StartInBackground());
294 ASSERT_TRUE(ws_server.StartInBackground());
295 ASSERT_TRUE(proxy_server.BlockUntilStarted());
296 ASSERT_TRUE(ws_server.BlockUntilStarted());
297 std::string proxy_config =
298 "https=" + proxy_server.host_port_pair().ToString();
299 scoped_ptr<ProxyService> proxy_service(
300 ProxyService::CreateFixed(proxy_config));
301 ASSERT_TRUE(proxy_service);
302 context_.set_proxy_service(proxy_service.get());
303 EXPECT_FALSE(ConnectAndWait(ws_server.GetURL(kEchoServer)));
304 EXPECT_EQ("Proxy authentication failed", event_interface_->failure_message());
305}
306
307TEST_F(WebSocketEndToEndTest, DISABLED_ON_ANDROID(HttpsWssProxyUnauthedFails)) {
308 SpawnedTestServer proxy_server(SpawnedTestServer::TYPE_BASIC_AUTH_PROXY,
309 SpawnedTestServer::kLocalhost,
310 base::FilePath());
311 SpawnedTestServer wss_server(SpawnedTestServer::TYPE_WSS,
312 SpawnedTestServer::kLocalhost,
313 GetWebSocketTestDataDirectory());
314 ASSERT_TRUE(proxy_server.StartInBackground());
315 ASSERT_TRUE(wss_server.StartInBackground());
316 ASSERT_TRUE(proxy_server.BlockUntilStarted());
317 ASSERT_TRUE(wss_server.BlockUntilStarted());
318 std::string proxy_config =
319 "https=" + proxy_server.host_port_pair().ToString();
320 scoped_ptr<ProxyService> proxy_service(
321 ProxyService::CreateFixed(proxy_config));
322 ASSERT_TRUE(proxy_service);
323 context_.set_proxy_service(proxy_service.get());
324 EXPECT_FALSE(ConnectAndWait(wss_server.GetURL(kEchoServer)));
325 EXPECT_EQ("Proxy authentication failed", event_interface_->failure_message());
326}
327
328// Regression test for crbug/426736 "WebSocket connections not using configured
329// system HTTPS Proxy".
330TEST_F(WebSocketEndToEndTest, DISABLED_ON_ANDROID(HttpsProxyUsed)) {
331 SpawnedTestServer proxy_server(SpawnedTestServer::TYPE_BASIC_AUTH_PROXY,
332 SpawnedTestServer::kLocalhost,
333 base::FilePath());
334 SpawnedTestServer ws_server(SpawnedTestServer::TYPE_WS,
335 SpawnedTestServer::kLocalhost,
336 GetWebSocketTestDataDirectory());
337 ASSERT_TRUE(proxy_server.StartInBackground());
338 ASSERT_TRUE(ws_server.StartInBackground());
339 ASSERT_TRUE(proxy_server.BlockUntilStarted());
340 ASSERT_TRUE(ws_server.BlockUntilStarted());
341 std::string proxy_config = "https=" +
342 proxy_server.host_port_pair().ToString() + ";" +
343 "http=" + proxy_server.host_port_pair().ToString();
344 scoped_ptr<ProxyService> proxy_service(
345 ProxyService::CreateFixed(proxy_config));
346 context_.set_proxy_service(proxy_service.get());
347 InitialiseContext();
348
349 // The test server doesn't have an unauthenticated proxy mode. WebSockets
350 // cannot provide auth information that isn't already cached, so it's
351 // necessary to preflight an HTTP request to authenticate against the proxy.
ricea433bdab2015-01-26 07:25:37352 // It doesn't matter what the URL is, as long as it is an HTTP navigation.
353 GURL http_page =
Adam Ricecb76ac62015-02-20 05:33:25354 ReplaceUrlScheme(ws_server.GetURL("connect_check.html"), "http");
ricea433bdab2015-01-26 07:25:37355 TestDelegate delegate;
356 delegate.set_credentials(
357 AuthCredentials(base::ASCIIToUTF16("foo"), base::ASCIIToUTF16("bar")));
358 {
359 scoped_ptr<URLRequest> request(
davidben151423e2015-03-23 18:48:36360 context_.CreateRequest(http_page, DEFAULT_PRIORITY, &delegate));
ricea433bdab2015-01-26 07:25:37361 request->Start();
362 // TestDelegate exits the message loop when the request completes by
363 // default.
364 base::RunLoop().Run();
365 EXPECT_TRUE(delegate.auth_required_called());
366 }
367
368 GURL ws_url = ws_server.GetURL(kEchoServer);
369 EXPECT_TRUE(ConnectAndWait(ws_url));
370 const TestNetworkDelegateWithProxyInfo::ResolvedProxyInfo& info =
371 network_delegate_->resolved_proxy_info();
372 EXPECT_EQ(ws_url, info.url);
373 EXPECT_TRUE(info.proxy_info.is_http());
374}
375
ricea23c3f942015-02-02 13:35:13376// This is a regression test for crbug.com/408061 Crash in
377// net::WebSocketBasicHandshakeStream::Upgrade.
378TEST_F(WebSocketEndToEndTest, DISABLED_ON_ANDROID(TruncatedResponse)) {
379 SpawnedTestServer ws_server(SpawnedTestServer::TYPE_WS,
380 SpawnedTestServer::kLocalhost,
381 GetWebSocketTestDataDirectory());
382 ASSERT_TRUE(ws_server.Start());
383 InitialiseContext();
384
385 GURL ws_url = ws_server.GetURL("truncated-headers");
386 EXPECT_FALSE(ConnectAndWait(ws_url));
387}
388
Adam Ricecb76ac62015-02-20 05:33:25389// Regression test for crbug.com/455215 "HSTS not applied to WebSocket"
390TEST_F(WebSocketEndToEndTest, DISABLED_ON_ANDROID(HstsHttpsToWebSocket)) {
estarka5da76702015-04-09 04:00:16391 SpawnedTestServer::SSLOptions ssl_options(
392 SpawnedTestServer::SSLOptions::CERT_COMMON_NAME_IS_DOMAIN);
Adam Ricecb76ac62015-02-20 05:33:25393 SpawnedTestServer https_server(
394 SpawnedTestServer::TYPE_HTTPS, ssl_options,
395 base::FilePath(FILE_PATH_LITERAL("net/data/url_request_unittest")));
396 SpawnedTestServer wss_server(SpawnedTestServer::TYPE_WSS, ssl_options,
397 GetWebSocketTestDataDirectory());
estarka5da76702015-04-09 04:00:16398
Adam Ricecb76ac62015-02-20 05:33:25399 ASSERT_TRUE(https_server.StartInBackground());
400 ASSERT_TRUE(wss_server.StartInBackground());
401 ASSERT_TRUE(https_server.BlockUntilStarted());
402 ASSERT_TRUE(wss_server.BlockUntilStarted());
403 InitialiseContext();
404 // Set HSTS via https:
405 TestDelegate delegate;
406 GURL https_page = https_server.GetURL("files/hsts-headers.html");
407 scoped_ptr<URLRequest> request(
davidben151423e2015-03-23 18:48:36408 context_.CreateRequest(https_page, DEFAULT_PRIORITY, &delegate));
Adam Ricecb76ac62015-02-20 05:33:25409 request->Start();
410 // TestDelegate exits the message loop when the request completes.
411 base::RunLoop().Run();
412 EXPECT_TRUE(request->status().is_success());
413
414 // Check HSTS with ws:
415 // Change the scheme from wss: to ws: to verify that it is switched back.
416 GURL ws_url = ReplaceUrlScheme(wss_server.GetURL(kEchoServer), "ws");
417 EXPECT_TRUE(ConnectAndWait(ws_url));
418}
419
420TEST_F(WebSocketEndToEndTest, DISABLED_ON_ANDROID(HstsWebSocketToHttps)) {
estarka5da76702015-04-09 04:00:16421 SpawnedTestServer::SSLOptions ssl_options(
422 SpawnedTestServer::SSLOptions::CERT_COMMON_NAME_IS_DOMAIN);
Adam Ricecb76ac62015-02-20 05:33:25423 SpawnedTestServer https_server(
424 SpawnedTestServer::TYPE_HTTPS, ssl_options,
425 base::FilePath(FILE_PATH_LITERAL("net/data/url_request_unittest")));
426 SpawnedTestServer wss_server(SpawnedTestServer::TYPE_WSS, ssl_options,
427 GetWebSocketTestDataDirectory());
428 ASSERT_TRUE(https_server.StartInBackground());
429 ASSERT_TRUE(wss_server.StartInBackground());
430 ASSERT_TRUE(https_server.BlockUntilStarted());
431 ASSERT_TRUE(wss_server.BlockUntilStarted());
432 InitialiseContext();
433 // Set HSTS via wss:
434 GURL wss_url = wss_server.GetURL("set-hsts");
435 EXPECT_TRUE(ConnectAndWait(wss_url));
436
437 // Verify via http:
438 TestDelegate delegate;
439 GURL http_page =
440 ReplaceUrlScheme(https_server.GetURL("files/simple.html"), "http");
441 scoped_ptr<URLRequest> request(
davidben151423e2015-03-23 18:48:36442 context_.CreateRequest(http_page, DEFAULT_PRIORITY, &delegate));
Adam Ricecb76ac62015-02-20 05:33:25443 request->Start();
444 // TestDelegate exits the message loop when the request completes.
445 base::RunLoop().Run();
446 EXPECT_TRUE(request->status().is_success());
447 EXPECT_TRUE(request->url().SchemeIs("https"));
448}
449
450TEST_F(WebSocketEndToEndTest, DISABLED_ON_ANDROID(HstsWebSocketToWebSocket)) {
estarka5da76702015-04-09 04:00:16451 SpawnedTestServer::SSLOptions ssl_options(
452 SpawnedTestServer::SSLOptions::CERT_COMMON_NAME_IS_DOMAIN);
Adam Ricecb76ac62015-02-20 05:33:25453 SpawnedTestServer wss_server(SpawnedTestServer::TYPE_WSS, ssl_options,
454 GetWebSocketTestDataDirectory());
455 ASSERT_TRUE(wss_server.Start());
456 InitialiseContext();
457 // Set HSTS via wss:
458 GURL wss_url = wss_server.GetURL("set-hsts");
459 EXPECT_TRUE(ConnectAndWait(wss_url));
460
461 // Verify via wss:
462 GURL ws_url = ReplaceUrlScheme(wss_server.GetURL(kEchoServer), "ws");
463 EXPECT_TRUE(ConnectAndWait(ws_url));
464}
465
ricea5acb1faf72015-03-16 15:34:00466// Regression test for crbug.com/180504 "WebSocket handshake fails when HTTP
467// headers have trailing LWS".
468TEST_F(WebSocketEndToEndTest, DISABLED_ON_ANDROID(TrailingWhitespace)) {
469 SpawnedTestServer ws_server(SpawnedTestServer::TYPE_WS,
470 SpawnedTestServer::kLocalhost,
471 GetWebSocketTestDataDirectory());
472 ASSERT_TRUE(ws_server.Start());
473
474 GURL ws_url = ws_server.GetURL("trailing-whitespace");
475 sub_protocols_.push_back("sip");
476 EXPECT_TRUE(ConnectAndWait(ws_url));
477 EXPECT_EQ("sip", event_interface_->selected_subprotocol());
478}
479
riceae1d67672015-03-19 10:10:17480// This is a regression test for crbug.com/169448 "WebSockets should support
481// header continuations"
482// TODO(ricea): HTTP continuation headers have been deprecated by RFC7230. If
483// support for continuation headers is removed from Chrome, then this test will
484// break and should be removed.
485TEST_F(WebSocketEndToEndTest, DISABLED_ON_ANDROID(HeaderContinuations)) {
486 SpawnedTestServer ws_server(SpawnedTestServer::TYPE_WS,
487 SpawnedTestServer::kLocalhost,
488 GetWebSocketTestDataDirectory());
489 ASSERT_TRUE(ws_server.Start());
490
491 GURL ws_url = ws_server.GetURL("header-continuation");
492
493 EXPECT_TRUE(ConnectAndWait(ws_url));
494 EXPECT_EQ("permessage-deflate; server_max_window_bits=10",
495 event_interface_->extensions());
496}
497
ricea433bdab2015-01-26 07:25:37498} // namespace
499
500} // namespace net