Update web-platform-tests to revision e92532746b7615dcccdfa060937a87664816b1db

This commit is contained in:
WPT Sync Bot 2018-02-21 20:12:51 -05:00
parent cccca27f4f
commit 726b56aa12
149 changed files with 22796 additions and 1884 deletions

View file

@ -0,0 +1,43 @@
# Copyright 2012, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
from mod_pywebsocket import handshake
def web_socket_do_extra_handshake(request):
raise handshake.AbortedByUserException(
"Aborted in web_socket_do_extra_handshake")
def web_socket_transfer_data(request):
pass
# vi:sts=4 sw=4 et

View file

@ -0,0 +1,43 @@
# Copyright 2012, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
from mod_pywebsocket import handshake
def web_socket_do_extra_handshake(request):
pass
def web_socket_transfer_data(request):
raise handshake.AbortedByUserException(
"Aborted in web_socket_transfer_data")
# vi:sts=4 sw=4 et

View file

@ -0,0 +1,134 @@
<!--
Copyright 2013, Google Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-->
<html>
<head>
<title>ArrayBuffer benchmark</title>
<script src="util.js"></script>
<script>
var PRINT_SIZE = true;
// Initial size of arrays.
var START_SIZE = 10 * 1024;
// Stops benchmark when the size of an array exceeds this threshold.
var STOP_THRESHOLD = 100000 * 1024;
// If the size of each array is small, write/read the array multiple times
// until the sum of sizes reaches this threshold.
var MIN_TOTAL = 100000 * 1024;
var MULTIPLIERS = [5, 2];
// Repeat benchmark for several times to measure performance of optimized
// (such as JIT) run.
var REPEAT_FOR_WARMUP = 3;
function writeBenchmark(size, minTotal) {
var totalSize = 0;
while (totalSize < minTotal) {
var arrayBuffer = new ArrayBuffer(size);
// Write 'a's.
fillArrayBuffer(arrayBuffer, 0x61);
totalSize += size;
}
return totalSize;
}
function readBenchmark(size, minTotal) {
var totalSize = 0;
while (totalSize < minTotal) {
var arrayBuffer = new ArrayBuffer(size);
if (!verifyArrayBuffer(arrayBuffer, 0x00)) {
queueLog('Verification failed');
return -1;
}
totalSize += size;
}
return totalSize;
}
function runBenchmark(benchmarkFunction,
size,
stopThreshold,
minTotal,
multipliers,
multiplierIndex) {
while (size <= stopThreshold) {
var maxSpeed = 0;
for (var i = 0; i < REPEAT_FOR_WARMUP; ++i) {
var startTimeInMs = getTimeStamp();
var totalSize = benchmarkFunction(size, minTotal);
maxSpeed = Math.max(maxSpeed,
calculateSpeedInKB(totalSize, startTimeInMs));
}
queueLog(formatResultInKiB(size, maxSpeed, PRINT_SIZE));
size *= multipliers[multiplierIndex];
multiplierIndex = (multiplierIndex + 1) % multipliers.length;
}
}
function runBenchmarks() {
queueLog('Message size in KiB, Speed in kB/s');
queueLog('Write benchmark');
runBenchmark(
writeBenchmark, START_SIZE, STOP_THRESHOLD, MIN_TOTAL, MULTIPLIERS, 0);
queueLog('Finished');
queueLog('Read benchmark');
runBenchmark(
readBenchmark, START_SIZE, STOP_THRESHOLD, MIN_TOTAL, MULTIPLIERS, 0);
addToLog('Finished');
}
function init() {
logBox = document.getElementById('log');
queueLog(window.navigator.userAgent.toLowerCase());
addToLog('Started...');
setTimeout(runBenchmarks, 0);
}
</script>
</head>
<body onload="init()">
<textarea
id="log" rows="50" style="width: 100%" readonly></textarea>
</body>
</html>

View file

@ -0,0 +1,60 @@
# Copyright 2011, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""A simple load tester for WebSocket clients.
A client program sends a message formatted as "<time> <count> <message>" to
this handler. This handler starts sending total <count> WebSocket messages
containing <message> every <time> seconds. <time> can be a floating point
value. <count> must be an integer value.
"""
import time
def web_socket_do_extra_handshake(request):
pass # Always accept.
def web_socket_transfer_data(request):
line = request.ws_stream.receive_message()
parts = line.split(' ')
if len(parts) != 3:
raise ValueError('Bad parameter format')
wait = float(parts[0])
count = int(parts[1])
message = parts[2]
for i in xrange(count):
request.ws_stream.send_message(message)
time.sleep(wait)
# vi:sts=4 sw=4 et

View file

@ -0,0 +1,175 @@
<!--
Copyright 2013, Google Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-->
<html>
<head>
<title>WebSocket benchmark</title>
<script src="util_main.js"></script>
<script src="util.js"></script>
<script src="benchmark.js"></script>
<script>
var addressBox = null;
function getConfig() {
return {
prefixUrl: addressBox.value,
printSize: getBoolFromCheckBox('printsize'),
numSockets: getIntFromInput('numsockets'),
// Initial size of messages.
numIterations: getIntFromInput('numiterations'),
numWarmUpIterations: getIntFromInput('numwarmupiterations'),
startSize: getIntFromInput('startsize'),
// Stops benchmark when the size of message exceeds this threshold.
stopThreshold: getIntFromInput('stopthreshold'),
// If the size of each message is small, send/receive multiple messages
// until the sum of sizes reaches this threshold.
minTotal: getIntFromInput('mintotal'),
multipliers: getFloatArrayFromInput('multipliers'),
verifyData: getBoolFromCheckBox('verifydata'),
addToLog: addToLog,
addToSummary: addToSummary,
measureValue: measureValue,
notifyAbort: notifyAbort
};
}
function onSendBenchmark() {
var config = getConfig();
doAction(config, getBoolFromCheckBox('worker'), 'sendBenchmark');
}
function onReceiveBenchmark() {
var config = getConfig();
doAction(config, getBoolFromCheckBox('worker'), 'receiveBenchmark');
}
function onBatchBenchmark() {
var config = getConfig();
doAction(config, getBoolFromCheckBox('worker'), 'batchBenchmark');
}
function onStop() {
var config = getConfig();
doAction(config, getBoolFromCheckBox('worker'), 'stop');
}
function init() {
addressBox = document.getElementById('address');
logBox = document.getElementById('log');
summaryBox = document.getElementById('summary');
var scheme = window.location.protocol == 'https:' ? 'wss://' : 'ws://';
var defaultAddress = scheme + window.location.host + '/benchmark_helper';
addressBox.value = defaultAddress;
addToLog(window.navigator.userAgent.toLowerCase());
addToSummary(window.navigator.userAgent.toLowerCase());
if (!('WebSocket' in window)) {
addToLog('WebSocket is not available');
}
initWorker('WebSocket', '');
}
</script>
</head>
<body onload="init()">
<div id="benchmark_div">
url <input type="text" id="address" size="40">
<input type="button" value="send" onclick="onSendBenchmark()">
<input type="button" value="receive" onclick="onReceiveBenchmark()">
<input type="button" value="batch" onclick="onBatchBenchmark()">
<input type="button" value="stop" onclick="onStop()">
<br/>
<input type="checkbox" id="printsize" checked>
<label for="printsize">Print size and time per message</label>
<input type="checkbox" id="verifydata" checked>
<label for="verifydata">Verify data</label>
<input type="checkbox" id="worker">
<label for="worker">Run on worker</label>
<br/>
Parameters:
<br/>
<table>
<tr>
<td>Num sockets</td>
<td><input type="text" id="numsockets" value="1"></td>
</tr>
<tr>
<td>Number of iterations</td>
<td><input type="text" id="numiterations" value="1"></td>
</tr>
<tr>
<td>Number of warm-up iterations</td>
<td><input type="text" id="numwarmupiterations" value="0"></td>
</tr>
<tr>
<td>Start size</td>
<td><input type="text" id="startsize" value="10240"></td>
</tr>
<tr>
<td>Stop threshold</td>
<td><input type="text" id="stopthreshold" value="102400000"></td>
</tr>
<tr>
<td>Minimum total</td>
<td><input type="text" id="mintotal" value="102400000"></td>
</tr>
<tr>
<td>Multipliers</td>
<td><input type="text" id="multipliers" value="5, 2"></td>
</tr>
</table>
</div>
<div id="log_div">
<textarea
id="log" rows="20" style="width: 100%" readonly></textarea>
</div>
<div id="summary_div">
Summary
<textarea
id="summary" rows="20" style="width: 100%" readonly></textarea>
</div>
Note: Effect of RTT is not eliminated.
</body>
</html>

View file

@ -0,0 +1,214 @@
// Copyright 2014 Google Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the COPYING file or at
// https://developers.google.com/open-source/licenses/bsd
if (typeof importScripts !== "undefined") {
// Running on a worker
importScripts('util.js', 'util_worker.js');
}
// Namespace for holding globals.
var benchmark = {startTimeInMs: 0};
var sockets = [];
var numEstablishedSockets = 0;
var timerID = null;
function destroySocket(socket) {
socket.onopen = null;
socket.onmessage = null;
socket.onerror = null;
socket.onclose = null;
socket.close();
}
function destroyAllSockets() {
for (var i = 0; i < sockets.length; ++i) {
destroySocket(sockets[i]);
}
sockets = [];
}
function sendBenchmarkStep(size, config, isWarmUp) {
timerID = null;
var totalSize = 0;
var totalReplied = 0;
var onMessageHandler = function(event) {
if (!verifyAcknowledgement(config, event.data, size)) {
destroyAllSockets();
config.notifyAbort();
return;
}
totalReplied += size;
if (totalReplied < totalSize) {
return;
}
calculateAndLogResult(config, size, benchmark.startTimeInMs, totalSize,
isWarmUp);
runNextTask(config);
};
for (var i = 0; i < sockets.length; ++i) {
var socket = sockets[i];
socket.onmessage = onMessageHandler;
}
var dataArray = [];
while (totalSize < config.minTotal) {
var buffer = new ArrayBuffer(size);
fillArrayBuffer(buffer, 0x61);
dataArray.push(buffer);
totalSize += size;
}
benchmark.startTimeInMs = getTimeStamp();
totalSize = 0;
var socketIndex = 0;
var dataIndex = 0;
while (totalSize < config.minTotal) {
var command = ['send'];
command.push(config.verifyData ? '1' : '0');
sockets[socketIndex].send(command.join(' '));
sockets[socketIndex].send(dataArray[dataIndex]);
socketIndex = (socketIndex + 1) % sockets.length;
totalSize += size;
++dataIndex;
}
}
function receiveBenchmarkStep(size, config, isWarmUp) {
timerID = null;
var totalSize = 0;
var totalReplied = 0;
var onMessageHandler = function(event) {
var bytesReceived = event.data.byteLength;
if (bytesReceived != size) {
config.addToLog('Expected ' + size + 'B but received ' +
bytesReceived + 'B');
destroyAllSockets();
config.notifyAbort();
return;
}
if (config.verifyData && !verifyArrayBuffer(event.data, 0x61)) {
config.addToLog('Response verification failed');
destroyAllSockets();
config.notifyAbort();
return;
}
totalReplied += bytesReceived;
if (totalReplied < totalSize) {
return;
}
calculateAndLogResult(config, size, benchmark.startTimeInMs, totalSize,
isWarmUp);
runNextTask(config);
};
for (var i = 0; i < sockets.length; ++i) {
var socket = sockets[i];
socket.binaryType = 'arraybuffer';
socket.onmessage = onMessageHandler;
}
benchmark.startTimeInMs = getTimeStamp();
var socketIndex = 0;
while (totalSize < config.minTotal) {
sockets[socketIndex].send('receive ' + size);
socketIndex = (socketIndex + 1) % sockets.length;
totalSize += size;
}
}
function createSocket(config) {
// TODO(tyoshino): Add TCP warm up.
var url = config.prefixUrl;
config.addToLog('Connect ' + url);
var socket = new WebSocket(url);
socket.onmessage = function(event) {
config.addToLog('Unexpected message received. Aborting.');
};
socket.onerror = function() {
config.addToLog('Error');
};
socket.onclose = function(event) {
config.addToLog('Closed');
config.notifyAbort();
};
return socket;
}
function startBenchmark(config) {
clearTimeout(timerID);
destroyAllSockets();
numEstablishedSockets = 0;
for (var i = 0; i < config.numSockets; ++i) {
var socket = createSocket(config);
socket.onopen = function() {
config.addToLog('Opened');
++numEstablishedSockets;
if (numEstablishedSockets == sockets.length) {
runNextTask(config);
}
};
sockets.push(socket);
}
}
function getConfigString(config) {
return '(WebSocket' +
', ' + (typeof importScripts !== "undefined" ? 'Worker' : 'Main') +
', numSockets=' + config.numSockets +
', numIterations=' + config.numIterations +
', verifyData=' + config.verifyData +
', minTotal=' + config.minTotal +
', numWarmUpIterations=' + config.numWarmUpIterations +
')';
}
function batchBenchmark(config) {
config.addToLog('Batch benchmark');
config.addToLog(buildLegendString(config));
tasks = [];
clearAverageData();
addTasks(config, sendBenchmarkStep);
addResultReportingTask(config, 'Send Benchmark ' + getConfigString(config));
addTasks(config, receiveBenchmarkStep);
addResultReportingTask(config, 'Receive Benchmark ' +
getConfigString(config));
startBenchmark(config);
}
function cleanup() {
destroyAllSockets();
}

View file

@ -0,0 +1,85 @@
# Copyright 2013, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""Handler for benchmark.html."""
def web_socket_do_extra_handshake(request):
# Turn off compression.
request.ws_extension_processors = []
def web_socket_transfer_data(request):
data = ''
while True:
command = request.ws_stream.receive_message()
if command is None:
return
if not isinstance(command, unicode):
raise ValueError('Invalid command data:' + command)
commands = command.split(' ')
if len(commands) == 0:
raise ValueError('Invalid command data: ' + command)
if commands[0] == 'receive':
if len(commands) != 2:
raise ValueError(
'Illegal number of arguments for send command' +
command)
size = int(commands[1])
# Reuse data if possible.
if len(data) != size:
data = 'a' * size
request.ws_stream.send_message(data, binary=True)
elif commands[0] == 'send':
if len(commands) != 2:
raise ValueError(
'Illegal number of arguments for receive command' +
command)
verify_data = commands[1] == '1'
data = request.ws_stream.receive_message()
if data is None:
raise ValueError('Payload not received')
size = len(data)
if verify_data:
if data != 'a' * size:
raise ValueError('Payload verification failed')
request.ws_stream.send_message(str(size))
else:
raise ValueError('Invalid command: ' + commands[0])
# vi:sts=4 sw=4 et

View file

@ -0,0 +1,69 @@
# Copyright 2012, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import struct
from mod_pywebsocket import common
from mod_pywebsocket import stream
def web_socket_do_extra_handshake(request):
pass
def web_socket_transfer_data(request):
while True:
line = request.ws_stream.receive_message()
if line is None:
return
code, reason = line.split(' ', 1)
if code is None or reason is None:
return
request.ws_stream.close_connection(int(code), reason)
# close_connection() initiates closing handshake. It validates code
# and reason. If you want to send a broken close frame for a test,
# following code will be useful.
# > data = struct.pack('!H', int(code)) + reason.encode('UTF-8')
# > request.connection.write(stream.create_close_frame(data))
# > # Suppress to re-respond client responding close frame.
# > raise Exception("customized server initiated closing handshake")
def web_socket_passive_closing_handshake(request):
# Simply echo a close status code
code, reason = request.ws_close_code, request.ws_close_reason
# pywebsocket sets pseudo code for receiving an empty body close frame.
if code == common.STATUS_NO_STATUS_RECEIVED:
code = None
reason = ''
return code, reason
# vi:sts=4 sw=4 et

View file

