зеркало из https://github.com/mozilla/gecko-dev.git
bug 477578 - http methods should be case sensitive r=hurley
This commit is contained in:
Родитель
42c1c237ba
Коммит
ae599f9845
|
@ -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]
|
||||
|
|
Загрузка…
Ссылка в новой задаче