Web Inspector: Request / response headers should be stored in name-value pairs array...
authorvsevik@chromium.org <vsevik@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 14 May 2012 15:44:05 +0000 (15:44 +0000)
committervsevik@chromium.org <vsevik@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 14 May 2012 15:44:05 +0000 (15:44 +0000)
https://bugs.webkit.org/show_bug.cgi?id=86357

Reviewed by Pavel Feldman.

Source/WebCore:

Storing headers as name-value pairs array information more accurate and allows
to treat Set-Cookie headers (which become not parseable when joined by comma) correctly.

* inspector/front-end/AuditRules.js:
(WebInspector.AuditRules.GzipRule.prototype._isCompressed):
(WebInspector.AuditRules.CacheControlRule.prototype.responseHeader):
(WebInspector.AuditRules.CacheControlRule.prototype.hasResponseHeader):
(WebInspector.AuditRules.CacheControlRule.prototype.responseHeaderMatch):
* inspector/front-end/HAREntry.js:
(WebInspector.HAREntry.prototype._buildRequest):
(WebInspector.HAREntry.prototype._buildResponse):
* inspector/front-end/NetworkManager.js:
(WebInspector.NetworkDispatcher.prototype._headersMapToHeadersArray):
(WebInspector.NetworkDispatcher.prototype._updateNetworkRequestWithRequest):
(WebInspector.NetworkDispatcher.prototype._updateNetworkRequestWithResponse):
(WebInspector.NetworkDispatcher.prototype.webSocketWillSendHandshakeRequest):
(WebInspector.NetworkDispatcher.prototype.webSocketHandshakeResponseReceived):
* inspector/front-end/NetworkRequest.js:
(WebInspector.NetworkRequest.prototype.get transferSize):
(WebInspector.NetworkRequest.prototype.get requestHeaders):
(WebInspector.NetworkRequest.prototype.get requestHeadersText):
(WebInspector.NetworkRequest.prototype.get responseHeaders):
(WebInspector.NetworkRequest.prototype.get responseHeadersText):
(WebInspector.NetworkRequest.prototype._headerValue):
* inspector/front-end/RequestHeadersView.js:
(WebInspector.RequestHeadersView.prototype._refreshRequestHeaders):
(WebInspector.RequestHeadersView.prototype._refreshResponseHeaders):
(WebInspector.RequestHeadersView.prototype._refreshHeaders):
* platform/chromium/support/WebHTTPLoadInfo.cpp:
(WebKit::addHeader):

LayoutTests:

* http/tests/inspector/resource-har-conversion.html:
* http/tests/inspector/resource-har-headers-expected.txt:
* http/tests/inspector/resource-har-headers.html:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@116952 268f45cc-cd09-0410-ab3c-d52691b4dbfc

LayoutTests/ChangeLog
LayoutTests/http/tests/inspector/resource-har-conversion.html
LayoutTests/http/tests/inspector/resource-har-headers-expected.txt
LayoutTests/http/tests/inspector/resource-har-headers.html
Source/WebCore/ChangeLog
Source/WebCore/inspector/front-end/AuditRules.js
Source/WebCore/inspector/front-end/HAREntry.js
Source/WebCore/inspector/front-end/NetworkManager.js
Source/WebCore/inspector/front-end/NetworkRequest.js
Source/WebCore/inspector/front-end/RequestHeadersView.js
Source/WebCore/platform/chromium/support/WebHTTPLoadInfo.cpp

index f500a54..30c0029 100644 (file)
@@ -1,3 +1,14 @@
+2012-05-14  Vsevolod Vlasov  <vsevik@chromium.org>
+
+        Web Inspector: Request / response headers should be stored in name-value pairs array, not a map on front-end.
+        https://bugs.webkit.org/show_bug.cgi?id=86357
+
+        Reviewed by Pavel Feldman.
+
+        * http/tests/inspector/resource-har-conversion.html:
+        * http/tests/inspector/resource-har-headers-expected.txt:
+        * http/tests/inspector/resource-har-headers.html:
+
 2012-05-14  Christophe Dumez  <christophe.dumez@intel.com>
 
         [EFL] fast/loader/null-request-after-willSendRequest.html and fast/loader/recursive-before-unload-crash.html can be unskipped
