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: #ifndef mozilla_net_HttpBaseChannel_h michael@0: #define mozilla_net_HttpBaseChannel_h michael@0: michael@0: #include "nsHttp.h" michael@0: #include "nsAutoPtr.h" michael@0: #include "nsHashPropertyBag.h" michael@0: #include "nsProxyInfo.h" michael@0: #include "nsHttpRequestHead.h" michael@0: #include "nsHttpResponseHead.h" michael@0: #include "nsHttpConnectionInfo.h" michael@0: #include "nsIEncodedChannel.h" michael@0: #include "nsIHttpChannel.h" michael@0: #include "nsHttpHandler.h" michael@0: #include "nsIHttpChannelInternal.h" michael@0: #include "nsIUploadChannel.h" michael@0: #include "nsIUploadChannel2.h" michael@0: #include "nsIProgressEventSink.h" michael@0: #include "nsIURI.h" michael@0: #include "nsIEffectiveTLDService.h" michael@0: #include "nsIStringEnumerator.h" michael@0: #include "nsISupportsPriority.h" michael@0: #include "nsIApplicationCache.h" michael@0: #include "nsIResumableChannel.h" michael@0: #include "nsITraceableChannel.h" michael@0: #include "nsILoadContext.h" michael@0: #include "mozilla/net/NeckoCommon.h" michael@0: #include "nsThreadUtils.h" michael@0: #include "PrivateBrowsingChannel.h" michael@0: #include "mozilla/net/DNS.h" michael@0: #include "nsITimedChannel.h" michael@0: #include "nsISecurityConsoleMessage.h" michael@0: michael@0: extern PRLogModuleInfo *gHttpLog; michael@0: michael@0: namespace mozilla { michael@0: namespace net { michael@0: michael@0: /* michael@0: * This class is a partial implementation of nsIHttpChannel. It contains code michael@0: * shared by nsHttpChannel and HttpChannelChild. michael@0: * - Note that this class has nothing to do with nsBaseChannel, which is an michael@0: * earlier effort at a base class for channels that somehow never made it all michael@0: * the way to the HTTP channel. michael@0: */ michael@0: class HttpBaseChannel : public nsHashPropertyBag michael@0: , public nsIEncodedChannel michael@0: , public nsIHttpChannel michael@0: , public nsIHttpChannelInternal michael@0: , public nsIUploadChannel michael@0: , public nsIUploadChannel2 michael@0: , public nsISupportsPriority michael@0: , public nsIResumableChannel michael@0: , public nsITraceableChannel michael@0: , public PrivateBrowsingChannel michael@0: , public nsITimedChannel michael@0: { michael@0: public: michael@0: NS_DECL_ISUPPORTS_INHERITED michael@0: NS_DECL_NSIUPLOADCHANNEL michael@0: NS_DECL_NSIUPLOADCHANNEL2 michael@0: NS_DECL_NSITRACEABLECHANNEL michael@0: NS_DECL_NSITIMEDCHANNEL michael@0: michael@0: HttpBaseChannel(); michael@0: virtual ~HttpBaseChannel(); michael@0: michael@0: virtual nsresult Init(nsIURI *aURI, uint32_t aCaps, nsProxyInfo *aProxyInfo, michael@0: uint32_t aProxyResolveFlags, michael@0: nsIURI *aProxyURI); michael@0: michael@0: // nsIRequest michael@0: NS_IMETHOD GetName(nsACString& aName); michael@0: NS_IMETHOD IsPending(bool *aIsPending); michael@0: NS_IMETHOD GetStatus(nsresult *aStatus); michael@0: NS_IMETHOD GetLoadGroup(nsILoadGroup **aLoadGroup); michael@0: NS_IMETHOD SetLoadGroup(nsILoadGroup *aLoadGroup); michael@0: NS_IMETHOD GetLoadFlags(nsLoadFlags *aLoadFlags); michael@0: NS_IMETHOD SetLoadFlags(nsLoadFlags aLoadFlags); michael@0: michael@0: // nsIChannel michael@0: NS_IMETHOD GetOriginalURI(nsIURI **aOriginalURI); michael@0: NS_IMETHOD SetOriginalURI(nsIURI *aOriginalURI); michael@0: NS_IMETHOD GetURI(nsIURI **aURI); michael@0: NS_IMETHOD GetOwner(nsISupports **aOwner); michael@0: NS_IMETHOD SetOwner(nsISupports *aOwner); michael@0: NS_IMETHOD GetNotificationCallbacks(nsIInterfaceRequestor **aCallbacks); michael@0: NS_IMETHOD SetNotificationCallbacks(nsIInterfaceRequestor *aCallbacks); michael@0: NS_IMETHOD GetContentType(nsACString& aContentType); michael@0: NS_IMETHOD SetContentType(const nsACString& aContentType); michael@0: NS_IMETHOD GetContentCharset(nsACString& aContentCharset); michael@0: NS_IMETHOD SetContentCharset(const nsACString& aContentCharset); michael@0: NS_IMETHOD GetContentDisposition(uint32_t *aContentDisposition); michael@0: NS_IMETHOD SetContentDisposition(uint32_t aContentDisposition); michael@0: NS_IMETHOD GetContentDispositionFilename(nsAString& aContentDispositionFilename); michael@0: NS_IMETHOD SetContentDispositionFilename(const nsAString& aContentDispositionFilename); michael@0: NS_IMETHOD GetContentDispositionHeader(nsACString& aContentDispositionHeader); michael@0: NS_IMETHOD GetContentLength(int64_t *aContentLength); michael@0: NS_IMETHOD SetContentLength(int64_t aContentLength); michael@0: NS_IMETHOD Open(nsIInputStream **aResult); michael@0: michael@0: // nsIEncodedChannel michael@0: NS_IMETHOD GetApplyConversion(bool *value); michael@0: NS_IMETHOD SetApplyConversion(bool value); michael@0: NS_IMETHOD GetContentEncodings(nsIUTF8StringEnumerator** aEncodings); michael@0: michael@0: // HttpBaseChannel::nsIHttpChannel michael@0: NS_IMETHOD GetRequestMethod(nsACString& aMethod); michael@0: NS_IMETHOD SetRequestMethod(const nsACString& aMethod); michael@0: NS_IMETHOD GetReferrer(nsIURI **referrer); michael@0: NS_IMETHOD SetReferrer(nsIURI *referrer); michael@0: NS_IMETHOD GetProxyURI(nsIURI **proxyURI); michael@0: NS_IMETHOD GetRequestHeader(const nsACString& aHeader, nsACString& aValue); michael@0: NS_IMETHOD SetRequestHeader(const nsACString& aHeader, michael@0: const nsACString& aValue, bool aMerge); michael@0: NS_IMETHOD VisitRequestHeaders(nsIHttpHeaderVisitor *visitor); michael@0: NS_IMETHOD GetResponseHeader(const nsACString &header, nsACString &value); michael@0: NS_IMETHOD SetResponseHeader(const nsACString& header, michael@0: const nsACString& value, bool merge); michael@0: NS_IMETHOD VisitResponseHeaders(nsIHttpHeaderVisitor *visitor); michael@0: NS_IMETHOD GetAllowPipelining(bool *value); michael@0: NS_IMETHOD SetAllowPipelining(bool value); michael@0: NS_IMETHOD GetRedirectionLimit(uint32_t *value); michael@0: NS_IMETHOD SetRedirectionLimit(uint32_t value); michael@0: NS_IMETHOD IsNoStoreResponse(bool *value); michael@0: NS_IMETHOD IsNoCacheResponse(bool *value); michael@0: NS_IMETHOD GetResponseStatus(uint32_t *aValue); michael@0: NS_IMETHOD GetResponseStatusText(nsACString& aValue); michael@0: NS_IMETHOD GetRequestSucceeded(bool *aValue); michael@0: NS_IMETHOD RedirectTo(nsIURI *newURI); michael@0: michael@0: // nsIHttpChannelInternal michael@0: NS_IMETHOD GetDocumentURI(nsIURI **aDocumentURI); michael@0: NS_IMETHOD SetDocumentURI(nsIURI *aDocumentURI); michael@0: NS_IMETHOD GetRequestVersion(uint32_t *major, uint32_t *minor); michael@0: NS_IMETHOD GetResponseVersion(uint32_t *major, uint32_t *minor); michael@0: NS_IMETHOD SetCookie(const char *aCookieHeader); michael@0: NS_IMETHOD GetForceAllowThirdPartyCookie(bool *aForce); michael@0: NS_IMETHOD SetForceAllowThirdPartyCookie(bool aForce); michael@0: NS_IMETHOD GetCanceled(bool *aCanceled); michael@0: NS_IMETHOD GetChannelIsForDownload(bool *aChannelIsForDownload); michael@0: NS_IMETHOD SetChannelIsForDownload(bool aChannelIsForDownload); michael@0: NS_IMETHOD SetCacheKeysRedirectChain(nsTArray *cacheKeys); michael@0: NS_IMETHOD GetLocalAddress(nsACString& addr); michael@0: NS_IMETHOD GetLocalPort(int32_t* port); michael@0: NS_IMETHOD GetRemoteAddress(nsACString& addr); michael@0: NS_IMETHOD GetRemotePort(int32_t* port); michael@0: NS_IMETHOD GetAllowSpdy(bool *aAllowSpdy); michael@0: NS_IMETHOD SetAllowSpdy(bool aAllowSpdy); michael@0: NS_IMETHOD GetLoadAsBlocking(bool *aLoadAsBlocking); michael@0: NS_IMETHOD SetLoadAsBlocking(bool aLoadAsBlocking); michael@0: NS_IMETHOD GetLoadUnblocked(bool *aLoadUnblocked); michael@0: NS_IMETHOD SetLoadUnblocked(bool aLoadUnblocked); michael@0: NS_IMETHOD GetApiRedirectToURI(nsIURI * *aApiRedirectToURI); michael@0: NS_IMETHOD AddSecurityMessage(const nsAString &aMessageTag, const nsAString &aMessageCategory); michael@0: NS_IMETHOD TakeAllSecurityMessages(nsCOMArray &aMessages); michael@0: NS_IMETHOD GetResponseTimeoutEnabled(bool *aEnable); michael@0: NS_IMETHOD SetResponseTimeoutEnabled(bool aEnable); michael@0: michael@0: inline void CleanRedirectCacheChainIfNecessary() michael@0: { michael@0: mRedirectedCachekeys = nullptr; michael@0: } michael@0: NS_IMETHOD HTTPUpgrade(const nsACString & aProtocolName, michael@0: nsIHttpUpgradeListener *aListener); michael@0: michael@0: // nsISupportsPriority michael@0: NS_IMETHOD GetPriority(int32_t *value); michael@0: NS_IMETHOD AdjustPriority(int32_t delta); michael@0: michael@0: // nsIResumableChannel michael@0: NS_IMETHOD GetEntityID(nsACString& aEntityID); michael@0: michael@0: class nsContentEncodings : public nsIUTF8StringEnumerator michael@0: { michael@0: public: michael@0: NS_DECL_ISUPPORTS michael@0: NS_DECL_NSIUTF8STRINGENUMERATOR michael@0: michael@0: nsContentEncodings(nsIHttpChannel* aChannel, const char* aEncodingHeader); michael@0: virtual ~nsContentEncodings(); michael@0: michael@0: private: michael@0: nsresult PrepareForNext(void); michael@0: michael@0: // We do not own the buffer. The channel owns it. michael@0: const char* mEncodingHeader; michael@0: const char* mCurStart; // points to start of current header michael@0: const char* mCurEnd; // points to end of current header michael@0: michael@0: // Hold a ref to our channel so that it can't go away and take the michael@0: // header with it. michael@0: nsCOMPtr mChannel; michael@0: michael@0: bool mReady; michael@0: }; michael@0: michael@0: nsHttpResponseHead * GetResponseHead() const { return mResponseHead; } michael@0: nsHttpRequestHead * GetRequestHead() { return &mRequestHead; } michael@0: michael@0: const NetAddr& GetSelfAddr() { return mSelfAddr; } michael@0: const NetAddr& GetPeerAddr() { return mPeerAddr; } michael@0: michael@0: public: /* Necko internal use only... */ michael@0: michael@0: michael@0: // Return whether upon a redirect code of httpStatus for method, the michael@0: // request method should be rewritten to GET. michael@0: static bool ShouldRewriteRedirectToGET(uint32_t httpStatus, michael@0: nsHttpRequestHead::ParsedMethodType method); michael@0: michael@0: protected: michael@0: nsCOMArray mSecurityConsoleMessages; michael@0: michael@0: // Handle notifying listener, removing from loadgroup if request failed. michael@0: void DoNotifyListener(); michael@0: virtual void DoNotifyListenerCleanup() = 0; michael@0: michael@0: // drop reference to listener, its callbacks, and the progress sink michael@0: void ReleaseListeners(); michael@0: michael@0: nsresult ApplyContentConversions(); michael@0: michael@0: void AddCookiesToRequest(); michael@0: virtual nsresult SetupReplacementChannel(nsIURI *, michael@0: nsIChannel *, michael@0: bool preserveMethod); michael@0: michael@0: // bundle calling OMR observers and marking flag into one function michael@0: inline void CallOnModifyRequestObservers() { michael@0: gHttpHandler->OnModifyRequest(this); michael@0: mRequestObserversCalled = true; michael@0: } michael@0: michael@0: // Helper function to simplify getting notification callbacks. michael@0: template michael@0: void GetCallback(nsCOMPtr &aResult) michael@0: { michael@0: NS_QueryNotificationCallbacks(mCallbacks, mLoadGroup, michael@0: NS_GET_TEMPLATE_IID(T), michael@0: getter_AddRefs(aResult)); michael@0: } michael@0: michael@0: // Redirect tracking michael@0: // Checks whether or not aURI and mOriginalURI share the same domain. michael@0: bool SameOriginWithOriginalUri(nsIURI *aURI); michael@0: michael@0: friend class PrivateBrowsingChannel; michael@0: michael@0: nsCOMPtr mURI; michael@0: nsCOMPtr mOriginalURI; michael@0: nsCOMPtr mDocumentURI; michael@0: nsCOMPtr mListener; michael@0: nsCOMPtr mListenerContext; michael@0: nsCOMPtr mLoadGroup; michael@0: nsCOMPtr mOwner; michael@0: nsCOMPtr mCallbacks; michael@0: nsCOMPtr mProgressSink; michael@0: nsCOMPtr mReferrer; michael@0: nsCOMPtr mApplicationCache; michael@0: michael@0: nsHttpRequestHead mRequestHead; michael@0: nsCOMPtr mUploadStream; michael@0: nsAutoPtr mResponseHead; michael@0: nsRefPtr mConnectionInfo; michael@0: nsCOMPtr mProxyInfo; michael@0: michael@0: nsCString mSpec; // ASCII encoded URL spec michael@0: nsCString mContentTypeHint; michael@0: nsCString mContentCharsetHint; michael@0: nsCString mUserSetCookieHeader; michael@0: michael@0: NetAddr mSelfAddr; michael@0: NetAddr mPeerAddr; michael@0: michael@0: // HTTP Upgrade Data michael@0: nsCString mUpgradeProtocol; michael@0: nsCOMPtr mUpgradeProtocolCallback; michael@0: michael@0: // Resumable channel specific data michael@0: nsCString mEntityID; michael@0: uint64_t mStartPos; michael@0: michael@0: nsresult mStatus; michael@0: uint32_t mLoadFlags; michael@0: uint32_t mCaps; michael@0: int16_t mPriority; michael@0: uint8_t mRedirectionLimit; michael@0: michael@0: uint32_t mApplyConversion : 1; michael@0: uint32_t mCanceled : 1; michael@0: uint32_t mIsPending : 1; michael@0: uint32_t mWasOpened : 1; michael@0: // if 1 all "http-on-{opening|modify|etc}-request" observers have been called michael@0: uint32_t mRequestObserversCalled : 1; michael@0: uint32_t mResponseHeadersModified : 1; michael@0: uint32_t mAllowPipelining : 1; michael@0: uint32_t mForceAllowThirdPartyCookie : 1; michael@0: uint32_t mUploadStreamHasHeaders : 1; michael@0: uint32_t mInheritApplicationCache : 1; michael@0: uint32_t mChooseApplicationCache : 1; michael@0: uint32_t mLoadedFromApplicationCache : 1; michael@0: uint32_t mChannelIsForDownload : 1; michael@0: uint32_t mTracingEnabled : 1; michael@0: // True if timing collection is enabled michael@0: uint32_t mTimingEnabled : 1; michael@0: uint32_t mAllowSpdy : 1; michael@0: uint32_t mLoadAsBlocking : 1; michael@0: uint32_t mLoadUnblocked : 1; michael@0: uint32_t mResponseTimeoutEnabled : 1; michael@0: // A flag that should be false only if a cross-domain redirect occurred michael@0: uint32_t mAllRedirectsSameOrigin : 1; michael@0: michael@0: // Current suspension depth for this channel object michael@0: uint32_t mSuspendCount; michael@0: michael@0: nsCOMPtr mAPIRedirectToURI; michael@0: nsAutoPtr > mRedirectedCachekeys; michael@0: michael@0: uint32_t mProxyResolveFlags; michael@0: nsCOMPtr mProxyURI; michael@0: michael@0: uint32_t mContentDispositionHint; michael@0: nsAutoPtr mContentDispositionFilename; michael@0: michael@0: nsRefPtr mHttpHandler; // keep gHttpHandler alive michael@0: michael@0: // Performance tracking michael@0: // The initiator type (for this resource) - how was the resource referenced in michael@0: // the HTML file. michael@0: nsString mInitiatorType; michael@0: // Number of redirects that has occurred. michael@0: int16_t mRedirectCount; michael@0: // A time value equal to the starting time of the fetch that initiates the michael@0: // redirect. michael@0: mozilla::TimeStamp mRedirectStartTimeStamp; michael@0: // A time value equal to the time immediately after receiving the last byte of michael@0: // the response of the last redirect. michael@0: mozilla::TimeStamp mRedirectEndTimeStamp; michael@0: michael@0: PRTime mChannelCreationTime; michael@0: TimeStamp mChannelCreationTimestamp; michael@0: TimeStamp mAsyncOpenTime; michael@0: TimeStamp mCacheReadStart; michael@0: TimeStamp mCacheReadEnd; michael@0: // copied from the transaction before we null out mTransaction michael@0: // so that the timing can still be queried from OnStopRequest michael@0: TimingStruct mTransactionTimings; michael@0: }; michael@0: michael@0: // Share some code while working around C++'s absurd inability to handle casting michael@0: // of member functions between base/derived types. michael@0: // - We want to store member function pointer to call at resume time, but one michael@0: // such function--HandleAsyncAbort--we want to share between the michael@0: // nsHttpChannel/HttpChannelChild. Can't define it in base class, because michael@0: // then we'd have to cast member function ptr between base/derived class michael@0: // types. Sigh... michael@0: template michael@0: class HttpAsyncAborter michael@0: { michael@0: public: michael@0: HttpAsyncAborter(T *derived) : mThis(derived), mCallOnResume(0) {} michael@0: michael@0: // Aborts channel: calls OnStart/Stop with provided status, removes channel michael@0: // from loadGroup. michael@0: nsresult AsyncAbort(nsresult status); michael@0: michael@0: // Does most the actual work. michael@0: void HandleAsyncAbort(); michael@0: michael@0: // AsyncCall calls a member function asynchronously (via an event). michael@0: // retval isn't refcounted and is set only when event was successfully michael@0: // posted, the event is returned for the purpose of cancelling when needed michael@0: nsresult AsyncCall(void (T::*funcPtr)(), michael@0: nsRunnableMethod **retval = nullptr); michael@0: private: michael@0: T *mThis; michael@0: michael@0: protected: michael@0: // Function to be called at resume time michael@0: void (T::* mCallOnResume)(void); michael@0: }; michael@0: michael@0: template michael@0: nsresult HttpAsyncAborter::AsyncAbort(nsresult status) michael@0: { michael@0: PR_LOG(gHttpLog, 4, michael@0: ("HttpAsyncAborter::AsyncAbort [this=%p status=%x]\n", mThis, status)); michael@0: michael@0: mThis->mStatus = status; michael@0: mThis->mIsPending = false; michael@0: michael@0: // if this fails? Callers ignore our return value anyway.... michael@0: return AsyncCall(&T::HandleAsyncAbort); michael@0: } michael@0: michael@0: // Each subclass needs to define its own version of this (which just calls this michael@0: // base version), else we wind up casting base/derived member function ptrs michael@0: template michael@0: inline void HttpAsyncAborter::HandleAsyncAbort() michael@0: { michael@0: NS_PRECONDITION(!mCallOnResume, "How did that happen?"); michael@0: michael@0: if (mThis->mSuspendCount) { michael@0: PR_LOG(gHttpLog, 4, michael@0: ("Waiting until resume to do async notification [this=%p]\n", mThis)); michael@0: mCallOnResume = &T::HandleAsyncAbort; michael@0: return; michael@0: } michael@0: michael@0: mThis->DoNotifyListener(); michael@0: michael@0: // finally remove ourselves from the load group. michael@0: if (mThis->mLoadGroup) michael@0: mThis->mLoadGroup->RemoveRequest(mThis, nullptr, mThis->mStatus); michael@0: } michael@0: michael@0: template michael@0: nsresult HttpAsyncAborter::AsyncCall(void (T::*funcPtr)(), michael@0: nsRunnableMethod **retval) michael@0: { michael@0: nsresult rv; michael@0: michael@0: nsRefPtr > event = NS_NewRunnableMethod(mThis, funcPtr); michael@0: rv = NS_DispatchToCurrentThread(event); michael@0: if (NS_SUCCEEDED(rv) && retval) { michael@0: *retval = event; michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: } // namespace net michael@0: } // namespace mozilla michael@0: michael@0: #endif // mozilla_net_HttpBaseChannel_h