Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
michael@0 | 2 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 5 | |
michael@0 | 6 | #ifndef nsHttpTransaction_h__ |
michael@0 | 7 | #define nsHttpTransaction_h__ |
michael@0 | 8 | |
michael@0 | 9 | #include "nsHttp.h" |
michael@0 | 10 | #include "nsAHttpTransaction.h" |
michael@0 | 11 | #include "nsAHttpConnection.h" |
michael@0 | 12 | #include "EventTokenBucket.h" |
michael@0 | 13 | #include "nsCOMPtr.h" |
michael@0 | 14 | #include "nsThreadUtils.h" |
michael@0 | 15 | #include "nsILoadGroup.h" |
michael@0 | 16 | #include "nsIInterfaceRequestor.h" |
michael@0 | 17 | #include "TimingStruct.h" |
michael@0 | 18 | |
michael@0 | 19 | #ifdef MOZ_WIDGET_GONK |
michael@0 | 20 | #include "nsINetworkManager.h" |
michael@0 | 21 | #include "nsProxyRelease.h" |
michael@0 | 22 | #endif |
michael@0 | 23 | |
michael@0 | 24 | //----------------------------------------------------------------------------- |
michael@0 | 25 | |
michael@0 | 26 | class nsIHttpActivityObserver; |
michael@0 | 27 | class nsIEventTarget; |
michael@0 | 28 | class nsIInputStream; |
michael@0 | 29 | class nsIOutputStream; |
michael@0 | 30 | |
michael@0 | 31 | namespace mozilla { namespace net { |
michael@0 | 32 | |
michael@0 | 33 | class nsHttpChunkedDecoder; |
michael@0 | 34 | class nsHttpRequestHead; |
michael@0 | 35 | class nsHttpResponseHead; |
michael@0 | 36 | |
michael@0 | 37 | //----------------------------------------------------------------------------- |
michael@0 | 38 | // nsHttpTransaction represents a single HTTP transaction. It is thread-safe, |
michael@0 | 39 | // intended to run on the socket thread. |
michael@0 | 40 | //----------------------------------------------------------------------------- |
michael@0 | 41 | |
michael@0 | 42 | class nsHttpTransaction : public nsAHttpTransaction |
michael@0 | 43 | , public ATokenBucketEvent |
michael@0 | 44 | , public nsIInputStreamCallback |
michael@0 | 45 | , public nsIOutputStreamCallback |
michael@0 | 46 | { |
michael@0 | 47 | public: |
michael@0 | 48 | NS_DECL_THREADSAFE_ISUPPORTS |
michael@0 | 49 | NS_DECL_NSAHTTPTRANSACTION |
michael@0 | 50 | NS_DECL_NSIINPUTSTREAMCALLBACK |
michael@0 | 51 | NS_DECL_NSIOUTPUTSTREAMCALLBACK |
michael@0 | 52 | |
michael@0 | 53 | nsHttpTransaction(); |
michael@0 | 54 | virtual ~nsHttpTransaction(); |
michael@0 | 55 | |
michael@0 | 56 | // |
michael@0 | 57 | // called to initialize the transaction |
michael@0 | 58 | // |
michael@0 | 59 | // @param caps |
michael@0 | 60 | // the transaction capabilities (see nsHttp.h) |
michael@0 | 61 | // @param connInfo |
michael@0 | 62 | // the connection type for this transaction. |
michael@0 | 63 | // @param reqHeaders |
michael@0 | 64 | // the request header struct |
michael@0 | 65 | // @param reqBody |
michael@0 | 66 | // the request body (POST or PUT data stream) |
michael@0 | 67 | // @param reqBodyIncludesHeaders |
michael@0 | 68 | // fun stuff to support NPAPI plugins. |
michael@0 | 69 | // @param target |
michael@0 | 70 | // the dispatch target were notifications should be sent. |
michael@0 | 71 | // @param callbacks |
michael@0 | 72 | // the notification callbacks to be given to PSM. |
michael@0 | 73 | // @param responseBody |
michael@0 | 74 | // the input stream that will contain the response data. async |
michael@0 | 75 | // wait on this input stream for data. on first notification, |
michael@0 | 76 | // headers should be available (check transaction status). |
michael@0 | 77 | // |
michael@0 | 78 | nsresult Init(uint32_t caps, |
michael@0 | 79 | nsHttpConnectionInfo *connInfo, |
michael@0 | 80 | nsHttpRequestHead *reqHeaders, |
michael@0 | 81 | nsIInputStream *reqBody, |
michael@0 | 82 | bool reqBodyIncludesHeaders, |
michael@0 | 83 | nsIEventTarget *consumerTarget, |
michael@0 | 84 | nsIInterfaceRequestor *callbacks, |
michael@0 | 85 | nsITransportEventSink *eventsink, |
michael@0 | 86 | nsIAsyncInputStream **responseBody); |
michael@0 | 87 | |
michael@0 | 88 | // attributes |
michael@0 | 89 | nsHttpConnectionInfo *ConnectionInfo() { return mConnInfo; } |
michael@0 | 90 | nsHttpResponseHead *ResponseHead() { return mHaveAllHeaders ? mResponseHead : nullptr; } |
michael@0 | 91 | nsISupports *SecurityInfo() { return mSecurityInfo; } |
michael@0 | 92 | |
michael@0 | 93 | nsIEventTarget *ConsumerTarget() { return mConsumerTarget; } |
michael@0 | 94 | |
michael@0 | 95 | void SetSecurityCallbacks(nsIInterfaceRequestor* aCallbacks); |
michael@0 | 96 | |
michael@0 | 97 | // Called to take ownership of the response headers; the transaction |
michael@0 | 98 | // will drop any reference to the response headers after this call. |
michael@0 | 99 | nsHttpResponseHead *TakeResponseHead(); |
michael@0 | 100 | |
michael@0 | 101 | // Provides a thread safe reference of the connection |
michael@0 | 102 | // nsHttpTransaction::Connection should only be used on the socket thread |
michael@0 | 103 | already_AddRefed<nsAHttpConnection> GetConnectionReference(); |
michael@0 | 104 | |
michael@0 | 105 | // Called to find out if the transaction generated a complete response. |
michael@0 | 106 | bool ResponseIsComplete() { return mResponseIsComplete; } |
michael@0 | 107 | |
michael@0 | 108 | bool ProxyConnectFailed() { return mProxyConnectFailed; } |
michael@0 | 109 | |
michael@0 | 110 | // SetPriority() may only be used by the connection manager. |
michael@0 | 111 | void SetPriority(int32_t priority) { mPriority = priority; } |
michael@0 | 112 | int32_t Priority() { return mPriority; } |
michael@0 | 113 | |
michael@0 | 114 | const TimingStruct& Timings() const { return mTimings; } |
michael@0 | 115 | enum Classifier Classification() { return mClassification; } |
michael@0 | 116 | |
michael@0 | 117 | void PrintDiagnostics(nsCString &log); |
michael@0 | 118 | |
michael@0 | 119 | // Sets mPendingTime to the current time stamp or to a null time stamp (if now is false) |
michael@0 | 120 | void SetPendingTime(bool now = true) { mPendingTime = now ? TimeStamp::Now() : TimeStamp(); } |
michael@0 | 121 | const TimeStamp GetPendingTime() { return mPendingTime; } |
michael@0 | 122 | bool UsesPipelining() const { return mCaps & NS_HTTP_ALLOW_PIPELINING; } |
michael@0 | 123 | |
michael@0 | 124 | // overload of nsAHttpTransaction::LoadGroupConnectionInfo() |
michael@0 | 125 | nsILoadGroupConnectionInfo *LoadGroupConnectionInfo() { return mLoadGroupCI.get(); } |
michael@0 | 126 | void SetLoadGroupConnectionInfo(nsILoadGroupConnectionInfo *aLoadGroupCI) { mLoadGroupCI = aLoadGroupCI; } |
michael@0 | 127 | void DispatchedAsBlocking(); |
michael@0 | 128 | void RemoveDispatchedAsBlocking(); |
michael@0 | 129 | |
michael@0 | 130 | private: |
michael@0 | 131 | nsresult Restart(); |
michael@0 | 132 | nsresult RestartInProgress(); |
michael@0 | 133 | char *LocateHttpStart(char *buf, uint32_t len, |
michael@0 | 134 | bool aAllowPartialMatch); |
michael@0 | 135 | nsresult ParseLine(char *line); |
michael@0 | 136 | nsresult ParseLineSegment(char *seg, uint32_t len); |
michael@0 | 137 | nsresult ParseHead(char *, uint32_t count, uint32_t *countRead); |
michael@0 | 138 | nsresult HandleContentStart(); |
michael@0 | 139 | nsresult HandleContent(char *, uint32_t count, uint32_t *contentRead, uint32_t *contentRemaining); |
michael@0 | 140 | nsresult ProcessData(char *, uint32_t, uint32_t *); |
michael@0 | 141 | void DeleteSelfOnConsumerThread(); |
michael@0 | 142 | void ReleaseBlockingTransaction(); |
michael@0 | 143 | |
michael@0 | 144 | Classifier Classify(); |
michael@0 | 145 | void CancelPipeline(uint32_t reason); |
michael@0 | 146 | |
michael@0 | 147 | static NS_METHOD ReadRequestSegment(nsIInputStream *, void *, const char *, |
michael@0 | 148 | uint32_t, uint32_t, uint32_t *); |
michael@0 | 149 | static NS_METHOD WritePipeSegment(nsIOutputStream *, void *, char *, |
michael@0 | 150 | uint32_t, uint32_t, uint32_t *); |
michael@0 | 151 | |
michael@0 | 152 | bool TimingEnabled() const { return mCaps & NS_HTTP_TIMING_ENABLED; } |
michael@0 | 153 | |
michael@0 | 154 | bool ResponseTimeoutEnabled() const MOZ_FINAL; |
michael@0 | 155 | |
michael@0 | 156 | private: |
michael@0 | 157 | class UpdateSecurityCallbacks : public nsRunnable |
michael@0 | 158 | { |
michael@0 | 159 | public: |
michael@0 | 160 | UpdateSecurityCallbacks(nsHttpTransaction* aTrans, |
michael@0 | 161 | nsIInterfaceRequestor* aCallbacks) |
michael@0 | 162 | : mTrans(aTrans), mCallbacks(aCallbacks) {} |
michael@0 | 163 | |
michael@0 | 164 | NS_IMETHOD Run() |
michael@0 | 165 | { |
michael@0 | 166 | if (mTrans->mConnection) |
michael@0 | 167 | mTrans->mConnection->SetSecurityCallbacks(mCallbacks); |
michael@0 | 168 | return NS_OK; |
michael@0 | 169 | } |
michael@0 | 170 | private: |
michael@0 | 171 | nsRefPtr<nsHttpTransaction> mTrans; |
michael@0 | 172 | nsCOMPtr<nsIInterfaceRequestor> mCallbacks; |
michael@0 | 173 | }; |
michael@0 | 174 | |
michael@0 | 175 | Mutex mLock; |
michael@0 | 176 | |
michael@0 | 177 | nsCOMPtr<nsIInterfaceRequestor> mCallbacks; |
michael@0 | 178 | nsCOMPtr<nsITransportEventSink> mTransportSink; |
michael@0 | 179 | nsCOMPtr<nsIEventTarget> mConsumerTarget; |
michael@0 | 180 | nsCOMPtr<nsISupports> mSecurityInfo; |
michael@0 | 181 | nsCOMPtr<nsIAsyncInputStream> mPipeIn; |
michael@0 | 182 | nsCOMPtr<nsIAsyncOutputStream> mPipeOut; |
michael@0 | 183 | nsCOMPtr<nsILoadGroupConnectionInfo> mLoadGroupCI; |
michael@0 | 184 | |
michael@0 | 185 | nsCOMPtr<nsISupports> mChannel; |
michael@0 | 186 | nsCOMPtr<nsIHttpActivityObserver> mActivityDistributor; |
michael@0 | 187 | |
michael@0 | 188 | nsCString mReqHeaderBuf; // flattened request headers |
michael@0 | 189 | nsCOMPtr<nsIInputStream> mRequestStream; |
michael@0 | 190 | uint64_t mRequestSize; |
michael@0 | 191 | |
michael@0 | 192 | nsAHttpConnection *mConnection; // hard ref |
michael@0 | 193 | nsHttpConnectionInfo *mConnInfo; // hard ref |
michael@0 | 194 | nsHttpRequestHead *mRequestHead; // weak ref |
michael@0 | 195 | nsHttpResponseHead *mResponseHead; // hard ref |
michael@0 | 196 | |
michael@0 | 197 | nsAHttpSegmentReader *mReader; |
michael@0 | 198 | nsAHttpSegmentWriter *mWriter; |
michael@0 | 199 | |
michael@0 | 200 | nsCString mLineBuf; // may contain a partial line |
michael@0 | 201 | |
michael@0 | 202 | int64_t mContentLength; // equals -1 if unknown |
michael@0 | 203 | int64_t mContentRead; // count of consumed content bytes |
michael@0 | 204 | |
michael@0 | 205 | // After a 304/204 or other "no-content" style response we will skip over |
michael@0 | 206 | // up to MAX_INVALID_RESPONSE_BODY_SZ bytes when looking for the next |
michael@0 | 207 | // response header to deal with servers that actually sent a response |
michael@0 | 208 | // body where they should not have. This member tracks how many bytes have |
michael@0 | 209 | // so far been skipped. |
michael@0 | 210 | uint32_t mInvalidResponseBytesRead; |
michael@0 | 211 | |
michael@0 | 212 | nsHttpChunkedDecoder *mChunkedDecoder; |
michael@0 | 213 | |
michael@0 | 214 | TimingStruct mTimings; |
michael@0 | 215 | |
michael@0 | 216 | nsresult mStatus; |
michael@0 | 217 | |
michael@0 | 218 | int16_t mPriority; |
michael@0 | 219 | |
michael@0 | 220 | uint16_t mRestartCount; // the number of times this transaction has been restarted |
michael@0 | 221 | uint32_t mCaps; |
michael@0 | 222 | // mCapsToClear holds flags that should be cleared in mCaps, e.g. unset |
michael@0 | 223 | // NS_HTTP_REFRESH_DNS when DNS refresh request has completed to avoid |
michael@0 | 224 | // redundant requests on the network. To deal with raciness, only unsetting |
michael@0 | 225 | // bitfields should be allowed: 'lost races' will thus err on the |
michael@0 | 226 | // conservative side, e.g. by going ahead with a 2nd DNS refresh. |
michael@0 | 227 | uint32_t mCapsToClear; |
michael@0 | 228 | enum Classifier mClassification; |
michael@0 | 229 | int32_t mPipelinePosition; |
michael@0 | 230 | int64_t mMaxPipelineObjectSize; |
michael@0 | 231 | |
michael@0 | 232 | nsHttpVersion mHttpVersion; |
michael@0 | 233 | |
michael@0 | 234 | // state flags, all logically boolean, but not packed together into a |
michael@0 | 235 | // bitfield so as to avoid bitfield-induced races. See bug 560579. |
michael@0 | 236 | bool mClosed; |
michael@0 | 237 | bool mConnected; |
michael@0 | 238 | bool mHaveStatusLine; |
michael@0 | 239 | bool mHaveAllHeaders; |
michael@0 | 240 | bool mTransactionDone; |
michael@0 | 241 | bool mResponseIsComplete; |
michael@0 | 242 | bool mDidContentStart; |
michael@0 | 243 | bool mNoContent; // expecting an empty entity body |
michael@0 | 244 | bool mSentData; |
michael@0 | 245 | bool mReceivedData; |
michael@0 | 246 | bool mStatusEventPending; |
michael@0 | 247 | bool mHasRequestBody; |
michael@0 | 248 | bool mProxyConnectFailed; |
michael@0 | 249 | bool mHttpResponseMatched; |
michael@0 | 250 | bool mPreserveStream; |
michael@0 | 251 | bool mDispatchedAsBlocking; |
michael@0 | 252 | bool mResponseTimeoutEnabled; |
michael@0 | 253 | |
michael@0 | 254 | // mClosed := transaction has been explicitly closed |
michael@0 | 255 | // mTransactionDone := transaction ran to completion or was interrupted |
michael@0 | 256 | // mResponseComplete := transaction ran to completion |
michael@0 | 257 | |
michael@0 | 258 | // For Restart-In-Progress Functionality |
michael@0 | 259 | bool mReportedStart; |
michael@0 | 260 | bool mReportedResponseHeader; |
michael@0 | 261 | |
michael@0 | 262 | // protected by nsHttp::GetLock() |
michael@0 | 263 | nsHttpResponseHead *mForTakeResponseHead; |
michael@0 | 264 | bool mResponseHeadTaken; |
michael@0 | 265 | |
michael@0 | 266 | // The time when the transaction was submitted to the Connection Manager |
michael@0 | 267 | TimeStamp mPendingTime; |
michael@0 | 268 | |
michael@0 | 269 | class RestartVerifier |
michael@0 | 270 | { |
michael@0 | 271 | |
michael@0 | 272 | // When a idemptotent transaction has received part of its response body |
michael@0 | 273 | // and incurs an error it can be restarted. To do this we mark the place |
michael@0 | 274 | // where we stopped feeding the body to the consumer and start the |
michael@0 | 275 | // network call over again. If everything we track (headers, length, etc..) |
michael@0 | 276 | // matches up to the place where we left off then the consumer starts being |
michael@0 | 277 | // fed data again with the new information. This can be done N times up |
michael@0 | 278 | // to the normal restart (i.e. with no response info) limit. |
michael@0 | 279 | |
michael@0 | 280 | public: |
michael@0 | 281 | RestartVerifier() |
michael@0 | 282 | : mContentLength(-1) |
michael@0 | 283 | , mAlreadyProcessed(0) |
michael@0 | 284 | , mToReadBeforeRestart(0) |
michael@0 | 285 | , mSetup(false) |
michael@0 | 286 | {} |
michael@0 | 287 | ~RestartVerifier() {} |
michael@0 | 288 | |
michael@0 | 289 | void Set(int64_t contentLength, nsHttpResponseHead *head); |
michael@0 | 290 | bool Verify(int64_t contentLength, nsHttpResponseHead *head); |
michael@0 | 291 | bool IsDiscardingContent() { return mToReadBeforeRestart != 0; } |
michael@0 | 292 | bool IsSetup() { return mSetup; } |
michael@0 | 293 | int64_t AlreadyProcessed() { return mAlreadyProcessed; } |
michael@0 | 294 | void SetAlreadyProcessed(int64_t val) { |
michael@0 | 295 | mAlreadyProcessed = val; |
michael@0 | 296 | mToReadBeforeRestart = val; |
michael@0 | 297 | } |
michael@0 | 298 | int64_t ToReadBeforeRestart() { return mToReadBeforeRestart; } |
michael@0 | 299 | void HaveReadBeforeRestart(uint32_t amt) |
michael@0 | 300 | { |
michael@0 | 301 | MOZ_ASSERT(amt <= mToReadBeforeRestart, |
michael@0 | 302 | "too large of a HaveReadBeforeRestart deduction"); |
michael@0 | 303 | mToReadBeforeRestart -= amt; |
michael@0 | 304 | } |
michael@0 | 305 | |
michael@0 | 306 | private: |
michael@0 | 307 | // This is the data from the first complete response header |
michael@0 | 308 | // used to make sure that all subsequent response headers match |
michael@0 | 309 | |
michael@0 | 310 | int64_t mContentLength; |
michael@0 | 311 | nsCString mETag; |
michael@0 | 312 | nsCString mLastModified; |
michael@0 | 313 | nsCString mContentRange; |
michael@0 | 314 | nsCString mContentEncoding; |
michael@0 | 315 | nsCString mTransferEncoding; |
michael@0 | 316 | |
michael@0 | 317 | // This is the amount of data that has been passed to the channel |
michael@0 | 318 | // from previous iterations of the transaction and must therefore |
michael@0 | 319 | // be skipped in the new one. |
michael@0 | 320 | int64_t mAlreadyProcessed; |
michael@0 | 321 | |
michael@0 | 322 | // The amount of data that must be discarded in the current iteration |
michael@0 | 323 | // (where iteration > 0) to reach the mAlreadyProcessed high water |
michael@0 | 324 | // mark. |
michael@0 | 325 | int64_t mToReadBeforeRestart; |
michael@0 | 326 | |
michael@0 | 327 | // true when ::Set has been called with a response header |
michael@0 | 328 | bool mSetup; |
michael@0 | 329 | } mRestartInProgressVerifier; |
michael@0 | 330 | |
michael@0 | 331 | // For Rate Pacing via an EventTokenBucket |
michael@0 | 332 | public: |
michael@0 | 333 | // called by the connection manager to run this transaction through the |
michael@0 | 334 | // token bucket. If the token bucket admits the transaction immediately it |
michael@0 | 335 | // returns true. The function is called repeatedly until it returns true. |
michael@0 | 336 | bool TryToRunPacedRequest(); |
michael@0 | 337 | |
michael@0 | 338 | // ATokenBucketEvent pure virtual implementation. Called by the token bucket |
michael@0 | 339 | // when the transaction is ready to run. If this happens asynchrounously to |
michael@0 | 340 | // token bucket submission the transaction just posts an event that causes |
michael@0 | 341 | // the pending transaction queue to be rerun (and TryToRunPacedRequest() to |
michael@0 | 342 | // be run again. |
michael@0 | 343 | void OnTokenBucketAdmitted(); // ATokenBucketEvent |
michael@0 | 344 | |
michael@0 | 345 | // CancelPacing() can be used to tell the token bucket to remove this |
michael@0 | 346 | // transaction from the list of pending transactions. This is used when a |
michael@0 | 347 | // transaction is believed to be HTTP/1 (and thus subject to rate pacing) |
michael@0 | 348 | // but later can be dispatched via spdy (not subject to rate pacing). |
michael@0 | 349 | void CancelPacing(nsresult reason); |
michael@0 | 350 | |
michael@0 | 351 | private: |
michael@0 | 352 | bool mSubmittedRatePacing; |
michael@0 | 353 | bool mPassedRatePacing; |
michael@0 | 354 | bool mSynchronousRatePaceRequest; |
michael@0 | 355 | nsCOMPtr<nsICancelable> mTokenBucketCancel; |
michael@0 | 356 | |
michael@0 | 357 | // These members are used for network per-app metering (bug 746073) |
michael@0 | 358 | // Currently, they are only available on gonk. |
michael@0 | 359 | uint64_t mCountRecv; |
michael@0 | 360 | uint64_t mCountSent; |
michael@0 | 361 | uint32_t mAppId; |
michael@0 | 362 | #ifdef MOZ_WIDGET_GONK |
michael@0 | 363 | nsMainThreadPtrHandle<nsINetworkInterface> mActiveNetwork; |
michael@0 | 364 | #endif |
michael@0 | 365 | nsresult SaveNetworkStats(bool); |
michael@0 | 366 | void CountRecvBytes(uint64_t recvBytes) |
michael@0 | 367 | { |
michael@0 | 368 | mCountRecv += recvBytes; |
michael@0 | 369 | SaveNetworkStats(false); |
michael@0 | 370 | } |
michael@0 | 371 | void CountSentBytes(uint64_t sentBytes) |
michael@0 | 372 | { |
michael@0 | 373 | mCountSent += sentBytes; |
michael@0 | 374 | SaveNetworkStats(false); |
michael@0 | 375 | } |
michael@0 | 376 | }; |
michael@0 | 377 | |
michael@0 | 378 | }} // namespace mozilla::net |
michael@0 | 379 | |
michael@0 | 380 | #endif // nsHttpTransaction_h__ |