Commit a79f65e9 authored by Adam Rice's avatar Adam Rice Committed by Commit Bot

Add layout tests for WebSocket handshake throttle

Test handshake throttling from layout tests. This involves adding a
test-specific throttle, content::TestWebSocketHandshakeThrottle, which
is used when the --run-layout-tests option is supplied. It looks for a
content-shell-websocket-delay-ms query parameter in the WebSocket URL
and applies the specified delay if it exists. As a result it does not
interfere with other tests.

Includes a regression test for crbug.com/786776. This verifies that a
message sent by the server is delivered even when the open is delayed by
throttling.

BUG=786776

Change-Id: If8b2be86ac617d037bc1ee0059512b5d950a313d
Reviewed-on: https://chromium-review.googlesource.com/867806
Commit-Queue: Adam Rice <ricea@chromium.org>
Reviewed-by: default avatarPeter Beverloo <peter@chromium.org>
Reviewed-by: default avatarTakeshi Yoshino <tyoshino@chromium.org>
Cr-Commit-Position: refs/heads/master@{#532329}
parent 751cc857
...@@ -190,6 +190,8 @@ static_library("content_shell_lib") { ...@@ -190,6 +190,8 @@ static_library("content_shell_lib") {
"renderer/layout_test/test_media_stream_renderer_factory.h", "renderer/layout_test/test_media_stream_renderer_factory.h",
"renderer/layout_test/test_media_stream_video_renderer.cc", "renderer/layout_test/test_media_stream_video_renderer.cc",
"renderer/layout_test/test_media_stream_video_renderer.h", "renderer/layout_test/test_media_stream_video_renderer.h",
"renderer/layout_test/test_websocket_handshake_throttle.cc",
"renderer/layout_test/test_websocket_handshake_throttle.h",
"renderer/shell_content_renderer_client.cc", "renderer/shell_content_renderer_client.cc",
"renderer/shell_content_renderer_client.h", "renderer/shell_content_renderer_client.h",
"renderer/shell_render_view_observer.cc", "renderer/shell_render_view_observer.cc",
......
...@@ -4,6 +4,9 @@ ...@@ -4,6 +4,9 @@
#include "content/shell/renderer/layout_test/layout_test_content_renderer_client.h" #include "content/shell/renderer/layout_test/layout_test_content_renderer_client.h"
#include <string>
#include <utility>
#include "base/callback.h" #include "base/callback.h"
#include "base/command_line.h" #include "base/command_line.h"
#include "base/debug/debugger.h" #include "base/debug/debugger.h"
...@@ -23,6 +26,7 @@ ...@@ -23,6 +26,7 @@
#include "content/shell/renderer/layout_test/layout_test_render_frame_observer.h" #include "content/shell/renderer/layout_test/layout_test_render_frame_observer.h"
#include "content/shell/renderer/layout_test/layout_test_render_thread_observer.h" #include "content/shell/renderer/layout_test/layout_test_render_thread_observer.h"
#include "content/shell/renderer/layout_test/test_media_stream_renderer_factory.h" #include "content/shell/renderer/layout_test/test_media_stream_renderer_factory.h"
#include "content/shell/renderer/layout_test/test_websocket_handshake_throttle.h"
#include "content/shell/renderer/shell_render_view_observer.h" #include "content/shell/renderer/shell_render_view_observer.h"
#include "content/shell/test_runner/web_frame_test_proxy.h" #include "content/shell/test_runner/web_frame_test_proxy.h"
#include "content/shell/test_runner/web_test_interfaces.h" #include "content/shell/test_runner/web_test_interfaces.h"
...@@ -230,6 +234,11 @@ LayoutTestContentRendererClient::CreateMediaStreamRendererFactory() { ...@@ -230,6 +234,11 @@ LayoutTestContentRendererClient::CreateMediaStreamRendererFactory() {
#endif #endif
} }
std::unique_ptr<blink::WebSocketHandshakeThrottle>
LayoutTestContentRendererClient::CreateWebSocketHandshakeThrottle() {
return std::make_unique<TestWebSocketHandshakeThrottle>();
}
void LayoutTestContentRendererClient::DidInitializeWorkerContextOnWorkerThread( void LayoutTestContentRendererClient::DidInitializeWorkerContextOnWorkerThread(
v8::Local<v8::Context> context) { v8::Local<v8::Context> context) {
blink::WebTestingSupport::InjectInternalsObject(context); blink::WebTestingSupport::InjectInternalsObject(context);
......
...@@ -31,6 +31,8 @@ class LayoutTestContentRendererClient : public ShellContentRendererClient { ...@@ -31,6 +31,8 @@ class LayoutTestContentRendererClient : public ShellContentRendererClient {
blink::WebThemeEngine* OverrideThemeEngine() override; blink::WebThemeEngine* OverrideThemeEngine() override;
std::unique_ptr<MediaStreamRendererFactory> CreateMediaStreamRendererFactory() std::unique_ptr<MediaStreamRendererFactory> CreateMediaStreamRendererFactory()
override; override;
std::unique_ptr<blink::WebSocketHandshakeThrottle>
CreateWebSocketHandshakeThrottle() override;
void DidInitializeWorkerContextOnWorkerThread( void DidInitializeWorkerContextOnWorkerThread(
v8::Local<v8::Context> context) override; v8::Local<v8::Context> context) override;
void SetRuntimeFeaturesDefaultsBeforeBlinkInitialization() override; void SetRuntimeFeaturesDefaultsBeforeBlinkInitialization() override;
......
// Copyright 2018 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 "content/shell/renderer/layout_test/test_websocket_handshake_throttle.h"
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_piece.h"
#include "base/time/time.h"
#include "third_party/WebKit/public/platform/WebURL.h"
#include "url/gurl.h"
namespace {
using Callbacks = blink::WebCallbacks<void, const blink::WebString&>;
// Checks for a valid "content-shell-websocket-delay-ms" parameter and returns
// it as a TimeDelta if it exists. Otherwise returns a zero TimeDelta.
base::TimeDelta ExtractDelayFromUrl(const GURL& url) {
if (!url.has_query())
return base::TimeDelta();
url::Component query = url.parsed_for_possibly_invalid_spec().query;
url::Component key;
url::Component value;
base::StringPiece spec = url.possibly_invalid_spec();
while (url::ExtractQueryKeyValue(spec.data(), &query, &key, &value)) {
base::StringPiece key_piece = spec.substr(key.begin, key.len);
if (key_piece != "content-shell-websocket-delay-ms")
continue;
base::StringPiece value_piece = spec.substr(value.begin, value.len);
int value_int;
if (!base::StringToInt(value_piece, &value_int) || value_int < 0)
return base::TimeDelta();
return base::TimeDelta::FromMilliseconds(value_int);
}
// Parameter was not found.
return base::TimeDelta();
}
} // namespace
namespace content {
void TestWebSocketHandshakeThrottle::ThrottleHandshake(
const blink::WebURL& url,
blink::WebLocalFrame* frame,
Callbacks* callbacks) {
DCHECK(callbacks);
// This use of Unretained is safe because this object is destroyed before
// |callbacks| is freed. Destroying this object prevents the timer from
// firing.
timer_.Start(
FROM_HERE, ExtractDelayFromUrl(url),
base::BindRepeating(&Callbacks::OnSuccess, base::Unretained(callbacks)));
}
} // namespace content
// Copyright 2018 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.
#ifndef CONTENT_SHELL_RENDERER_LAYOUT_TEST_TEST_WEBSOCKET_HANDSHAKE_THROTTLE_H_
#define CONTENT_SHELL_RENDERER_LAYOUT_TEST_TEST_WEBSOCKET_HANDSHAKE_THROTTLE_H_
#include "base/timer/timer.h"
#include "third_party/WebKit/public/platform/WebCallbacks.h"
#include "third_party/WebKit/public/platform/WebSocketHandshakeThrottle.h"
namespace blink {
class WebLocalFrame;
class WebString;
class WebURL;
} // namespace blink
namespace content {
// A simple WebSocketHandshakeThrottle that calls callbacks->IsSuccess() after n
// milli-seconds if the URL query contains
// content-shell-websocket-delay-ms=n. Otherwise it calls IsSuccess()
// immediately.
class TestWebSocketHandshakeThrottle
: public blink::WebSocketHandshakeThrottle {
public:
~TestWebSocketHandshakeThrottle() override = default;
void ThrottleHandshake(
const blink::WebURL& url,
blink::WebLocalFrame* frame,
blink::WebCallbacks<void, const blink::WebString&>* callbacks) override;
private:
base::OneShotTimer timer_;
};
} // namespace content
#endif // CONTENT_SHELL_RENDERER_LAYOUT_TEST_TEST_WEBSOCKET_HANDSHAKE_THROTTLE_H_
<!DOCTYPE html>
<script src="/js-test-resources/testharness.js"></script>
<script src="/js-test-resources/testharnessreport.js"></script>
<script>
'use strict';
// These tests test WebSocket handshake throttling, which is a feature of
// Blink's implementation, not part of the web platform. The special throttle
// content::TestWebSocketHandshakeThrottle is used when content_shell is run
// with the --run-layout-test option. It detects the
// content-shell-websocket-delay-ms query parameter in the URL and applies a
// delay of the specified number of milliseconds.
//
// These tests will not function correctly without the --run-layout-test
// option. To debug them interactively you can follow the instructions for a
// "legacy test" in
// https://chromium.googlesource.com/chromium/src/+/master/docs/testing/layout_tests.md#Debugging-DevTools-Tests.
async_test(t => {
const startTime = Date.now();
const ws = new WebSocket(
'ws://localhost:8880/echo?content-shell-websocket-delay-ms=250');
ws.onopen = t.step_func(() => {
const endTime = Date.now();
assert_greater_than_equal(endTime - startTime, 250,
'elapsed time should be greater than 250ms');
ws.close();
});
ws.onclose = t.step_func(e => {
assert_true(e.wasClean, 'close handshake should complete successfully');
t.done();
});
}, 'throttle should delay connection success');
async_test(t => {
const startTime = Date.now();
// The test doesn't have to wait for this delay, so it is extra-long to reduce
// flakiness on slow bots.
const ws = new WebSocket(
'ws://localhost:8880/handshake-error?content-shell-websocket-delay-ms=2000');
ws.onopen = t.unreached_func('onopen should not be called');
ws.onclose = t.step_func(e => {
const endTime = Date.now();
assert_false(e.wasClean, 'WebSocket should not be cleanly closed');
assert_less_than(endTime - startTime, 2000,
'elapsed time should be less than 2000ms');
t.done();
});
}, 'throttle should not delay connection failure');
// Regression test for crbug.com/786776.
async_test(t => {
// The "echo-request-headers" handler sends a message immediately on connect,
// then closes.
const ws = new WebSocket(
'ws://localhost:8880/echo-request-headers?content-shell-websocket-delay-ms=250');
let onOpenCalled = false;
let onMessageCalled = false;
ws.onopen = () => {
onOpenCalled = true;
};
ws.onmessage = t.step_func(() => {
assert_true(onOpenCalled, 'onopen should happen before onmessage');
onMessageCalled = true;
});
ws.onclose = t.step_func(e => {
assert_true(e.wasClean, 'close handshake should complete successfully');
assert_true(onMessageCalled, 'onmessage should be called');
t.done();
});
}, 'throttle should not prevent onmessage events');
</script>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment