196 lines
6.4 KiB
JavaScript
196 lines
6.4 KiB
JavaScript
/*
|
|
* Copyright (C) 2018 Apple Inc. All rights reserved.
|
|
* Copyright 2017 The Chromium Authors. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. 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.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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.
|
|
*/
|
|
|
|
WI.ServerTimingEntry = class ServerTimingEntry
|
|
{
|
|
constructor(name)
|
|
{
|
|
this._name = name;
|
|
this._duration = undefined;
|
|
this._description = undefined;
|
|
}
|
|
|
|
// Static
|
|
|
|
static parseHeaders(valueString = "")
|
|
{
|
|
// https://w3c.github.io/server-timing/#the-server-timing-header-field
|
|
function trimLeadingWhiteSpace() {
|
|
valueString = valueString.trimStart();
|
|
}
|
|
|
|
function consumeDelimiter(char) {
|
|
console.assert(char.length === 1);
|
|
trimLeadingWhiteSpace();
|
|
if (valueString.charAt(0) !== char)
|
|
return false;
|
|
|
|
valueString = valueString.substring(1);
|
|
return true;
|
|
}
|
|
|
|
function consumeToken() {
|
|
// https://tools.ietf.org/html/rfc7230#appendix-B
|
|
let result = /^(?:\s*)([\w!#$%&'*+\-.^`|~]+)(?:\s*)(.*)/.exec(valueString);
|
|
if (!result)
|
|
return null;
|
|
|
|
valueString = result[2];
|
|
return result[1];
|
|
}
|
|
|
|
function consumeTokenOrQuotedString() {
|
|
trimLeadingWhiteSpace();
|
|
if (valueString.charAt(0) === "\"")
|
|
return consumeQuotedString();
|
|
|
|
return consumeToken();
|
|
}
|
|
|
|
function consumeQuotedString() {
|
|
// https://tools.ietf.org/html/rfc7230#section-3.2.6
|
|
console.assert(valueString.charAt(0) === "\"");
|
|
|
|
// Consume the leading DQUOTE.
|
|
valueString = valueString.substring(1);
|
|
|
|
let unescapedValueString = "";
|
|
for (let i = 0; i < valueString.length; ++i) {
|
|
let char = valueString.charAt(i);
|
|
switch (char) {
|
|
case "\\":
|
|
// Backslash character found, ignore it.
|
|
++i;
|
|
if (i < valueString.length) {
|
|
// Take the character after the backslash.
|
|
unescapedValueString += valueString.charAt(i);
|
|
}
|
|
break;
|
|
case "\"":
|
|
// Trailing DQUOTE.
|
|
valueString = valueString.substring(i + 1);
|
|
return unescapedValueString;
|
|
default:
|
|
unescapedValueString += char;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// No trailing DQUOTE found, this was not a valid quoted-string. Consume the entire string to complete parsing.
|
|
valueString = "";
|
|
return null;
|
|
}
|
|
|
|
function consumeExtraneous() {
|
|
let result = /([,;].*)/.exec(valueString);
|
|
if (result)
|
|
valueString = result[1];
|
|
}
|
|
|
|
function getParserForParameter(paramName) {
|
|
switch (paramName) {
|
|
case "dur":
|
|
return function(paramValue, entry) {
|
|
if (paramValue !== null) {
|
|
let duration = parseFloat(paramValue);
|
|
if (!isNaN(duration)) {
|
|
entry.duration = duration;
|
|
return;
|
|
}
|
|
}
|
|
entry.duration = 0;
|
|
};
|
|
|
|
case "desc":
|
|
return function(paramValue, entry) {
|
|
entry.description = paramValue || "";
|
|
};
|
|
|
|
default:
|
|
return null;
|
|
}
|
|
}
|
|
|
|
let entries = [];
|
|
let name;
|
|
while ((name = consumeToken()) !== null) {
|
|
let entry = new WI.ServerTimingEntry(name);
|
|
|
|
while (consumeDelimiter(";")) {
|
|
let paramName;
|
|
if ((paramName = consumeToken()) === null)
|
|
continue;
|
|
|
|
paramName = paramName.toLowerCase();
|
|
let parseParameter = getParserForParameter(paramName);
|
|
let paramValue = null;
|
|
if (consumeDelimiter("=")) {
|
|
// Always parse the value, even if we don't recognize the parameter name.
|
|
paramValue = consumeTokenOrQuotedString();
|
|
consumeExtraneous();
|
|
}
|
|
|
|
if (parseParameter)
|
|
parseParameter(paramValue, entry);
|
|
else
|
|
console.warn("Unknown Server-Timing parameter:", paramName, paramValue);
|
|
}
|
|
|
|
entries.push(entry);
|
|
if (!consumeDelimiter(","))
|
|
break;
|
|
}
|
|
|
|
return entries;
|
|
}
|
|
|
|
// Public
|
|
|
|
get name() { return this._name; }
|
|
get duration() { return this._duration; }
|
|
get description() { return this._description; }
|
|
|
|
set duration(duration)
|
|
{
|
|
if (this._duration !== undefined) {
|
|
console.warn("Ignoring redundant duration.");
|
|
return;
|
|
}
|
|
|
|
this._duration = duration;
|
|
}
|
|
|
|
set description(description)
|
|
{
|
|
if (this._description !== undefined) {
|
|
console.warn("Ignoring redundant description.");
|
|
return;
|
|
}
|
|
|
|
this._description = description;
|
|
}
|
|
};
|