michael@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 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 nsHttpTransaction_h__ michael@0: #define nsHttpTransaction_h__ michael@0: michael@0: #include "nsHttp.h" michael@0: #include "nsAHttpTransaction.h" michael@0: #include "nsAHttpConnection.h" michael@0: #include "EventTokenBucket.h" michael@0: #include "nsCOMPtr.h" michael@0: #include "nsThreadUtils.h" michael@0: #include "nsILoadGroup.h" michael@0: #include "nsIInterfaceRequestor.h" michael@0: #include "TimingStruct.h" michael@0: michael@0: #ifdef MOZ_WIDGET_GONK michael@0: #include "nsINetworkManager.h" michael@0: #include "nsProxyRelease.h" michael@0: #endif michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: class nsIHttpActivityObserver; michael@0: class nsIEventTarget; michael@0: class nsIInputStream; michael@0: class nsIOutputStream; michael@0: michael@0: namespace mozilla { namespace net { michael@0: michael@0: class nsHttpChunkedDecoder; michael@0: class nsHttpRequestHead; michael@0: class nsHttpResponseHead; michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // nsHttpTransaction represents a single HTTP transaction. It is thread-safe, michael@0: // intended to run on the socket thread. michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: class nsHttpTransaction : public nsAHttpTransaction michael@0: , public ATokenBucketEvent michael@0: , public nsIInputStreamCallback michael@0: , public nsIOutputStreamCallback michael@0: { michael@0: public: michael@0: NS_DECL_THREADSAFE_ISUPPORTS michael@0: NS_DECL_NSAHTTPTRANSACTION michael@0: NS_DECL_NSIINPUTSTREAMCALLBACK michael@0: NS_DECL_NSIOUTPUTSTREAMCALLBACK michael@0: michael@0: nsHttpTransaction(); michael@0: virtual ~nsHttpTransaction(); michael@0: michael@0: // michael@0: // called to initialize the transaction michael@0: // michael@0: // @param caps michael@0: // the transaction capabilities (see nsHttp.h) michael@0: // @param connInfo michael@0: // the connection type for this transaction. michael@0: // @param reqHeaders michael@0: // the request header struct michael@0: // @param reqBody michael@0: // the request body (POST or PUT data stream) michael@0: // @param reqBodyIncludesHeaders michael@0: // fun stuff to support NPAPI plugins. michael@0: // @param target michael@0: // the dispatch target were notifications should be sent. michael@0: // @param callbacks michael@0: // the notification callbacks to be given to PSM. michael@0: // @param responseBody michael@0: // the input stream that will contain the response data. async michael@0: // wait on this input stream for data. on first notification, michael@0: // headers should be available (check transaction status). michael@0: // michael@0: nsresult Init(uint32_t caps, michael@0: nsHttpConnectionInfo *connInfo, michael@0: nsHttpRequestHead *reqHeaders, michael@0: nsIInputStream *reqBody, michael@0: bool reqBodyIncludesHeaders, michael@0: nsIEventTarget *consumerTarget, michael@0: nsIInterfaceRequestor *callbacks, michael@0: nsITransportEventSink *eventsink, michael@0: nsIAsyncInputStream **responseBody); michael@0: michael@0: // attributes michael@0: nsHttpConnectionInfo *ConnectionInfo() { return mConnInfo; } michael@0: nsHttpResponseHead *ResponseHead() { return mHaveAllHeaders ? mResponseHead : nullptr; } michael@0: nsISupports *SecurityInfo() { return mSecurityInfo; } michael@0: michael@0: nsIEventTarget *ConsumerTarget() { return mConsumerTarget; } michael@0: michael@0: void SetSecurityCallbacks(nsIInterfaceRequestor* aCallbacks); michael@0: michael@0: // Called to take ownership of the response headers; the transaction michael@0: // will drop any reference to the response headers after this call. michael@0: nsHttpResponseHead *TakeResponseHead(); michael@0: michael@0: // Provides a thread safe reference of the connection michael@0: // nsHttpTransaction::Connection should only be used on the socket thread michael@0: already_AddRefed GetConnectionReference(); michael@0: michael@0: // Called to find out if the transaction generated a complete response. michael@0: bool ResponseIsComplete() { return mResponseIsComplete; } michael@0: michael@0: bool ProxyConnectFailed() { return mProxyConnectFailed; } michael@0: michael@0: // SetPriority() may only be used by the connection manager. michael@0: void SetPriority(int32_t priority) { mPriority = priority; } michael@0: int32_t Priority() { return mPriority; } michael@0: michael@0: const TimingStruct& Timings() const { return mTimings; } michael@0: enum Classifier Classification() { return mClassification; } michael@0: michael@0: void PrintDiagnostics(nsCString &log); michael@0: michael@0: // Sets mPendingTime to the current time stamp or to a null time stamp (if now is false) michael@0: void SetPendingTime(bool now = true) { mPendingTime = now ? TimeStamp::Now() : TimeStamp(); } michael@0: const TimeStamp GetPendingTime() { return mPendingTime; } michael@0: bool UsesPipelining() const { return mCaps & NS_HTTP_ALLOW_PIPELINING; } michael@0: michael@0: // overload of nsAHttpTransaction::LoadGroupConnectionInfo() michael@0: nsILoadGroupConnectionInfo *LoadGroupConnectionInfo() { return mLoadGroupCI.get(); } michael@0: void SetLoadGroupConnectionInfo(nsILoadGroupConnectionInfo *aLoadGroupCI) { mLoadGroupCI = aLoadGroupCI; } michael@0: void DispatchedAsBlocking(); michael@0: void RemoveDispatchedAsBlocking(); michael@0: michael@0: private: michael@0: nsresult Restart(); michael@0: nsresult RestartInProgress(); michael@0: char *LocateHttpStart(char *buf, uint32_t len, michael@0: bool aAllowPartialMatch); michael@0: nsresult ParseLine(char *line); michael@0: nsresult ParseLineSegment(char *seg, uint32_t len); michael@0: nsresult ParseHead(char *, uint32_t count, uint32_t *countRead); michael@0: nsresult HandleContentStart(); michael@0: nsresult HandleContent(char *, uint32_t count, uint32_t *contentRead, uint32_t *contentRemaining); michael@0: nsresult ProcessData(char *, uint32_t, uint32_t *); michael@0: void DeleteSelfOnConsumerThread(); michael@0: void ReleaseBlockingTransaction(); michael@0: michael@0: Classifier Classify(); michael@0: void CancelPipeline(uint32_t reason); michael@0: michael@0: static NS_METHOD ReadRequestSegment(nsIInputStream *, void *, const char *, michael@0: uint32_t, uint32_t, uint32_t *); michael@0: static NS_METHOD WritePipeSegment(nsIOutputStream *, void *, char *, michael@0: uint32_t, uint32_t, uint32_t *); michael@0: michael@0: bool TimingEnabled() const { return mCaps & NS_HTTP_TIMING_ENABLED; } michael@0: michael@0: bool ResponseTimeoutEnabled() const MOZ_FINAL; michael@0: michael@0: private: michael@0: class UpdateSecurityCallbacks : public nsRunnable michael@0: { michael@0: public: michael@0: UpdateSecurityCallbacks(nsHttpTransaction* aTrans, michael@0: nsIInterfaceRequestor* aCallbacks) michael@0: : mTrans(aTrans), mCallbacks(aCallbacks) {} michael@0: michael@0: NS_IMETHOD Run() michael@0: { michael@0: if (mTrans->mConnection) michael@0: mTrans->mConnection->SetSecurityCallbacks(mCallbacks); michael@0: return NS_OK; michael@0: } michael@0: private: michael@0: nsRefPtr mTrans; michael@0: nsCOMPtr mCallbacks; michael@0: }; michael@0: michael@0: Mutex mLock; michael@0: michael@0: nsCOMPtr mCallbacks; michael@0: nsCOMPtr mTransportSink; michael@0: nsCOMPtr mConsumerTarget; michael@0: nsCOMPtr mSecurityInfo; michael@0: nsCOMPtr mPipeIn; michael@0: nsCOMPtr mPipeOut; michael@0: nsCOMPtr mLoadGroupCI; michael@0: michael@0: nsCOMPtr mChannel; michael@0: nsCOMPtr mActivityDistributor; michael@0: michael@0: nsCString mReqHeaderBuf; // flattened request headers michael@0: nsCOMPtr mRequestStream; michael@0: uint64_t mRequestSize; michael@0: michael@0: nsAHttpConnection *mConnection; // hard ref michael@0: nsHttpConnectionInfo *mConnInfo; // hard ref michael@0: nsHttpRequestHead *mRequestHead; // weak ref michael@0: nsHttpResponseHead *mResponseHead; // hard ref michael@0: michael@0: nsAHttpSegmentReader *mReader; michael@0: nsAHttpSegmentWriter *mWriter; michael@0: michael@0: nsCString mLineBuf; // may contain a partial line michael@0: michael@0: int64_t mContentLength; // equals -1 if unknown michael@0: int64_t mContentRead; // count of consumed content bytes michael@0: michael@0: // After a 304/204 or other "no-content" style response we will skip over michael@0: // up to MAX_INVALID_RESPONSE_BODY_SZ bytes when looking for the next michael@0: // response header to deal with servers that actually sent a response michael@0: // body where they should not have. This member tracks how many bytes have michael@0: // so far been skipped. michael@0: uint32_t mInvalidResponseBytesRead; michael@0: michael@0: nsHttpChunkedDecoder *mChunkedDecoder; michael@0: michael@0: TimingStruct mTimings; michael@0: michael@0: nsresult mStatus; michael@0: michael@0: int16_t mPriority; michael@0: michael@0: uint16_t mRestartCount; // the number of times this transaction has been restarted michael@0: uint32_t mCaps; michael@0: // mCapsToClear holds flags that should be cleared in mCaps, e.g. unset michael@0: // NS_HTTP_REFRESH_DNS when DNS refresh request has completed to avoid michael@0: // redundant requests on the network. To deal with raciness, only unsetting michael@0: // bitfields should be allowed: 'lost races' will thus err on the michael@0: // conservative side, e.g. by going ahead with a 2nd DNS refresh. michael@0: uint32_t mCapsToClear; michael@0: enum Classifier mClassification; michael@0: int32_t mPipelinePosition; michael@0: int64_t mMaxPipelineObjectSize; michael@0: michael@0: nsHttpVersion mHttpVersion; michael@0: michael@0: // state flags, all logically boolean, but not packed together into a michael@0: // bitfield so as to avoid bitfield-induced races. See bug 560579. michael@0: bool mClosed; michael@0: bool mConnected; michael@0: bool mHaveStatusLine; michael@0: bool mHaveAllHeaders; michael@0: bool mTransactionDone; michael@0: bool mResponseIsComplete; michael@0: bool mDidContentStart; michael@0: bool mNoContent; // expecting an empty entity body michael@0: bool mSentData; michael@0: bool mReceivedData; michael@0: bool mStatusEventPending; michael@0: bool mHasRequestBody; michael@0: bool mProxyConnectFailed; michael@0: bool mHttpResponseMatched; michael@0: bool mPreserveStream; michael@0: bool mDispatchedAsBlocking; michael@0: bool mResponseTimeoutEnabled; michael@0: michael@0: // mClosed := transaction has been explicitly closed michael@0: // mTransactionDone := transaction ran to completion or was interrupted michael@0: // mResponseComplete := transaction ran to completion michael@0: michael@0: // For Restart-In-Progress Functionality michael@0: bool mReportedStart; michael@0: bool mReportedResponseHeader; michael@0: michael@0: // protected by nsHttp::GetLock() michael@0: nsHttpResponseHead *mForTakeResponseHead; michael@0: bool mResponseHeadTaken; michael@0: michael@0: // The time when the transaction was submitted to the Connection Manager michael@0: TimeStamp mPendingTime; michael@0: michael@0: class RestartVerifier michael@0: { michael@0: michael@0: // When a idemptotent transaction has received part of its response body michael@0: // and incurs an error it can be restarted. To do this we mark the place michael@0: // where we stopped feeding the body to the consumer and start the michael@0: // network call over again. If everything we track (headers, length, etc..) michael@0: // matches up to the place where we left off then the consumer starts being michael@0: // fed data again with the new information. This can be done N times up michael@0: // to the normal restart (i.e. with no response info) limit. michael@0: michael@0: public: michael@0: RestartVerifier() michael@0: : mContentLength(-1) michael@0: , mAlreadyProcessed(0) michael@0: , mToReadBeforeRestart(0) michael@0: , mSetup(false) michael@0: {} michael@0: ~RestartVerifier() {} michael@0: michael@0: void Set(int64_t contentLength, nsHttpResponseHead *head); michael@0: bool Verify(int64_t contentLength, nsHttpResponseHead *head); michael@0: bool IsDiscardingContent() { return mToReadBeforeRestart != 0; } michael@0: bool IsSetup() { return mSetup; } michael@0: int64_t AlreadyProcessed() { return mAlreadyProcessed; } michael@0: void SetAlreadyProcessed(int64_t val) { michael@0: mAlreadyProcessed = val; michael@0: mToReadBeforeRestart = val; michael@0: } michael@0: int64_t ToReadBeforeRestart() { return mToReadBeforeRestart; } michael@0: void HaveReadBeforeRestart(uint32_t amt) michael@0: { michael@0: MOZ_ASSERT(amt <= mToReadBeforeRestart, michael@0: "too large of a HaveReadBeforeRestart deduction"); michael@0: mToReadBeforeRestart -= amt; michael@0: } michael@0: michael@0: private: michael@0: // This is the data from the first complete response header michael@0: // used to make sure that all subsequent response headers match michael@0: michael@0: int64_t mContentLength; michael@0: nsCString mETag; michael@0: nsCString mLastModified; michael@0: nsCString mContentRange; michael@0: nsCString mContentEncoding; michael@0: nsCString mTransferEncoding; michael@0: michael@0: // This is the amount of data that has been passed to the channel michael@0: // from previous iterations of the transaction and must therefore michael@0: // be skipped in the new one. michael@0: int64_t mAlreadyProcessed; michael@0: michael@0: // The amount of data that must be discarded in the current iteration michael@0: // (where iteration > 0) to reach the mAlreadyProcessed high water michael@0: // mark. michael@0: int64_t mToReadBeforeRestart; michael@0: michael@0: // true when ::Set has been called with a response header michael@0: bool mSetup; michael@0: } mRestartInProgressVerifier; michael@0: michael@0: // For Rate Pacing via an EventTokenBucket michael@0: public: michael@0: // called by the connection manager to run this transaction through the michael@0: // token bucket. If the token bucket admits the transaction immediately it michael@0: // returns true. The function is called repeatedly until it returns true. michael@0: bool TryToRunPacedRequest(); michael@0: michael@0: // ATokenBucketEvent pure virtual implementation. Called by the token bucket michael@0: // when the transaction is ready to run. If this happens asynchrounously to michael@0: // token bucket submission the transaction just posts an event that causes michael@0: // the pending transaction queue to be rerun (and TryToRunPacedRequest() to michael@0: // be run again. michael@0: void OnTokenBucketAdmitted(); // ATokenBucketEvent michael@0: michael@0: // CancelPacing() can be used to tell the token bucket to remove this michael@0: // transaction from the list of pending transactions. This is used when a michael@0: // transaction is believed to be HTTP/1 (and thus subject to rate pacing) michael@0: // but later can be dispatched via spdy (not subject to rate pacing). michael@0: void CancelPacing(nsresult reason); michael@0: michael@0: private: michael@0: bool mSubmittedRatePacing; michael@0: bool mPassedRatePacing; michael@0: bool mSynchronousRatePaceRequest; michael@0: nsCOMPtr mTokenBucketCancel; michael@0: michael@0: // These members are used for network per-app metering (bug 746073) michael@0: // Currently, they are only available on gonk. michael@0: uint64_t mCountRecv; michael@0: uint64_t mCountSent; michael@0: uint32_t mAppId; michael@0: #ifdef MOZ_WIDGET_GONK michael@0: nsMainThreadPtrHandle mActiveNetwork; michael@0: #endif michael@0: nsresult SaveNetworkStats(bool); michael@0: void CountRecvBytes(uint64_t recvBytes) michael@0: { michael@0: mCountRecv += recvBytes; michael@0: SaveNetworkStats(false); michael@0: } michael@0: void CountSentBytes(uint64_t sentBytes) michael@0: { michael@0: mCountSent += sentBytes; michael@0: SaveNetworkStats(false); michael@0: } michael@0: }; michael@0: michael@0: }} // namespace mozilla::net michael@0: michael@0: #endif // nsHttpTransaction_h__