[Network, DOM] Align our implementation with the current CORS/Fetch spec.

This commit is contained in:
Moonchild 2022-11-18 20:00:59 +00:00 • committed by roytam1
commit 37f0199c79
9 changed files with 366 additions and 31 deletions

View file

@ -7058,16 +7058,26 @@ nsContentUtils::HasDistributedChildren(nsIContent* aContent)
// static // static
bool bool
nsContentUtils::IsForbiddenRequestHeader(const nsACString& aHeader) nsContentUtils::IsForbiddenRequestHeader(const nsACString& aHeader,
const nsACString& aValue)
{ {
if (IsForbiddenSystemRequestHeader(aHeader)) { if (IsForbiddenSystemRequestHeader(aHeader)) {
return true; return true;
} }
if ((nsContentUtils::IsOverrideMethodHeader(aHeader) &&
nsContentUtils::ContainsForbiddenMethod(aValue))) {
return true;
}
return StringBeginsWith(aHeader, NS_LITERAL_CSTRING("proxy-"), if (StringBeginsWith(aHeader, NS_LITERAL_CSTRING("proxy-"),
nsCaseInsensitiveCStringComparator()) || nsCaseInsensitiveCStringComparator()) ||
StringBeginsWith(aHeader, NS_LITERAL_CSTRING("sec-"), StringBeginsWith(aHeader, NS_LITERAL_CSTRING("sec-"),
nsCaseInsensitiveCStringComparator()); nsCaseInsensitiveCStringComparator())) {
return true;
}
return false;
} }
// static // static
@ -7096,6 +7106,64 @@ nsContentUtils::IsForbiddenResponseHeader(const nsACString& aHeader)
aHeader.LowerCaseEqualsASCII("set-cookie2")); aHeader.LowerCaseEqualsASCII("set-cookie2"));
} }
// static
bool
nsContentUtils::IsOverrideMethodHeader(const nsACString& headerName) {
return headerName.LowerCaseEqualsASCII("x-http-method-override") ||
headerName.LowerCaseEqualsASCII("x-http-method") ||
headerName.LowerCaseEqualsASCII("x-method-override");
}
// static
bool
nsContentUtils::ContainsForbiddenMethod(const nsACString& headerValue) {
bool hasInsecureMethod = false;
nsCCharSeparatedTokenizer tokenizer(headerValue, ',');
while (tokenizer.hasMoreTokens()) {
const nsDependentCSubstring& value = tokenizer.nextToken();
if (value.LowerCaseEqualsASCII("connect") ||
value.LowerCaseEqualsASCII("trace") ||
value.LowerCaseEqualsASCII("track")) {
hasInsecureMethod = true;
break;
}
}
return hasInsecureMethod;
}
// static
bool nsContentUtils::IsCorsUnsafeRequestHeaderValue(
const nsACString& aHeaderValue) {
const char* cur = aHeaderValue.BeginReading();
const char* end = aHeaderValue.EndReading();
while (cur != end) {
// Implementation of
// https://fetch.spec.whatwg.org/#cors-unsafe-request-header-byte Is less
// than a space but not a horizontal tab
if ((*cur < ' ' && *cur != '\t') || *cur == '"' || *cur == '(' ||
*cur == ')' || *cur == ':' || *cur == '<' || *cur == '>' ||
*cur == '?' || *cur == '@' || *cur == '[' || *cur == '\\' ||
*cur == ']' || *cur == '{' || *cur == '}' ||
*cur == 0x7F) { // 0x75 is DEL
return true;
}
cur++;
}
return false;
}
// static
bool nsContentUtils::IsAllowedNonCorsAccept(const nsACString& aHeaderValue) {
if (IsCorsUnsafeRequestHeaderValue(aHeaderValue)) {
return false;
}
return true;
}
// static // static
bool bool
nsContentUtils::IsAllowedNonCorsContentType(const nsACString& aHeaderValue) nsContentUtils::IsAllowedNonCorsContentType(const nsACString& aHeaderValue)
@ -7103,6 +7171,10 @@ nsContentUtils::IsAllowedNonCorsContentType(const nsACString& aHeaderValue)
nsAutoCString contentType; nsAutoCString contentType;
nsAutoCString unused; nsAutoCString unused;
if (IsCorsUnsafeRequestHeaderValue(aHeaderValue)) {
return false;
}
nsresult rv = NS_ParseRequestContentType(aHeaderValue, contentType, unused); nsresult rv = NS_ParseRequestContentType(aHeaderValue, contentType, unused);
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
return false; return false;
@ -7113,6 +7185,41 @@ nsContentUtils::IsAllowedNonCorsContentType(const nsACString& aHeaderValue)
contentType.LowerCaseEqualsLiteral("multipart/form-data"); contentType.LowerCaseEqualsLiteral("multipart/form-data");
} }
// static
bool nsContentUtils::IsAllowedNonCorsLanguage(const nsACString& aHeaderValue) {
const char* cur = aHeaderValue.BeginReading();
const char* end = aHeaderValue.EndReading();
while (cur != end) {
if ((*cur >= '0' && *cur <= '9') || (*cur >= 'A' && *cur <= 'Z') ||
(*cur >= 'a' && *cur <= 'z') || *cur == ' ' || *cur == '*' ||
*cur == ',' || *cur == '-' || *cur == '.' || *cur == ';' ||
*cur == '=') {
cur++;
continue;
}
return false;
}
return true;
}
// static
bool nsContentUtils::IsCORSSafelistedRequestHeader(const nsACString& aName,
const nsACString& aValue) {
// See https://fetch.spec.whatwg.org/#cors-safelisted-request-header
if (aValue.Length() > 128) {
return false;
}
return (aName.LowerCaseEqualsLiteral("accept") &&
nsContentUtils::IsAllowedNonCorsAccept(aValue)) ||
(aName.LowerCaseEqualsLiteral("accept-language") &&
nsContentUtils::IsAllowedNonCorsLanguage(aValue)) ||
(aName.LowerCaseEqualsLiteral("content-language") &&
nsContentUtils::IsAllowedNonCorsLanguage(aValue)) ||
(aName.LowerCaseEqualsLiteral("content-type") &&
nsContentUtils::IsAllowedNonCorsContentType(aValue));
}
bool bool
nsContentUtils::DOMWindowDumpEnabled() nsContentUtils::DOMWindowDumpEnabled()
{ {

View file

@ -2425,7 +2425,8 @@ public:
* Returns whether a given header is forbidden for an XHR or fetch * Returns whether a given header is forbidden for an XHR or fetch
* request. * request.
*/ */
static bool IsForbiddenRequestHeader(const nsACString& aHeader); static bool IsForbiddenRequestHeader(const nsACString& aHeader,
const nsACString& aValue);
/** /**
* Returns whether a given header is forbidden for a system XHR * Returns whether a given header is forbidden for a system XHR
@ -2433,18 +2434,50 @@ public:
*/ */
static bool IsForbiddenSystemRequestHeader(const nsACString& aHeader); static bool IsForbiddenSystemRequestHeader(const nsACString& aHeader);
/**
* Returns whether a given header has characters that aren't permitted
*/
static bool IsCorsUnsafeRequestHeaderValue(const nsACString& aHeaderValue);
/**
* Returns whether a given Accept header value is allowed
* for a non-CORS XHR or fetch request.
*/
static bool IsAllowedNonCorsAccept(const nsACString& aHeaderValue);
/** /**
* Returns whether a given Content-Type header value is allowed * Returns whether a given Content-Type header value is allowed
* for a non-CORS XHR or fetch request. * for a non-CORS XHR or fetch request.
*/ */
static bool IsAllowedNonCorsContentType(const nsACString& aHeaderValue); static bool IsAllowedNonCorsContentType(const nsACString& aHeaderValue);
/**
* Returns whether a given Content-Language or accept-language header value is
* allowed for a non-CORS XHR or fetch request.
*/
static bool IsAllowedNonCorsLanguage(const nsACString& aHeaderValue);
/**
* Returns whether a given header and value is a CORS-safelisted request
* header per https://fetch.spec.whatwg.org/#cors-safelisted-request-header
*/
static bool IsCORSSafelistedRequestHeader(const nsACString& aName,
const nsACString& aValue);
/** /**
* Returns whether a given header is forbidden for an XHR or fetch * Returns whether a given header is forbidden for an XHR or fetch
* response. * response.
*/ */
static bool IsForbiddenResponseHeader(const nsACString& aHeader); static bool IsForbiddenResponseHeader(const nsACString& aHeader);
/**
* Checks whether the header overrides any http methods
*/
static bool IsOverrideMethodHeader(const nsACString& headerName);
/**
* Checks whether the header value contains any forbidden method
*/
static bool ContainsForbiddenMethod(const nsACString& headerValue);
/** /**
* Returns the inner window ID for the window associated with a request, * Returns the inner window ID for the window associated with a request,
*/ */

View file

@ -5,6 +5,7 @@
#include "mozilla/dom/InternalHeaders.h" #include "mozilla/dom/InternalHeaders.h"
#include "FetchUtil.h"
#include "mozilla/dom/FetchTypes.h" #include "mozilla/dom/FetchTypes.h"
#include "mozilla/ErrorResult.h" #include "mozilla/ErrorResult.h"
@ -46,20 +47,112 @@ InternalHeaders::ToIPC(nsTArray<HeadersEntry>& aIPCHeaders,
} }
} }
bool
InternalHeaders::IsValidHeaderValue(const nsCString& aLowerName,
const nsCString& aNormalizedValue,
ErrorResult& aRv) {
// Steps 2 to 6 for ::Set() and ::Append() in the spec.
// Step 2
if (IsInvalidName(aLowerName, aRv) || IsInvalidValue(aNormalizedValue, aRv)) {
return false;
}
// Step 3
if (IsImmutable(aRv)) {
return false;
}
// Step 4
if (mGuard == HeadersGuardEnum::Request) {
if (IsForbiddenRequestHeader(aLowerName, aNormalizedValue)) {
return false;
}
}
// Step 5
if (mGuard == HeadersGuardEnum::Request_no_cors) {
nsAutoCString tempValue;
Get(aLowerName, tempValue, aRv);
if (tempValue.IsVoid()) {
tempValue = aNormalizedValue;
} else {
tempValue.Append(", ");
tempValue.Append(aNormalizedValue);
}
if (!nsContentUtils::IsCORSSafelistedRequestHeader(aLowerName, tempValue)) {
return false;
}
}
// Step 6
else if (IsForbiddenResponseHeader(aLowerName)) {
return false;
}
return true;
}
void void
InternalHeaders::Append(const nsACString& aName, const nsACString& aValue, InternalHeaders::Append(const nsACString& aName, const nsACString& aValue,
ErrorResult& aRv) ErrorResult& aRv)
{ {
// Step 1
nsAutoCString trimValue;
NS_TrimHTTPWhitespace(aValue, trimValue);
// Steps 2 to 6
nsAutoCString lowerName; nsAutoCString lowerName;
ToLowerCase(aName, lowerName); ToLowerCase(aName, lowerName);
if (!IsValidHeaderValue(lowerName, trimValue, aRv)) {
if (IsInvalidMutableHeader(lowerName, aValue, aRv)) {
return; return;
} }
// Step 7
SetListDirty(); SetListDirty();
mList.AppendElement(Entry(lowerName, aValue)); mList.AppendElement(Entry(lowerName, aValue));
// Step 8
if (mGuard == HeadersGuardEnum::Request_no_cors) {
RemovePrivilegedNoCorsRequestHeaders();
}
}
void InternalHeaders::RemovePrivilegedNoCorsRequestHeaders() {
bool dirty = false;
// remove in reverse order to minimize copying
for (int32_t i = mList.Length() - 1; i >= 0; --i) {
if (IsPrivilegedNoCorsRequestHeaderName(mList[i].mName)) {
mList.RemoveElementAt(i);
dirty = true;
}
}
if (dirty) {
SetListDirty();
}
}
bool InternalHeaders::DeleteInternal(const nsCString& aLowerName,
ErrorResult& aRv) {
bool dirty = false;
// remove in reverse order to minimize copying
for (int32_t i = mList.Length() - 1; i >= 0; --i) {
if (mList[i].mName.EqualsIgnoreCase(aLowerName.get())) {
mList.RemoveElementAt(i);
dirty = true;
}
}
if (dirty) {
SetListDirty();
}
return dirty;
} }
void void
@ -68,17 +161,43 @@ InternalHeaders::Delete(const nsACString& aName, ErrorResult& aRv)
nsAutoCString lowerName; nsAutoCString lowerName;
ToLowerCase(aName, lowerName); ToLowerCase(aName, lowerName);
if (IsInvalidMutableHeader(lowerName, aRv)) { // Step 1
if (IsInvalidName(lowerName, aRv)) {
return; return;
} }
SetListDirty(); // Step 2
if (IsImmutable(aRv)) {
return;
}
// remove in reverse order to minimize copying // Step 3
for (int32_t i = mList.Length() - 1; i >= 0; --i) { nsAutoCString value;
if (lowerName == mList[i].mName) { GetInternal(lowerName, value, aRv);
mList.RemoveElementAt(i); if (IsForbiddenRequestHeader(lowerName, value)) {
} return;
}
// Step 4
if (mGuard == HeadersGuardEnum::Request_no_cors &&
!IsNoCorsSafelistedRequestHeaderName(lowerName) &&
!IsPrivilegedNoCorsRequestHeaderName(lowerName)) {
return;
}
// Step 5
if (IsForbiddenResponseHeader(lowerName)) {
return;
}
// Steps 6 and 7
if (!DeleteInternal(lowerName, aRv)) {
return;
}
// Step 8
if (mGuard == HeadersGuardEnum::Request_no_cors) {
RemovePrivilegedNoCorsRequestHeaders();
} }
} }
@ -91,12 +210,18 @@ InternalHeaders::Get(const nsACString& aName, nsACString& aValue, ErrorResult& a
if (IsInvalidName(lowerName, aRv)) { if (IsInvalidName(lowerName, aRv)) {
return; return;
} }
GetInternal(lowerName, aValue, aRv);
}
void
InternalHeaders::GetInternal(const nsCString& aLowerName,
nsACString& aValue,
ErrorResult& aRv) const {
const char* delimiter = ","; const char* delimiter = ",";
bool firstValueFound = false; bool firstValueFound = false;
for (uint32_t i = 0; i < mList.Length(); ++i) { for (uint32_t i = 0; i < mList.Length(); ++i) {
if (lowerName == mList[i].mName) { if (aLowerName == mList[i].mName) {
if (firstValueFound) { if (firstValueFound) {
aValue += delimiter; aValue += delimiter;
} }
@ -153,13 +278,18 @@ InternalHeaders::Has(const nsACString& aName, ErrorResult& aRv) const
void void
InternalHeaders::Set(const nsACString& aName, const nsACString& aValue, ErrorResult& aRv) InternalHeaders::Set(const nsACString& aName, const nsACString& aValue, ErrorResult& aRv)
{ {
// Step 1
nsAutoCString trimValue;
NS_TrimHTTPWhitespace(aValue, trimValue);
// Steps 2 to 6
nsAutoCString lowerName; nsAutoCString lowerName;
ToLowerCase(aName, lowerName); ToLowerCase(aName, lowerName);
if (!IsValidHeaderValue(lowerName, trimValue, aRv)) {
if (IsInvalidMutableHeader(lowerName, aValue, aRv)) {
return; return;
} }
// Step 7
SetListDirty(); SetListDirty();
int32_t firstIndex = INT32_MAX; int32_t firstIndex = INT32_MAX;
@ -179,6 +309,11 @@ InternalHeaders::Set(const nsACString& aName, const nsACString& aValue, ErrorRes
} else { } else {
mList.AppendElement(Entry(lowerName, aValue)); mList.AppendElement(Entry(lowerName, aValue));
} }
// Step 8
if (mGuard == HeadersGuardEnum::Request_no_cors) {
RemovePrivilegedNoCorsRequestHeaders();
}
} }
void void
@ -200,6 +335,22 @@ InternalHeaders::~InternalHeaders()
{ {
} }
// static
bool
InternalHeaders::IsNoCorsSafelistedRequestHeaderName(const nsCString& aName) {
return aName.EqualsIgnoreCase("accept") ||
aName.EqualsIgnoreCase("accept-language") ||
aName.EqualsIgnoreCase("content-language") ||
aName.EqualsIgnoreCase("content-type");
}
// static
bool
InternalHeaders::IsPrivilegedNoCorsRequestHeaderName(
const nsCString& aName) {
return aName.EqualsIgnoreCase("range");
}
// static // static
bool bool
InternalHeaders::IsSimpleHeader(const nsACString& aName, const nsACString& aValue) InternalHeaders::IsSimpleHeader(const nsACString& aName, const nsACString& aValue)
@ -207,9 +358,12 @@ InternalHeaders::IsSimpleHeader(const nsACString& aName, const nsACString& aValu
// Note, we must allow a null content-type value here to support // Note, we must allow a null content-type value here to support
// get("content-type"), but the IsInvalidValue() check will prevent null // get("content-type"), but the IsInvalidValue() check will prevent null
// from being set or appended. // from being set or appended.
return aName.EqualsLiteral("accept") || return (aName.EqualsLiteral("accept") &&
aName.EqualsLiteral("accept-language") || nsContentUtils::IsAllowedNonCorsAccept(aValue)) ||
aName.EqualsLiteral("content-language") || (aName.EqualsLiteral("accept-language") &&
nsContentUtils::IsAllowedNonCorsLanguage(aValue))||
(aName.EqualsLiteral("content-language") &&
nsContentUtils::IsAllowedNonCorsLanguage(aValue))||
(aName.EqualsLiteral("content-type") && (aName.EqualsLiteral("content-type") &&
nsContentUtils::IsAllowedNonCorsContentType(aValue)); nsContentUtils::IsAllowedNonCorsContentType(aValue));
} }
@ -261,10 +415,11 @@ InternalHeaders::IsImmutable(ErrorResult& aRv) const
} }
bool bool
InternalHeaders::IsForbiddenRequestHeader(const nsACString& aName) const InternalHeaders::IsForbiddenRequestHeader(const nsACString& aName,
const nsACString& aValue) const
{ {
return mGuard == HeadersGuardEnum::Request && return mGuard == HeadersGuardEnum::Request &&
nsContentUtils::IsForbiddenRequestHeader(aName); nsContentUtils::IsForbiddenRequestHeader(aName, aValue);
} }
bool bool
@ -370,7 +525,7 @@ InternalHeaders::CORSHeaders(InternalHeaders* aHeaders, RequestCredentials aCred
ErrorResult result; ErrorResult result;
nsAutoCString acExposedNames; nsAutoCString acExposedNames;
aHeaders->GetFirst(NS_LITERAL_CSTRING("Access-Control-Expose-Headers"), acExposedNames, result); aHeaders->Get(NS_LITERAL_CSTRING("Access-Control-Expose-Headers"), acExposedNames, result);
MOZ_ASSERT(!result.Failed()); MOZ_ASSERT(!result.Failed());
bool allowAllHeaders = false; bool allowAllHeaders = false;

View file

@ -136,8 +136,11 @@ private:
static bool IsInvalidName(const nsACString& aName, ErrorResult& aRv); static bool IsInvalidName(const nsACString& aName, ErrorResult& aRv);
static bool IsInvalidValue(const nsACString& aValue, ErrorResult& aRv); static bool IsInvalidValue(const nsACString& aValue, ErrorResult& aRv);
bool IsValidHeaderValue(const nsCString& aLowerName,
const nsCString& aNormalizedValue, ErrorResult& aRv);
bool IsImmutable(ErrorResult& aRv) const; bool IsImmutable(ErrorResult& aRv) const;
bool IsForbiddenRequestHeader(const nsACString& aName) const; bool IsForbiddenRequestHeader(const nsACString& aName,
const nsACString& aValue) const;
bool IsForbiddenRequestNoCorsHeader(const nsACString& aName) const; bool IsForbiddenRequestNoCorsHeader(const nsACString& aName) const;
bool IsForbiddenRequestNoCorsHeader(const nsACString& aName, bool IsForbiddenRequestNoCorsHeader(const nsACString& aName,
const nsACString& aValue) const; const nsACString& aValue) const;
@ -156,11 +159,22 @@ private:
return IsInvalidName(aName, aRv) || return IsInvalidName(aName, aRv) ||
IsInvalidValue(aValue, aRv) || IsInvalidValue(aValue, aRv) ||
IsImmutable(aRv) || IsImmutable(aRv) ||
IsForbiddenRequestHeader(aName) || IsForbiddenRequestHeader(aName, aValue) ||
IsForbiddenRequestNoCorsHeader(aName, aValue) || IsForbiddenRequestNoCorsHeader(aName, aValue) ||
IsForbiddenResponseHeader(aName); IsForbiddenResponseHeader(aName);
} }
void RemovePrivilegedNoCorsRequestHeaders();
void GetInternal(const nsCString& aLowerName, nsACString& aValue,
ErrorResult& aRv) const;
bool DeleteInternal(const nsCString& aLowerName, ErrorResult& aRv);
static bool IsNoCorsSafelistedRequestHeaderName(const nsCString& aName);
static bool IsPrivilegedNoCorsRequestHeaderName(const nsCString& aName);
static bool IsSimpleHeader(const nsACString& aName, static bool IsSimpleHeader(const nsACString& aName,
const nsACString& aValue); const nsACString& aValue);

