michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim: set sw=2 ts=8 et tw=80 : */ michael@0: michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: // HttpLog.h should generally be included first michael@0: #include "HttpLog.h" michael@0: michael@0: #include "mozilla/net/HttpBaseChannel.h" michael@0: michael@0: #include "nsHttpHandler.h" michael@0: #include "nsMimeTypes.h" michael@0: #include "nsNetUtil.h" michael@0: michael@0: #include "nsICachingChannel.h" michael@0: #include "nsIPrincipal.h" michael@0: #include "nsISeekableStream.h" michael@0: #include "nsITimedChannel.h" michael@0: #include "nsIEncodedChannel.h" michael@0: #include "nsIApplicationCacheChannel.h" michael@0: #include "nsEscape.h" michael@0: #include "nsStreamListenerWrapper.h" michael@0: #include "nsISecurityConsoleMessage.h" michael@0: #include "nsURLHelper.h" michael@0: #include "nsICookieService.h" michael@0: #include "nsIStreamConverterService.h" michael@0: #include "nsCRT.h" michael@0: #include "nsContentUtils.h" michael@0: #include "nsIScriptSecurityManager.h" michael@0: #include "nsIObserverService.h" michael@0: michael@0: #include michael@0: michael@0: namespace mozilla { michael@0: namespace net { michael@0: michael@0: HttpBaseChannel::HttpBaseChannel() michael@0: : mStartPos(UINT64_MAX) michael@0: , mStatus(NS_OK) michael@0: , mLoadFlags(LOAD_NORMAL) michael@0: , mCaps(0) michael@0: , mPriority(PRIORITY_NORMAL) michael@0: , mRedirectionLimit(gHttpHandler->RedirectionLimit()) michael@0: , mApplyConversion(true) michael@0: , mCanceled(false) michael@0: , mIsPending(false) michael@0: , mWasOpened(false) michael@0: , mRequestObserversCalled(false) michael@0: , mResponseHeadersModified(false) michael@0: , mAllowPipelining(true) michael@0: , mForceAllowThirdPartyCookie(false) michael@0: , mUploadStreamHasHeaders(false) michael@0: , mInheritApplicationCache(true) michael@0: , mChooseApplicationCache(false) michael@0: , mLoadedFromApplicationCache(false) michael@0: , mChannelIsForDownload(false) michael@0: , mTracingEnabled(true) michael@0: , mTimingEnabled(false) michael@0: , mAllowSpdy(true) michael@0: , mLoadAsBlocking(false) michael@0: , mLoadUnblocked(false) michael@0: , mResponseTimeoutEnabled(true) michael@0: , mAllRedirectsSameOrigin(true) michael@0: , mSuspendCount(0) michael@0: , mProxyResolveFlags(0) michael@0: , mContentDispositionHint(UINT32_MAX) michael@0: , mHttpHandler(gHttpHandler) michael@0: , mRedirectCount(0) michael@0: , mProxyURI(nullptr) michael@0: { michael@0: LOG(("Creating HttpBaseChannel @%x\n", this)); michael@0: michael@0: // Subfields of unions cannot be targeted in an initializer list michael@0: mSelfAddr.raw.family = PR_AF_UNSPEC; michael@0: mPeerAddr.raw.family = PR_AF_UNSPEC; michael@0: } michael@0: michael@0: HttpBaseChannel::~HttpBaseChannel() michael@0: { michael@0: LOG(("Destroying HttpBaseChannel @%x\n", this)); michael@0: michael@0: // Make sure we don't leak michael@0: CleanRedirectCacheChainIfNecessary(); michael@0: } michael@0: michael@0: nsresult michael@0: HttpBaseChannel::Init(nsIURI *aURI, michael@0: uint32_t aCaps, michael@0: nsProxyInfo *aProxyInfo, michael@0: uint32_t aProxyResolveFlags, michael@0: nsIURI *aProxyURI) michael@0: { michael@0: LOG(("HttpBaseChannel::Init [this=%p]\n", this)); michael@0: michael@0: NS_PRECONDITION(aURI, "null uri"); michael@0: michael@0: mURI = aURI; michael@0: mOriginalURI = aURI; michael@0: mDocumentURI = nullptr; michael@0: mCaps = aCaps; michael@0: mProxyResolveFlags = aProxyResolveFlags; michael@0: mProxyURI = aProxyURI; michael@0: michael@0: // Construct connection info object michael@0: nsAutoCString host; michael@0: int32_t port = -1; michael@0: bool usingSSL = false; michael@0: michael@0: nsresult rv = mURI->SchemeIs("https", &usingSSL); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = mURI->GetAsciiHost(host); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: // Reject the URL if it doesn't specify a host michael@0: if (host.IsEmpty()) michael@0: return NS_ERROR_MALFORMED_URI; michael@0: michael@0: rv = mURI->GetPort(&port); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: LOG(("host=%s port=%d\n", host.get(), port)); michael@0: michael@0: rv = mURI->GetAsciiSpec(mSpec); michael@0: if (NS_FAILED(rv)) return rv; michael@0: LOG(("uri=%s\n", mSpec.get())); michael@0: michael@0: // Assert default request method michael@0: MOZ_ASSERT(mRequestHead.EqualsMethod(nsHttpRequestHead::kMethod_Get)); michael@0: michael@0: // Set request headers michael@0: nsAutoCString hostLine; michael@0: rv = nsHttpHandler::GenerateHostPort(host, port, hostLine); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = mRequestHead.SetHeader(nsHttp::Host, hostLine); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = gHttpHandler->AddStandardRequestHeaders(&mRequestHead.Headers()); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: nsAutoCString type; michael@0: if (aProxyInfo && NS_SUCCEEDED(aProxyInfo->GetType(type)) && michael@0: !type.EqualsLiteral("unknown")) michael@0: mProxyInfo = aProxyInfo; michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // HttpBaseChannel::nsISupports michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: NS_IMPL_ADDREF(HttpBaseChannel) michael@0: NS_IMPL_RELEASE(HttpBaseChannel) michael@0: michael@0: NS_INTERFACE_MAP_BEGIN(HttpBaseChannel) michael@0: NS_INTERFACE_MAP_ENTRY(nsIRequest) michael@0: NS_INTERFACE_MAP_ENTRY(nsIChannel) michael@0: NS_INTERFACE_MAP_ENTRY(nsIEncodedChannel) michael@0: NS_INTERFACE_MAP_ENTRY(nsIHttpChannel) michael@0: NS_INTERFACE_MAP_ENTRY(nsIHttpChannelInternal) michael@0: NS_INTERFACE_MAP_ENTRY(nsIUploadChannel) michael@0: NS_INTERFACE_MAP_ENTRY(nsIUploadChannel2) michael@0: NS_INTERFACE_MAP_ENTRY(nsISupportsPriority) michael@0: NS_INTERFACE_MAP_ENTRY(nsITraceableChannel) michael@0: NS_INTERFACE_MAP_ENTRY(nsIPrivateBrowsingChannel) michael@0: NS_INTERFACE_MAP_ENTRY(nsITimedChannel) michael@0: NS_INTERFACE_MAP_END_INHERITING(nsHashPropertyBag) michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // HttpBaseChannel::nsIRequest michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::GetName(nsACString& aName) michael@0: { michael@0: aName = mSpec; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::IsPending(bool *aIsPending) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aIsPending); michael@0: *aIsPending = mIsPending; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::GetStatus(nsresult *aStatus) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aStatus); michael@0: *aStatus = mStatus; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::GetLoadGroup(nsILoadGroup **aLoadGroup) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aLoadGroup); michael@0: *aLoadGroup = mLoadGroup; michael@0: NS_IF_ADDREF(*aLoadGroup); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::SetLoadGroup(nsILoadGroup *aLoadGroup) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread(), "Should only be called on the main thread."); michael@0: michael@0: if (!CanSetLoadGroup(aLoadGroup)) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: mLoadGroup = aLoadGroup; michael@0: mProgressSink = nullptr; michael@0: mPrivateBrowsing = NS_UsePrivateBrowsing(this); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::GetLoadFlags(nsLoadFlags *aLoadFlags) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aLoadFlags); michael@0: *aLoadFlags = mLoadFlags; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::SetLoadFlags(nsLoadFlags aLoadFlags) michael@0: { michael@0: mLoadFlags = aLoadFlags; michael@0: return NS_OK; michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // HttpBaseChannel::nsIChannel michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::GetOriginalURI(nsIURI **aOriginalURI) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aOriginalURI); michael@0: *aOriginalURI = mOriginalURI; michael@0: NS_ADDREF(*aOriginalURI); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::SetOriginalURI(nsIURI *aOriginalURI) michael@0: { michael@0: ENSURE_CALLED_BEFORE_CONNECT(); michael@0: michael@0: NS_ENSURE_ARG_POINTER(aOriginalURI); michael@0: mOriginalURI = aOriginalURI; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::GetURI(nsIURI **aURI) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aURI); michael@0: *aURI = mURI; michael@0: NS_ADDREF(*aURI); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::GetOwner(nsISupports **aOwner) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aOwner); michael@0: *aOwner = mOwner; michael@0: NS_IF_ADDREF(*aOwner); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::SetOwner(nsISupports *aOwner) michael@0: { michael@0: mOwner = aOwner; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::GetNotificationCallbacks(nsIInterfaceRequestor **aCallbacks) michael@0: { michael@0: *aCallbacks = mCallbacks; michael@0: NS_IF_ADDREF(*aCallbacks); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::SetNotificationCallbacks(nsIInterfaceRequestor *aCallbacks) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread(), "Should only be called on the main thread."); michael@0: michael@0: if (!CanSetCallbacks(aCallbacks)) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: mCallbacks = aCallbacks; michael@0: mProgressSink = nullptr; michael@0: michael@0: mPrivateBrowsing = NS_UsePrivateBrowsing(this); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::GetContentType(nsACString& aContentType) michael@0: { michael@0: if (!mResponseHead) { michael@0: aContentType.Truncate(); michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: michael@0: if (!mResponseHead->ContentType().IsEmpty()) { michael@0: aContentType = mResponseHead->ContentType(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: aContentType.AssignLiteral(UNKNOWN_CONTENT_TYPE); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::SetContentType(const nsACString& aContentType) michael@0: { michael@0: if (mListener || mWasOpened) { michael@0: if (!mResponseHead) michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: michael@0: nsAutoCString contentTypeBuf, charsetBuf; michael@0: bool hadCharset; michael@0: net_ParseContentType(aContentType, contentTypeBuf, charsetBuf, &hadCharset); michael@0: michael@0: mResponseHead->SetContentType(contentTypeBuf); michael@0: michael@0: // take care not to stomp on an existing charset michael@0: if (hadCharset) michael@0: mResponseHead->SetContentCharset(charsetBuf); michael@0: michael@0: } else { michael@0: // We are being given a content-type hint. michael@0: bool dummy; michael@0: net_ParseContentType(aContentType, mContentTypeHint, mContentCharsetHint, michael@0: &dummy); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::GetContentCharset(nsACString& aContentCharset) michael@0: { michael@0: if (!mResponseHead) michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: michael@0: aContentCharset = mResponseHead->ContentCharset(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::SetContentCharset(const nsACString& aContentCharset) michael@0: { michael@0: if (mListener) { michael@0: if (!mResponseHead) michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: michael@0: mResponseHead->SetContentCharset(aContentCharset); michael@0: } else { michael@0: // Charset hint michael@0: mContentCharsetHint = aContentCharset; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::GetContentDisposition(uint32_t *aContentDisposition) michael@0: { michael@0: nsresult rv; michael@0: nsCString header; michael@0: michael@0: rv = GetContentDispositionHeader(header); michael@0: if (NS_FAILED(rv)) { michael@0: if (mContentDispositionHint == UINT32_MAX) michael@0: return rv; michael@0: michael@0: *aContentDisposition = mContentDispositionHint; michael@0: return NS_OK; michael@0: } michael@0: michael@0: *aContentDisposition = NS_GetContentDispositionFromHeader(header, this); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::SetContentDisposition(uint32_t aContentDisposition) michael@0: { michael@0: mContentDispositionHint = aContentDisposition; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::GetContentDispositionFilename(nsAString& aContentDispositionFilename) michael@0: { michael@0: aContentDispositionFilename.Truncate(); michael@0: nsresult rv; michael@0: nsCString header; michael@0: michael@0: rv = GetContentDispositionHeader(header); michael@0: if (NS_FAILED(rv)) { michael@0: if (!mContentDispositionFilename) michael@0: return rv; michael@0: michael@0: aContentDispositionFilename = *mContentDispositionFilename; michael@0: return NS_OK; michael@0: } michael@0: michael@0: return NS_GetFilenameFromDisposition(aContentDispositionFilename, michael@0: header, mURI); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::SetContentDispositionFilename(const nsAString& aContentDispositionFilename) michael@0: { michael@0: mContentDispositionFilename = new nsString(aContentDispositionFilename); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::GetContentDispositionHeader(nsACString& aContentDispositionHeader) michael@0: { michael@0: if (!mResponseHead) michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: michael@0: nsresult rv = mResponseHead->GetHeader(nsHttp::Content_Disposition, michael@0: aContentDispositionHeader); michael@0: if (NS_FAILED(rv) || aContentDispositionHeader.IsEmpty()) michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::GetContentLength(int64_t *aContentLength) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aContentLength); michael@0: michael@0: if (!mResponseHead) michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: michael@0: *aContentLength = mResponseHead->ContentLength(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::SetContentLength(int64_t value) michael@0: { michael@0: NS_NOTYETIMPLEMENTED("HttpBaseChannel::SetContentLength"); michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::Open(nsIInputStream **aResult) michael@0: { michael@0: NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_IN_PROGRESS); michael@0: return NS_ImplementChannelOpen(this, aResult); michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // HttpBaseChannel::nsIUploadChannel michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::GetUploadStream(nsIInputStream **stream) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(stream); michael@0: *stream = mUploadStream; michael@0: NS_IF_ADDREF(*stream); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::SetUploadStream(nsIInputStream *stream, michael@0: const nsACString &contentType, michael@0: int64_t contentLength) michael@0: { michael@0: // NOTE: for backwards compatibility and for compatibility with old style michael@0: // plugins, |stream| may include headers, specifically Content-Type and michael@0: // Content-Length headers. in this case, |contentType| and |contentLength| michael@0: // would be unspecified. this is traditionally the case of a POST request, michael@0: // and so we select POST as the request method if contentType and michael@0: // contentLength are unspecified. michael@0: michael@0: if (stream) { michael@0: nsAutoCString method; michael@0: bool hasHeaders; michael@0: michael@0: if (contentType.IsEmpty()) { michael@0: method = NS_LITERAL_CSTRING("POST"); michael@0: hasHeaders = true; michael@0: } else { michael@0: method = NS_LITERAL_CSTRING("PUT"); michael@0: hasHeaders = false; michael@0: } michael@0: return ExplicitSetUploadStream(stream, contentType, contentLength, michael@0: method, hasHeaders); michael@0: } michael@0: michael@0: // if stream is null, ExplicitSetUploadStream returns error. michael@0: // So we need special case for GET method. michael@0: mUploadStreamHasHeaders = false; michael@0: mRequestHead.SetMethod(NS_LITERAL_CSTRING("GET")); // revert to GET request michael@0: mUploadStream = stream; michael@0: return NS_OK; michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // HttpBaseChannel::nsIUploadChannel2 michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::ExplicitSetUploadStream(nsIInputStream *aStream, michael@0: const nsACString &aContentType, michael@0: int64_t aContentLength, michael@0: const nsACString &aMethod, michael@0: bool aStreamHasHeaders) michael@0: { michael@0: // Ensure stream is set and method is valid michael@0: NS_ENSURE_TRUE(aStream, NS_ERROR_FAILURE); michael@0: michael@0: if (aContentLength < 0 && !aStreamHasHeaders) { michael@0: nsresult rv = aStream->Available(reinterpret_cast(&aContentLength)); michael@0: if (NS_FAILED(rv) || aContentLength < 0) { michael@0: NS_ERROR("unable to determine content length"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: } michael@0: michael@0: nsresult rv = SetRequestMethod(aMethod); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (!aStreamHasHeaders) { michael@0: // SetRequestHeader propagates headers to chrome if HttpChannelChild michael@0: nsAutoCString contentLengthStr; michael@0: contentLengthStr.AppendInt(aContentLength); michael@0: SetRequestHeader(NS_LITERAL_CSTRING("Content-Length"), contentLengthStr, michael@0: false); michael@0: SetRequestHeader(NS_LITERAL_CSTRING("Content-Type"), aContentType, michael@0: false); michael@0: } michael@0: michael@0: mUploadStreamHasHeaders = aStreamHasHeaders; michael@0: mUploadStream = aStream; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::GetUploadStreamHasHeaders(bool *hasHeaders) michael@0: { michael@0: NS_ENSURE_ARG(hasHeaders); michael@0: michael@0: *hasHeaders = mUploadStreamHasHeaders; michael@0: return NS_OK; michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // HttpBaseChannel::nsIEncodedChannel michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::GetApplyConversion(bool *value) michael@0: { michael@0: *value = mApplyConversion; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::SetApplyConversion(bool value) michael@0: { michael@0: LOG(("HttpBaseChannel::SetApplyConversion [this=%p value=%d]\n", this, value)); michael@0: mApplyConversion = value; michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: HttpBaseChannel::ApplyContentConversions() michael@0: { michael@0: if (!mResponseHead) michael@0: return NS_OK; michael@0: michael@0: LOG(("HttpBaseChannel::ApplyContentConversions [this=%p]\n", this)); michael@0: michael@0: if (!mApplyConversion) { michael@0: LOG(("not applying conversion per mApplyConversion\n")); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsAutoCString contentEncoding; michael@0: char *cePtr, *val; michael@0: nsresult rv; michael@0: michael@0: rv = mResponseHead->GetHeader(nsHttp::Content_Encoding, contentEncoding); michael@0: if (NS_FAILED(rv) || contentEncoding.IsEmpty()) michael@0: return NS_OK; michael@0: michael@0: // The encodings are listed in the order they were applied michael@0: // (see rfc 2616 section 14.11), so they need to removed in reverse michael@0: // order. This is accomplished because the converter chain ends up michael@0: // being a stack with the last converter created being the first one michael@0: // to accept the raw network data. michael@0: michael@0: cePtr = contentEncoding.BeginWriting(); michael@0: uint32_t count = 0; michael@0: while ((val = nsCRT::strtok(cePtr, HTTP_LWS ",", &cePtr))) { michael@0: if (++count > 16) { michael@0: // That's ridiculous. We only understand 2 different ones :) michael@0: // but for compatibility with old code, we will just carry on without michael@0: // removing the encodings michael@0: LOG(("Too many Content-Encodings. Ignoring remainder.\n")); michael@0: break; michael@0: } michael@0: michael@0: if (gHttpHandler->IsAcceptableEncoding(val)) { michael@0: nsCOMPtr serv; michael@0: rv = gHttpHandler->GetStreamConverterService(getter_AddRefs(serv)); michael@0: michael@0: // we won't fail to load the page just because we couldn't load the michael@0: // stream converter service.. carry on.. michael@0: if (NS_FAILED(rv)) { michael@0: if (val) michael@0: LOG(("Unknown content encoding '%s', ignoring\n", val)); michael@0: continue; michael@0: } michael@0: michael@0: nsCOMPtr converter; michael@0: nsAutoCString from(val); michael@0: ToLowerCase(from); michael@0: rv = serv->AsyncConvertData(from.get(), michael@0: "uncompressed", michael@0: mListener, michael@0: mListenerContext, michael@0: getter_AddRefs(converter)); michael@0: if (NS_FAILED(rv)) { michael@0: LOG(("Unexpected failure of AsyncConvertData %s\n", val)); michael@0: return rv; michael@0: } michael@0: michael@0: LOG(("converter removed '%s' content-encoding\n", val)); michael@0: mListener = converter; michael@0: } michael@0: else { michael@0: if (val) michael@0: LOG(("Unknown content encoding '%s', ignoring\n", val)); michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::GetContentEncodings(nsIUTF8StringEnumerator** aEncodings) michael@0: { michael@0: if (!mResponseHead) { michael@0: *aEncodings = nullptr; michael@0: return NS_OK; michael@0: } michael@0: michael@0: const char *encoding = mResponseHead->PeekHeader(nsHttp::Content_Encoding); michael@0: if (!encoding) { michael@0: *aEncodings = nullptr; michael@0: return NS_OK; michael@0: } michael@0: nsContentEncodings* enumerator = new nsContentEncodings(this, encoding); michael@0: NS_ADDREF(*aEncodings = enumerator); michael@0: return NS_OK; michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // HttpBaseChannel::nsContentEncodings michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: HttpBaseChannel::nsContentEncodings::nsContentEncodings(nsIHttpChannel* aChannel, michael@0: const char* aEncodingHeader) michael@0: : mEncodingHeader(aEncodingHeader) michael@0: , mChannel(aChannel) michael@0: , mReady(false) michael@0: { michael@0: mCurEnd = aEncodingHeader + strlen(aEncodingHeader); michael@0: mCurStart = mCurEnd; michael@0: } michael@0: michael@0: HttpBaseChannel::nsContentEncodings::~nsContentEncodings() michael@0: { michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // HttpBaseChannel::nsContentEncodings::nsISimpleEnumerator michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::nsContentEncodings::HasMore(bool* aMoreEncodings) michael@0: { michael@0: if (mReady) { michael@0: *aMoreEncodings = true; michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult rv = PrepareForNext(); michael@0: *aMoreEncodings = NS_SUCCEEDED(rv); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::nsContentEncodings::GetNext(nsACString& aNextEncoding) michael@0: { michael@0: aNextEncoding.Truncate(); michael@0: if (!mReady) { michael@0: nsresult rv = PrepareForNext(); michael@0: if (NS_FAILED(rv)) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: } michael@0: michael@0: const nsACString & encoding = Substring(mCurStart, mCurEnd); michael@0: michael@0: nsACString::const_iterator start, end; michael@0: encoding.BeginReading(start); michael@0: encoding.EndReading(end); michael@0: michael@0: bool haveType = false; michael@0: if (CaseInsensitiveFindInReadable(NS_LITERAL_CSTRING("gzip"), start, end)) { michael@0: aNextEncoding.AssignLiteral(APPLICATION_GZIP); michael@0: haveType = true; michael@0: } michael@0: michael@0: if (!haveType) { michael@0: encoding.BeginReading(start); michael@0: if (CaseInsensitiveFindInReadable(NS_LITERAL_CSTRING("compress"), start, end)) { michael@0: aNextEncoding.AssignLiteral(APPLICATION_COMPRESS); michael@0: haveType = true; michael@0: } michael@0: } michael@0: michael@0: if (!haveType) { michael@0: encoding.BeginReading(start); michael@0: if (CaseInsensitiveFindInReadable(NS_LITERAL_CSTRING("deflate"), start, end)) { michael@0: aNextEncoding.AssignLiteral(APPLICATION_ZIP); michael@0: haveType = true; michael@0: } michael@0: } michael@0: michael@0: // Prepare to fetch the next encoding michael@0: mCurEnd = mCurStart; michael@0: mReady = false; michael@0: michael@0: if (haveType) michael@0: return NS_OK; michael@0: michael@0: NS_WARNING("Unknown encoding type"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // HttpBaseChannel::nsContentEncodings::nsISupports michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: NS_IMPL_ISUPPORTS(HttpBaseChannel::nsContentEncodings, nsIUTF8StringEnumerator) michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // HttpBaseChannel::nsContentEncodings michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: nsresult michael@0: HttpBaseChannel::nsContentEncodings::PrepareForNext(void) michael@0: { michael@0: MOZ_ASSERT(mCurStart == mCurEnd, "Indeterminate state"); michael@0: michael@0: // At this point both mCurStart and mCurEnd point to somewhere michael@0: // past the end of the next thing we want to return michael@0: michael@0: while (mCurEnd != mEncodingHeader) { michael@0: --mCurEnd; michael@0: if (*mCurEnd != ',' && !nsCRT::IsAsciiSpace(*mCurEnd)) michael@0: break; michael@0: } michael@0: if (mCurEnd == mEncodingHeader) michael@0: return NS_ERROR_NOT_AVAILABLE; // no more encodings michael@0: ++mCurEnd; michael@0: michael@0: // At this point mCurEnd points to the first char _after_ the michael@0: // header we want. Furthermore, mCurEnd - 1 != mEncodingHeader michael@0: michael@0: mCurStart = mCurEnd - 1; michael@0: while (mCurStart != mEncodingHeader && michael@0: *mCurStart != ',' && !nsCRT::IsAsciiSpace(*mCurStart)) michael@0: --mCurStart; michael@0: if (*mCurStart == ',' || nsCRT::IsAsciiSpace(*mCurStart)) michael@0: ++mCurStart; // we stopped because of a weird char, so move up one michael@0: michael@0: // At this point mCurStart and mCurEnd bracket the encoding string michael@0: // we want. Check that it's not "identity" michael@0: if (Substring(mCurStart, mCurEnd).Equals("identity", michael@0: nsCaseInsensitiveCStringComparator())) { michael@0: mCurEnd = mCurStart; michael@0: return PrepareForNext(); michael@0: } michael@0: michael@0: mReady = true; michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // HttpBaseChannel::nsIHttpChannel michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::GetRequestMethod(nsACString& aMethod) michael@0: { michael@0: aMethod = mRequestHead.Method(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::SetRequestMethod(const nsACString& aMethod) michael@0: { michael@0: ENSURE_CALLED_BEFORE_CONNECT(); michael@0: michael@0: const nsCString& flatMethod = PromiseFlatCString(aMethod); michael@0: michael@0: // Method names are restricted to valid HTTP tokens. michael@0: if (!nsHttp::IsValidToken(flatMethod)) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: mRequestHead.SetMethod(flatMethod); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::GetReferrer(nsIURI **referrer) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(referrer); michael@0: *referrer = mReferrer; michael@0: NS_IF_ADDREF(*referrer); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::SetReferrer(nsIURI *referrer) michael@0: { michael@0: ENSURE_CALLED_BEFORE_CONNECT(); michael@0: michael@0: // clear existing referrer, if any michael@0: mReferrer = nullptr; michael@0: mRequestHead.ClearHeader(nsHttp::Referer); michael@0: michael@0: if (!referrer) michael@0: return NS_OK; michael@0: michael@0: // 0: never send referer michael@0: // 1: send referer for direct user action michael@0: // 2: always send referer michael@0: uint32_t userReferrerLevel = gHttpHandler->ReferrerLevel(); michael@0: michael@0: // false: use real referrer michael@0: // true: spoof with URI of the current request michael@0: bool userSpoofReferrerSource = gHttpHandler->SpoofReferrerSource(); michael@0: michael@0: // 0: full URI michael@0: // 1: scheme+host+port+path michael@0: // 2: scheme+host+port michael@0: int userReferrerTrimmingPolicy = gHttpHandler->ReferrerTrimmingPolicy(); michael@0: michael@0: // 0: send referer no matter what michael@0: // 1: send referer ONLY when base domains match michael@0: // 2: send referer ONLY when hosts match michael@0: int userReferrerXOriginPolicy = gHttpHandler->ReferrerXOriginPolicy(); michael@0: michael@0: // check referrer blocking pref michael@0: uint32_t referrerLevel; michael@0: if (mLoadFlags & LOAD_INITIAL_DOCUMENT_URI) michael@0: referrerLevel = 1; // user action michael@0: else michael@0: referrerLevel = 2; // inline content michael@0: if (userReferrerLevel < referrerLevel) michael@0: return NS_OK; michael@0: michael@0: nsCOMPtr referrerGrip; michael@0: nsresult rv; michael@0: bool match; michael@0: michael@0: // michael@0: // Strip off "wyciwyg://123/" from wyciwyg referrers. michael@0: // michael@0: // XXX this really belongs elsewhere since wyciwyg URLs aren't part of necko. michael@0: // perhaps some sort of generic nsINestedURI could be used. then, if an URI michael@0: // fails the whitelist test, then we could check for an inner URI and try michael@0: // that instead. though, that might be too automatic. michael@0: // michael@0: rv = referrer->SchemeIs("wyciwyg", &match); michael@0: if (NS_FAILED(rv)) return rv; michael@0: if (match) { michael@0: nsAutoCString path; michael@0: rv = referrer->GetPath(path); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: uint32_t pathLength = path.Length(); michael@0: if (pathLength <= 2) return NS_ERROR_FAILURE; michael@0: michael@0: // Path is of the form "//123/http://foo/bar", with a variable number of michael@0: // digits. To figure out where the "real" URL starts, search path for a michael@0: // '/', starting at the third character. michael@0: int32_t slashIndex = path.FindChar('/', 2); michael@0: if (slashIndex == kNotFound) return NS_ERROR_FAILURE; michael@0: michael@0: // Get charset of the original URI so we can pass it to our fixed up URI. michael@0: nsAutoCString charset; michael@0: referrer->GetOriginCharset(charset); michael@0: michael@0: // Replace |referrer| with a URI without wyciwyg://123/. michael@0: rv = NS_NewURI(getter_AddRefs(referrerGrip), michael@0: Substring(path, slashIndex + 1, pathLength - slashIndex - 1), michael@0: charset.get()); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: referrer = referrerGrip.get(); michael@0: } michael@0: michael@0: // michael@0: // block referrer if not on our white list... michael@0: // michael@0: static const char *const referrerWhiteList[] = { michael@0: "http", michael@0: "https", michael@0: "ftp", michael@0: nullptr michael@0: }; michael@0: match = false; michael@0: const char *const *scheme = referrerWhiteList; michael@0: for (; *scheme && !match; ++scheme) { michael@0: rv = referrer->SchemeIs(*scheme, &match); michael@0: if (NS_FAILED(rv)) return rv; michael@0: } michael@0: if (!match) michael@0: return NS_OK; // kick out.... michael@0: michael@0: // michael@0: // Handle secure referrals. michael@0: // michael@0: // Support referrals from a secure server if this is a secure site michael@0: // and (optionally) if the host names are the same. michael@0: // michael@0: rv = referrer->SchemeIs("https", &match); michael@0: if (NS_FAILED(rv)) return rv; michael@0: if (match) { michael@0: rv = mURI->SchemeIs("https", &match); michael@0: if (NS_FAILED(rv)) return rv; michael@0: if (!match) michael@0: return NS_OK; michael@0: michael@0: if (!gHttpHandler->SendSecureXSiteReferrer()) { michael@0: nsAutoCString referrerHost; michael@0: nsAutoCString host; michael@0: michael@0: rv = referrer->GetAsciiHost(referrerHost); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = mURI->GetAsciiHost(host); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: // GetAsciiHost returns lowercase hostname. michael@0: if (!referrerHost.Equals(host)) michael@0: return NS_OK; michael@0: } michael@0: } michael@0: michael@0: nsCOMPtr clone; michael@0: // michael@0: // we need to clone the referrer, so we can: michael@0: // (1) modify it michael@0: // (2) keep a reference to it after returning from this function michael@0: // michael@0: // Use CloneIgnoringRef to strip away any fragment per RFC 2616 section 14.36 michael@0: rv = referrer->CloneIgnoringRef(getter_AddRefs(clone)); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: nsAutoCString currentHost; michael@0: nsAutoCString referrerHost; michael@0: michael@0: rv = mURI->GetAsciiHost(currentHost); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = clone->GetAsciiHost(referrerHost); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: // check policy for sending ref only when hosts match michael@0: if (userReferrerXOriginPolicy == 2 && !currentHost.Equals(referrerHost)) michael@0: return NS_OK; michael@0: michael@0: if (userReferrerXOriginPolicy == 1) { michael@0: nsAutoCString currentDomain = currentHost; michael@0: nsAutoCString referrerDomain = referrerHost; michael@0: uint32_t extraDomains = 0; michael@0: nsCOMPtr eTLDService = do_GetService( michael@0: NS_EFFECTIVETLDSERVICE_CONTRACTID); michael@0: if (eTLDService) { michael@0: rv = eTLDService->GetBaseDomain(mURI, extraDomains, currentDomain); michael@0: if (NS_FAILED(rv)) return rv; michael@0: rv = eTLDService->GetBaseDomain(clone, extraDomains, referrerDomain); michael@0: if (NS_FAILED(rv)) return rv; michael@0: } michael@0: michael@0: // check policy for sending only when effective top level domain matches. michael@0: // this falls back on using host if eTLDService does not work michael@0: if (!currentDomain.Equals(referrerDomain)) michael@0: return NS_OK; michael@0: } michael@0: michael@0: // send spoofed referrer if desired michael@0: if (userSpoofReferrerSource) { michael@0: nsCOMPtr mURIclone; michael@0: rv = mURI->CloneIgnoringRef(getter_AddRefs(mURIclone)); michael@0: if (NS_FAILED(rv)) return rv; michael@0: clone = mURIclone; michael@0: currentHost = referrerHost; michael@0: } michael@0: michael@0: // strip away any userpass; we don't want to be giving out passwords ;-) michael@0: rv = clone->SetUserPass(EmptyCString()); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: nsAutoCString spec; michael@0: michael@0: // check how much referer to send michael@0: switch (userReferrerTrimmingPolicy) { michael@0: michael@0: case 1: { michael@0: // scheme+host+port+path michael@0: nsAutoCString prepath, path; michael@0: rv = clone->GetPrePath(prepath); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: nsCOMPtr url(do_QueryInterface(clone)); michael@0: if (!url) { michael@0: // if this isn't a url, play it safe michael@0: // and just send the prepath michael@0: spec = prepath; michael@0: break; michael@0: } michael@0: rv = url->GetFilePath(path); michael@0: if (NS_FAILED(rv)) return rv; michael@0: spec = prepath + path; michael@0: break; michael@0: } michael@0: case 2: michael@0: // scheme+host+port michael@0: rv = clone->GetPrePath(spec); michael@0: if (NS_FAILED(rv)) return rv; michael@0: break; michael@0: michael@0: default: michael@0: // full URI michael@0: rv = clone->GetAsciiSpec(spec); michael@0: if (NS_FAILED(rv)) return rv; michael@0: break; michael@0: } michael@0: michael@0: // finally, remember the referrer URI and set the Referer header. michael@0: mReferrer = clone; michael@0: mRequestHead.SetHeader(nsHttp::Referer, spec); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::GetProxyURI(nsIURI** proxyURI) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(proxyURI); michael@0: *proxyURI = mProxyURI; michael@0: NS_IF_ADDREF(*proxyURI); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::GetRequestHeader(const nsACString& aHeader, michael@0: nsACString& aValue) michael@0: { michael@0: // XXX might be better to search the header list directly instead of michael@0: // hitting the http atom hash table. michael@0: nsHttpAtom atom = nsHttp::ResolveAtom(aHeader); michael@0: if (!atom) michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: michael@0: return mRequestHead.GetHeader(atom, aValue); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::SetRequestHeader(const nsACString& aHeader, michael@0: const nsACString& aValue, michael@0: bool aMerge) michael@0: { michael@0: const nsCString &flatHeader = PromiseFlatCString(aHeader); michael@0: const nsCString &flatValue = PromiseFlatCString(aValue); michael@0: michael@0: LOG(("HttpBaseChannel::SetRequestHeader [this=%p header=\"%s\" value=\"%s\" merge=%u]\n", michael@0: this, flatHeader.get(), flatValue.get(), aMerge)); michael@0: michael@0: // Header names are restricted to valid HTTP tokens. michael@0: if (!nsHttp::IsValidToken(flatHeader)) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: // Header values MUST NOT contain line-breaks. RFC 2616 technically michael@0: // permits CTL characters, including CR and LF, in header values provided michael@0: // they are quoted. However, this can lead to problems if servers do not michael@0: // interpret quoted strings properly. Disallowing CR and LF here seems michael@0: // reasonable and keeps things simple. We also disallow a null byte. michael@0: if (flatValue.FindCharInSet("\r\n") != kNotFound || michael@0: flatValue.Length() != strlen(flatValue.get())) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: nsHttpAtom atom = nsHttp::ResolveAtom(flatHeader.get()); michael@0: if (!atom) { michael@0: NS_WARNING("failed to resolve atom"); michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: michael@0: return mRequestHead.SetHeader(atom, flatValue, aMerge); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::VisitRequestHeaders(nsIHttpHeaderVisitor *visitor) michael@0: { michael@0: return mRequestHead.Headers().VisitHeaders(visitor); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::GetResponseHeader(const nsACString &header, nsACString &value) michael@0: { michael@0: if (!mResponseHead) michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: michael@0: nsHttpAtom atom = nsHttp::ResolveAtom(header); michael@0: if (!atom) michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: michael@0: return mResponseHead->GetHeader(atom, value); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::SetResponseHeader(const nsACString& header, michael@0: const nsACString& value, michael@0: bool merge) michael@0: { michael@0: LOG(("HttpBaseChannel::SetResponseHeader [this=%p header=\"%s\" value=\"%s\" merge=%u]\n", michael@0: this, PromiseFlatCString(header).get(), PromiseFlatCString(value).get(), merge)); michael@0: michael@0: if (!mResponseHead) michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: michael@0: nsHttpAtom atom = nsHttp::ResolveAtom(header); michael@0: if (!atom) michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: michael@0: // these response headers must not be changed michael@0: if (atom == nsHttp::Content_Type || michael@0: atom == nsHttp::Content_Length || michael@0: atom == nsHttp::Content_Encoding || michael@0: atom == nsHttp::Trailer || michael@0: atom == nsHttp::Transfer_Encoding) michael@0: return NS_ERROR_ILLEGAL_VALUE; michael@0: michael@0: mResponseHeadersModified = true; michael@0: michael@0: return mResponseHead->SetHeader(atom, value, merge); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::VisitResponseHeaders(nsIHttpHeaderVisitor *visitor) michael@0: { michael@0: if (!mResponseHead) michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: return mResponseHead->Headers().VisitHeaders(visitor); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::GetAllowPipelining(bool *value) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(value); michael@0: *value = mAllowPipelining; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::SetAllowPipelining(bool value) michael@0: { michael@0: ENSURE_CALLED_BEFORE_CONNECT(); michael@0: michael@0: mAllowPipelining = value; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::GetRedirectionLimit(uint32_t *value) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(value); michael@0: *value = mRedirectionLimit; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::SetRedirectionLimit(uint32_t value) michael@0: { michael@0: ENSURE_CALLED_BEFORE_CONNECT(); michael@0: michael@0: mRedirectionLimit = std::min(value, 0xff); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::IsNoStoreResponse(bool *value) michael@0: { michael@0: if (!mResponseHead) michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: *value = mResponseHead->NoStore(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::IsNoCacheResponse(bool *value) michael@0: { michael@0: if (!mResponseHead) michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: *value = mResponseHead->NoCache(); michael@0: if (!*value) michael@0: *value = mResponseHead->ExpiresInPast(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::GetResponseStatus(uint32_t *aValue) michael@0: { michael@0: if (!mResponseHead) michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: *aValue = mResponseHead->Status(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::GetResponseStatusText(nsACString& aValue) michael@0: { michael@0: if (!mResponseHead) michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: aValue = mResponseHead->StatusText(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::GetRequestSucceeded(bool *aValue) michael@0: { michael@0: if (!mResponseHead) michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: uint32_t status = mResponseHead->Status(); michael@0: *aValue = (status / 100 == 2); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::RedirectTo(nsIURI *newURI) michael@0: { michael@0: // We can only redirect unopened channels michael@0: ENSURE_CALLED_BEFORE_CONNECT(); michael@0: michael@0: // The redirect is stored internally for use in AsyncOpen michael@0: mAPIRedirectToURI = newURI; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // HttpBaseChannel::nsIHttpChannelInternal michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::GetDocumentURI(nsIURI **aDocumentURI) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aDocumentURI); michael@0: *aDocumentURI = mDocumentURI; michael@0: NS_IF_ADDREF(*aDocumentURI); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::SetDocumentURI(nsIURI *aDocumentURI) michael@0: { michael@0: ENSURE_CALLED_BEFORE_CONNECT(); michael@0: michael@0: mDocumentURI = aDocumentURI; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::GetRequestVersion(uint32_t *major, uint32_t *minor) michael@0: { michael@0: nsHttpVersion version = mRequestHead.Version(); michael@0: michael@0: if (major) { *major = version / 10; } michael@0: if (minor) { *minor = version % 10; } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::GetResponseVersion(uint32_t *major, uint32_t *minor) michael@0: { michael@0: if (!mResponseHead) michael@0: { michael@0: *major = *minor = 0; // we should at least be kind about it michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: michael@0: nsHttpVersion version = mResponseHead->Version(); michael@0: michael@0: if (major) { *major = version / 10; } michael@0: if (minor) { *minor = version % 10; } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: namespace { michael@0: michael@0: class CookieNotifierRunnable : public nsRunnable michael@0: { michael@0: public: michael@0: CookieNotifierRunnable(HttpBaseChannel* aChannel, char const * aCookie) michael@0: : mChannel(aChannel), mCookie(aCookie) michael@0: { } michael@0: michael@0: NS_IMETHOD Run() michael@0: { michael@0: nsCOMPtr obs = services::GetObserverService(); michael@0: if (obs) { michael@0: obs->NotifyObservers(static_cast(mChannel.get()), michael@0: "http-on-response-set-cookie", michael@0: mCookie.get()); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: private: michael@0: nsRefPtr mChannel; michael@0: NS_ConvertASCIItoUTF16 mCookie; michael@0: }; michael@0: michael@0: } // anonymous namespace michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::SetCookie(const char *aCookieHeader) michael@0: { michael@0: if (mLoadFlags & LOAD_ANONYMOUS) michael@0: return NS_OK; michael@0: michael@0: // empty header isn't an error michael@0: if (!(aCookieHeader && *aCookieHeader)) michael@0: return NS_OK; michael@0: michael@0: nsICookieService *cs = gHttpHandler->GetCookieService(); michael@0: NS_ENSURE_TRUE(cs, NS_ERROR_FAILURE); michael@0: michael@0: nsresult rv = michael@0: cs->SetCookieStringFromHttp(mURI, nullptr, nullptr, aCookieHeader, michael@0: mResponseHead->PeekHeader(nsHttp::Date), this); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: nsRefPtr r = michael@0: new CookieNotifierRunnable(this, aCookieHeader); michael@0: NS_DispatchToMainThread(r); michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::GetForceAllowThirdPartyCookie(bool *aForce) michael@0: { michael@0: *aForce = mForceAllowThirdPartyCookie; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::SetForceAllowThirdPartyCookie(bool aForce) michael@0: { michael@0: ENSURE_CALLED_BEFORE_ASYNC_OPEN(); michael@0: michael@0: mForceAllowThirdPartyCookie = aForce; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::GetCanceled(bool *aCanceled) michael@0: { michael@0: *aCanceled = mCanceled; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::GetChannelIsForDownload(bool *aChannelIsForDownload) michael@0: { michael@0: *aChannelIsForDownload = mChannelIsForDownload; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::SetChannelIsForDownload(bool aChannelIsForDownload) michael@0: { michael@0: mChannelIsForDownload = aChannelIsForDownload; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::SetCacheKeysRedirectChain(nsTArray *cacheKeys) michael@0: { michael@0: mRedirectedCachekeys = cacheKeys; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::GetLocalAddress(nsACString& addr) michael@0: { michael@0: if (mSelfAddr.raw.family == PR_AF_UNSPEC) michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: michael@0: addr.SetCapacity(kIPv6CStrBufSize); michael@0: NetAddrToString(&mSelfAddr, addr.BeginWriting(), kIPv6CStrBufSize); michael@0: addr.SetLength(strlen(addr.BeginReading())); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::TakeAllSecurityMessages( michael@0: nsCOMArray &aMessages) michael@0: { michael@0: aMessages.Clear(); michael@0: aMessages.SwapElements(mSecurityConsoleMessages); michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* Please use this method with care. This can cause the message michael@0: * queue to grow large and cause the channel to take up a lot michael@0: * of memory. Use only static string messages and do not add michael@0: * server side data to the queue, as that can be large. michael@0: * Add only a limited number of messages to the queue to keep michael@0: * the channel size down and do so only in rare erroneous situations. michael@0: * More information can be found here: michael@0: * https://bugzilla.mozilla.org/show_bug.cgi?id=846918 michael@0: */ michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::AddSecurityMessage(const nsAString &aMessageTag, michael@0: const nsAString &aMessageCategory) michael@0: { michael@0: nsresult rv; michael@0: nsCOMPtr message = michael@0: do_CreateInstance(NS_SECURITY_CONSOLE_MESSAGE_CONTRACTID, &rv); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: message->SetTag(aMessageTag); michael@0: message->SetCategory(aMessageCategory); michael@0: mSecurityConsoleMessages.AppendElement(message); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::GetLocalPort(int32_t* port) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(port); michael@0: michael@0: if (mSelfAddr.raw.family == PR_AF_INET) { michael@0: *port = (int32_t)ntohs(mSelfAddr.inet.port); michael@0: } michael@0: else if (mSelfAddr.raw.family == PR_AF_INET6) { michael@0: *port = (int32_t)ntohs(mSelfAddr.inet6.port); michael@0: } michael@0: else michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::GetRemoteAddress(nsACString& addr) michael@0: { michael@0: if (mPeerAddr.raw.family == PR_AF_UNSPEC) michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: michael@0: addr.SetCapacity(kIPv6CStrBufSize); michael@0: NetAddrToString(&mPeerAddr, addr.BeginWriting(), kIPv6CStrBufSize); michael@0: addr.SetLength(strlen(addr.BeginReading())); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::GetRemotePort(int32_t* port) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(port); michael@0: michael@0: if (mPeerAddr.raw.family == PR_AF_INET) { michael@0: *port = (int32_t)ntohs(mPeerAddr.inet.port); michael@0: } michael@0: else if (mPeerAddr.raw.family == PR_AF_INET6) { michael@0: *port = (int32_t)ntohs(mPeerAddr.inet6.port); michael@0: } michael@0: else michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::HTTPUpgrade(const nsACString &aProtocolName, michael@0: nsIHttpUpgradeListener *aListener) michael@0: { michael@0: NS_ENSURE_ARG(!aProtocolName.IsEmpty()); michael@0: NS_ENSURE_ARG_POINTER(aListener); michael@0: michael@0: mUpgradeProtocol = aProtocolName; michael@0: mUpgradeProtocolCallback = aListener; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::GetAllowSpdy(bool *aAllowSpdy) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aAllowSpdy); michael@0: michael@0: *aAllowSpdy = mAllowSpdy; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::SetAllowSpdy(bool aAllowSpdy) michael@0: { michael@0: mAllowSpdy = aAllowSpdy; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::GetLoadAsBlocking(bool *aLoadAsBlocking) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aLoadAsBlocking); michael@0: *aLoadAsBlocking = mLoadAsBlocking; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::SetLoadAsBlocking(bool aLoadAsBlocking) michael@0: { michael@0: mLoadAsBlocking = aLoadAsBlocking; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::GetLoadUnblocked(bool *aLoadUnblocked) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aLoadUnblocked); michael@0: *aLoadUnblocked = mLoadUnblocked; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::SetLoadUnblocked(bool aLoadUnblocked) michael@0: { michael@0: mLoadUnblocked = aLoadUnblocked; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::GetApiRedirectToURI(nsIURI ** aResult) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aResult); michael@0: NS_IF_ADDREF(*aResult = mAPIRedirectToURI); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::GetResponseTimeoutEnabled(bool *aEnable) michael@0: { michael@0: if (NS_WARN_IF(!aEnable)) { michael@0: return NS_ERROR_NULL_POINTER; michael@0: } michael@0: *aEnable = mResponseTimeoutEnabled; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::SetResponseTimeoutEnabled(bool aEnable) michael@0: { michael@0: mResponseTimeoutEnabled = aEnable; michael@0: return NS_OK; michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // HttpBaseChannel::nsISupportsPriority michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::GetPriority(int32_t *value) michael@0: { michael@0: *value = mPriority; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::AdjustPriority(int32_t delta) michael@0: { michael@0: return SetPriority(mPriority + delta); michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // HttpBaseChannel::nsIResumableChannel michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::GetEntityID(nsACString& aEntityID) michael@0: { michael@0: // Don't return an entity ID for Non-GET requests which require michael@0: // additional data michael@0: if (!mRequestHead.IsGet()) { michael@0: return NS_ERROR_NOT_RESUMABLE; michael@0: } michael@0: michael@0: uint64_t size = UINT64_MAX; michael@0: nsAutoCString etag, lastmod; michael@0: if (mResponseHead) { michael@0: // Don't return an entity if the server sent the following header: michael@0: // Accept-Ranges: none michael@0: // Not sending the Accept-Ranges header means we can still try michael@0: // sending range requests. michael@0: const char* acceptRanges = michael@0: mResponseHead->PeekHeader(nsHttp::Accept_Ranges); michael@0: if (acceptRanges && michael@0: !nsHttp::FindToken(acceptRanges, "bytes", HTTP_HEADER_VALUE_SEPS)) { michael@0: return NS_ERROR_NOT_RESUMABLE; michael@0: } michael@0: michael@0: size = mResponseHead->TotalEntitySize(); michael@0: const char* cLastMod = mResponseHead->PeekHeader(nsHttp::Last_Modified); michael@0: if (cLastMod) michael@0: lastmod = cLastMod; michael@0: const char* cEtag = mResponseHead->PeekHeader(nsHttp::ETag); michael@0: if (cEtag) michael@0: etag = cEtag; michael@0: } michael@0: nsCString entityID; michael@0: NS_EscapeURL(etag.BeginReading(), etag.Length(), esc_AlwaysCopy | michael@0: esc_FileBaseName | esc_Forced, entityID); michael@0: entityID.Append('/'); michael@0: entityID.AppendInt(int64_t(size)); michael@0: entityID.Append('/'); michael@0: entityID.Append(lastmod); michael@0: // NOTE: Appending lastmod as the last part avoids having to escape it michael@0: michael@0: aEntityID = entityID; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // nsHttpChannel::nsITraceableChannel michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::SetNewListener(nsIStreamListener *aListener, nsIStreamListener **_retval) michael@0: { michael@0: if (!mTracingEnabled) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: NS_ENSURE_ARG_POINTER(aListener); michael@0: michael@0: nsCOMPtr wrapper = new nsStreamListenerWrapper(mListener); michael@0: michael@0: wrapper.forget(_retval); michael@0: mListener = aListener; michael@0: return NS_OK; michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // HttpBaseChannel helpers michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: void michael@0: HttpBaseChannel::ReleaseListeners() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread(), "Should only be called on the main thread."); michael@0: michael@0: mListener = nullptr; michael@0: mListenerContext = nullptr; michael@0: mCallbacks = nullptr; michael@0: mProgressSink = nullptr; michael@0: } michael@0: michael@0: void michael@0: HttpBaseChannel::DoNotifyListener() michael@0: { michael@0: // Make sure mIsPending is set to false. At this moment we are done from michael@0: // the point of view of our consumer and we have to report our self michael@0: // as not-pending. michael@0: if (mListener) { michael@0: mListener->OnStartRequest(this, mListenerContext); michael@0: mIsPending = false; michael@0: mListener->OnStopRequest(this, mListenerContext, mStatus); michael@0: } else { michael@0: mIsPending = false; michael@0: } michael@0: // We have to make sure to drop the references to listeners and callbacks michael@0: // no longer needed michael@0: ReleaseListeners(); michael@0: michael@0: DoNotifyListenerCleanup(); michael@0: } michael@0: michael@0: void michael@0: HttpBaseChannel::AddCookiesToRequest() michael@0: { michael@0: if (mLoadFlags & LOAD_ANONYMOUS) { michael@0: return; michael@0: } michael@0: michael@0: bool useCookieService = michael@0: (XRE_GetProcessType() == GeckoProcessType_Default); michael@0: nsXPIDLCString cookie; michael@0: if (useCookieService) { michael@0: nsICookieService *cs = gHttpHandler->GetCookieService(); michael@0: if (cs) { michael@0: cs->GetCookieStringFromHttp(mURI, michael@0: nullptr, michael@0: this, getter_Copies(cookie)); michael@0: } michael@0: michael@0: if (cookie.IsEmpty()) { michael@0: cookie = mUserSetCookieHeader; michael@0: } michael@0: else if (!mUserSetCookieHeader.IsEmpty()) { michael@0: cookie.Append(NS_LITERAL_CSTRING("; ") + mUserSetCookieHeader); michael@0: } michael@0: } michael@0: else { michael@0: cookie = mUserSetCookieHeader; michael@0: } michael@0: michael@0: // If we are in the child process, we want the parent seeing any michael@0: // cookie headers that might have been set by SetRequestHeader() michael@0: SetRequestHeader(nsDependentCString(nsHttp::Cookie), cookie, false); michael@0: } michael@0: michael@0: static PLDHashOperator michael@0: CopyProperties(const nsAString& aKey, nsIVariant *aData, void *aClosure) michael@0: { michael@0: nsIWritablePropertyBag* bag = static_cast michael@0: (aClosure); michael@0: bag->SetProperty(aKey, aData); michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: michael@0: bool michael@0: HttpBaseChannel::ShouldRewriteRedirectToGET(uint32_t httpStatus, michael@0: nsHttpRequestHead::ParsedMethodType method) michael@0: { michael@0: // for 301 and 302, only rewrite POST michael@0: if (httpStatus == 301 || httpStatus == 302) michael@0: return method == nsHttpRequestHead::kMethod_Post; michael@0: michael@0: // rewrite for 303 unless it was HEAD michael@0: if (httpStatus == 303) michael@0: return method != nsHttpRequestHead::kMethod_Head; michael@0: michael@0: // otherwise, such as for 307, do not rewrite michael@0: return false; michael@0: } michael@0: michael@0: nsresult michael@0: HttpBaseChannel::SetupReplacementChannel(nsIURI *newURI, michael@0: nsIChannel *newChannel, michael@0: bool preserveMethod) michael@0: { michael@0: LOG(("HttpBaseChannel::SetupReplacementChannel " michael@0: "[this=%p newChannel=%p preserveMethod=%d]", michael@0: this, newChannel, preserveMethod)); michael@0: uint32_t newLoadFlags = mLoadFlags | LOAD_REPLACE; michael@0: // if the original channel was using SSL and this channel is not using michael@0: // SSL, then no need to inhibit persistent caching. however, if the michael@0: // original channel was not using SSL and has INHIBIT_PERSISTENT_CACHING michael@0: // set, then allow the flag to apply to the redirected channel as well. michael@0: // since we force set INHIBIT_PERSISTENT_CACHING on all HTTPS channels, michael@0: // we only need to check if the original channel was using SSL. michael@0: bool usingSSL = false; michael@0: nsresult rv = mURI->SchemeIs("https", &usingSSL); michael@0: if (NS_SUCCEEDED(rv) && usingSSL) michael@0: newLoadFlags &= ~INHIBIT_PERSISTENT_CACHING; michael@0: michael@0: // Do not pass along LOAD_CHECK_OFFLINE_CACHE michael@0: newLoadFlags &= ~nsICachingChannel::LOAD_CHECK_OFFLINE_CACHE; michael@0: michael@0: newChannel->SetLoadGroup(mLoadGroup); michael@0: newChannel->SetNotificationCallbacks(mCallbacks); michael@0: newChannel->SetLoadFlags(newLoadFlags); michael@0: michael@0: // If our owner is a null principal it will have been set as a security michael@0: // measure, so we want to propagate it to the new channel. michael@0: nsCOMPtr ownerPrincipal = do_QueryInterface(mOwner); michael@0: if (ownerPrincipal && ownerPrincipal->GetIsNullPrincipal()) { michael@0: newChannel->SetOwner(mOwner); michael@0: } michael@0: michael@0: // Try to preserve the privacy bit if it has been overridden michael@0: if (mPrivateBrowsingOverriden) { michael@0: nsCOMPtr newPBChannel = michael@0: do_QueryInterface(newChannel); michael@0: if (newPBChannel) { michael@0: newPBChannel->SetPrivate(mPrivateBrowsing); michael@0: } michael@0: } michael@0: michael@0: nsCOMPtr httpChannel = do_QueryInterface(newChannel); michael@0: if (!httpChannel) michael@0: return NS_OK; // no other options to set michael@0: michael@0: if (preserveMethod) { michael@0: nsCOMPtr uploadChannel = michael@0: do_QueryInterface(httpChannel); michael@0: nsCOMPtr uploadChannel2 = michael@0: do_QueryInterface(httpChannel); michael@0: if (mUploadStream && (uploadChannel2 || uploadChannel)) { michael@0: // rewind upload stream michael@0: nsCOMPtr seekable = do_QueryInterface(mUploadStream); michael@0: if (seekable) michael@0: seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0); michael@0: michael@0: // replicate original call to SetUploadStream... michael@0: if (uploadChannel2) { michael@0: const char *ctype = mRequestHead.PeekHeader(nsHttp::Content_Type); michael@0: if (!ctype) michael@0: ctype = ""; michael@0: const char *clen = mRequestHead.PeekHeader(nsHttp::Content_Length); michael@0: int64_t len = clen ? nsCRT::atoll(clen) : -1; michael@0: uploadChannel2->ExplicitSetUploadStream( michael@0: mUploadStream, nsDependentCString(ctype), len, michael@0: mRequestHead.Method(), michael@0: mUploadStreamHasHeaders); michael@0: } else { michael@0: if (mUploadStreamHasHeaders) { michael@0: uploadChannel->SetUploadStream(mUploadStream, EmptyCString(), michael@0: -1); michael@0: } else { michael@0: const char *ctype = michael@0: mRequestHead.PeekHeader(nsHttp::Content_Type); michael@0: const char *clen = michael@0: mRequestHead.PeekHeader(nsHttp::Content_Length); michael@0: if (!ctype) { michael@0: ctype = "application/octet-stream"; michael@0: } michael@0: if (clen) { michael@0: uploadChannel->SetUploadStream(mUploadStream, michael@0: nsDependentCString(ctype), michael@0: nsCRT::atoll(clen)); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: // since preserveMethod is true, we need to ensure that the appropriate michael@0: // request method gets set on the channel, regardless of whether or not michael@0: // we set the upload stream above. This means SetRequestMethod() will michael@0: // be called twice if ExplicitSetUploadStream() gets called above. michael@0: michael@0: httpChannel->SetRequestMethod(mRequestHead.Method()); michael@0: } michael@0: // convey the referrer if one was used for this channel to the next one michael@0: if (mReferrer) michael@0: httpChannel->SetReferrer(mReferrer); michael@0: // convey the mAllowPipelining flag michael@0: httpChannel->SetAllowPipelining(mAllowPipelining); michael@0: // convey the new redirection limit michael@0: httpChannel->SetRedirectionLimit(mRedirectionLimit - 1); michael@0: michael@0: // convey the Accept header value michael@0: { michael@0: nsAutoCString oldAcceptValue; michael@0: nsresult hasHeader = mRequestHead.GetHeader(nsHttp::Accept, oldAcceptValue); michael@0: if (NS_SUCCEEDED(hasHeader)) { michael@0: httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept"), michael@0: oldAcceptValue, michael@0: false); michael@0: } michael@0: } michael@0: michael@0: nsCOMPtr httpInternal = do_QueryInterface(newChannel); michael@0: if (httpInternal) { michael@0: // convey the mForceAllowThirdPartyCookie flag michael@0: httpInternal->SetForceAllowThirdPartyCookie(mForceAllowThirdPartyCookie); michael@0: // convey the spdy flag michael@0: httpInternal->SetAllowSpdy(mAllowSpdy); michael@0: michael@0: // update the DocumentURI indicator since we are being redirected. michael@0: // if this was a top-level document channel, then the new channel michael@0: // should have its mDocumentURI point to newURI; otherwise, we michael@0: // just need to pass along our mDocumentURI to the new channel. michael@0: if (newURI && (mURI == mDocumentURI)) michael@0: httpInternal->SetDocumentURI(newURI); michael@0: else michael@0: httpInternal->SetDocumentURI(mDocumentURI); michael@0: michael@0: // if there is a chain of keys for redirect-responses we transfer it to michael@0: // the new channel (see bug #561276) michael@0: if (mRedirectedCachekeys) { michael@0: LOG(("HttpBaseChannel::SetupReplacementChannel " michael@0: "[this=%p] transferring chain of redirect cache-keys", this)); michael@0: httpInternal->SetCacheKeysRedirectChain(mRedirectedCachekeys.forget()); michael@0: } michael@0: } michael@0: michael@0: // transfer application cache information michael@0: nsCOMPtr appCacheChannel = michael@0: do_QueryInterface(newChannel); michael@0: if (appCacheChannel) { michael@0: appCacheChannel->SetApplicationCache(mApplicationCache); michael@0: appCacheChannel->SetInheritApplicationCache(mInheritApplicationCache); michael@0: // We purposely avoid transfering mChooseApplicationCache. michael@0: } michael@0: michael@0: // transfer any properties michael@0: nsCOMPtr bag(do_QueryInterface(newChannel)); michael@0: if (bag) michael@0: mPropertyHash.EnumerateRead(CopyProperties, bag.get()); michael@0: michael@0: // Transfer the timing data (if we are dealing with an nsITimedChannel). michael@0: nsCOMPtr newTimedChannel(do_QueryInterface(newChannel)); michael@0: nsCOMPtr oldTimedChannel( michael@0: do_QueryInterface(static_cast(this))); michael@0: if (oldTimedChannel && newTimedChannel) { michael@0: newTimedChannel->SetTimingEnabled(mTimingEnabled); michael@0: newTimedChannel->SetRedirectCount(mRedirectCount + 1); michael@0: michael@0: // If the RedirectStart is null, we will use the AsyncOpen value of the michael@0: // previous channel (this is the first redirect in the redirects chain). michael@0: if (mRedirectStartTimeStamp.IsNull()) { michael@0: TimeStamp asyncOpen; michael@0: oldTimedChannel->GetAsyncOpen(&asyncOpen); michael@0: newTimedChannel->SetRedirectStart(asyncOpen); michael@0: } michael@0: else { michael@0: newTimedChannel->SetRedirectStart(mRedirectStartTimeStamp); michael@0: } michael@0: michael@0: // The RedirectEnd timestamp is equal to the previous channel response end. michael@0: TimeStamp prevResponseEnd; michael@0: oldTimedChannel->GetResponseEnd(&prevResponseEnd); michael@0: newTimedChannel->SetRedirectEnd(prevResponseEnd); michael@0: michael@0: // Check whether or not this was a cross-domain redirect. michael@0: newTimedChannel->SetAllRedirectsSameOrigin( michael@0: mAllRedirectsSameOrigin && SameOriginWithOriginalUri(newURI)); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Redirect Tracking michael@0: bool michael@0: HttpBaseChannel::SameOriginWithOriginalUri(nsIURI *aURI) michael@0: { michael@0: nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager(); michael@0: nsresult rv = ssm->CheckSameOriginURI(aURI, mOriginalURI, false); michael@0: return (NS_SUCCEEDED(rv)); michael@0: } michael@0: michael@0: michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // HttpBaseChannel::nsITimedChannel michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::SetTimingEnabled(bool enabled) { michael@0: mTimingEnabled = enabled; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::GetTimingEnabled(bool* _retval) { michael@0: *_retval = mTimingEnabled; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::GetChannelCreation(TimeStamp* _retval) { michael@0: *_retval = mChannelCreationTimestamp; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::GetAsyncOpen(TimeStamp* _retval) { michael@0: *_retval = mAsyncOpenTime; michael@0: return NS_OK; michael@0: } michael@0: michael@0: /** michael@0: * @return the number of redirects. There is no check for cross-domain michael@0: * redirects. This check must be done by the consumers. michael@0: */ michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::GetRedirectCount(uint16_t *aRedirectCount) michael@0: { michael@0: *aRedirectCount = mRedirectCount; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::SetRedirectCount(uint16_t aRedirectCount) michael@0: { michael@0: mRedirectCount = aRedirectCount; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::GetRedirectStart(TimeStamp* _retval) michael@0: { michael@0: *_retval = mRedirectStartTimeStamp; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::SetRedirectStart(TimeStamp aRedirectStart) michael@0: { michael@0: mRedirectStartTimeStamp = aRedirectStart; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::GetRedirectEnd(TimeStamp* _retval) michael@0: { michael@0: *_retval = mRedirectEndTimeStamp; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::SetRedirectEnd(TimeStamp aRedirectEnd) michael@0: { michael@0: mRedirectEndTimeStamp = aRedirectEnd; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::GetAllRedirectsSameOrigin(bool *aAllRedirectsSameOrigin) michael@0: { michael@0: *aAllRedirectsSameOrigin = mAllRedirectsSameOrigin; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::SetAllRedirectsSameOrigin(bool aAllRedirectsSameOrigin) michael@0: { michael@0: mAllRedirectsSameOrigin = aAllRedirectsSameOrigin; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::GetDomainLookupStart(TimeStamp* _retval) { michael@0: *_retval = mTransactionTimings.domainLookupStart; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::GetDomainLookupEnd(TimeStamp* _retval) { michael@0: *_retval = mTransactionTimings.domainLookupEnd; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::GetConnectStart(TimeStamp* _retval) { michael@0: *_retval = mTransactionTimings.connectStart; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::GetConnectEnd(TimeStamp* _retval) { michael@0: *_retval = mTransactionTimings.connectEnd; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::GetRequestStart(TimeStamp* _retval) { michael@0: *_retval = mTransactionTimings.requestStart; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::GetResponseStart(TimeStamp* _retval) { michael@0: *_retval = mTransactionTimings.responseStart; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::GetResponseEnd(TimeStamp* _retval) { michael@0: *_retval = mTransactionTimings.responseEnd; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::GetCacheReadStart(TimeStamp* _retval) { michael@0: *_retval = mCacheReadStart; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::GetCacheReadEnd(TimeStamp* _retval) { michael@0: *_retval = mCacheReadEnd; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::GetInitiatorType(nsAString & aInitiatorType) michael@0: { michael@0: aInitiatorType = mInitiatorType; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpBaseChannel::SetInitiatorType(const nsAString & aInitiatorType) michael@0: { michael@0: mInitiatorType = aInitiatorType; michael@0: return NS_OK; michael@0: } michael@0: michael@0: #define IMPL_TIMING_ATTR(name) \ michael@0: NS_IMETHODIMP \ michael@0: HttpBaseChannel::Get##name##Time(PRTime* _retval) { \ michael@0: TimeStamp stamp; \ michael@0: Get##name(&stamp); \ michael@0: if (stamp.IsNull()) { \ michael@0: *_retval = 0; \ michael@0: return NS_OK; \ michael@0: } \ michael@0: *_retval = mChannelCreationTime + \ michael@0: (PRTime) ((stamp - mChannelCreationTimestamp).ToSeconds() * 1e6); \ michael@0: return NS_OK; \ michael@0: } michael@0: michael@0: IMPL_TIMING_ATTR(ChannelCreation) michael@0: IMPL_TIMING_ATTR(AsyncOpen) michael@0: IMPL_TIMING_ATTR(DomainLookupStart) michael@0: IMPL_TIMING_ATTR(DomainLookupEnd) michael@0: IMPL_TIMING_ATTR(ConnectStart) michael@0: IMPL_TIMING_ATTR(ConnectEnd) michael@0: IMPL_TIMING_ATTR(RequestStart) michael@0: IMPL_TIMING_ATTR(ResponseStart) michael@0: IMPL_TIMING_ATTR(ResponseEnd) michael@0: IMPL_TIMING_ATTR(CacheReadStart) michael@0: IMPL_TIMING_ATTR(CacheReadEnd) michael@0: IMPL_TIMING_ATTR(RedirectStart) michael@0: IMPL_TIMING_ATTR(RedirectEnd) michael@0: michael@0: #undef IMPL_TIMING_ATTR michael@0: michael@0: michael@0: //------------------------------------------------------------------------------ michael@0: michael@0: } // namespace net michael@0: } // namespace mozilla michael@0: