bug 477578 - http methods should be case sensitive r=hurley

This commit is contained in:
Patrick McManus 2014-03-18 12:36:18 -04:00
Родитель 42c1c237ba
Коммит ae599f9845
26 изменённых файлов: 345 добавлений и 149 удалений

Просмотреть файл

@ -1537,11 +1537,11 @@ nsXMLHttpRequest::Open(const nsACString& method, const nsACString& url,
}
nsresult
nsXMLHttpRequest::Open(const nsACString& method, const nsACString& url,
nsXMLHttpRequest::Open(const nsACString& inMethod, const nsACString& url,
bool async, const Optional<nsAString>& user,
const Optional<nsAString>& password)
{
NS_ENSURE_ARG(!method.IsEmpty());
NS_ENSURE_ARG(!inMethod.IsEmpty());
if (!async && !DontWarnAboutSyncXHR() && GetOwner() &&
GetOwner()->GetExtantDoc()) {
@ -1555,9 +1555,29 @@ nsXMLHttpRequest::Open(const nsACString& method, const nsACString& url,
// Disallow HTTP/1.1 TRACE method (see bug 302489)
// and MS IIS equivalent TRACK (see bug 381264)
if (method.LowerCaseEqualsLiteral("trace") ||
method.LowerCaseEqualsLiteral("track")) {
return NS_ERROR_INVALID_ARG;
// and CONNECT
if (inMethod.LowerCaseEqualsLiteral("trace") ||
inMethod.LowerCaseEqualsLiteral("connect") ||
inMethod.LowerCaseEqualsLiteral("track")) {
return NS_ERROR_DOM_SECURITY_ERR;
}
nsAutoCString method;
// GET, POST, DELETE, HEAD, OPTIONS, PUT methods normalized to upper case
if (inMethod.LowerCaseEqualsLiteral("get")) {
method.Assign(NS_LITERAL_CSTRING("GET"));
} else if (inMethod.LowerCaseEqualsLiteral("post")) {
method.Assign(NS_LITERAL_CSTRING("POST"));
} else if (inMethod.LowerCaseEqualsLiteral("delete")) {
method.Assign(NS_LITERAL_CSTRING("DELETE"));
} else if (inMethod.LowerCaseEqualsLiteral("head")) {
method.Assign(NS_LITERAL_CSTRING("HEAD"));
} else if (inMethod.LowerCaseEqualsLiteral("options")) {
method.Assign(NS_LITERAL_CSTRING("OPTIONS"));
} else if (inMethod.LowerCaseEqualsLiteral("put")) {
method.Assign(NS_LITERAL_CSTRING("PUT"));
} else {
method = inMethod; // other methods are not normalized
}
// sync request is not allowed using withCredential or responseType

Просмотреть файл

@ -33,7 +33,7 @@ struct HttpChannelOpenArgs
OptionalURIParams apiRedirectTo;
uint32_t loadFlags;
RequestHeaderTuples requestHeaders;
nsHttpAtom requestMethod;
nsCString requestMethod;
OptionalInputStreamParams uploadStream;
bool uploadStreamHasHeaders;
uint16_t priority;

Просмотреть файл

@ -300,7 +300,7 @@ Http2Stream::ParseHttpRequestHeaders(const char *buf,
mOrigin, hashkey);
// check the push cache for GET
if (mTransaction->RequestHead()->Method() == nsHttp::Get) {
if (mTransaction->RequestHead()->IsGet()) {
// from :scheme, :authority, :path
nsILoadGroupConnectionInfo *loadGroupCI = mTransaction->LoadGroupConnectionInfo();
SpdyPushCache *cache = nullptr;
@ -356,7 +356,7 @@ Http2Stream::ParseHttpRequestHeaders(const char *buf,
nsCString compressedData;
mSession->Compressor()->EncodeHeaderBlock(mFlatHttpRequestHeaders,
nsCString(mTransaction->RequestHead()->Method().get()),
mTransaction->RequestHead()->Method(),
mTransaction->RequestHead()->RequestURI(),
hostHeader,
NS_LITERAL_CSTRING("https"),
@ -366,17 +366,17 @@ Http2Stream::ParseHttpRequestHeaders(const char *buf,
// to wait for a data packet to put it on.
uint8_t firstFrameFlags = Http2Session::kFlag_PRIORITY;
if (mTransaction->RequestHead()->Method() == nsHttp::Get ||
mTransaction->RequestHead()->Method() == nsHttp::Connect ||
mTransaction->RequestHead()->Method() == nsHttp::Head) {
if (mTransaction->RequestHead()->IsGet() ||
mTransaction->RequestHead()->IsConnect() ||
mTransaction->RequestHead()->IsHead()) {
// for GET, CONNECT, and HEAD place the fin bit right on the
// header packet
SetSentFin(true);
firstFrameFlags |= Http2Session::kFlag_END_STREAM;
} else if (mTransaction->RequestHead()->Method() == nsHttp::Post ||
mTransaction->RequestHead()->Method() == nsHttp::Put ||
mTransaction->RequestHead()->Method() == nsHttp::Options) {
} else if (mTransaction->RequestHead()->IsPost() ||
mTransaction->RequestHead()->IsPut() ||
mTransaction->RequestHead()->IsOptions()) {
// place fin in a data frame even for 0 length messages for iterop
} else if (!mRequestBodyLenRemaining) {
// for other HTTP extension methods, rely on the content-length

Просмотреть файл

@ -121,8 +121,8 @@ HttpBaseChannel::Init(nsIURI *aURI,
if (NS_FAILED(rv)) return rv;
LOG(("uri=%s\n", mSpec.get()));
// Set default request method
mRequestHead.SetMethod(nsHttp::Get);
// Assert default request method
MOZ_ASSERT(mRequestHead.EqualsMethod(nsHttpRequestHead::kMethod_Get));
// Set request headers
nsAutoCString hostLine;
@ -490,10 +490,10 @@ HttpBaseChannel::SetUploadStream(nsIInputStream *stream,
bool hasHeaders;
if (contentType.IsEmpty()) {
method = nsHttp::Post;
method = NS_LITERAL_CSTRING("POST");
hasHeaders = true;
} else {
method = nsHttp::Put;
method = NS_LITERAL_CSTRING("PUT");
hasHeaders = false;
}
return ExplicitSetUploadStream(stream, contentType, contentLength,
@ -503,7 +503,7 @@ HttpBaseChannel::SetUploadStream(nsIInputStream *stream,
// if stream is null, ExplicitSetUploadStream returns error.
// So we need special case for GET method.
mUploadStreamHasHeaders = false;
mRequestHead.SetMethod(nsHttp::Get); // revert to GET request
mRequestHead.SetMethod(NS_LITERAL_CSTRING("GET")); // revert to GET request
mUploadStream = stream;
return NS_OK;
}
@ -826,11 +826,7 @@ HttpBaseChannel::SetRequestMethod(const nsACString& aMethod)
if (!nsHttp::IsValidToken(flatMethod))
return NS_ERROR_INVALID_ARG;
nsHttpAtom atom = nsHttp::ResolveAtom(flatMethod.get());
if (!atom)
return NS_ERROR_FAILURE;
mRequestHead.SetMethod(atom);
mRequestHead.SetMethod(flatMethod);
return NS_OK;
}
@ -1610,7 +1606,7 @@ HttpBaseChannel::GetEntityID(nsACString& aEntityID)
{
// Don't return an entity ID for Non-GET requests which require
// additional data
if (mRequestHead.Method() != nsHttp::Get) {
if (!mRequestHead.IsGet()) {
return NS_ERROR_NOT_RESUMABLE;
}
@ -1747,6 +1743,22 @@ CopyProperties(const nsAString& aKey, nsIVariant *aData, void *aClosure)
return PL_DHASH_NEXT;
}
bool
HttpBaseChannel::ShouldRewriteRedirectToGET(uint32_t httpStatus,
nsHttpRequestHead::ParsedMethodType method)
{
// for 301 and 302, only rewrite POST
if (httpStatus == 301 || httpStatus == 302)
return method == nsHttpRequestHead::kMethod_Post;
// rewrite for 303 unless it was HEAD
if (httpStatus == 303)
return method != nsHttpRequestHead::kMethod_Head;
// otherwise, such as for 307, do not rewrite
return false;
}
nsresult
HttpBaseChannel::SetupReplacementChannel(nsIURI *newURI,
nsIChannel *newChannel,
@ -1807,7 +1819,7 @@ HttpBaseChannel::SetupReplacementChannel(nsIURI *newURI,
int64_t len = clen ? nsCRT::atoll(clen) : -1;
uploadChannel2->ExplicitSetUploadStream(
mUploadStream, nsDependentCString(ctype), len,
nsDependentCString(mRequestHead.Method()),
mRequestHead.Method(),
mUploadStreamHasHeaders);
} else {
if (mUploadStreamHasHeaders) {
@ -1834,7 +1846,7 @@ HttpBaseChannel::SetupReplacementChannel(nsIURI *newURI,
// we set the upload stream above. This means SetRequestMethod() will
// be called twice if ExplicitSetUploadStream() gets called above.
httpChannel->SetRequestMethod(nsDependentCString(mRequestHead.Method()));
httpChannel->SetRequestMethod(mRequestHead.Method());
}
// convey the referrer if one was used for this channel to the next one
if (mReferrer)

Просмотреть файл

@ -205,6 +205,12 @@ public:
public: /* Necko internal use only... */
// Return whether upon a redirect code of httpStatus for method, the
// request method should be rewritten to GET.
static bool ShouldRewriteRedirectToGET(uint32_t httpStatus,
nsHttpRequestHead::ParsedMethodType method);
protected:
nsCOMArray<nsISecurityConsoleMessage> mSecurityConsoleMessages;

Просмотреть файл

@ -786,8 +786,8 @@ HttpChannelChild::Redirect1Begin(const uint32_t& newChannelId,
mResponseHead = new nsHttpResponseHead(responseHead);
SetCookie(mResponseHead->PeekHeader(nsHttp::Set_Cookie));
bool rewriteToGET = nsHttp::ShouldRewriteRedirectToGET(
mResponseHead->Status(), mRequestHead.Method());
bool rewriteToGET = HttpBaseChannel::ShouldRewriteRedirectToGET(mResponseHead->Status(),
mRequestHead.ParsedMethod());
rv = SetupReplacementChannel(uri, newChannel, !rewriteToGET);
if (NS_FAILED(rv)) {

Просмотреть файл

@ -147,7 +147,7 @@ HttpChannelParent::DoAsyncOpen( const URIParams& aURI,
const OptionalURIParams& aAPIRedirectToURI,
const uint32_t& loadFlags,
const RequestHeaderTuples& requestHeaders,
const nsHttpAtom& requestMethod,
const nsCString& requestMethod,
const OptionalInputStreamParams& uploadStream,
const bool& uploadStreamHasHeaders,
const uint16_t& priority,

Просмотреть файл

@ -78,7 +78,7 @@ protected:
const OptionalURIParams& internalRedirectUri,
const uint32_t& loadFlags,
const RequestHeaderTuples& requestHeaders,
const nsHttpAtom& requestMethod,
const nsCString& requestMethod,
const OptionalInputStreamParams& uploadStream,
const bool& uploadStreamHasHeaders,
const uint16_t& priority,

Просмотреть файл

@ -289,7 +289,7 @@ SpdyStream3::ParseHttpRequestHeaders(const char *buf,
mOrigin, hashkey);
// check the push cache for GET
if (mTransaction->RequestHead()->Method() == nsHttp::Get) {
if (mTransaction->RequestHead()->IsGet()) {
// from :scheme, :host, :path
nsILoadGroupConnectionInfo *loadGroupCI = mTransaction->LoadGroupConnectionInfo();
SpdyPushCache *cache = nullptr;
@ -483,18 +483,18 @@ SpdyStream3::ParseHttpRequestHeaders(const char *buf,
// Determine whether to put the fin bit on the syn stream frame or whether
// to wait for a data packet to put it on.
if (mTransaction->RequestHead()->Method() == nsHttp::Get ||
mTransaction->RequestHead()->Method() == nsHttp::Connect ||
mTransaction->RequestHead()->Method() == nsHttp::Head) {
if (mTransaction->RequestHead()->IsGet() ||
mTransaction->RequestHead()->IsConnect() ||
mTransaction->RequestHead()->IsHead()) {
// for GET, CONNECT, and HEAD place the fin bit right on the
// syn stream packet
mSentFinOnData = 1;
mTxInlineFrame[4] = SpdySession3::kFlag_Data_FIN;
}
else if (mTransaction->RequestHead()->Method() == nsHttp::Post ||
mTransaction->RequestHead()->Method() == nsHttp::Put ||
mTransaction->RequestHead()->Method() == nsHttp::Options) {
else if (mTransaction->RequestHead()->IsPost() ||
mTransaction->RequestHead()->IsPut() ||
mTransaction->RequestHead()->IsOptions()) {
// place fin in a data frame even for 0 length messages, I've seen
// the google gateway be unhappy with fin-on-syn for 0 length POST
}

Просмотреть файл

@ -295,7 +295,7 @@ SpdyStream31::ParseHttpRequestHeaders(const char *buf,
mOrigin, hashkey);
// check the push cache for GET
if (mTransaction->RequestHead()->Method() == nsHttp::Get) {
if (mTransaction->RequestHead()->IsGet()) {
// from :scheme, :host, :path
nsILoadGroupConnectionInfo *loadGroupCI = mTransaction->LoadGroupConnectionInfo();
SpdyPushCache *cache = nullptr;
@ -490,18 +490,18 @@ SpdyStream31::ParseHttpRequestHeaders(const char *buf,
// Determine whether to put the fin bit on the syn stream frame or whether
// to wait for a data packet to put it on.
if (mTransaction->RequestHead()->Method() == nsHttp::Get ||
mTransaction->RequestHead()->Method() == nsHttp::Connect ||
mTransaction->RequestHead()->Method() == nsHttp::Head) {
if (mTransaction->RequestHead()->IsGet() ||
mTransaction->RequestHead()->IsConnect() ||
mTransaction->RequestHead()->IsHead()) {
// for GET, CONNECT, and HEAD place the fin bit right on the
// syn stream packet
mSentFinOnData = 1;
mTxInlineFrame[4] = SpdySession31::kFlag_Data_FIN;
}
else if (mTransaction->RequestHead()->Method() == nsHttp::Post ||
mTransaction->RequestHead()->Method() == nsHttp::Put ||
mTransaction->RequestHead()->Method() == nsHttp::Options) {
else if (mTransaction->RequestHead()->IsPost() ||
mTransaction->RequestHead()->IsPut() ||
mTransaction->RequestHead()->IsOptions()) {
// place fin in a data frame even for 0 length messages, I've seen
// the google gateway be unhappy with fin-on-syn for 0 length POST
}

Просмотреть файл

@ -295,34 +295,5 @@ nsHttp::IsPermanentRedirect(uint32_t httpStatus)
return httpStatus == 301 || httpStatus == 308;
}
bool
nsHttp::ShouldRewriteRedirectToGET(uint32_t httpStatus, nsHttpAtom method)
{
// for 301 and 302, only rewrite POST
if (httpStatus == 301 || httpStatus == 302)
return method == nsHttp::Post;
// rewrite for 303 unless it was HEAD
if (httpStatus == 303)
return method != nsHttp::Head;
// otherwise, such as for 307, do not rewrite
return false;
}
bool
nsHttp::IsSafeMethod(nsHttpAtom method)
{
// This code will need to be extended for new safe methods, otherwise
// they'll default to "not safe".
return method == nsHttp::Get ||
method == nsHttp::Head ||
method == nsHttp::Options ||
method == nsHttp::Propfind ||
method == nsHttp::Report ||
method == nsHttp::Search ||
method == nsHttp::Trace;
}
} // namespace mozilla::net
} // namespace mozilla

Просмотреть файл

@ -160,14 +160,6 @@ struct nsHttp
// Return whether the HTTP status code represents a permanent redirect
static bool IsPermanentRedirect(uint32_t httpStatus);
// Return whether upon a redirect code of httpStatus for method, the
// request method should be rewritten to GET.
static bool ShouldRewriteRedirectToGET(uint32_t httpStatus, nsHttpAtom method);
// Return whether the specified method is safe as per RFC 2616,
// Section 9.1.1.
static bool IsSafeMethod(nsHttpAtom method);
// Declare all atoms
//
// The atom names and values are stored in nsHttpAtomList.h and are brought

Просмотреть файл

@ -86,27 +86,4 @@ HTTP_ATOM(WWW_Authenticate, "WWW-Authenticate")
HTTP_ATOM(Warning, "Warning")
HTTP_ATOM(X_Firefox_Spdy, "X-Firefox-Spdy")
// methods are atoms too.
//
// Note: winnt.h defines DELETE macro, so we'll just keep the methods mixedcase
// even though they're normally written all uppercase. -- darin
HTTP_ATOM(Connect, "CONNECT")
HTTP_ATOM(Copy, "COPY")
HTTP_ATOM(Delete, "DELETE")
HTTP_ATOM(Get, "GET")
HTTP_ATOM(Head, "HEAD")
HTTP_ATOM(Index, "INDEX")
HTTP_ATOM(Lock, "LOCK")
HTTP_ATOM(M_Post, "M-POST")
HTTP_ATOM(Mkcol, "MKCOL")
HTTP_ATOM(Move, "MOVE")
HTTP_ATOM(Options, "OPTIONS")
HTTP_ATOM(Post, "POST")
HTTP_ATOM(Propfind, "PROPFIND")
HTTP_ATOM(Proppatch, "PROPPATCH")
HTTP_ATOM(Put, "PUT")
HTTP_ATOM(Report, "REPORT")
HTTP_ATOM(Search, "SEARCH")
HTTP_ATOM(Trace, "TRACE")
HTTP_ATOM(Unlock, "UNLOCK")
// methods are case sensitive and do not use atom table

Просмотреть файл

@ -622,6 +622,24 @@ nsHttpChannel::RetrieveSSLOptions()
}
}
static bool
SafeForPipelining(nsHttpRequestHead::ParsedMethodType method,
const nsCString &methodString)
{
if (method == nsHttpRequestHead::kMethod_Get ||
method == nsHttpRequestHead::kMethod_Head ||
method == nsHttpRequestHead::kMethod_Options) {
return true;
}
if (method != nsHttpRequestHead::kMethod_Custom) {
return false;
}
return (!strcmp(methodString.get(), "PROPFIND") ||
!strcmp(methodString.get(), "PROPPATCH"));
}
nsresult
nsHttpChannel::SetupTransaction()
{
@ -642,11 +660,7 @@ nsHttpChannel::SetupTransaction()
//
if (!mAllowPipelining ||
(mLoadFlags & (LOAD_INITIAL_DOCUMENT_URI | INHIBIT_PIPELINE)) ||
!(mRequestHead.Method() == nsHttp::Get ||
mRequestHead.Method() == nsHttp::Head ||
mRequestHead.Method() == nsHttp::Options ||
mRequestHead.Method() == nsHttp::Propfind ||
mRequestHead.Method() == nsHttp::Proppatch)) {
!SafeForPipelining(mRequestHead.ParsedMethod(), mRequestHead.Method())) {
LOG((" pipelining disallowed\n"));
mCaps &= ~NS_HTTP_ALLOW_PIPELINING;
}
@ -2483,7 +2497,7 @@ nsHttpChannel::OpenCacheEntry(bool usingSSL)
nsresult rv;
mLoadedFromApplicationCache = false;
mHasQueryString = HasQueryString(mRequestHead.Method(), mURI);
mHasQueryString = HasQueryString(mRequestHead.ParsedMethod(), mURI);
LOG(("nsHttpChannel::OpenCacheEntry [this=%p]", this));
@ -2492,15 +2506,14 @@ nsHttpChannel::OpenCacheEntry(bool usingSSL)
nsAutoCString cacheKey;
if (mRequestHead.Method() == nsHttp::Post) {
if (mRequestHead.IsPost()) {
// If the post id is already set then this is an attempt to replay
// a post transaction via the cache. Otherwise, we need a unique
// post id for this transaction.
if (mPostID == 0)
mPostID = gHttpHandler->GenerateUniqueID();
}
else if ((mRequestHead.Method() != nsHttp::Get) &&
(mRequestHead.Method() != nsHttp::Head)) {
else if (!mRequestHead.IsGet() && !mRequestHead.IsHead()) {
// don't use the cache for other types of requests
return NS_OK;
}
@ -2613,7 +2626,7 @@ bypassCacheEntryOpen:
return NS_OK;
}
if (mRequestHead.Method() != nsHttp::Get) {
if (!mRequestHead.IsGet()) {
// only cache complete documents offline
return NS_OK;
}
@ -2685,12 +2698,15 @@ nsHttpChannel::OnCacheEntryCheck(nsICacheEntry* entry, nsIApplicationCache* appC
rv = entry->GetMetaDataElement("request-method", getter_Copies(buf));
NS_ENSURE_SUCCESS(rv, rv);
nsHttpAtom method = nsHttp::ResolveAtom(buf);
if (method == nsHttp::Head) {
bool methodWasHead = buf.EqualsLiteral("HEAD");
bool methodWasGet = buf.EqualsLiteral("GET");
if (methodWasHead) {
// The cached response does not contain an entity. We can only reuse
// the response if the current request is also HEAD.
if (mRequestHead.Method() != nsHttp::Head)
if (!mRequestHead.IsHead()) {
return NS_OK;
}
}
buf.Adopt(0);
@ -2739,7 +2755,7 @@ nsHttpChannel::OnCacheEntryCheck(nsICacheEntry* entry, nsIApplicationCache* appC
bool wantCompleteEntry = false;
if (method != nsHttp::Head && !isCachedRedirect) {
if (!methodWasHead && !isCachedRedirect) {
// If the cached content-length is set and it does not match the data
// size of the cached content, then the cached response is partial...
// either we need to issue a byte range request or we need to refetch
@ -2870,7 +2886,7 @@ nsHttpChannel::OnCacheEntryCheck(nsICacheEntry* entry, nsIApplicationCache* appC
}
if (!doValidation && mRequestHead.PeekHeader(nsHttp::If_Match) &&
(method == nsHttp::Get || method == nsHttp::Head)) {
(methodWasGet || methodWasHead)) {
const char *requestedETag, *cachedETag;
cachedETag = mCachedResponseHead->PeekHeader(nsHttp::ETag);
requestedETag = mRequestHead.PeekHeader(nsHttp::If_Match);
@ -2939,8 +2955,7 @@ nsHttpChannel::OnCacheEntryCheck(nsICacheEntry* entry, nsIApplicationCache* appC
//
// do not override conditional headers when consumer has defined its own
if (!mCachedResponseHead->NoStore() &&
(mRequestHead.Method() == nsHttp::Get ||
mRequestHead.Method() == nsHttp::Head) &&
(mRequestHead.IsGet() || mRequestHead.IsHead()) &&
!mCustomConditionalRequest) {
if (mConcurentCacheAccess) {
@ -3294,13 +3309,14 @@ nsHttpChannel::UpdateExpirationTime()
}
/*static*/ inline bool
nsHttpChannel::HasQueryString(nsHttpAtom method, nsIURI * uri)
nsHttpChannel::HasQueryString(nsHttpRequestHead::ParsedMethodType method, nsIURI * uri)
{
// Must be called on the main thread because nsIURI does not implement
// thread-safe QueryInterface.
MOZ_ASSERT(NS_IsMainThread());
if (method != nsHttp::Get && method != nsHttp::Head)
if (method != nsHttpRequestHead::kMethod_Get &&
method != nsHttpRequestHead::kMethod_Head)
return false;
nsAutoCString query;
@ -4179,12 +4195,11 @@ nsHttpChannel::ContinueProcessRedirectionAfterFallback(nsresult rv)
}
}
bool rewriteToGET = nsHttp::ShouldRewriteRedirectToGET(
mRedirectType, mRequestHead.Method());
bool rewriteToGET = ShouldRewriteRedirectToGET(mRedirectType,
mRequestHead.ParsedMethod());
// prompt if the method is not safe (such as POST, PUT, DELETE, ...)
if (!rewriteToGET &&
!nsHttp::IsSafeMethod(mRequestHead.Method())) {
if (!rewriteToGET && !mRequestHead.IsSafeMethod()) {
rv = PromptTempRedirect();
if (NS_FAILED(rv)) return rv;
}
@ -6054,13 +6069,11 @@ nsHttpChannel::MaybeInvalidateCacheEntryForSubsequentGet()
// referred resource. POST, PUT and DELETE as well as any
// other method not listed here will potentially invalidate
// any cached copy of the resource
if (mRequestHead.Method() == nsHttp::Options ||
mRequestHead.Method() == nsHttp::Get ||
mRequestHead.Method() == nsHttp::Head ||
mRequestHead.Method() == nsHttp::Trace ||
mRequestHead.Method() == nsHttp::Connect)
if (mRequestHead.IsGet() || mRequestHead.IsOptions() ||
mRequestHead.IsHead() || mRequestHead.IsTrace() ||
mRequestHead.IsConnect()) {
return;
}
// Invalidate the request-uri.
#ifdef PR_LOGGING

Просмотреть файл

@ -316,7 +316,7 @@ private:
// and ensure the transaction is updated to use it.
void UpdateAggregateCallbacks();
static bool HasQueryString(nsHttpAtom method, nsIURI * uri);
static bool HasQueryString(nsHttpRequestHead::ParsedMethodType method, nsIURI * uri);
bool ResponseWouldVary(nsICacheEntry* entry) const;
bool MustValidateBasedOnQueryUrl() const;
bool IsResumable(int64_t partialLen, int64_t contentLength,

Просмотреть файл

@ -1577,7 +1577,7 @@ nsHttpConnection::SetupProxyConnect()
// CONNECT host:port HTTP/1.1
nsHttpRequestHead request;
request.SetMethod(nsHttp::Connect);
request.SetMethod(NS_LITERAL_CSTRING("CONNECT"));
request.SetVersion(gHttpHandler->HttpVersion());
request.SetRequestURI(buf);
request.SetHeader(nsHttp::User_Agent, gHttpHandler->UserAgent());

Просмотреть файл

@ -15,12 +15,59 @@
namespace mozilla {
namespace net {
nsHttpRequestHead::nsHttpRequestHead()
: mMethod(NS_LITERAL_CSTRING("GET"))
, mVersion(NS_HTTP_VERSION_1_1)
, mParsedMethod(kMethod_Get)
{
}
void
nsHttpRequestHead::SetMethod(const nsACString &method)
{
mParsedMethod = kMethod_Custom;
mMethod = method;
if (!strcmp(mMethod.get(), "GET")) {
mParsedMethod = kMethod_Get;
} else if (!strcmp(mMethod.get(), "POST")) {
mParsedMethod = kMethod_Post;
} else if (!strcmp(mMethod.get(), "OPTIONS")) {
mParsedMethod = kMethod_Options;
} else if (!strcmp(mMethod.get(), "CONNECT")) {
mParsedMethod = kMethod_Connect;
} else if (!strcmp(mMethod.get(), "HEAD")) {
mParsedMethod = kMethod_Head;
} else if (!strcmp(mMethod.get(), "PUT")) {
mParsedMethod = kMethod_Put;
} else if (!strcmp(mMethod.get(), "TRACE")) {
mParsedMethod = kMethod_Trace;
}
}
bool
nsHttpRequestHead::IsSafeMethod() const
{
// This code will need to be extended for new safe methods, otherwise
// they'll default to "not safe".
if (IsGet() || IsHead() || IsOptions() || IsTrace()) {
return true;
}
if (mParsedMethod != kMethod_Custom) {
return false;
}
return (!strcmp(mMethod.get(), "PROPFIND") ||
!strcmp(mMethod.get(), "REPORT") ||
!strcmp(mMethod.get(), "SEARCH"));
}
void
nsHttpRequestHead::Flatten(nsACString &buf, bool pruneProxyHeaders)
{
// note: the first append is intentional.
buf.Append(mMethod.get());
buf.Append(mMethod);
buf.Append(' ');
buf.Append(mRequestURI);
buf.AppendLiteral(" HTTP/");

Просмотреть файл

@ -20,15 +20,15 @@ namespace mozilla { namespace net {
class nsHttpRequestHead
{
public:
nsHttpRequestHead() : mMethod(nsHttp::Get), mVersion(NS_HTTP_VERSION_1_1) {}
nsHttpRequestHead();
void SetMethod(nsHttpAtom method) { mMethod = method; }
void SetMethod(const nsACString &method);
void SetVersion(nsHttpVersion version) { mVersion = version; }
void SetRequestURI(const nsCSubstring &s) { mRequestURI = s; }
const nsHttpHeaderArray &Headers() const { return mHeaders; }
nsHttpHeaderArray & Headers() { return mHeaders; }
nsHttpAtom Method() const { return mMethod; }
const nsCString &Method() const { return mMethod; }
nsHttpVersion Version() const { return mVersion; }
const nsCSubstring &RequestURI() const { return mRequestURI; }
@ -63,12 +63,37 @@ public:
return NS_OK;
}
bool IsSafeMethod() const;
enum ParsedMethodType
{
kMethod_Custom,
kMethod_Get,
kMethod_Post,
kMethod_Options,
kMethod_Connect,
kMethod_Head,
kMethod_Put,
kMethod_Trace
};
ParsedMethodType ParsedMethod() const { return mParsedMethod; }
bool EqualsMethod(ParsedMethodType aType) const { return mParsedMethod == aType; }
bool IsGet() const { return EqualsMethod(kMethod_Get); }
bool IsPost() const { return EqualsMethod(kMethod_Post); }
bool IsOptions() const { return EqualsMethod(kMethod_Options); }
bool IsConnect() const { return EqualsMethod(kMethod_Connect); }
bool IsHead() const { return EqualsMethod(kMethod_Head); }
bool IsPut() const { return EqualsMethod(kMethod_Put); }
bool IsTrace() const { return EqualsMethod(kMethod_Trace); }
private:
// All members must be copy-constructable and assignable
nsHttpHeaderArray mHeaders;
nsHttpAtom mMethod;
nsCString mMethod;
nsHttpVersion mVersion;
nsCString mRequestURI;
ParsedMethodType mParsedMethod;
};
}} // namespace mozilla::net

Просмотреть файл

@ -274,8 +274,9 @@ nsHttpTransaction::Init(uint32_t caps,
mConsumerTarget = target;
mCaps = caps;
if (requestHead->Method() == nsHttp::Head)
if (requestHead->IsHead()) {
mNoContent = true;
}
// Make sure that there is "Content-Length: 0" header in the requestHead
// in case of POST and PUT methods when there is no requestBody and
@ -289,7 +290,7 @@ nsHttpTransaction::Init(uint32_t caps,
// For compatibility with HTTP/1.0 applications, HTTP/1.1 requests
// containing a message-body MUST include a valid Content-Length header
// field unless the server is known to be HTTP/1.1 compliant.
if ((requestHead->Method() == nsHttp::Post || requestHead->Method() == nsHttp::Put) &&
if ((requestHead->IsPost() || requestHead->IsPut()) &&
!requestBody && !requestHead->PeekHeader(nsHttp::Transfer_Encoding)) {
requestHead->SetHeader(nsHttp::Content_Length, NS_LITERAL_CSTRING("0"));
}
@ -1314,7 +1315,7 @@ nsHttpTransaction::ParseHead(char *buf,
char *p = LocateHttpStart(buf, std::min<uint32_t>(count, 11), true);
if (!p) {
// Treat any 0.9 style response of a put as a failure.
if (mRequestHead->Method() == nsHttp::Put)
if (mRequestHead->IsPut())
return NS_ERROR_ABORT;
mResponseHead->ParseStatusLine("");
@ -1497,7 +1498,7 @@ nsHttpTransaction::HandleContentStart()
// The verifier only initializes itself once (from the first iteration of
// a transaction that gets far enough to have response headers)
if (mRequestHead->Method() == nsHttp::Get)
if (mRequestHead->IsGet())
mRestartInProgressVerifier.Set(mContentLength, mResponseHead);
return NS_OK;

Просмотреть файл

@ -0,0 +1,12 @@
function handleRequest(request, response)
{
// avoid confusing cache behaviors
response.setHeader("Cache-Control", "no-cache", false);
response.setHeader("Content-Type", "text/plain", false);
response.setHeader("Access-Control-Allow-Origin", "*", false);
// echo method
response.write(request.method);
}

Просмотреть файл

@ -1,6 +1,7 @@
[DEFAULT]
skip-if = buildapp == 'b2g' || e10s
support-files =
method.sjs
partial_content.sjs
user_agent.sjs
user_agent_update.sjs
@ -10,3 +11,4 @@ support-files =
[test_user_agent_overrides.html]
[test_user_agent_updates.html]
[test_user_agent_updates_reset.html]
[test_xhr_method_case.html]

Просмотреть файл

@ -0,0 +1,66 @@
<!DOCTYPE HTML>
<html>
<!--
XHR uppercases certain method names, but not others
-->
<head>
<title>Test for XHR Method casing</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
<script type="text/javascript">
const testMethods = [
// these methods should be normalized
["get", "GET"],
["GET", "GET"],
["GeT", "GET"],
["geT", "GET"],
["GEt", "GET"],
["post", "POST"],
["POST", "POST"],
["delete", "DELETE"],
["DELETE", "DELETE"],
["options", "OPTIONS"],
["OPTIONS", "OPTIONS"],
["put", "PUT"],
["PUT", "PUT"],
// HEAD is not tested because we use the resposne body as part of the test
// ["head", "HEAD"],
// ["HEAD", "HEAD"],
// other custom methods should not be normalized
["Foo", "Foo"],
["bAR", "bAR"],
["foobar", "foobar"],
["FOOBAR", "FOOBAR"]
]
function doIter(index)
{
var xhr = new XMLHttpRequest();
xhr.open(testMethods[index][0], 'method.sjs', false); // sync request
xhr.send();
is(xhr.status, 200, 'transaction failed');
is(xhr.response, testMethods[index][1], 'unexpected method');
}
function dotest()
{
SimpleTest.waitForExplicitFinish();
for (var i = 0; i < testMethods.length; i++) {
doIter(i);
}
SimpleTest.finish();
}
</script>
</head>
<body onload="dotest();">
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test">
</pre>
</body>
</html>

Просмотреть файл

@ -27,7 +27,7 @@ function run_test() {
"/bug412945", null, null);
channel.QueryInterface(Components.interfaces.nsIHttpChannel);
channel.requestMethod = "post";
channel.requestMethod = "POST";
channel.asyncOpen(new TestListener(), null);
do_test_pending();

Просмотреть файл

@ -0,0 +1,51 @@
// test that methods are not normalized
const testMethods = [
["GET"],
["get"],
["Get"],
["gET"],
["gEt"],
["post"],
["POST"],
["head"],
["HEAD"],
["put"],
["PUT"],
["delete"],
["DELETE"],
["connect"],
["CONNECT"],
["options"],
["trace"],
["track"],
["copy"],
["index"],
["lock"],
["m-post"],
["mkcol"],
["move"],
["propfind"],
["proppatch"],
["unlock"],
["link"],
["LINK"],
["foo"],
["foO"],
["fOo"],
["Foo"]
]
function run_test() {
var ios =
Cc["@mozilla.org/network/io-service;1"].
getService(Ci.nsIIOService);
var chan = ios.newChannel("http://localhost/", null, null)
.QueryInterface(Components.interfaces.nsIHttpChannel);
for (var i = 0; i < testMethods.length; i++) {
chan.requestMethod = testMethods[i];
do_check_eq(chan.requestMethod, testMethods[i]);
}
}

Просмотреть файл

@ -31,7 +31,7 @@ InitialListener.prototype = {
do_execute_soon(function() {
var channel = setupChannel("http://localhost:" +
httpserv.identity.primaryPort + "/post");
channel.requestMethod = "post";
channel.requestMethod = "POST";
channel.asyncOpen(new RedirectingListener(), null);
});
}
@ -46,7 +46,7 @@ RedirectingListener.prototype = {
do_execute_soon(function() {
var channel = setupChannel("http://localhost:" +
httpserv.identity.primaryPort + "/post");
channel.requestMethod = "post";
channel.requestMethod = "POST";
channel.asyncOpen(new VerifyingListener(), null);
});
}

Просмотреть файл

@ -111,6 +111,7 @@ skip-if = os == "android"
# Bug 675039: intermittent fail on Android-armv6
skip-if = os == "android"
[test_bug470716.js]
[test_bug477578.js]
[test_bug479413.js]
[test_bug479485.js]
[test_bug482601.js]