Commit 7fe8a733 authored by yutak@chromium.org's avatar yutak@chromium.org

WebSocket: Send Blob as WebSocket binary message

https://bugs.webkit.org/show_bug.cgi?id=67465

Reviewed by Kent Tamura.

Re-lands r94399 with a fix for Leopard builds.

Source/WebCore:

* bindings/js/JSWebSocketCustom.cpp:
(WebCore::JSWebSocket::send):
* bindings/v8/custom/V8WebSocketCustom.cpp:
(WebCore::V8WebSocket::sendCallback):
* websockets/ThreadableWebSocketChannel.h:
* websockets/WebSocket.cpp:
(WebCore::WebSocket::send):
* websockets/WebSocket.h:
* websockets/WebSocket.idl:
* websockets/WebSocketChannel.cpp:
(WebCore::WebSocketChannel::send):
* websockets/WebSocketChannel.h:
* websockets/WorkerThreadableWebSocketChannel.cpp:
(WebCore::WorkerThreadableWebSocketChannel::send):
(WebCore::WorkerThreadableWebSocketChannel::Peer::send):
(WebCore::WorkerThreadableWebSocketChannel::mainThreadSendBlob):
(WebCore::WorkerThreadableWebSocketChannel::Bridge::send):
* websockets/WorkerThreadableWebSocketChannel.h:

LayoutTests:

