211 lines
7.1 KiB
JavaScript
211 lines
7.1 KiB
JavaScript
|
/*
|
||
|
* Copyright (C) 2010 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.
|
||
|
*/
|
||
|
|
||
|
// Ideally, we would rely on platform support for parsing a cookie, since
|
||
|
// this would save us from any potential inconsistency. However, exposing
|
||
|
// platform cookie parsing logic would require quite a bit of additional
|
||
|
// plumbing, and at least some platforms lack support for parsing Cookie,
|
||
|
// which is in a format slightly different from Set-Cookie and is normally
|
||
|
// only required on the server side.
|
||
|
|
||
|
WebInspector.CookieParser = function()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
WebInspector.CookieParser.prototype = {
|
||
|
get cookies()
|
||
|
{
|
||
|
return this._cookies;
|
||
|
},
|
||
|
|
||
|
parseCookie: function(cookieHeader)
|
||
|
{
|
||
|
if (!this._initialize(cookieHeader))
|
||
|
return;
|
||
|
|
||
|
for (var kv = this._extractKeyValue(); kv; kv = this._extractKeyValue()) {
|
||
|
if (kv.key.charAt(0) === "$" && this._lastCookie)
|
||
|
this._lastCookie.addAttribute(kv.key.slice(1), kv.value);
|
||
|
else if (kv.key.toLowerCase() !== "$version" && typeof kv.value === "string")
|
||
|
this._addCookie(kv, WebInspector.Cookie.Type.Request);
|
||
|
this._advanceAndCheckCookieDelimiter();
|
||
|
}
|
||
|
this._flushCookie();
|
||
|
return this._cookies;
|
||
|
},
|
||
|
|
||
|
parseSetCookie: function(setCookieHeader)
|
||
|
{
|
||
|
if (!this._initialize(setCookieHeader))
|
||
|
return;
|
||
|
for (var kv = this._extractKeyValue(); kv; kv = this._extractKeyValue()) {
|
||
|
if (this._lastCookie)
|
||
|
this._lastCookie.addAttribute(kv.key, kv.value);
|
||
|
else
|
||
|
this._addCookie(kv, WebInspector.Cookie.Type.Response);
|
||
|
if (this._advanceAndCheckCookieDelimiter())
|
||
|
this._flushCookie();
|
||
|
}
|
||
|
this._flushCookie();
|
||
|
return this._cookies;
|
||
|
},
|
||
|
|
||
|
_initialize: function(headerValue)
|
||
|
{
|
||
|
this._input = headerValue;
|
||
|
if (typeof headerValue !== "string")
|
||
|
return false;
|
||
|
this._cookies = [];
|
||
|
this._lastCookie = null;
|
||
|
this._originalInputLength = this._input.length;
|
||
|
return true;
|
||
|
},
|
||
|
|
||
|
_flushCookie: function()
|
||
|
{
|
||
|
if (this._lastCookie)
|
||
|
this._lastCookie.size = this._originalInputLength - this._input.length - this._lastCookiePosition;
|
||
|
this._lastCookie = null;
|
||
|
},
|
||
|
|
||
|
_extractKeyValue: function()
|
||
|
{
|
||
|
if (!this._input || !this._input.length)
|
||
|
return null;
|
||
|
// Note: RFCs offer an option for quoted values that may contain commas and semicolons.
|
||
|
// Many browsers/platforms do not support this, however (see http://webkit.org/b/16699
|
||
|
// and http://crbug.com/12361). The logic below matches latest versions of IE, Firefox,
|
||
|
// Chrome and Safari on some old platforms. The latest version of Safari supports quoted
|
||
|
// cookie values, though.
|
||
|
var keyValueMatch = /^[ \t]*([^\s=;]+)[ \t]*(?:=[ \t]*([^;\n]*))?/.exec(this._input);
|
||
|
if (!keyValueMatch) {
|
||
|
console.log("Failed parsing cookie header before: " + this._input);
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
var result = {
|
||
|
key: keyValueMatch[1],
|
||
|
value: keyValueMatch[2] && keyValueMatch[2].trim(),
|
||
|
position: this._originalInputLength - this._input.length
|
||
|
};
|
||
|
this._input = this._input.slice(keyValueMatch[0].length);
|
||
|
return result;
|
||
|
},
|
||
|
|
||
|
_advanceAndCheckCookieDelimiter: function()
|
||
|
{
|
||
|
var match = /^\s*[\n;]\s*/.exec(this._input);
|
||
|
if (!match)
|
||
|
return false;
|
||
|
this._input = this._input.slice(match[0].length);
|
||
|
return match[0].match("\n") !== null;
|
||
|
},
|
||
|
|
||
|
_addCookie: function(keyValue, type)
|
||
|
{
|
||
|
if (this._lastCookie)
|
||
|
this._lastCookie.size = keyValue.position - this._lastCookiePosition;
|
||
|
// Mozilla bug 169091: Mozilla, IE and Chrome treat single token (w/o "=") as
|
||
|
// specifying a value for a cookie with empty name.
|
||
|
this._lastCookie = keyValue.value ? new WebInspector.Cookie(keyValue.key, keyValue.value, type) :
|
||
|
new WebInspector.Cookie("", keyValue.key, type);
|
||
|
this._lastCookiePosition = keyValue.position;
|
||
|
this._cookies.push(this._lastCookie);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
WebInspector.CookieParser.parseCookie = function(header)
|
||
|
{
|
||
|
return (new WebInspector.CookieParser()).parseCookie(header);
|
||
|
}
|
||
|
|
||
|
WebInspector.CookieParser.parseSetCookie = function(header)
|
||
|
{
|
||
|
return (new WebInspector.CookieParser()).parseSetCookie(header);
|
||
|
}
|
||
|
|
||
|
WebInspector.Cookie = function(name, value, type)
|
||
|
{
|
||
|
this.name = name;
|
||
|
this.value = value;
|
||
|
this.type = type;
|
||
|
this._attributes = {};
|
||
|
}
|
||
|
|
||
|
WebInspector.Cookie.prototype = {
|
||
|
get httpOnly()
|
||
|
{
|
||
|
return "httponly" in this._attributes;
|
||
|
},
|
||
|
|
||
|
get secure()
|
||
|
{
|
||
|
return "secure" in this._attributes;
|
||
|
},
|
||
|
|
||
|
get session()
|
||
|
{
|
||
|
// RFC 2965 suggests using Discard attribute to mark session cookies, but this does not seem to be widely used.
|
||
|
// Check for absence of explicity max-age or expiry date instead.
|
||
|
return !("expries" in this._attributes || "max-age" in this._attributes);
|
||
|
},
|
||
|
|
||
|
get path()
|
||
|
{
|
||
|
return this._attributes.path;
|
||
|
},
|
||
|
|
||
|
get domain()
|
||
|
{
|
||
|
return this._attributes.domain;
|
||
|
},
|
||
|
|
||
|
expires: function(requestDate)
|
||
|
{
|
||
|
return this._attributes.expires ? new Date(this._attributes.expires) :
|
||
|
(this._attributes["max-age"] ? new Date(requestDate.getTime() + 1000 * this._attributes["max-age"]) : null);
|
||
|
},
|
||
|
|
||
|
get attributes()
|
||
|
{
|
||
|
return this._attributes;
|
||
|
},
|
||
|
|
||
|
addAttribute: function(key, value)
|
||
|
{
|
||
|
this._attributes[key.toLowerCase()] = value;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
WebInspector.Cookie.Type = {
|
||
|
Request: 0,
|
||
|
Response: 1
|
||
|
};
|