@ -0,0 +1,317 @@
<!--
Copyright 2011, Google Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-->
<!--
A simple console for testing WebSocket server.
Type an address into the top text input and click connect to establish
WebSocket. Then, type some message into the bottom text input and click send
to send the message. Received/sent messages and connection state will be shown
on the middle textarea.
-->
<html>
<head>
<title>WebSocket console</title>
<script>
var socket = null;
var showTimeStamp = false;
var addressBox = null;
var protocolsBox = null;
var logBox = null;
var messageBox = null;
var fileBox = null;
var codeBox = null;
var reasonBox = null;
function getTimeStamp() {
return new Date().getTime();
}
function addToLog(log) {
if (showTimeStamp) {
logBox.value += '[' + getTimeStamp() + '] ';
}
logBox.value += log + '\n'
// Large enough to keep showing the latest message.
logBox.scrollTop = 1000000;
}
function setbinarytype(binaryType) {
if (!socket) {
addToLog('Not connected');
return;
}
socket.binaryType = binaryType;
addToLog('Set binaryType to ' + binaryType);
}
function send() {
if (!socket) {
addToLog('Not connected');
return;
}
socket.send(messageBox.value);
addToLog('> ' + messageBox.value);
messageBox.value = '';
}
function sendfile() {
if (!socket) {
addToLog('Not connected');
return;
}
var files = fileBox.files;
if (files.length == 0) {
addToLog('File not selected');
return;
}
socket.send(files[0]);
addToLog('> Send ' + files[0].name);
}
function parseProtocols(protocolsText) {
var protocols = protocolsText.split(',');
for (var i = 0; i < protocols.length; ++i) {
protocols[i] = protocols[i].trim();
}
if (protocols.length == 0) {
// Don't pass.
protocols = null;
} else if (protocols.length == 1) {
if (protocols[0].length == 0) {
// Don't pass.
protocols = null;
} else {
// Pass as a string.
protocols = protocols[0];
}
}
return protocols;
}
function connect() {
var url = addressBox.value;
var protocols = parseProtocols(protocolsBox.value);
if ('WebSocket' in window) {
if (protocols) {
socket = new WebSocket(url, protocols);
} else {
socket = new WebSocket(url);
}
} else {
return;
}
socket.onopen = function () {
var extraInfo = [];
if (('protocol' in socket) && socket.protocol) {
extraInfo.push('protocol = ' + socket.protocol);
}
if (('extensions' in socket) && socket.extensions) {
extraInfo.push('extensions = ' + socket.extensions);
}
var logMessage = 'Opened';
if (extraInfo.length > 0) {
logMessage += ' (' + extraInfo.join(', ') + ')';
}
addToLog(logMessage);
};
socket.onmessage = function (event) {
if (('ArrayBuffer' in window) && (event.data instanceof ArrayBuffer)) {
addToLog('< Received an ArrayBuffer of ' + event.data.byteLength +
' bytes')
} else if (('Blob' in window) && (event.data instanceof Blob)) {
addToLog('< Received a Blob of ' + event.data.size + ' bytes')
} else {
addToLog('< ' + event.data);
}
};
socket.onerror = function () {
addToLog('Error');
};
socket.onclose = function (event) {
var logMessage = 'Closed (';
if ((arguments.length == 1) && ('CloseEvent' in window) &&
(event instanceof CloseEvent)) {
logMessage += 'wasClean = ' + event.wasClean;
// code and reason are present only for
// draft-ietf-hybi-thewebsocketprotocol-06 and later
if ('code' in event) {
logMessage += ', code = ' + event.code;
}
if ('reason' in event) {
logMessage += ', reason = ' + event.reason;
}
} else {
logMessage += 'CloseEvent is not available';
}
addToLog(logMessage + ')');
};
if (protocols) {
addToLog('Connect ' + url + ' (protocols = ' + protocols + ')');
} else {
addToLog('Connect ' + url);
}
}
function closeSocket() {
if (!socket) {
addToLog('Not connected');
return;
}
if (codeBox.value || reasonBox.value) {
socket.close(codeBox.value, reasonBox.value);
} else {
socket.close();
}
}
function printState() {
if (!socket) {
addToLog('Not connected');
return;
}
addToLog(
'url = ' + socket.url +
', readyState = ' + socket.readyState +
', bufferedAmount = ' + socket.bufferedAmount);
}
function init() {
var scheme = window.location.protocol == 'https:' ? 'wss://' : 'ws://';
var defaultAddress = scheme + window.location.host + '/echo';
addressBox = document.getElementById('address');
protocolsBox = document.getElementById('protocols');
logBox = document.getElementById('log');
messageBox = document.getElementById('message');
fileBox = document.getElementById('file');
codeBox = document.getElementById('code');
reasonBox = document.getElementById('reason');
addressBox.value = defaultAddress;
if (!('WebSocket' in window)) {
addToLog('WebSocket is not available');
}
}
</script>
<style type="text/css">
form {
margin: 0px;
}
#connect_div, #log_div, #send_div, #sendfile_div, #close_div, #printstate_div {
padding: 5px;
margin: 5px;
border-width: 0px 0px 0px 10px;
border-style: solid;
border-color: silver;
}
</style>
</head>
<body onload="init()">
<div>
<div id="connect_div">
<form action="#" onsubmit="connect(); return false;">
url <input type="text" id="address" size="40">
<input type="submit" value="connect">
<br/>
protocols <input type="text" id="protocols" size="20">
</form>
</div>
<div id="log_div">
<textarea id="log" rows="10" cols="40" readonly></textarea>
<br/>
<input type="checkbox"
name="showtimestamp"
value="showtimestamp"
onclick="showTimeStamp = this.checked">Show time stamp
</div>
<div id="send_div">
<form action="#" onsubmit="send(); return false;">
data <input type="text" id="message" size="40">
<input type="submit" value="send">
</form>
</div>
<div id="sendfile_div">
<form action="#" onsubmit="sendfile(); return false;">
<input type="file" id="file" size="40">
<input type="submit" value="send file">
</form>
Set binaryType
<input type="radio"
name="binarytype"
value="blob"
onclick="setbinarytype('blob')" checked>blob
<input type="radio"
name="binarytype"
value="arraybuffer"
onclick="setbinarytype('arraybuffer')">arraybuffer
</div>
<div id="close_div">
<form action="#" onsubmit="closeSocket(); return false;">
code <input type="text" id="code" size="10">
reason <input type="text" id="reason" size="20">
<input type="submit" value="close">
</form>
</div>
<div id="printstate_div">
<input type="button" value="print state" onclick="printState();">
</div>
</div>
</body>
</html>

View file

@ -0,0 +1,32 @@
# Copyright 2014 Google Inc. All rights reserved.
#
# Use of this source code is governed by a BSD-style
# license that can be found in the COPYING file or at
# https://developers.google.com/open-source/licenses/bsd
import urlparse
def _add_set_cookie(request, value):
request.extra_headers.append(('Set-Cookie', value))
def web_socket_do_extra_handshake(request):
components = urlparse.urlparse(request.uri)
command = components[4]
ONE_DAY_LIFE = 'Max-Age=86400'
if command == 'set':
_add_set_cookie(request, '; '.join(['foo=bar', ONE_DAY_LIFE]))
elif command == 'set_httponly':
_add_set_cookie(request,
'; '.join(['httpOnlyFoo=bar', ONE_DAY_LIFE, 'httpOnly']))
elif command == 'clear':
_add_set_cookie(request, 'foo=0; Max-Age=0')
_add_set_cookie(request, 'httpOnlyFoo=0; Max-Age=0')
def web_socket_transfer_data(request):
pass

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,61 @@
# Copyright 2013, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
_GOODBYE_MESSAGE = u'Goodbye'
def web_socket_do_extra_handshake(request):
"""Received Sec-WebSocket-Extensions header value is parsed into
request.ws_requested_extensions. pywebsocket creates extension
processors using it before do_extra_handshake call and never looks at it
after the call.
To reject requested extensions, clear the processor list.
"""
request.ws_extension_processors = []
def web_socket_transfer_data(request):
"""Echo. Same as echo_wsh.py."""
while True:
line = request.ws_stream.receive_message()
if line is None:
return
if isinstance(line, unicode):
request.ws_stream.send_message(line, binary=False)
if line == _GOODBYE_MESSAGE:
return
else:
request.ws_stream.send_message(line, binary=True)
# vi:sts=4 sw=4 et

View file

@ -0,0 +1,54 @@
# Copyright 2011, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
_GOODBYE_MESSAGE = u'Goodbye'
def web_socket_do_extra_handshake(request):
# This example handler accepts any request. See origin_check_wsh.py for how
# to reject access from untrusted scripts based on origin value.
pass # Always accept.
def web_socket_transfer_data(request):
while True:
line = request.ws_stream.receive_message()
if line is None:
return
if isinstance(line, unicode):
request.ws_stream.send_message(line, binary=False)
if line == _GOODBYE_MESSAGE:
return
else:
request.ws_stream.send_message(line, binary=True)
# vi:sts=4 sw=4 et

View file