View file

@ -3071,9 +3071,8 @@ XMLHttpRequestMainThread::SetRequestHeader(const nsACString& aName,
} }
// Step 3 // Step 3
nsAutoCString value(aValue); nsAutoCString value;
static const char kHTTPWhitespace[] = "\n\t\r "; NS_TrimHTTPWhitespace(aValue, value);
value.Trim(kHTTPWhitespace);
// Step 4 // Step 4
if (!NS_IsValidHTTPToken(aName) || !NS_IsReasonableHTTPHeaderValue(value)) { if (!NS_IsValidHTTPToken(aName) || !NS_IsReasonableHTTPHeaderValue(value)) {
@ -3082,7 +3081,7 @@ XMLHttpRequestMainThread::SetRequestHeader(const nsACString& aName,
// Step 5 // Step 5
bool isPrivilegedCaller = IsSystemXHR(); bool isPrivilegedCaller = IsSystemXHR();
bool isForbiddenHeader = nsContentUtils::IsForbiddenRequestHeader(aName); bool isForbiddenHeader = nsContentUtils::IsForbiddenRequestHeader(aName, aValue);
if (!isPrivilegedCaller && isForbiddenHeader) { if (!isPrivilegedCaller && isForbiddenHeader) {
NS_ConvertUTF8toUTF16 name(aName); NS_ConvertUTF8toUTF16 name(aName);
const char16_t* params[] = { name.get() }; const char16_t* params[] = { name.get() };

View file

@ -520,6 +520,12 @@ bool NS_IsValidHTTPToken(const nsACString &aToken)
return mozilla::net::nsHttp::IsValidToken(aToken); return mozilla::net::nsHttp::IsValidToken(aToken);
} }
void
NS_TrimHTTPWhitespace(const nsACString& aSource, nsACString& aDest)
{
mozilla::net::nsHttp::TrimHTTPWhitespace(aSource, aDest);
}
nsresult nsresult
NS_NewLoadGroup(nsILoadGroup **aResult, nsIPrincipal *aPrincipal) NS_NewLoadGroup(nsILoadGroup **aResult, nsIPrincipal *aPrincipal)
{ {

View file

@ -959,6 +959,11 @@ bool NS_IsReasonableHTTPHeaderValue(const nsACString &aValue);
*/ */
bool NS_IsValidHTTPToken(const nsACString &aToken); bool NS_IsValidHTTPToken(const nsACString &aToken);
/**
* Strip the leading or trailing HTTP whitespace per fetch spec section 2.2.
*/
void NS_TrimHTTPWhitespace(const nsACString& aSource, nsACString& aDest);
/** /**
* Return true if the given request must be upgraded to HTTPS. * Return true if the given request must be upgraded to HTTPS.
*/ */

View file

@ -245,6 +245,18 @@ nsHttp::GetProtocolVersion(uint32_t pv)
} }
} }
// static
void
nsHttp::TrimHTTPWhitespace(const nsACString& aSource, nsACString& aDest)
{
nsAutoCString str(aSource);
// HTTP whitespace 0x09: '\t', 0x0A: '\n', 0x0D: '\r', 0x20: ' '
static const char kHTTPWhitespace[] = "\t\n\r ";
str.Trim(kHTTPWhitespace);
aDest.Assign(str);
}
// static // static
bool bool
nsHttp::IsReasonableHeaderValue(const nsACString &s) nsHttp::IsReasonableHeaderValue(const nsACString &s)

View file

@ -148,6 +148,10 @@ struct nsHttp
static inline bool IsValidToken(const nsACString &s) { static inline bool IsValidToken(const nsACString &s) {
return IsValidToken(s.BeginReading(), s.EndReading()); return IsValidToken(s.BeginReading(), s.EndReading());
} }
// Strip the leading or trailing HTTP whitespace per fetch spec section 2.2.
static void TrimHTTPWhitespace(const nsACString& aSource,
nsACString& aDest);
// Returns true if the specified value is reasonable given the defintion // Returns true if the specified value is reasonable given the defintion
// in RFC 2616 section 4.2. Full strict validation is not performed // in RFC 2616 section 4.2. Full strict validation is not performed