index 75353b8..e27f7e8 100644 (file)
@@ -36,13 +36,13 @@ var test = function()
 
         function addCookieHeadersToRequest(request)
         {
-            request.requestHeaders = {
-                "Cookie": "a=b; $Path=/path; $Domain=example.com; a1=b1\nc1=d1"
-            };
+            request.requestHeaders = [
+                { name: "Cookie", value: "a=b; $Path=/path; $Domain=example.com; a1=b1\nc1=d1" }
+            ];
 
-            request.responseHeaders = {
-                "Set-Cookie": "x=y; Path=/path; Domain=example.com; Discard; httpOnly; Secure; Version=1\nx1=y1\nz2=y2"
-            };
+            request.responseHeaders = [
+                { name: "Set-Cookie", value: "x=y; Path=/path; Domain=example.com; Discard; httpOnly; Secure; Version=1\nx1=y1\nz2=y2" }
+            ];
         }
 
         addCookieHeadersToRequest(findRequestByURL(/inspector-test\.js$/));
index d64961a..a8c2bb9 100644 (file)
@@ -2,16 +2,22 @@ Tests the nondeterministic bits of HAR conversion via the magic of hard-coded va
 
 Resource:{
     request : {
-        headers : {
-            Request : "request-value"
-        }
+        headers : [
+            {
+                name : "Request"
+                value : "request-value"
+            }
+        ]
         headersText : "GET http://example.com/inspector-test.js HTTP/1.1\r\nRequest: headers-text"
         headersSize : 72
     }
     response : {
-        headers : {
-            Response : "response-value"
-        }
+        headers : [
+            {
+                name : "Response"
+                value : "response-value"
+            }
+        ]
         headersText : "HTTP/1.1 200 OK\r\nResponse: headers-text"
         headersSize : 39
         resourceSize : 1000
index 6dbda15..549db69 100644 (file)
@@ -14,14 +14,14 @@ var test = function()
 
     function setRequestValues(request)
     {
-        request.requestHeaders = {
-            "Request": "request-value"
-        };
+        request.requestHeaders = [
+            { name: "Request", value: "request-value" }
+        ];
         request.requestHeadersText = "GET http://example.com/inspector-test.js HTTP/1.1\r\nRequest: headers-text";
 
-        request.responseHeaders = {
-            "Response": "response-value"
-        };
+        request.responseHeaders = [
+            { name: "Response", value: "response-value" }
+        ];
         request.responseHeadersText = "HTTP/1.1 200 OK\r\nResponse: headers-text";
 
         request.documentURL = "http://example.com/inspector-test.js";
index d61eff2..01bd30b 100644 (file)
@@ -1,3 +1,41 @@
+2012-05-14  Vsevolod Vlasov  <vsevik@chromium.org>
+
+        Web Inspector: Request / response headers should be stored in name-value pairs array, not a map on front-end.
+        https://bugs.webkit.org/show_bug.cgi?id=86357
+
+        Reviewed by Pavel Feldman.
+
+        Storing headers as name-value pairs array information more accurate and allows
+        to treat Set-Cookie headers (which become not parseable when joined by comma) correctly.
+
+        * inspector/front-end/AuditRules.js:
+        (WebInspector.AuditRules.GzipRule.prototype._isCompressed):
+        (WebInspector.AuditRules.CacheControlRule.prototype.responseHeader):
+        (WebInspector.AuditRules.CacheControlRule.prototype.hasResponseHeader):
+        (WebInspector.AuditRules.CacheControlRule.prototype.responseHeaderMatch):
+        * inspector/front-end/HAREntry.js:
+        (WebInspector.HAREntry.prototype._buildRequest):
+        (WebInspector.HAREntry.prototype._buildResponse):
+        * inspector/front-end/NetworkManager.js:
+        (WebInspector.NetworkDispatcher.prototype._headersMapToHeadersArray):
+        (WebInspector.NetworkDispatcher.prototype._updateNetworkRequestWithRequest):
+        (WebInspector.NetworkDispatcher.prototype._updateNetworkRequestWithResponse):
+        (WebInspector.NetworkDispatcher.prototype.webSocketWillSendHandshakeRequest):
+        (WebInspector.NetworkDispatcher.prototype.webSocketHandshakeResponseReceived):
+        * inspector/front-end/NetworkRequest.js:
+        (WebInspector.NetworkRequest.prototype.get transferSize):
+        (WebInspector.NetworkRequest.prototype.get requestHeaders):
+        (WebInspector.NetworkRequest.prototype.get requestHeadersText):
+        (WebInspector.NetworkRequest.prototype.get responseHeaders):
+        (WebInspector.NetworkRequest.prototype.get responseHeadersText):
+        (WebInspector.NetworkRequest.prototype._headerValue):
+        * inspector/front-end/RequestHeadersView.js:
+        (WebInspector.RequestHeadersView.prototype._refreshRequestHeaders):
+        (WebInspector.RequestHeadersView.prototype._refreshResponseHeaders):
+        (WebInspector.RequestHeadersView.prototype._refreshHeaders):
+        * platform/chromium/support/WebHTTPLoadInfo.cpp:
+        (WebKit::addHeader):
+
 2012-05-14  Sriram Neelakandan  <sriram.neelakandan@gmail.com>
 
         [Gtk][DOM Bindings] Feature-protected interface usage in set/get property must be under condition guards
index e93fbbe..a6459b2 100644 (file)
@@ -107,7 +107,7 @@ WebInspector.AuditRules.GzipRule.prototype = {
 
     _isCompressed: function(request)
     {
-        var encodingHeader = request.responseHeaders["Content-Encoding"];
+        var encodingHeader = request.responseHeaderValue("Content-Encoding");
         if (!encodingHeader)
             return false;
 
@@ -517,12 +517,12 @@ WebInspector.AuditRules.CacheControlRule.prototype = {
 
     responseHeader: function(request, header)
     {
-        return request.responseHeaders[header];
+        return request.responseHeaderValue(header);
     },
 
     hasResponseHeader: function(request, header)
     {
-        return request.responseHeaders[header] !== undefined;
+        return request.responseHeaderValue(header) !== undefined;
     },
 
     isCompressible: function(request)
@@ -543,8 +543,8 @@ WebInspector.AuditRules.CacheControlRule.prototype = {
 
     responseHeaderMatch: function(request, header, regexp)
     {
-        return request.responseHeaders[header]
-            ? request.responseHeaders[header].match(new RegExp(regexp, "im"))
+        return request.responseHeaderValue(header)
+            ? request.responseHeaderValue(header).match(new RegExp(regexp, "im"))
             : undefined;
     },
 
index f541b23..eea5339 100644 (file)
@@ -72,7 +72,7 @@ WebInspector.HAREntry.prototype = {
             method: this._request.requestMethod,
             url: this._buildRequestURL(this._request.url),
             httpVersion: this._request.requestHttpVersion,
-            headers: this._buildHeaders(this._request.requestHeaders),
+            headers: this._request.requestHeaders,
             queryString: this._buildParameters(this._request.queryParameters || []),
             cookies: this._buildCookies(this._request.requestCookies || []),
             headersSize: this._request.requestHeadersSize,
@@ -93,7 +93,7 @@ WebInspector.HAREntry.prototype = {
             status: this._request.statusCode,
             statusText: this._request.statusText,
             httpVersion: this._request.responseHttpVersion,
-            headers: this._buildHeaders(this._request.responseHeaders),
+            headers: this._request.responseHeaders,
             cookies: this._buildCookies(this._request.responseCookies || []),
             content: this._buildContent(),
             redirectURL: this._request.responseHeaderValue("Location") || "",
@@ -157,17 +157,6 @@ WebInspector.HAREntry.prototype = {
     /**
      * @return {Object}
      */
-    _buildHeaders: function(headers)
-    {
-        var result = [];
-        for (var name in headers)
-            result.push({ name: name, value: headers[name] });
-        return result;
-    },
-
-    /**
-     * @return {Object}
-     */
     _buildPostData: function()
     {
         var res = {
index 4c3915f..a8f0770 100644 (file)
@@ -152,13 +152,28 @@ WebInspector.NetworkDispatcher = function(manager)
 
 WebInspector.NetworkDispatcher.prototype = {
     /**
+     * @param {NetworkAgent.Headers} headersMap
+     * @return {Array.<Object>}
+     */
+    _headersMapToHeadersArray: function(headersMap)
+    {
+        var result = [];
+        for (var name in headersMap) {
+            var values = headersMap[name].split("\n");
+            for (var i = 0; i < values.length; ++i)
+                result.push({ name: name, value: values[i] });
+        }
+        return result;
+    },
+
+    /**
      * @param {WebInspector.NetworkRequest} networkRequest
      * @param {NetworkAgent.Request} request
      */
     _updateNetworkRequestWithRequest: function(networkRequest, request)
     {
         networkRequest.requestMethod = request.method;
-        networkRequest.requestHeaders = request.headers;
+        networkRequest.requestHeaders = this._headersMapToHeadersArray(request.headers);
         networkRequest.requestFormData = request.postData;
     },
 
@@ -176,11 +191,11 @@ WebInspector.NetworkDispatcher.prototype = {
         networkRequest.mimeType = response.mimeType;
         networkRequest.statusCode = response.status;
         networkRequest.statusText = response.statusText;
-        networkRequest.responseHeaders = response.headers;
+        networkRequest.responseHeaders = this._headersMapToHeadersArray(response.headers);
         if (response.headersText)
             networkRequest.responseHeadersText = response.headersText;
         if (response.requestHeaders)
-            networkRequest.requestHeaders = response.requestHeaders;
+            networkRequest.requestHeaders = this._headersMapToHeadersArray(response.requestHeaders);
         if (response.requestHeadersText)
             networkRequest.requestHeadersText = response.requestHeadersText;
 
@@ -426,7 +441,7 @@ WebInspector.NetworkDispatcher.prototype = {
             return;
 
         networkRequest.requestMethod = "GET";
-        networkRequest.requestHeaders = request.headers;
+        networkRequest.requestHeaders = this._headersMapToHeadersArray(request.headers);
         networkRequest.webSocketRequestKey3 = request.requestKey3;
         networkRequest.startTime = time;
 
@@ -446,7 +461,7 @@ WebInspector.NetworkDispatcher.prototype = {
 
         networkRequest.statusCode = response.status;
         networkRequest.statusText = response.statusText;
-        networkRequest.responseHeaders = response.headers;
+        networkRequest.responseHeaders = this._headersMapToHeadersArray(response.headers);
         networkRequest.webSocketChallengeResponse = response.challengeResponse;
         networkRequest.responseReceivedTime = time;
 
index 577050f..476c0f1 100644 (file)
@@ -240,7 +240,7 @@ WebInspector.NetworkRequest.prototype = {
         // resourceSize when we don't have Content-Length. This still won't
         // work for chunks with non-trivial encodings. We need a way to
         // get actual transfer size from the network stack.
-        var bodySize = Number(this.responseHeaders["Content-Length"] || this.resourceSize);
+        var bodySize = Number(this.responseHeaderValue("Content-Length") || this.resourceSize);
         return this.responseHeadersSize + bodySize;
     },
 
@@ -399,11 +399,11 @@ WebInspector.NetworkRequest.prototype = {
     },
 
     /**
-     * @return {Object}
+     * @return {Array.<Object>}
      */
     get requestHeaders()
     {
-        return this._requestHeaders || {};
+        return this._requestHeaders || [];
     },
 
     set requestHeaders(x)
@@ -422,8 +422,8 @@ WebInspector.NetworkRequest.prototype = {
     {
         if (this._requestHeadersText === undefined) {
             this._requestHeadersText = this.requestMethod + " " + this.url + " HTTP/1.1\r\n";
-            for (var key in this.requestHeaders)
-                this._requestHeadersText += key + ": " + this.requestHeaders[key] + "\r\n";
+            for (var i = 0; i < this.requestHeaders; ++i)
+                this._requestHeadersText += this.requestHeaders[i].name + ": " + this.requestHeaders[i].value + "\r\n";
         }
         return this._requestHeadersText;
     },
@@ -452,10 +452,8 @@ WebInspector.NetworkRequest.prototype = {
             return this._sortedRequestHeaders;
 
         this._sortedRequestHeaders = [];
-        for (var key in this.requestHeaders)
-            this._sortedRequestHeaders.push({header: key, value: this.requestHeaders[key]});
-        this._sortedRequestHeaders.sort(function(a,b) { return a.header.localeCompare(b.header) });
-
+        this._sortedRequestHeaders = this.requestHeaders.slice();
+        this._sortedRequestHeaders.sort(function(a,b) { return a.name.toLowerCase().localeCompare(b.name.toLowerCase()) });
         return this._sortedRequestHeaders;
     },
 
@@ -503,11 +501,11 @@ WebInspector.NetworkRequest.prototype = {
     },
 
     /**
-     * @return {Object}
+     * @return {Array.<Object>}
      */
     get responseHeaders()
     {
-        return this._responseHeaders || {};
+        return this._responseHeaders || [];
     },
 
     set responseHeaders(x)
@@ -526,8 +524,8 @@ WebInspector.NetworkRequest.prototype = {
     {
         if (this._responseHeadersText === undefined) {
             this._responseHeadersText = "HTTP/1.1 " + this.statusCode + " " + this.statusText + "\r\n";
-            for (var key in this.responseHeaders)
-                this._responseHeadersText += key + ": " + this.responseHeaders[key] + "\r\n";
+            for (var i = 0; i < this.requestHeaders; ++i)
+                this._responseHeadersText += this.responseHeaders[i].name + ": " + this.responseHeaders[i].value + "\r\n";
         }
         return this._responseHeadersText;
     },
@@ -554,12 +552,10 @@ WebInspector.NetworkRequest.prototype = {
     {
         if (this._sortedResponseHeaders !== undefined)
             return this._sortedResponseHeaders;
-
+        
         this._sortedResponseHeaders = [];
-        for (var key in this.responseHeaders)
-            this._sortedResponseHeaders.push({header: key, value: this.responseHeaders[key]});
-        this._sortedResponseHeaders.sort(function(a,b) { return a.header.localeCompare(b.header) });
-
+        this._sortedResponseHeaders = this.responseHeaders.slice();
+        this._sortedResponseHeaders.sort(function(a,b) { return a.name.toLowerCase().localeCompare(b.name.toLowerCase()) });
         return this._sortedResponseHeaders;
     },
 
@@ -651,10 +647,16 @@ WebInspector.NetworkRequest.prototype = {
     _headerValue: function(headers, headerName)
     {
         headerName = headerName.toLowerCase();
-        for (var header in headers) {
-            if (header.toLowerCase() === headerName)
-                return headers[header];
+        
+        var values = [];
+        for (var i = 0; i < headers.length; ++i) {
+            if (headers[i].name.toLowerCase() === headerName)
+                values.push(headers[i].value);
         }
+        // Set-Cookie values should be separated by '\n', not comma, otherwise cookies could not be parsed.
+        if (headerName === "set-cookie")
+            return values.join("\n");
+        return values.join(", ");
     },
 
     /**
index 3118b37..75cb024 100644 (file)
@@ -269,7 +269,7 @@ WebInspector.RequestHeadersView.prototype = {
     {
         var additionalRow = null;
         if (typeof this._request.webSocketRequestKey3 !== "undefined")
-            additionalRow = {header: "(Key3)", value: this._request.webSocketRequestKey3};
+            additionalRow = {name: "(Key3)", value: this._request.webSocketRequestKey3};
         if (this._showRequestHeadersText)
             this._refreshHeadersText(WebInspector.UIString("Request Headers"), this._request.sortedRequestHeaders, this._request.requestHeadersText, this._requestHeadersTreeElement);
         else
@@ -288,7 +288,7 @@ WebInspector.RequestHeadersView.prototype = {
     {
         var additionalRow = null;
         if (typeof this._request.webSocketChallengeResponse !== "undefined")
-            additionalRow = {header: "(Challenge Response)", value: this._request.webSocketChallengeResponse};
+            additionalRow = {name: "(Challenge Response)", value: this._request.webSocketChallengeResponse};
         if (this._showResponseHeadersText)
             this._refreshHeadersText(WebInspector.UIString("Response Headers"), this._request.sortedResponseHeaders, this._request.responseHeadersText, this._responseHeadersTreeElement);
         else
@@ -354,14 +354,14 @@ WebInspector.RequestHeadersView.prototype = {
         headersTreeElement.hidden = !length;
         for (var i = 0; i < length; ++i) {
             var headerTreeElement = new TreeElement(null, null, false);
-            headerTreeElement.title = this._formatHeader(headers[i].header, headers[i].value);
+            headerTreeElement.title = this._formatHeader(headers[i].name, headers[i].value);
             headerTreeElement.selectable = false;
             headersTreeElement.appendChild(headerTreeElement);
         }
 
         if (additionalRow) {
             var headerTreeElement = new TreeElement(null, null, false);
-            headerTreeElement.title = this._formatHeader(additionalRow.header, additionalRow.value);
+            headerTreeElement.title = this._formatHeader(additionalRow.name, additionalRow.value);
             headerTreeElement.selectable = false;
             headersTreeElement.appendChild(headerTreeElement);
         }
index d7d57f2..7762fd8 100644 (file)
@@ -104,8 +104,9 @@ void WebHTTPLoadInfo::setEncodedDataLength(long long encodedDataLength)
 static void addHeader(HTTPHeaderMap* map, const WebString& name, const WebString& value)
 {
     HTTPHeaderMap::AddResult result = map->add(name, value);
+    // It is important that values are separated by '\n', not comma, otherwise Set-Cookie header is not parseable.
     if (!result.isNewEntry)
-        result.iterator->second += "" + String(value);
+        result.iterator->second += "\n" + String(value);
 }
 
 void WebHTTPLoadInfo::addRequestHeader(const WebString& name, const WebString& value)