@ -0,0 +1,54 @@
#!/usr/bin/python
# Copyright 2013, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""This CGI script generates text/event-stream type data stream for testing
the Server-Sent Events.
It will only work correctly with HTTP servers that do not buffer the output of
CGI scripts.
"""
import sys
import time
sys.stdout.write('Content-type: text/event-stream\r\n\r\n')
id = 0
while True:
sys.stdout.write('data: Hello\r\nid: %d\r\n\r\n' % id)
sys.stdout.flush()
id = id + 1
time.sleep(1)

View file

@ -0,0 +1,74 @@
<!--
Copyright 2013, Google Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-->
<!--
Simple example of the Server-Sent Events
http://dev.w3.org/html5/eventsource/
For comparison with the WebSocket Protocol & API.
Run the pywebsocket with the --cgi_path parameter.
-->
<html>
<head>
<title>Server-Sent Events Example</title>
<script>
var eventSource = null;
function addToLog(data) {
logBox.value += data + '\n';
logBox.scrollTop = 1000000;
}
function init() {
logBox = document.getElementById('log');
eventSource = new EventSource('/eventsource.cgi');
eventSource.onopen = function() {
addToLog('onopen (readyState = ' + eventSource.readyState + ')');
}
eventSource.onmessage = function(event) {
addToLog(event.data);
}
eventSource.onerror = function(event) {
addToLog('onerror (readyState = ' + eventSource.readyState + ')');
}
}
</script>
</head>
<body onload="init()">
<textarea id="log" rows="10" cols="40" readonly></textarea>
<p style="font-size: small">
Make sure that pywebsocket is run with --cgi_path parameter.
</p>
</body>
</html>

View file

@ -0,0 +1,163 @@
<!--
Copyright 2015 Google Inc. All rights reserved.
Use of this source code is governed by a BSD-style
license that can be found in the COPYING file or at
https://developers.google.com/open-source/licenses/bsd
-->
<html>
<head>
<title>Fetch API benchmark</title>
<script src="util_main.js"></script>
<script src="util.js"></script>
<script src="fetch_benchmark.js"></script>
<script>
var addressBox = null;
function getConfig() {
return {
prefixUrl: addressBox.value,
printSize: getBoolFromCheckBox('printsize'),
numFetches: getIntFromInput('numFetches'),
// Initial size of messages.
numIterations: getIntFromInput('numiterations'),
numWarmUpIterations: getIntFromInput('numwarmupiterations'),
startSize: getIntFromInput('startsize'),
// Stops benchmark when the size of message exceeds this threshold.
stopThreshold: getIntFromInput('stopthreshold'),
multipliers: getFloatArrayFromInput('multipliers'),
verifyData: getBoolFromCheckBox('verifydata'),
addToLog: addToLog,
addToSummary: addToSummary,
measureValue: measureValue,
notifyAbort: notifyAbort
};
}
function onSendBenchmark() {
var config = getConfig();
config.dataType = getStringFromRadioBox('datatyperadio');
doAction(config, getBoolFromCheckBox('worker'), 'sendBenchmark');
}
function onReceiveBenchmark() {
var config = getConfig();
config.dataType = getStringFromRadioBox('datatyperadio');
doAction(config, getBoolFromCheckBox('worker'), 'receiveBenchmark');
}
function onBatchBenchmark() {
var config = getConfig();
doAction(config, getBoolFromCheckBox('worker'), 'batchBenchmark');
}
function onStop() {
var config = getConfig();
doAction(config, getBoolFromCheckBox('worker'), 'stop');
}
function init() {
addressBox = document.getElementById('address');
logBox = document.getElementById('log');
summaryBox = document.getElementById('summary');
// Special address of pywebsocket for XHR/Fetch API benchmark.
addressBox.value = '/073be001e10950692ccbf3a2ad21c245';
addToLog(window.navigator.userAgent.toLowerCase());
addToSummary(window.navigator.userAgent.toLowerCase());
initWorker('fetch', '');
}
</script>
</head>
<body onload="init()">
<form id="benchmark_form">
url prefix <input type="text" id="address" size="40">
<input type="button" value="send" onclick="onSendBenchmark()">
<input type="button" value="receive" onclick="onReceiveBenchmark()">
<input type="button" value="batch" onclick="onBatchBenchmark()">
<input type="button" value="stop" onclick="onStop()">
<br/>
<input type="checkbox" id="printsize" checked>
<label for="printsize">Print size and time per message</label>
<input type="checkbox" id="verifydata" checked>
<label for="verifydata">Verify data</label>
<input type="checkbox" id="worker">
<label for="worker">Run on worker</label>
<br/>
Parameters:
<br/>
<table>
<tr>
<td>Number of fetch() requests</td>
<td><input type="text" id="numFetches" value="1"></td>
</tr>
<tr>
<td>Number of iterations</td>
<td><input type="text" id="numiterations" value="1"></td>
</tr>
<tr>
<td>Number of warm-up iterations</td>
<td><input type="text" id="numwarmupiterations" value="0"></td>
</tr>
<tr>
<td>Start size</td>
<td><input type="text" id="startsize" value="10240"></td>
</tr>
<tr>
<td>Stop threshold</td>
<td><input type="text" id="stopthreshold" value="102400000"></td>
</tr>
<tr>
<td>Multipliers</td>
<td><input type="text" id="multipliers" value="5, 2"></td>
</tr>
</table>
Set data type
<input type="radio"
name="datatyperadio"
id="datatyperadiotext"
value="text"
checked><label for="datatyperadiotext">text</label>
<input type="radio"
name="datatyperadio"
id="datatyperadioblob"
value="blob"
><label for="datatyperadioblob">blob</label>
<input type="radio"
name="datatyperadio"
id="datatyperadioarraybuffer"
value="arraybuffer"
><label for="datatyperadioarraybuffer">arraybuffer</label>
</form>
<div id="log_div">
<textarea
id="log" rows="20" style="width: 100%" readonly></textarea>
</div>
<div id="summary_div">
Summary
<textarea
id="summary" rows="20" style="width: 100%" readonly></textarea>
</div>
<div id="note_div">
Note:
<ul>
<li>Effect of RTT and time spent for ArrayBuffer creation in receive benchmarks are not eliminated.</li>
<li>The Stddev column shows NaN when the number of iterations is set to 1.</li>
</ul>
</div>
</body>
</html>

View file

@ -0,0 +1,225 @@
// Copyright 2015 Google Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the COPYING file or at
// https://developers.google.com/open-source/licenses/bsd
var isWorker = typeof importScripts !== "undefined";
if (isWorker) {
// Running on a worker
importScripts('util.js', 'util_worker.js');
}
// Namespace for holding globals.
var benchmark = {};
benchmark.startTimeInMs = 0;
var timerID = null;
function sendBenchmarkStep(size, config, isWarmUp) {
timerID = null;
benchmark.startTimeInMs = null;
// Prepare data.
var dataArray = [];
for (var i = 0; i < config.numFetches; ++i) {
var data = null;
if (config.dataType == 'arraybuffer' ||
config.dataType == 'blob') {
data = new ArrayBuffer(size);
fillArrayBuffer(data, 0x61);
if (config.dataType == 'blob') {
data = new Blob([data]);
}
} else {
data = repeatString('a', size);
}
dataArray.push(data);
}
// Start time measuring.
benchmark.startTimeInMs = getTimeStamp();
// Start fetch.
var promises = [];
for (var i = 0; i < config.numFetches; ++i) {
var data = dataArray[i];
var promise = fetch(config.prefixUrl + '_send',
{method: 'POST', body: data})
.then(function (response) {
if (response.status != 200) {
config.addToLog('Failed (status=' + response.status + ')');
return Promise.reject();
}
// Check and warn if proxy is enabled.
if (response.headers.get('Via') !== null) {
config.addToLog('WARNING: proxy seems enabled.');
}
if (config.verifyData) {
return response.text()
.then(function(text) {
if (!verifyAcknowledgement(config, text, size)) {
return Promise.reject();
}
});
}
});
promises.push(promise);
}
// Finish and report time measuring.
Promise.all(promises)
.then(function() {
if (benchmark.startTimeInMs == null) {
config.addToLog('startTimeInMs not set');
return Promise.reject();
}
calculateAndLogResult(config, size, benchmark.startTimeInMs,
size * config.numFetches, isWarmUp);
runNextTask(config);
})
.catch(function(e) {
config.addToLog("ERROR: " + e);
config.notifyAbort();
});
}
function receiveBenchmarkStep(size, config, isWarmUp) {
timerID = null;
benchmark.startTimeInMs = null;
// Start time measuring.
benchmark.startTimeInMs = getTimeStamp();
// Start fetch.
var promises = [];
for (var i = 0; i < config.numFetches; ++i) {
var request;
if (config.methodAndCache === 'GET-NOCACHE') {
request = new Request(config.prefixUrl + '_receive_getnocache?' + size,
{method: 'GET'});
} else if (config.methodAndCache === 'GET-CACHE') {
request = new Request(config.prefixUrl + '_receive_getcache?' + size,
{method: 'GET'});
} else {
request = new Request(config.prefixUrl + '_receive',
{method: 'POST', body: size + ' none'});
}
var promise = fetch(request)
.then(function(response) {
if (response.status != 200) {
config.addToLog('Failed (status=' + this.status + ')');
return Promise.reject();
}
// Check and warn if proxy is enabled.
if (response.headers.get('Via') !== null) {
config.addToLog('WARNING: proxy seems enabled.');
}
if (config.dataType === 'arraybuffer') {
return response.arrayBuffer()
.then(function(arrayBuffer) {
return [arrayBuffer.byteLength,
(!config.verifyData ||
verifyArrayBuffer(arrayBuffer, 0x61))];
});
} else if (config.dataType == 'blob') {
return response.blob()
.then(function(blob) {
return new Promise(function(resolve, reject) {
if (config.verifyData) {
verifyBlob(config, blob, 0x61,
function(receivedSize, verificationResult) {
resolve([receivedSize, verificationResult]);
});
} else {
resolve([blob.size, true]);
}
});
});
} else {
return response.text()
.then(function(text) {
return [text.length,
(!config.verifyData ||
text == repeatString('a', text.length))];
});
}
})
.then(function(receivedSizeAndVerificationResult) {
var receivedSize = receivedSizeAndVerificationResult[0];
var verificationResult = receivedSizeAndVerificationResult[1];
if (receivedSize !== size) {
config.addToLog('Expected ' + size +
'B but received ' + receivedSize + 'B');
return Promise.reject();
}
if (!verificationResult) {
config.addToLog('Response verification failed');
return Promise.reject();
}
});
promises.push(promise);
}
// Finish and report time measuring.
Promise.all(promises)
.then(function() {
if (benchmark.startTimeInMs == null) {
config.addToLog('startTimeInMs not set');
return Promise.reject();
}
calculateAndLogResult(config, size, benchmark.startTimeInMs,
size * config.numFetches, isWarmUp);
runNextTask(config);
})
.catch(function(e) {
config.addToLog("ERROR: " + e);
config.notifyAbort();
});
}
function getConfigString(config) {
return '(' + config.dataType +
', verifyData=' + config.verifyData +
', ' + (isWorker ? 'Worker' : 'Main') +
', numFetches=' + config.numFetches +
', numIterations=' + config.numIterations +
', numWarmUpIterations=' + config.numWarmUpIterations +
')';
}
function startBenchmark(config) {
clearTimeout(timerID);
runNextTask(config);
}
function batchBenchmark(originalConfig) {
originalConfig.addToLog('Batch benchmark');
tasks = [];
clearAverageData();
var dataTypes = ['text', 'blob', 'arraybuffer'];
var stepFuncs = [sendBenchmarkStep, receiveBenchmarkStep];
var names = ['Send', 'Receive'];
for (var i = 0; i < stepFuncs.length; ++i) {
for (var j = 0; j < dataTypes.length; ++j) {
var config = cloneConfig(originalConfig);
config.dataType = dataTypes[j];
addTasks(config, stepFuncs[i]);
addResultReportingTask(config,
names[i] + ' benchmark ' + getConfigString(config));
}
}
startBenchmark(config);
}
function cleanup() {
}

View file

@ -0,0 +1,6 @@
<!DOCTYPE html>
<head>
<script src="util.js"></script>
<script src="performance_test_iframe.js"></script>
<script src="fetch_benchmark.js"></script>
</head>

View file

@ -0,0 +1,11 @@
# websocket handler map file, used by standalone.py -m option.
# A line starting with '#' is a comment line.
# Each line consists of 'alias_resource_path' and 'existing_resource_path'
# separated by spaces.
# Aliasing is processed from the top to the bottom of the line, and
# 'existing_resource_path' must exist before it is aliased.
# For example,
# / /echo
# means that a request to '/' will be handled by handlers for '/echo'.
/ /echo

View file

@ -0,0 +1,40 @@
# Copyright 2013, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
def web_socket_do_extra_handshake(request):
request.extra_headers.append(
('Strict-Transport-Security', 'max-age=86400'))
def web_socket_transfer_data(request):
request.ws_stream.send_message('Hello', binary=False)
# vi:sts=4 sw=4 et

View file

@ -0,0 +1,42 @@
# Copyright 2012, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
from mod_pywebsocket import msgutil
def web_socket_do_extra_handshake(request):
pass
def web_socket_transfer_data(request):
raise msgutil.BadOperationException('Intentional')
# vi:sts=4 sw=4 et

View file

@ -0,0 +1,44 @@
# Copyright 2011, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# This example is derived from test/testdata/handlers/origin_check_wsh.py.
def web_socket_do_extra_handshake(request):
if request.ws_origin == 'http://example.com':
return
raise ValueError('Unacceptable origin: %r' % request.ws_origin)
def web_socket_transfer_data(request):
request.connection.write('origin_check_wsh.py is called for %s, %s' %
(request.ws_resource, request.ws_protocol))
# vi:sts=4 sw=4 et

View file

@ -0,0 +1,6 @@
<!DOCTYPE html>
<head>
<script src="util.js"></script>
<script src="performance_test_iframe.js"></script>
<script src="benchmark.js"></script>
</head>

View file

@ -0,0 +1,66 @@
function perfTestAddToLog(text) {
parent.postMessage({'command': 'log', 'value': text}, '*');
}
function perfTestAddToSummary(text) {
}
function perfTestMeasureValue(value) {
parent.postMessage({'command': 'measureValue', 'value': value}, '*');
}
function perfTestNotifyAbort() {
parent.postMessage({'command': 'notifyAbort'}, '*');
}
function getConfigForPerformanceTest(connectionType, dataType, async,
verifyData, numIterations,
numWarmUpIterations) {
var prefixUrl;
if (connectionType === 'WebSocket') {
prefixUrl = 'ws://' + location.host + '/benchmark_helper';
} else {
// XHR or fetch
prefixUrl = 'http://' + location.host + '/073be001e10950692ccbf3a2ad21c245';
}
return {
prefixUrl: prefixUrl,
printSize: true,
numXHRs: 1,
numFetches: 1,
numSockets: 1,
// + 1 is for a warmup iteration by the Telemetry framework.
numIterations: numIterations + numWarmUpIterations + 1,
numWarmUpIterations: numWarmUpIterations,
minTotal: 10240000,
startSize: 10240000,
stopThreshold: 10240000,
multipliers: [2],
verifyData: verifyData,
dataType: dataType,
async: async,
addToLog: perfTestAddToLog,
addToSummary: perfTestAddToSummary,
measureValue: perfTestMeasureValue,
notifyAbort: perfTestNotifyAbort
};
}
var data;
onmessage = function(message) {
var action;
if (message.data.command === 'start') {
data = message.data;
initWorker(data.connectionType, 'http://' + location.host);
action = data.benchmarkName;
} else {
action = 'stop';
}
var config = getConfigForPerformanceTest(data.connectionType, data.dataType,
data.async, data.verifyData,
data.numIterations,
data.numWarmUpIterations);
doAction(config, data.isWorker, action);
};

View file

@ -0,0 +1,42 @@
# Copyright 2011, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# Sample configuration file for apache2
#
LogLevel debug
<IfModule python_module>
PythonPath "sys.path+['/mod_pywebsocket']"
PythonOption mod_pywebsocket.handler_root /var/www
PythonOption mod_pywebsocket.handler_scan /var/www/ws
#PythonOption mod_pywebsocket.allow_draft75 On
<Location /ws>
PythonHeaderParserHandler mod_pywebsocket.headerparserhandler
</Location>
</IfModule>

View file

@ -0,0 +1,28 @@
#!/usr/bin/python
# Copyright 2014 Google Inc. All rights reserved.
#
# Use of this source code is governed by a BSD-style
# license that can be found in the COPYING file or at
# https://developers.google.com/open-source/licenses/bsd
"""CGI script sample for testing effect of HTTP headers on the origin page.
Note that CGI scripts don't work on the standalone pywebsocket running in TLS
mode.
"""
print """Content-type: text/html
Content-Security-Policy: connect-src self
<html>
<head>
<title></title>
</head>
<body>
<script>
var socket = new WebSocket("ws://example.com");
</script>
</body>
</html>"""

View file

@ -0,0 +1,327 @@
// Copyright 2013, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Utilities for example applications (for both main and worker thread).
var results = {};
function getTimeStamp() {
return Date.now();
}
function formatResultInKiB(size, timePerMessageInMs, stddevTimePerMessageInMs,
speed, printSize) {
if (printSize) {
return (size / 1024) +
'\t' + timePerMessageInMs.toFixed(3) +
(stddevTimePerMessageInMs == -1 ?
'' :
'\t' + stddevTimePerMessageInMs.toFixed(3)) +
'\t' + speed.toFixed(3);
} else {
return speed.toString();
}
}
function clearAverageData() {
results = {};
}
function reportAverageData(config) {
config.addToSummary(
'Size[KiB]\tAverage time[ms]\tStddev time[ms]\tSpeed[KB/s]');
for (var size in results) {
var averageTimePerMessageInMs = results[size].sum_t / results[size].n;
var speed = calculateSpeedInKB(size, averageTimePerMessageInMs);
// Calculate sample standard deviation
var stddevTimePerMessageInMs = Math.sqrt(
(results[size].sum_t2 / results[size].n -
averageTimePerMessageInMs * averageTimePerMessageInMs) *
results[size].n /
(results[size].n - 1));
config.addToSummary(formatResultInKiB(
size, averageTimePerMessageInMs, stddevTimePerMessageInMs, speed,
true));
}
}
function calculateSpeedInKB(size, timeSpentInMs) {
return Math.round(size / timeSpentInMs * 1000) / 1000;
}
function calculateAndLogResult(config, size, startTimeInMs, totalSize,
isWarmUp) {
var timeSpentInMs = getTimeStamp() - startTimeInMs;
var speed = calculateSpeedInKB(totalSize, timeSpentInMs);
var timePerMessageInMs = timeSpentInMs / (totalSize / size);
if (!isWarmUp) {
config.measureValue(timePerMessageInMs);
if (!results[size]) {
results[size] = {n: 0, sum_t: 0, sum_t2: 0};
}
results[size].n ++;
results[size].sum_t += timePerMessageInMs;
results[size].sum_t2 += timePerMessageInMs * timePerMessageInMs;
}
config.addToLog(formatResultInKiB(size, timePerMessageInMs, -1, speed,
config.printSize));
}
function repeatString(str, count) {
var data = '';
var expChunk = str;
var remain = count;
while (true) {
if (remain % 2) {
data += expChunk;
remain = (remain - 1) / 2;
} else {
remain /= 2;
}
if (remain == 0)
break;
expChunk = expChunk + expChunk;
}
return data;
}
function fillArrayBuffer(buffer, c) {
var i;
var u32Content = c * 0x01010101;
var u32Blocks = Math.floor(buffer.byteLength / 4);
var u32View = new Uint32Array(buffer, 0, u32Blocks);
// length attribute is slow on Chrome. Don't use it for loop condition.
for (i = 0; i < u32Blocks; ++i) {
u32View[i] = u32Content;
}
// Fraction
var u8Blocks = buffer.byteLength - u32Blocks * 4;
var u8View = new Uint8Array(buffer, u32Blocks * 4, u8Blocks);
for (i = 0; i < u8Blocks; ++i) {
u8View[i] = c;
}
}
function verifyArrayBuffer(buffer, expectedChar) {
var i;
var expectedU32Value = expectedChar * 0x01010101;
var u32Blocks = Math.floor(buffer.byteLength / 4);
var u32View = new Uint32Array(buffer, 0, u32Blocks);
for (i = 0; i < u32Blocks; ++i) {
if (u32View[i] != expectedU32Value) {
return false;
}
}
var u8Blocks = buffer.byteLength - u32Blocks * 4;
var u8View = new Uint8Array(buffer, u32Blocks * 4, u8Blocks);
for (i = 0; i < u8Blocks; ++i) {
if (u8View[i] != expectedChar) {
return false;
}
}
return true;
}
function verifyBlob(config, blob, expectedChar, doneCallback) {
var reader = new FileReader(blob);
reader.onerror = function() {
config.addToLog('FileReader Error: ' + reader.error.message);
doneCallback(blob.size, false);
}
reader.onloadend = function() {
var result = verifyArrayBuffer(reader.result, expectedChar);
doneCallback(blob.size, result);
}
reader.readAsArrayBuffer(blob);
}
function verifyAcknowledgement(config, message, size) {
if (typeof message != 'string') {
config.addToLog('Invalid ack type: ' + typeof message);
return false;
}
var parsedAck = parseInt(message);
if (isNaN(parsedAck)) {
config.addToLog('Invalid ack value: ' + message);
return false;
}
if (parsedAck != size) {
config.addToLog(
'Expected ack for ' + size + 'B but received one for ' + parsedAck +
'B');
return false;
}
return true;
}
function cloneConfig(obj) {
var newObj = {};
for (key in obj) {
newObj[key] = obj[key];
}
return newObj;
}
var tasks = [];
function runNextTask(config) {
var task = tasks.shift();
if (task == undefined) {
config.addToLog('Finished');
cleanup();
return;
}
timerID = setTimeout(task, 0);
}
function buildLegendString(config) {
var legend = ''
if (config.printSize)
legend = 'Message size in KiB, Time/message in ms, ';
legend += 'Speed in kB/s';
return legend;
}
function addTasks(config, stepFunc) {
for (var i = 0;
i < config.numWarmUpIterations + config.numIterations; ++i) {
var multiplierIndex = 0;
for (var size = config.startSize;
size <= config.stopThreshold;
++multiplierIndex) {
var task = stepFunc.bind(
null,
size,
config,
i < config.numWarmUpIterations);
tasks.push(task);
var multiplier = config.multipliers[
multiplierIndex % config.multipliers.length];
if (multiplier <= 1) {
config.addToLog('Invalid multiplier ' + multiplier);
config.notifyAbort();
throw new Error('Invalid multipler');
}
size = Math.ceil(size * multiplier);
}
}
}
function addResultReportingTask(config, title) {
tasks.push(function(){
timerID = null;
config.addToSummary(title);
reportAverageData(config);
clearAverageData();
runNextTask(config);
});
}
function sendBenchmark(config) {
config.addToLog('Send benchmark');
config.addToLog(buildLegendString(config));
tasks = [];
clearAverageData();
addTasks(config, sendBenchmarkStep);
addResultReportingTask(config, 'Send Benchmark ' + getConfigString(config));
startBenchmark(config);
}
function receiveBenchmark(config) {
config.addToLog('Receive benchmark');
config.addToLog(buildLegendString(config));
tasks = [];
clearAverageData();
addTasks(config, receiveBenchmarkStep);
addResultReportingTask(config,
'Receive Benchmark ' + getConfigString(config));
startBenchmark(config);
}
function stop(config) {
clearTimeout(timerID);
timerID = null;
tasks = [];
config.addToLog('Stopped');
cleanup();
}
var worker;
function initWorker(connectionType, origin) {
var scriptPath =
connectionType === 'WebSocket' ? '/benchmark.js' :
connectionType === 'XHR' ? '/xhr_benchmark.js' :
'/fetch_benchmark.js'; // connectionType === 'fetch'
worker = new Worker(origin + scriptPath);
}
function doAction(config, isWindowToWorker, action) {
if (isWindowToWorker) {
worker.onmessage = function(addToLog, addToSummary,
measureValue, notifyAbort, message) {
if (message.data.type === 'addToLog')
addToLog(message.data.data);
else if (message.data.type === 'addToSummary')
addToSummary(message.data.data);
else if (message.data.type === 'measureValue')
measureValue(message.data.data);
else if (message.data.type === 'notifyAbort')
notifyAbort();
}.bind(undefined, config.addToLog, config.addToSummary,
config.measureValue, config.notifyAbort);
config.addToLog = undefined;
config.addToSummary = undefined;
config.measureValue = undefined;
config.notifyAbort = undefined;
worker.postMessage({type: action, config: config});
} else {
if (action === 'sendBenchmark')
sendBenchmark(config);
else if (action === 'receiveBenchmark')
receiveBenchmark(config);
else if (action === 'batchBenchmark')
batchBenchmark(config);
else if (action === 'stop')
stop(config);
}
}

View file

@ -0,0 +1,65 @@
// Copyright 2014 Google Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the COPYING file or at
// https://developers.google.com/open-source/licenses/bsd
// Utilities for example applications (for the main thread only).
var logBox = null;
var queuedLog = '';
var summaryBox = null;
function queueLog(log) {
queuedLog += log + '\n';
}
function addToLog(log) {
logBox.value += queuedLog;
queuedLog = '';
logBox.value += log + '\n';
logBox.scrollTop = 1000000;
}
function addToSummary(log) {
summaryBox.value += log + '\n';
summaryBox.scrollTop = 1000000;
}
// value: execution time in milliseconds.
// config.measureValue is intended to be used in Performance Tests.
// Do nothing here in non-PerformanceTest.
function measureValue(value) {
}
// config.notifyAbort is called when the benchmark failed and aborted, and
// intended to be used in Performance Tests.
// Do nothing here in non-PerformanceTest.
function notifyAbort() {
}
function getIntFromInput(id) {
return parseInt(document.getElementById(id).value);
}
function getStringFromRadioBox(name) {
var list = document.getElementById('benchmark_form')[name];
for (var i = 0; i < list.length; ++i)
if (list.item(i).checked)
return list.item(i).value;
return undefined;
}
function getBoolFromCheckBox(id) {
return document.getElementById(id).checked;
}
function getIntArrayFromInput(id) {
var strArray = document.getElementById(id).value.split(',');
return strArray.map(function(str) { return parseInt(str, 10); });
}
function getFloatArrayFromInput(id) {
var strArray = document.getElementById(id).value.split(',');
return strArray.map(parseFloat);
}

View file

@ -0,0 +1,20 @@
// Copyright 2014 Google Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the COPYING file or at
// https://developers.google.com/open-source/licenses/bsd
// Utilities for example applications (for the worker threads only).
onmessage = function (message) {
var config = message.data.config;
config.addToLog = function(text) {
postMessage({type: 'addToLog', data: text}); };
config.addToSummary = function(text) {
postMessage({type: 'addToSummary', data: text}); };
config.measureValue = function(value) {
postMessage({type: 'measureValue', data: value}); };
config.notifyAbort = function() { postMessage({type: 'notifyAbort'}); };
doAction(config, false, message.data.type);
};

View file

@ -0,0 +1,212 @@
<!--
Copyright 2013, Google Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-->
<html>
<head>
<title>XMLHttpRequest benchmark</title>
<script src="util_main.js"></script>
<script src="util.js"></script>
<script src="xhr_benchmark.js"></script>
<script>
var addressBox = null;
function getConfig() {
return {
prefixUrl: addressBox.value,
printSize: getBoolFromCheckBox('printsize'),
numXHRs: getIntFromInput('numXHRs'),
async: getBoolFromCheckBox('async'),
// Initial size of messages.
numIterations: getIntFromInput('numiterations'),
numWarmUpIterations: getIntFromInput('numwarmupiterations'),
startSize: getIntFromInput('startsize'),
// Stops benchmark when the size of message exceeds this threshold.
stopThreshold: getIntFromInput('stopthreshold'),
multipliers: getFloatArrayFromInput('multipliers'),
verifyData: getBoolFromCheckBox('verifydata'),
methodAndCache: getStringFromRadioBox('methodandcache'),
addToLog: addToLog,
addToSummary: addToSummary,
measureValue: measureValue,
notifyAbort: notifyAbort
};
}
function onSendBenchmark() {
var config = getConfig();
config.dataType = getStringFromRadioBox('datatyperadio');
doAction(config, getBoolFromCheckBox('worker'), 'sendBenchmark');
}
function onReceiveBenchmark() {
var config = getConfig();
config.dataType = getStringFromRadioBox('datatyperadio');
doAction(config, getBoolFromCheckBox('worker'), 'receiveBenchmark');
}
function onBatchBenchmark() {
var config = getConfig();
doAction(config, getBoolFromCheckBox('worker'), 'batchBenchmark');
}
function onStop() {
var config = getConfig();
doAction(config, getBoolFromCheckBox('worker'), 'stop');
}
function init() {
addressBox = document.getElementById('address');
logBox = document.getElementById('log');
summaryBox = document.getElementById('summary');
// Special address of pywebsocket for XHR benchmark.
addressBox.value = '/073be001e10950692ccbf3a2ad21c245';
addToLog(window.navigator.userAgent.toLowerCase());
addToSummary(window.navigator.userAgent.toLowerCase());
initWorker('XHR', '');
}
</script>
</head>
<body onload="init()">
<form id="benchmark_form">
url prefix <input type="text" id="address" size="40">
<input type="button" value="send" onclick="onSendBenchmark()">
<input type="button" value="receive" onclick="onReceiveBenchmark()">
<input type="button" value="batch" onclick="onBatchBenchmark()">
<input type="button" value="stop" onclick="onStop()">
<br/>
<input type="checkbox" id="printsize" checked>
<label for="printsize">Print size and time per message</label>
<input type="checkbox" id="verifydata" checked>
<label for="verifydata">Verify data</label>
<input type="checkbox" id="worker">
<label for="worker">Run on worker</label>
<input type="checkbox" id="async" checked>
<label for="async">Async</label><br>
(Receive &amp;&amp; Non-Worker &amp;&amp; Sync is not supported by spec)
<br/>
Parameters:
<br/>
<table>
<tr>
<td>Num XHRs</td>
<td><input type="text" id="numXHRs" value="1"></td>
</tr>
<tr>
<td>Number of iterations</td>
<td><input type="text" id="numiterations" value="1"></td>
</tr>
<tr>
<td>Number of warm-up iterations</td>
<td><input type="text" id="numwarmupiterations" value="0"></td>
</tr>
<tr>
<td>Start size</td>
<td><input type="text" id="startsize" value="10240"></td>
</tr>
<tr>
<td>Stop threshold</td>
<td><input type="text" id="stopthreshold" value="102400000"></td>
</tr>
<tr>
<td>Multipliers</td>
<td><input type="text" id="multipliers" value="5, 2"></td>
</tr>
</table>
Set data type
<input type="radio"
name="datatyperadio"
id="datatyperadiotext"
value="text"
checked><label for="datatyperadiotext">text</label>
<input type="radio"
name="datatyperadio"
id="datatyperadioblob"
value="blob"
><label for="datatyperadioblob">blob</label>
<input type="radio"
name="datatyperadio"
id="datatyperadioarraybuffer"
value="arraybuffer"
><label for="datatyperadioarraybuffer">arraybuffer</label>
<br>
Set HTTP method and cache control
<input type="radio"
name="methodandcache"
id="methodandcachePOST"
value="POST"
checked><label for="methodandcachePOST">POST (No Cache)</label>
<input type="radio"
name="methodandcache"
id="methodandcacheGETNOCACHE"
value="GET-NOCACHE"
><label for="methodandcacheGETNOCACHE">GET (No Cache)</label>
<input type="radio"
name="methodandcache"
id="methodandcacheGETCACHE"
value="GET-CACHE"
><label for="methodandcacheGETCACHE">GET (Cache)</label>
<br>
<span style="font-size: 80%">(Cache control: receive only. Cache is valid for 10 seconds. This config controls Cache-control HTTP response header. Browsers might not cache data e.g. when it is large)</span>
</form>
<div id="log_div">
<textarea
id="log" rows="20" style="width: 100%" readonly></textarea>
</div>
<div id="summary_div">
Summary
<textarea
id="summary" rows="20" style="width: 100%" readonly></textarea>
</div>
<div id="note_div">
Note:
<ul>
<li>Effect of RTT and time spent for ArrayBuffer creation in receive benchmarks are not eliminated.</li>
<li>The Stddev column shows NaN when the number of iterations is set to 1.</li>
</ul>
</div>
</body>
</html>

View file

@ -0,0 +1,292 @@
// Copyright 2014 Google Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the COPYING file or at
// https://developers.google.com/open-source/licenses/bsd
var isWorker = typeof importScripts !== "undefined";
if (isWorker) {
// Running on a worker
importScripts('util.js', 'util_worker.js');
}
// Namespace for holding globals.
var benchmark = {};
benchmark.startTimeInMs = 0;
var xhrs = [];
var timerID = null;
function destroyAllXHRs() {
for (var i = 0; i < xhrs.length; ++i) {
xhrs[i].onreadystatechange = null;
// Abort XHRs if they are not yet DONE state.
// Calling abort() here (i.e. in onreadystatechange handler)
// causes "NetworkError" messages in DevTools in sync mode,
// even if it is after transition to DONE state.
if (xhrs[i].readyState != XMLHttpRequest.DONE)
xhrs[i].abort();
}
xhrs = [];
// gc() might be needed for Chrome/Blob
}
function sendBenchmarkStep(size, config, isWarmUp) {
timerID = null;
benchmark.startTimeInMs = null;
var totalSize = 0;
var totalReplied = 0;
var onReadyStateChangeHandler = function () {
if (this.readyState != this.DONE) {
return;
}
if (this.status != 200) {
config.addToLog('Failed (status=' + this.status + ')');
destroyAllXHRs();
config.notifyAbort();
return;
}
if (config.verifyData &&
!verifyAcknowledgement(config, this.response, size)) {
destroyAllXHRs();
config.notifyAbort();
return;
}
totalReplied += size;
if (totalReplied < totalSize) {
return;
}
if (benchmark.startTimeInMs == null) {
config.addToLog('startTimeInMs not set');
destroyAllXHRs();
config.notifyAbort();
return;
}
// Check and warn if proxy is enabled.
if (this.getResponseHeader('Via') !== null) {
config.addToLog('WARNING: proxy seems enabled.');
}
calculateAndLogResult(config, size, benchmark.startTimeInMs, totalSize,
isWarmUp);
destroyAllXHRs();
runNextTask(config);
};
for (var i = 0; i < config.numXHRs; ++i) {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = onReadyStateChangeHandler;
xhrs.push(xhr);
}
var dataArray = [];
for (var i = 0; i < xhrs.length; ++i) {
var data = null;
if (config.dataType == 'arraybuffer' ||
config.dataType == 'blob') {
data = new ArrayBuffer(size);
fillArrayBuffer(data, 0x61);
if (config.dataType == 'blob') {
data = new Blob([data]);
}
} else {
data = repeatString('a', size);
}
dataArray.push(data);
}
benchmark.startTimeInMs = getTimeStamp();
totalSize = size * xhrs.length;
for (var i = 0; i < xhrs.length; ++i) {
var data = dataArray[i];
var xhr = xhrs[i];
xhr.open('POST', config.prefixUrl + '_send', config.async);
xhr.send(data);
}
}
function receiveBenchmarkStep(size, config, isWarmUp) {
timerID = null;
benchmark.startTimeInMs = null;
var totalSize = 0;
var totalReplied = 0;
var checkResultAndContinue = function (bytesReceived, verificationResult) {
if (!verificationResult) {
config.addToLog('Response verification failed');
destroyAllXHRs();
config.notifyAbort();
return;
}
totalReplied += bytesReceived;
if (totalReplied < totalSize) {
return;
}
if (benchmark.startTimeInMs == null) {
config.addToLog('startTimeInMs not set');
destroyAllXHRs();
config.notifyAbort();
return;
}
calculateAndLogResult(config, size, benchmark.startTimeInMs, totalSize,
isWarmUp);
destroyAllXHRs();
runNextTask(config);
}
var onReadyStateChangeHandler = function () {
if (this.readyState != this.DONE) {
return;
}
if (this.status != 200) {
config.addToLog('Failed (status=' + this.status + ')');
destroyAllXHRs();
config.notifyAbort();
return;
}
// Check and warn if proxy is enabled.
if (this.getResponseHeader('Via') !== null) {
config.addToLog('WARNING: proxy seems enabled.');
}
var bytesReceived = -1;
if (this.responseType == 'arraybuffer') {
bytesReceived = this.response.byteLength;
} else if (this.responseType == 'blob') {
bytesReceived = this.response.size;
} else {
bytesReceived = this.response.length;
}
if (bytesReceived != size) {
config.addToLog('Expected ' + size +
'B but received ' + bytesReceived + 'B');
destroyAllXHRs();
config.notifyAbort();
return;
}
if (this.responseType == 'arraybuffer') {
checkResultAndContinue(bytesReceived,
!config.verifyData || verifyArrayBuffer(this.response, 0x61));
} else if (this.responseType == 'blob') {
if (config.verifyData)
verifyBlob(config, this.response, 0x61, checkResultAndContinue);
else
checkResultAndContinue(bytesReceived, true);
} else {
checkResultAndContinue(
bytesReceived,
!config.verifyData ||
this.response == repeatString('a', this.response.length));
}
};
for (var i = 0; i < config.numXHRs; ++i) {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = onReadyStateChangeHandler;
xhrs.push(xhr);
}
benchmark.startTimeInMs = getTimeStamp();
totalSize = size * xhrs.length;
for (var i = 0; i < xhrs.length; ++i) {
var xhr = xhrs[i];
if (config.methodAndCache === 'GET-NOCACHE') {
xhr.open('GET', config.prefixUrl + '_receive_getnocache?' + size,
config.async);
xhr.responseType = config.dataType;
xhr.send();
} else if (config.methodAndCache === 'GET-CACHE') {
xhr.open('GET', config.prefixUrl + '_receive_getcache?' + size,
config.async);
xhr.responseType = config.dataType;
xhr.send();
} else {
xhr.open('POST', config.prefixUrl + '_receive', config.async);
xhr.responseType = config.dataType;
xhr.send(size + ' none');
}
}
}
function getConfigString(config) {
return '(' + config.dataType +
', verifyData=' + config.verifyData +
', ' + (isWorker ? 'Worker' : 'Main') +
', ' + (config.async ? 'Async' : 'Sync') +
', numXHRs=' + config.numXHRs +
', numIterations=' + config.numIterations +
', numWarmUpIterations=' + config.numWarmUpIterations +
')';
}
function startBenchmark(config) {
clearTimeout(timerID);
destroyAllXHRs();
runNextTask(config);
}
function batchBenchmark(originalConfig) {
originalConfig.addToLog('Batch benchmark');
tasks = [];
clearAverageData();
var dataTypes = ['text', 'blob', 'arraybuffer'];
var stepFuncs = [sendBenchmarkStep, receiveBenchmarkStep];
var names = ['Send', 'Receive'];
var async = [true, false];
for (var i = 0; i < stepFuncs.length; ++i) {
for (var j = 0; j < dataTypes.length; ++j) {
for (var k = 0; k < async.length; ++k) {
var config = cloneConfig(originalConfig);
config.dataType = dataTypes[j];
config.async = async[k];
// Receive && Non-Worker && Sync is not supported by the spec
if (stepFuncs[i] === receiveBenchmarkStep && !isWorker &&
!config.async)
continue;
addTasks(config, stepFuncs[i]);
addResultReportingTask(config,
names[i] + ' benchmark ' + getConfigString(config));
}
}
}
startBenchmark(config);
}
function cleanup() {
}

View file

@ -0,0 +1,123 @@
<!--
Copyright 2014 Google Inc. All rights reserved.
Use of this source code is governed by a BSD-style
license that can be found in the COPYING file or at
https://developers.google.com/open-source/licenses/bsd
-->
<html>
<head>
<title>XHR event logger</title>
<script src="util_main.js"></script>
<script>
var events = [];
var startTime = 0;
function run() {
events = [];
startTime = Date.now();
function pushToLog(type) {
var time = Date.now();
if (events.length != 0 && type === events[events.length - 1].type) {
events[events.length - 1].count += 1;
events[events.length - 1].last = time;
} else {
events.push({type: type, count: 1, first: time, last: time});
}
}
var xhr = new XMLHttpRequest();
function getProgressEventDump(e) {
return '(' + e.lengthComputable + ', ' + e.loaded + ', ' + e.total + ')';
}
var dumpProgressEvent = getBoolFromCheckBox('dumpprogressevent');
function log(e) {
var type = e.type;
if (type === 'readystatechange') {
type += e.target.readyState;
}
if (dumpProgressEvent && (e instanceof ProgressEvent)) {
type += getProgressEventDump(e);
}
pushToLog(type);
};
function logUpload(e) {
var type = e.type;
if (dumpProgressEvent && (e instanceof ProgressEvent)) {
type += getProgressEventDump(e);
}
pushToLog('upload' + type);
}
if (getBoolFromCheckBox('upload')) {
var upload = xhr.upload;
upload.onloadstart = logUpload;
upload.onprogress = logUpload;
upload.onabort = logUpload;
upload.onerror = logUpload;
upload.onload = logUpload;
upload.ontimeout = logUpload;
upload.onloadend = logUpload;
}
xhr.onreadystatechange = log;
xhr.onloadstart = log;
xhr.onprogress = log;
xhr.onabort = log;
xhr.onerror = log;
xhr.onload = log;
xhr.ontimeout = log;
xhr.onloadend = log;
xhr.open('POST', '/073be001e10950692ccbf3a2ad21c245_receive',
getBoolFromCheckBox('async'));
var size = getIntFromInput('size');
var chunkedMode = 'none';
if (getBoolFromCheckBox('chunkedresponse')) {
chunkedMode = 'chunked';
}
xhr.send(size + ' ' + chunkedMode);
}
function print() {
var result = '';
for (var i = 0; i < events.length; ++i) {
var event = events[i];
var line = '';
line += (event.first - startTime) + "ms";
if (event.count > 1)
line += "-" + (event.last - startTime) + "ms";
else
line += " ";
while(line.length < 15)
line += " ";
line += ": " + event.type + ' * ' + event.count + '\n';
result += line;
}
document.getElementById('log').value = result;
}
</script>
<body>
<textarea id="log" rows="10" cols="70" readonly></textarea>
<br/>
Size: <input type="text" id="size" value="65536"><br/>
<input type="checkbox" id="chunkedresponse">
<label for="chunkedresponse">Use Chunked T-E for response</label><br/>
<input type="checkbox" id="upload">
<label for="upload">Upload progress</label><br/>
<input type="checkbox" id="dumpprogressevent">
<label for="dumpprogressevent">
Dump lengthComputable/loaded/total</label><br/>
<input type="checkbox" id="async" checked>
<label for="async">Async</label><br/>
<input type="button" onclick="run()" value="Run XHR">
<input type="button" onclick="print()" value="Print log">
</body>
</html>

View file

@ -0,0 +1,6 @@
<!DOCTYPE html>
<head>
<script src="util.js"></script>
<script src="performance_test_iframe.js"></script>
<script src="xhr_benchmark.js"></script>
</head>