Bug 1427718 - Display multiple headers with the same name; r=rickychien

MozReview-Commit-ID: 2nV51xLyCni

--HG--
extra : rebase_source : c5d368d84ce4766fe40e4b750441e1d4474db504
This commit is contained in:
Jan Odvarko 2018-01-17 09:56:09 +01:00
parent ba2bcb9d99
commit b8ea144988
9 changed files with 123 additions and 24 deletions

View File

@ -20,7 +20,10 @@ const {
fetchNetworkUpdatePacket,
writeHeaderText,
} = require("../utils/request-utils");
const { sortObjectKeys } = require("../utils/sort-utils");
const {
HeadersProvider,
HeaderList,
} = require("../utils/headers-provider");
// Components
const PropertiesView = createFactory(require("./PropertiesView"));
@ -53,7 +56,7 @@ const SUMMARY_URL = L10N.getStr("netmonitor.summary.url");
const SUMMARY_STATUS = L10N.getStr("netmonitor.summary.status");
const SUMMARY_VERSION = L10N.getStr("netmonitor.summary.version");
/*
/**
* Headers panel component
* Lists basic information about the request
*/
@ -103,13 +106,8 @@ class HeadersPanel extends Component {
if (headers && headers.headers.length) {
let headerKey = `${title} (${getFormattedSize(headers.headersSize, 3)})`;
let propertiesResult = {
[headerKey]:
headers.headers.reduce((acc, { name, value }) =>
name ? Object.assign(acc, { [name]: value }) : acc
, {})
[headerKey]: new HeaderList(headers.headers)
};
propertiesResult[headerKey] = sortObjectKeys(propertiesResult[headerKey]);
return propertiesResult;
}
@ -302,6 +300,7 @@ class HeadersPanel extends Component {
),
PropertiesView({
object,
provider: HeadersProvider,
filterPlaceHolder: HEADERS_FILTER_TEXT,
sectionNames: Object.keys(object),
renderValue: this.renderValue,

View File

@ -42,7 +42,7 @@ const AUTO_EXPAND_MAX_NODES = 50;
const EDITOR_CONFIG_ID = "EDITOR_CONFIG";
const HTML_PREVIEW_ID = "HTML_PREVIEW";
/*
/**
* Properties View component
* A scrollable tree view component which provides some useful features for
* representing object properties.
@ -57,6 +57,7 @@ class PropertiesView extends Component {
static get propTypes() {
return {
object: PropTypes.object,
provider: PropTypes.object,
enableInput: PropTypes.bool,
expandableStrings: PropTypes.bool,
filterPlaceHolder: PropTypes.string,
@ -190,6 +191,7 @@ class PropertiesView extends Component {
renderValue,
sectionNames,
openLink,
provider,
} = this.props;
return (
@ -206,6 +208,7 @@ class PropertiesView extends Component {
div({ className: "tree-container" },
TreeView({
object,
provider,
columns: [{
id: "value",
width: "100%",

View File

@ -0,0 +1,86 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const { ObjectProvider } = require("devtools/client/shared/components/tree/ObjectProvider");
/**
* Custom tree provider.
*
* This provider is used to provide set of headers and is
* utilized by the HeadersPanel.
* The default ObjectProvider can't be used since it doesn't
* allow duplicities by design and so it can't support duplicity
* headers (more headers with the same name).
*/
var HeadersProvider = {
...ObjectProvider,
getChildren(object) {
if (object.value instanceof HeaderList) {
return object.value.headers.map((header, index) =>
new Header(header.name, header.value, index));
}
return ObjectProvider.getChildren(object);
},
hasChildren: function (object) {
if (object.value instanceof HeaderList) {
return object.value.headers.length > 0;
} else if (object instanceof Header) {
return false;
}
return ObjectProvider.hasChildren(object);
},
getLabel: function (object) {
if (object instanceof Header) {
return object.name;
}
return ObjectProvider.getLabel(object);
},
getValue: function (object) {
if (object instanceof Header) {
return object.value;
}
return ObjectProvider.getValue(object);
},
getKey(object) {
if (object instanceof Header) {
return object.key;
}
return ObjectProvider.getKey(object);
},
getType: function (object) {
if (object instanceof Header) {
return "string";
}
return ObjectProvider.getType(object);
}
};
/**
* Helper data structures for list of headers.
*/
function HeaderList(headers) {
this.headers = headers;
this.headers.sort((a, b) => {
return a.name.toLowerCase().localeCompare(b.name.toLowerCase());
});
}
function Header(name, value, key) {
this.name = name;
this.value = value;
this.key = key;
}
module.exports = {
HeadersProvider,
HeaderList
};

View File

@ -13,6 +13,7 @@ DevToolsModules(
'filter-predicates.js',
'filter-text-utils.js',
'format-utils.js',
'headers-provider.js',
'l10n.js',
'mdn-utils.js',
'menu.js',

View File

@ -5,6 +5,8 @@
/**
* Tests if Request-Headers and Response-Headers are sorted in Headers tab.
* The test also verifies that headers with the same name and headers
* with an empty value are also displayed.
*/
add_task(function* () {
let { tab, monitor } = yield initNetMonitor(SIMPLE_SJS);
@ -35,7 +37,8 @@ add_task(function* () {
info("Check if Request-Headers and Response-Headers are sorted");
let expectedResponseHeaders = ["cache-control", "connection", "content-length",
"content-type", "date", "expires", "foo-bar",
"pragma", "server", "set-cookie"];
"foo-bar", "foo-bar", "pragma", "server", "set-cookie",
"set-cookie"];
let expectedRequestHeaders = ["Accept", "Accept-Encoding", "Accept-Language",
"Cache-Control", "Connection", "Cookie", "Host",
"Pragma", "Upgrade-Insecure-Requests", "User-Agent"];

View File

@ -174,9 +174,9 @@ function test() {
ok(requestItem.responseHeaders,
"There should be a responseHeaders data available.");
is(requestItem.responseHeaders.headers.length, 10,
is(requestItem.responseHeaders.headers.length, 13,
"The responseHeaders data has an incorrect |headers| property.");
is(requestItem.responseHeaders.headersSize, 330,
is(requestItem.responseHeaders.headersSize, 335,
"The responseHeaders data has an incorrect |headersSize| property.");
verifyRequestItemTarget(
@ -228,7 +228,7 @@ function test() {
"The status data has an incorrect value.");
is(requestItem.statusText, "Och Aye",
"The statusText data has an incorrect value.");
is(requestItem.headersSize, 330,
is(requestItem.headersSize, 335,
"The headersSize data has an incorrect value.");
let requestListItem = document.querySelector(".request-list-item");
@ -261,7 +261,7 @@ function test() {
let requestItem = getSortedRequests(store.getState()).get(0);
is(requestItem.transferredSize, "342",
is(requestItem.transferredSize, "347",
"The transferredSize data has an incorrect value.");
is(requestItem.contentSize, "12",
"The contentSize data has an incorrect value.");

View File

@ -8,10 +8,14 @@ function handleRequest(request, response) {
response.setHeader("Pragma", "no-cache");
response.setHeader("Expires", "0");
response.setHeader("Set-Cookie", "bob=true; Max-Age=10; HttpOnly", true);
response.setHeader("Set-Cookie", "tom=cool; Max-Age=10; HttpOnly", true);
response.setHeaderNoCheck("Set-Cookie", "bob=true; Max-Age=10; HttpOnly");
response.setHeaderNoCheck("Set-Cookie", "tom=cool; Max-Age=10; HttpOnly");
response.setHeader("Content-Type", "text/plain; charset=utf-8", false);
response.setHeader("Foo-Bar", "baz", false);
response.setHeaderNoCheck("Foo-Bar", "baz");
response.setHeaderNoCheck("Foo-Bar", "baz");
response.setHeaderNoCheck("Foo-Bar", "");
response.write("Hello world!");
}

View File

@ -63,7 +63,7 @@ define(function (require, exports, module) {
*/
class TreeView extends Component {
// The only required property (not set by default) is the input data
// object that is used to puputate the tree.
// object that is used to populate the tree.
static get propTypes() {
return {
// The input data object.
@ -93,7 +93,7 @@ define(function (require, exports, module) {
renderRow: PropTypes.func,
// Custom cell renderer
renderCell: PropTypes.func,
// Custom value renderef
// Custom value renderer
renderValue: PropTypes.func,
// Custom tree label (including a toggle button) renderer
renderLabelCell: PropTypes.func,

View File

@ -879,13 +879,13 @@ NetworkMonitor.prototype = {
cookies: [],
};
let setCookieHeader = null;
let setCookieHeaders = [];
channel.visitResponseHeaders({
channel.visitOriginalResponseHeaders({
visitHeader: function (name, value) {
let lowerName = name.toLowerCase();
if (lowerName == "set-cookie") {
setCookieHeader = value;
setCookieHeaders.push(value);
}
response.headers.push({ name: name, value: value });
}
@ -896,8 +896,11 @@ NetworkMonitor.prototype = {
return;
}
if (setCookieHeader) {
response.cookies = NetworkHelper.parseSetCookieHeader(setCookieHeader);
if (setCookieHeaders.length) {
response.cookies = setCookieHeaders.reduce((result, header) => {
let cookies = NetworkHelper.parseSetCookieHeader(header);
return result.concat(cookies);
}, []);
}
// Determine the HTTP version.