* http/tests/websocket/tests/hixie76/send-empty-expected.txt: Added.
* http/tests/websocket/tests/hixie76/send-empty.html: Added.
* http/tests/websocket/tests/hixie76/send-object-expected.txt: Added.
* http/tests/websocket/tests/hixie76/send-object.html: Added.
* http/tests/websocket/tests/hybi/bufferedAmount-after-close-expected.txt:
* http/tests/websocket/tests/hybi/bufferedAmount-after-close.html:
* http/tests/websocket/tests/hybi/send-blob-expected.txt: Added.
* http/tests/websocket/tests/hybi/send-blob.html: Added.
* http/tests/websocket/tests/hybi/send-blob_wsh.py: Added.
* http/tests/websocket/tests/hybi/send-empty-expected.txt: Added.
* http/tests/websocket/tests/hybi/send-empty.html: Added.
* http/tests/websocket/tests/hybi/send-file-blob-expected.txt: Added.
* http/tests/websocket/tests/hybi/send-file-blob-fail-expected.txt: Added.
* http/tests/websocket/tests/hybi/send-file-blob-fail.html: Added.
* http/tests/websocket/tests/hybi/send-file-blob.html: Added.
* http/tests/websocket/tests/hybi/send-file-blob_wsh.py: Added.
* http/tests/websocket/tests/hybi/workers/resources/send-blob.js: Added.
* http/tests/websocket/tests/hybi/workers/resources/send-blob_wsh.py: Added.
* http/tests/websocket/tests/hybi/workers/send-blob-expected.txt: Added.
* http/tests/websocket/tests/hybi/workers/send-blob.html: Added.
* platform/gtk/Skipped:
* platform/mac/Skipped:
* platform/qt/Skipped:
* platform/win/Skipped:

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@94405 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent 9f38ad0d
2011-09-02 Yuta Kitamura <yutak@chromium.org>
WebSocket: Send Blob as WebSocket binary message
https://bugs.webkit.org/show_bug.cgi?id=67465
Reviewed by Kent Tamura.
Re-lands r94399 with a fix for Leopard builds.
* http/tests/websocket/tests/hixie76/send-empty-expected.txt: Added.
* http/tests/websocket/tests/hixie76/send-empty.html: Added.
* http/tests/websocket/tests/hixie76/send-object-expected.txt: Added.
* http/tests/websocket/tests/hixie76/send-object.html: Added.
* http/tests/websocket/tests/hybi/bufferedAmount-after-close-expected.txt:
* http/tests/websocket/tests/hybi/bufferedAmount-after-close.html:
* http/tests/websocket/tests/hybi/send-blob-expected.txt: Added.
* http/tests/websocket/tests/hybi/send-blob.html: Added.
* http/tests/websocket/tests/hybi/send-blob_wsh.py: Added.
* http/tests/websocket/tests/hybi/send-empty-expected.txt: Added.
* http/tests/websocket/tests/hybi/send-empty.html: Added.
* http/tests/websocket/tests/hybi/send-file-blob-expected.txt: Added.
* http/tests/websocket/tests/hybi/send-file-blob-fail-expected.txt: Added.
* http/tests/websocket/tests/hybi/send-file-blob-fail.html: Added.
* http/tests/websocket/tests/hybi/send-file-blob.html: Added.
* http/tests/websocket/tests/hybi/send-file-blob_wsh.py: Added.
* http/tests/websocket/tests/hybi/workers/resources/send-blob.js: Added.
* http/tests/websocket/tests/hybi/workers/resources/send-blob_wsh.py: Added.
* http/tests/websocket/tests/hybi/workers/send-blob-expected.txt: Added.
* http/tests/websocket/tests/hybi/workers/send-blob.html: Added.
* platform/gtk/Skipped:
* platform/mac/Skipped:
* platform/qt/Skipped:
* platform/win/Skipped:
2011-09-02 Kenichi Ishibashi <bashi@chromium.org>
[chromium] editing/selection/regional-indicators.html timing out on Linux
WebSocket: Calling send() without arguments should raise SyntaxError.
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
PASS ws.send() threw exception SyntaxError: Not enough arguments.
PASS closeEvent.wasClean is true
PASS receivedMessages.length is 1
PASS receivedMessages[0] is "Goodbye"
PASS successfullyParsed is true
TEST COMPLETE
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="../../../../js-test-resources/js-test-style.css">
<script src="../../../../js-test-resources/js-test-pre.js"></script>
</head>
<body>
<div id="description"></div>
<div id="console"></div>
<script>
description("WebSocket: Calling send() without arguments should raise SyntaxError.");
window.jsTestIsAsync = true;
var ws = new WebSocket("ws://127.0.0.1:8880/websocket/tests/hixie76/echo");
var closeEvent;
var receivedMessages = [];
var expectedMessages = ["Goodbye"];
ws.onopen = function()
{
shouldThrow("ws.send()");
ws.send("Goodbye");
};
ws.onmessage = function(event)
{
receivedMessages.push(event.data);
};
ws.onclose = function(event)
{
closeEvent = event;
shouldBeTrue("closeEvent.wasClean");
shouldEvaluateTo("receivedMessages.length", expectedMessages.length);
for (var i = 0; i < expectedMessages.length; ++i)
shouldBeEqualToString("receivedMessages[" + i + "]", expectedMessages[i]);
finishJSTest();
};
var successfullyParsed = true;
</script>
<script src="../../../../js-test-resources/js-test-post.js"></script>
</body>
</html>
WebSocket: send(object) should be interpreted as send(object.toString()).
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
PASS closeEvent.wasClean is true
PASS receivedMessages.length is 4
PASS receivedMessages[0] is "[object Object]"
PASS receivedMessages[1] is "[object ArrayBuffer]"
PASS receivedMessages[2] is "[object Blob]"
PASS receivedMessages[3] is "Goodbye"
PASS successfullyParsed is true
TEST COMPLETE
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="../../../../js-test-resources/js-test-style.css">
<script src="../../../../js-test-resources/js-test-pre.js"></script>
</head>
<body>
<div id="description"></div>
<div id="console"></div>
<script>
description("WebSocket: send(object) should be interpreted as send(object.toString()).");
window.jsTestIsAsync = true;
function createEmptyBlob()
{
var builder = new WebKitBlobBuilder();
return builder.getBlob();
}
var ws = new WebSocket("ws://127.0.0.1:8880/websocket/tests/hixie76/echo");
var closeEvent;
var receivedMessages = [];
var expectedMessages = ["[object Object]", "[object ArrayBuffer]", "[object Blob]", "Goodbye"];
ws.onopen = function()
{
ws.send({});
ws.send(new ArrayBuffer());
ws.send(createEmptyBlob());
ws.send("Goodbye");
};
ws.onmessage = function(event)
{
receivedMessages.push(event.data);
};
ws.onclose = function(event)
{
closeEvent = event;
shouldBeTrue("closeEvent.wasClean");
shouldEvaluateTo("receivedMessages.length", expectedMessages.length);
for (var i = 0; i < expectedMessages.length; ++i)
shouldBeEqualToString("receivedMessages[" + i + "]", expectedMessages[i]);
finishJSTest();
};
var successfullyParsed = true;
</script>
<script src="../../../../js-test-resources/js-test-post.js"></script>
</body>
</html>
......@@ -6,6 +6,7 @@ Connected.
Closed.
PASS ws.readyState is 3
PASS ws.bufferedAmount is 0
Testing send(string)...
PASS ws.send(messageToSend) is false
PASS bufferedAmountDifference is 27
PASS ws.send(messageToSend) is false
......@@ -20,6 +21,19 @@ PASS ws.send(messageToSend) is false
PASS bufferedAmountDifference is 65543
PASS ws.send(messageToSend) is false
PASS bufferedAmountDifference is 65550
Testing send(Blob)...
PASS ws.send(messageToSend) is false
PASS bufferedAmountDifference is 6
PASS ws.send(messageToSend) is false
PASS bufferedAmountDifference is 7
PASS ws.send(messageToSend) is false
PASS bufferedAmountDifference is 131
PASS ws.send(messageToSend) is false
PASS bufferedAmountDifference is 134
PASS ws.send(messageToSend) is false
PASS bufferedAmountDifference is 65543
PASS ws.send(messageToSend) is false
PASS bufferedAmountDifference is 65550
PASS successfullyParsed is true
TEST COMPLETE
......
......@@ -22,6 +22,13 @@ function createStringWithLength(length)
return string.substring(0, length);
}
function createBlobWithLength(length)
{
var builder = new WebKitBlobBuilder();
builder.append(new ArrayBuffer(length));
return builder.getBlob();
}
var ws = new WebSocket("ws://localhost:8880/websocket/tests/hybi/simple");
ws.onopen = function()
......@@ -37,6 +44,7 @@ ws.onclose = function()
shouldBe("ws.bufferedAmount", "0");
var baseOverhead = 2 + 4; // Base header size and masking key size.
debug("Testing send(string)...");
testBufferedAmount('send to closed socket', 21 + baseOverhead);
testBufferedAmount('', baseOverhead);
testBufferedAmount('a', 1 + baseOverhead);
......@@ -45,6 +53,14 @@ ws.onclose = function()
testBufferedAmount(createStringWithLength(0xFFFF), 0xFFFF + baseOverhead + 2);
testBufferedAmount(createStringWithLength(0x10000), 0x10000 + baseOverhead + 8); // With 64-bit extended payload length field.
debug("Testing send(Blob)...");
testBufferedAmount(createBlobWithLength(0), baseOverhead);
testBufferedAmount(createBlobWithLength(1), 1 + baseOverhead);
testBufferedAmount(createBlobWithLength(125), 125 + baseOverhead);
testBufferedAmount(createBlobWithLength(126), 126 + baseOverhead + 2);
testBufferedAmount(createBlobWithLength(0xFFFF), 0xFFFF + baseOverhead + 2);
testBufferedAmount(createBlobWithLength(0x10000), 0x10000 + baseOverhead + 8);
finishJSTest();
};
......
WebSocket: Send Blobs.
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
PASS PASS: Message #0.
PASS PASS: Message #1.
PASS PASS: Message #2.
PASS closeEvent.wasClean is true
PASS successfullyParsed is true
TEST COMPLETE
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="../../../../js-test-resources/js-test-style.css">
<script src="../../../../js-test-resources/js-test-pre.js"></script>
</head>
<body>
<div id="description"></div>
<div id="console"></div>
<script type="text/javascript">
description("WebSocket: Send Blobs.");
window.jsTestIsAsync = true;
if (window.layoutTestController)
layoutTestController.overridePreference("WebKitHixie76WebSocketProtocolEnabled", 0);
function startsWith(target, prefix)
{
return target.indexOf(prefix) === 0;
}
function createBlobContainingHelloWorld()
{
var builder = new WebKitBlobBuilder();
builder.append("Hello, world!");
return builder.getBlob();
}
function createEmptyBlob()
{
var builder = new WebKitBlobBuilder();
return builder.getBlob();
}
function createBlobContainingAllDistinctBytes()
{
var array = new Uint8Array(256);
for (var i = 0; i < 256; ++i)
array[i] = i;
var builder = new WebKitBlobBuilder();
builder.append(array.buffer);
return builder.getBlob();
}
var url = "ws://127.0.0.1:8880/websocket/tests/hybi/send-blob";
var ws = new WebSocket(url);
var closeEvent;
ws.onopen = function()
{
ws.send(createBlobContainingHelloWorld());
ws.send(createEmptyBlob());
ws.send(createBlobContainingAllDistinctBytes());
};
ws.onmessage = function(event)
{
var message = event.data;
if (startsWith(message, "PASS"))
testPassed(message);
else
testFailed(message);
};
ws.onclose = function(event)
{
closeEvent = event;
shouldBeTrue("closeEvent.wasClean");
finishJSTest();
};
var successfullyParsed = true;
</script>
<script src="../../../../js-test-resources/js-test-post.js"></script>
</body>
</html>
from mod_pywebsocket import common
from mod_pywebsocket import msgutil
def web_socket_do_extra_handshake(request):
pass # Always accept.
def web_socket_transfer_data(request):
expected_messages = ['Hello, world!', '', all_distinct_bytes()]
for test_number, expected_message in enumerate(expected_messages):
message = msgutil.receive_message(request)
if type(message) == str and message == expected_message:
msgutil.send_message(request, 'PASS: Message #%d.' % test_number)
else:
msgutil.send_message(request, 'FAIL: Message #%d: Received unexpected message: %r' % (test_number, message))
def all_distinct_bytes():
return ''.join([chr(i) for i in xrange(256)])
WebSocket: Calling send() without arguments should raise SyntaxError.
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
PASS ws.send() threw exception SyntaxError: Not enough arguments.
PASS closeEvent.wasClean is true
PASS receivedMessages.length is 1
PASS receivedMessages[0] is "Goodbye"
PASS successfullyParsed is true
TEST COMPLETE
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="../../../../js-test-resources/js-test-style.css">
<script src="../../../../js-test-resources/js-test-pre.js"></script>
</head>
<body>
<div id="description"></div>
<div id="console"></div>
<script>
description("WebSocket: Calling send() without arguments should raise SyntaxError.");
window.jsTestIsAsync = true;
if (window.layoutTestController)
layoutTestController.overridePreference("WebKitHixie76WebSocketProtocolEnabled", 0);
var ws = new WebSocket("ws://127.0.0.1:8880/websocket/tests/hybi/echo");
var closeEvent;
var receivedMessages = [];
var expectedMessages = ["Goodbye"];
ws.onopen = function()
{
shouldThrow("ws.send()");
ws.send("Goodbye");
};
ws.onmessage = function(event)
{
receivedMessages.push(event.data);
};
ws.onclose = function(event)
{
closeEvent = event;
shouldBeTrue("closeEvent.wasClean");
shouldEvaluateTo("receivedMessages.length", expectedMessages.length);
for (var i = 0; i < expectedMessages.length; ++i)
shouldBeEqualToString("receivedMessages[" + i + "]", expectedMessages[i]);
finishJSTest();
};
var successfullyParsed = true;
</script>
<script src="../../../../js-test-resources/js-test-post.js"></script>
</body>
</html>
WebSocket: Send a File.
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
Got FileSystem object.
File created.
PASS fileEntry.isFile is true
Wrote to file.
Got File object.
PASS PASS: Message #0.
PASS closeEvent.wasClean is true
Deleting the file.
PASS successfullyParsed is true
TEST COMPLETE
CONSOLE MESSAGE: line 0: Failed to load Blob: error code = 1
WebSocket should fail the connection if it has failed to read a Blob.
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
Got FileSystem object.
File created.
PASS fileEntry.isFile is true
Wrote to file.
Got File object.
File deleted.
PASS closeEvent.wasClean is false
PASS successfullyParsed is true
TEST COMPLETE
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="../../../../js-test-resources/js-test-style.css">
<script src="../../../../js-test-resources/js-test-pre.js"></script>
</head>
<body>
<div id="description"></div>
<div id="console"></div>
<script type="text/javascript">
description("WebSocket should fail the connection if it has failed to read a Blob.");
window.jsTestIsAsync = true;
if (window.layoutTestController)
layoutTestController.overridePreference("WebKitHixie76WebSocketProtocolEnabled", 0);
function startsWith(target, prefix)
{
return target.indexOf(prefix) === 0;
}
var fileSystemSize = 1024;
var fileName = "websocket-send-file-blob-fail.txt";
var messageToWrite = "This message shouldn't be sent.";
function runTest()
{
if (!window.webkitRequestFileSystem) {
testFailed("window.webkitRequestFileSystem is not available.");
finishJSTest();
return;
}
webkitRequestFileSystem(TEMPORARY, fileSystemSize, didGetFileSystem, didFail);
}
function didGetFileSystem(fileSystem)
{
debug("Got FileSystem object.");
fileSystem.root.getFile(fileName, {create: true}, didCreateFile, didFail);
}
var fileEntry;
function didCreateFile(entry)
{
debug("File created.");
fileEntry = entry;
shouldBeTrue("fileEntry.isFile");
fileEntry.createWriter(didGetFileWriter, didFail);
}
function didGetFileWriter(writer)
{
writer.truncate(0);
writer.onerror = function()
{
testFailed("FileWriter operation failed.");
endTest();
};
writer.onwrite = function()
{
var builder = new WebKitBlobBuilder();
builder.append(messageToWrite);
writer.write(builder.getBlob());
writer.onwrite = didWriteFile;
};
}
function didWriteFile()
{
debug("Wrote to file.");
fileEntry.file(didGetFile, didFail);
}
var fileObject;
function didGetFile(file)
{
debug("Got File object.");
fileObject = file;
// Delete the file object before it is read. This should cause the subsequent read operation
// to fail reliably.
fileEntry.remove(didRemoveFile, didFail);
}
function didRemoveFile()
{
debug("File deleted.");
fileEntry = null; // To prevent the file from getting deleted again.
var ws = new WebSocket("ws://127.0.0.1:8880/websocket/tests/hybi/echo");
ws.onopen = function()
{
ws.send(fileObject); // This operation should fail and the connection should be aborted.
};
ws.onclose = function(event)
{
closeEvent = event;
shouldBeFalse("closeEvent.wasClean");
endTest();
};
}
function didFail(fileError)
{
testFailed("FileSystem API operation failed: error code = " + fileError.code);
endTest();
}
function endTest()
{
if (fileEntry) {
debug("Deleting the file.");
fileEntry.remove(finishJSTest, finishJSTest);
} else
finishJSTest();
}
runTest();
var successfullyParsed = true;
</script>
<script src="../../../../js-test-resources/js-test-post.js"></script>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="../../../../js-test-resources/js-test-style.css">
<script src="../../../../js-test-resources/js-test-pre.js"></script>
</head>
<body>
<div id="description"></div>
<div id="console"></div>
<script type="text/javascript">
description("WebSocket: Send a File.");
window.jsTestIsAsync = true;
if (window.layoutTestController)
layoutTestController.overridePreference("WebKitHixie76WebSocketProtocolEnabled", 0);
function startsWith(target, prefix)
{
return target.indexOf(prefix) === 0;
}
var fileSystemSize = 1024;
var fileName = "websocket-send-file-blob.txt";
var messageToWrite = "Hello, world!";
function runTest()
{
if (!window.webkitRequestFileSystem) {
testFailed("window.webkitRequestFileSystem is not available.");
finishJSTest();
return;