Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /* vim:set ts=4 sw=4 sts=4 et cin: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : // HttpLog.h should generally be included first
8 : #include "HttpLog.h"
9 :
10 : #include "base/basictypes.h"
11 :
12 : #include "nsHttpHandler.h"
13 : #include "nsHttpTransaction.h"
14 : #include "nsHttpRequestHead.h"
15 : #include "nsHttpResponseHead.h"
16 : #include "nsHttpChunkedDecoder.h"
17 : #include "nsTransportUtils.h"
18 : #include "nsNetCID.h"
19 : #include "nsNetUtil.h"
20 : #include "nsIChannel.h"
21 : #include "nsIPipe.h"
22 : #include "nsCRT.h"
23 : #include "mozilla/Tokenizer.h"
24 : #include "TCPFastOpenLayer.h"
25 :
26 : #include "nsISeekableStream.h"
27 : #include "nsMultiplexInputStream.h"
28 : #include "nsStringStream.h"
29 :
30 : #include "nsComponentManagerUtils.h" // do_CreateInstance
31 : #include "nsIHttpActivityObserver.h"
32 : #include "nsSocketTransportService2.h"
33 : #include "nsICancelable.h"
34 : #include "nsIClassOfService.h"
35 : #include "nsIEventTarget.h"
36 : #include "nsIHttpChannelInternal.h"
37 : #include "nsIInputStream.h"
38 : #include "nsIThrottledInputChannel.h"
39 : #include "nsITransport.h"
40 : #include "nsIOService.h"
41 : #include "nsIRequestContext.h"
42 : #include "nsIHttpAuthenticator.h"
43 : #include "NSSErrorsService.h"
44 : #include "sslerr.h"
45 : #include <algorithm>
46 :
47 : //-----------------------------------------------------------------------------
48 :
49 : static NS_DEFINE_CID(kMultiplexInputStream, NS_MULTIPLEXINPUTSTREAM_CID);
50 :
51 : // Place a limit on how much non-compliant HTTP can be skipped while
52 : // looking for a response header
53 : #define MAX_INVALID_RESPONSE_BODY_SIZE (1024 * 128)
54 :
55 : using namespace mozilla::net;
56 :
57 : namespace mozilla {
58 : namespace net {
59 :
60 : //-----------------------------------------------------------------------------
61 : // helpers
62 : //-----------------------------------------------------------------------------
63 :
64 : static void
65 0 : LogHeaders(const char *lineStart)
66 : {
67 0 : nsAutoCString buf;
68 : char *endOfLine;
69 0 : while ((endOfLine = PL_strstr(lineStart, "\r\n"))) {
70 0 : buf.Assign(lineStart, endOfLine - lineStart);
71 0 : if (PL_strcasestr(buf.get(), "authorization: ") ||
72 0 : PL_strcasestr(buf.get(), "proxy-authorization: ")) {
73 0 : char *p = PL_strchr(PL_strchr(buf.get(), ' ') + 1, ' ');
74 0 : while (p && *++p)
75 0 : *p = '*';
76 : }
77 0 : LOG3((" %s\n", buf.get()));
78 0 : lineStart = endOfLine + 2;
79 : }
80 0 : }
81 :
82 : //-----------------------------------------------------------------------------
83 : // nsHttpTransaction <public>
84 : //-----------------------------------------------------------------------------
85 :
86 0 : nsHttpTransaction::nsHttpTransaction()
87 : : mLock("transaction lock")
88 : , mRequestSize(0)
89 : , mRequestHead(nullptr)
90 : , mResponseHead(nullptr)
91 : , mReader(nullptr)
92 : , mWriter(nullptr)
93 : , mContentLength(-1)
94 : , mContentRead(0)
95 : , mTransferSize(0)
96 : , mInvalidResponseBytesRead(0)
97 : , mPushedStream(nullptr)
98 : , mInitialRwin(0)
99 : , mChunkedDecoder(nullptr)
100 : , mStatus(NS_OK)
101 : , mPriority(0)
102 : , mRestartCount(0)
103 : , mCaps(0)
104 : , mHttpVersion(HttpVersion::UNKNOWN)
105 : , mHttpResponseCode(0)
106 : , mCurrentHttpResponseHeaderSize(0)
107 : , mThrottlingReadAllowance(THROTTLE_NO_LIMIT)
108 : , mCapsToClear(0)
109 : , mResponseIsComplete(false)
110 : , mReadingStopped(false)
111 : , mClosed(false)
112 : , mConnected(false)
113 : , mActivated(false)
114 : , mHaveStatusLine(false)
115 : , mHaveAllHeaders(false)
116 : , mTransactionDone(false)
117 : , mDidContentStart(false)
118 : , mNoContent(false)
119 : , mSentData(false)
120 : , mReceivedData(false)
121 : , mStatusEventPending(false)
122 : , mHasRequestBody(false)
123 : , mProxyConnectFailed(false)
124 : , mHttpResponseMatched(false)
125 : , mPreserveStream(false)
126 : , mDispatchedAsBlocking(false)
127 : , mResponseTimeoutEnabled(true)
128 : , mForceRestart(false)
129 : , mReuseOnRestart(false)
130 : , mContentDecoding(false)
131 : , mContentDecodingCheck(false)
132 : , mDeferredSendProgress(false)
133 : , mWaitingOnPipeOut(false)
134 : , mReportedStart(false)
135 : , mReportedResponseHeader(false)
136 : , mResponseHeadTaken(false)
137 : , mForTakeResponseTrailers(nullptr)
138 : , mResponseTrailersTaken(false)
139 : , mTopLevelOuterContentWindowId(0)
140 : , mSubmittedRatePacing(false)
141 : , mPassedRatePacing(false)
142 : , mSynchronousRatePaceRequest(false)
143 : , mClassOfService(0)
144 : , m0RTTInProgress(false)
145 : , mDoNotTryEarlyData(false)
146 : , mEarlyDataDisposition(EARLY_NONE)
147 0 : , mFastOpenStatus(TFO_NOT_TRIED)
148 : {
149 0 : this->mSelfAddr.inet = {};
150 : this->mPeerAddr.inet = {};
151 : LOG(("Creating nsHttpTransaction @%p\n", this));
152 :
153 : #ifdef MOZ_VALGRIND
154 : memset(&mSelfAddr, 0, sizeof(NetAddr));
155 0 : memset(&mPeerAddr, 0, sizeof(NetAddr));
156 0 : #endif
157 0 : mSelfAddr.raw.family = PR_AF_UNSPEC;
158 : mPeerAddr.raw.family = PR_AF_UNSPEC;
159 0 : }
160 :
161 0 : void nsHttpTransaction::ResumeReading()
162 : {
163 0 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
164 :
165 : if (!mReadingStopped) {
166 : return;
167 0 : }
168 :
169 0 : LOG(("nsHttpTransaction::ResumeReading %p", this));
170 :
171 : mReadingStopped = false;
172 :
173 0 : // This with either reengage the limit when still throttled in WriteSegments or
174 : // simply reset to allow unlimeted reading again.
175 0 : mThrottlingReadAllowance = THROTTLE_NO_LIMIT;
176 0 :
177 0 : if (mConnection) {
178 0 : mConnection->TransactionHasDataToRecv(this);
179 0 : nsresult rv = mConnection->ResumeRecv();
180 : if (NS_FAILED(rv)) {
181 : LOG((" resume failed with rv=%" PRIx32, static_cast<uint32_t>(rv)));
182 : }
183 : }
184 0 : }
185 :
186 0 : bool nsHttpTransaction::EligibleForThrottling() const
187 : {
188 : return (mClassOfService & (nsIClassOfService::Throttleable |
189 : nsIClassOfService::DontThrottle |
190 0 : nsIClassOfService::Leader |
191 : nsIClassOfService::Unblocked)) ==
192 : nsIClassOfService::Throttleable;
193 0 : }
194 :
195 0 : void nsHttpTransaction::SetClassOfService(uint32_t cos)
196 0 : {
197 0 : bool wasThrottling = EligibleForThrottling();
198 : mClassOfService = cos;
199 0 : bool isThrottling = EligibleForThrottling();
200 :
201 : if (mConnection && wasThrottling != isThrottling) {
202 : // Do nothing until we are actually activated. For now
203 0 : // only remember the throttle flag. Call to UpdateActiveTransaction
204 : // would add this transaction to the list too early.
205 0 : gHttpHandler->ConnMgr()->UpdateActiveTransaction(this);
206 0 :
207 : if (mReadingStopped && !isThrottling) {
208 : ResumeReading();
209 0 : }
210 : }
211 0 : }
212 :
213 0 : nsHttpTransaction::~nsHttpTransaction()
214 0 : {
215 0 : LOG(("Destroying nsHttpTransaction @%p\n", this));
216 : if (mTransactionObserver) {
217 0 : mTransactionObserver->Complete(this, NS_OK);
218 0 : }
219 0 : if (mPushedStream) {
220 : mPushedStream->OnPushFailed();
221 : mPushedStream = nullptr;
222 0 : }
223 0 :
224 0 : if (mTokenBucketCancel) {
225 : mTokenBucketCancel->Cancel(NS_ERROR_ABORT);
226 : mTokenBucketCancel = nullptr;
227 : }
228 0 :
229 0 : // Force the callbacks and connection to be released right now
230 : mCallbacks = nullptr;
231 0 : mConnection = nullptr;
232 0 :
233 0 : delete mResponseHead;
234 0 : delete mChunkedDecoder;
235 : ReleaseBlockingTransaction();
236 : }
237 0 :
238 : nsresult
239 : nsHttpTransaction::Init(uint32_t caps,
240 : nsHttpConnectionInfo *cinfo,
241 : nsHttpRequestHead *requestHead,
242 : nsIInputStream *requestBody,
243 : uint64_t requestContentLength,
244 : bool requestBodyHasHeaders,
245 : nsIEventTarget *target,
246 : nsIInterfaceRequestor *callbacks,
247 : nsITransportEventSink *eventsink,
248 : uint64_t topLevelOuterContentWindowId,
249 : nsIAsyncInputStream **responseBody)
250 : {
251 0 : nsresult rv;
252 :
253 0 : LOG(("nsHttpTransaction::Init [this=%p caps=%x]\n", this, caps));
254 0 :
255 0 : MOZ_ASSERT(cinfo);
256 0 : MOZ_ASSERT(requestHead);
257 : MOZ_ASSERT(target);
258 0 : MOZ_ASSERT(NS_IsMainThread());
259 0 :
260 : mTopLevelOuterContentWindowId = topLevelOuterContentWindowId;
261 0 : LOG((" window-id = %" PRIx64, mTopLevelOuterContentWindowId));
262 0 :
263 : mActivityDistributor = services::GetActivityDistributor();
264 : if (!mActivityDistributor) {
265 : return NS_ERROR_NOT_AVAILABLE;
266 : }
267 0 :
268 0 : bool activityDistributorActive;
269 : rv = mActivityDistributor->GetIsActive(&activityDistributorActive);
270 : if (NS_SUCCEEDED(rv) && activityDistributorActive) {
271 0 : // there are some observers registered at activity distributor, gather
272 : // nsISupports for the channel that called Init()
273 : LOG(("nsHttpTransaction::Init() " \
274 : "mActivityDistributor is active " \
275 : "this=%p", this));
276 0 : } else {
277 0 : // there is no observer, so don't use it
278 : activityDistributorActive = false;
279 0 : mActivityDistributor = nullptr;
280 : }
281 : mChannel = do_QueryInterface(eventsink);
282 0 :
283 0 : nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal =
284 0 : do_QueryInterface(eventsink);
285 0 : if (httpChannelInternal) {
286 0 : rv = httpChannelInternal->GetResponseTimeoutEnabled(
287 0 : &mResponseTimeoutEnabled);
288 : if (NS_WARN_IF(NS_FAILED(rv))) {
289 0 : return rv;
290 0 : }
291 : rv = httpChannelInternal->GetInitialRwin(&mInitialRwin);
292 : MOZ_ASSERT(NS_SUCCEEDED(rv));
293 : }
294 :
295 0 : // create transport event sink proxy. it coalesces consecutive
296 : // events of the same status type.
297 : rv = net_NewTransportEventSinkProxy(getter_AddRefs(mTransportSink),
298 0 : eventsink, target);
299 :
300 0 : if (NS_FAILED(rv)) return rv;
301 0 :
302 0 : mConnInfo = cinfo;
303 0 : mCallbacks = callbacks;
304 : mConsumerTarget = target;
305 0 : mCaps = caps;
306 0 :
307 : if (requestHead->IsHead()) {
308 : mNoContent = true;
309 : }
310 :
311 : // Make sure that there is "Content-Length: 0" header in the requestHead
312 : // in case of POST and PUT methods when there is no requestBody and
313 : // requestHead doesn't contain "Transfer-Encoding" header.
314 : //
315 : // RFC1945 section 7.2.2:
316 : // HTTP/1.0 requests containing an entity body must include a valid
317 : // Content-Length header field.
318 : //
319 : // RFC2616 section 4.4:
320 : // For compatibility with HTTP/1.0 applications, HTTP/1.1 requests
321 0 : // containing a message-body MUST include a valid Content-Length header
322 0 : // field unless the server is known to be HTTP/1.1 compliant.
323 0 : if ((requestHead->IsPost() || requestHead->IsPut()) &&
324 0 : !requestBody && !requestHead->HasHeader(nsHttp::Transfer_Encoding)) {
325 : rv = requestHead->SetHeader(nsHttp::Content_Length, NS_LITERAL_CSTRING("0"));
326 : MOZ_ASSERT(NS_SUCCEEDED(rv));
327 : }
328 0 :
329 : // grab a weak reference to the request head
330 : mRequestHead = requestHead;
331 :
332 0 : // make sure we eliminate any proxy specific headers from
333 : // the request if we are using CONNECT
334 0 : bool pruneProxyHeaders = cinfo->UsingConnect();
335 0 :
336 : mReqHeaderBuf.Truncate();
337 0 : requestHead->Flatten(mReqHeaderBuf, pruneProxyHeaders);
338 0 :
339 0 : if (LOG3_ENABLED()) {
340 0 : LOG3(("http request [\n"));
341 : LogHeaders(mReqHeaderBuf.get());
342 : LOG3(("]\n"));
343 : }
344 :
345 0 : // If the request body does not include headers or if there is no request
346 0 : // body, then we must add the header/body separator manually.
347 : if (!requestBodyHasHeaders || !requestBody)
348 : mReqHeaderBuf.AppendLiteral("\r\n");
349 0 :
350 0 : // report the request header
351 : if (mActivityDistributor) {
352 : rv = mActivityDistributor->ObserveActivity(
353 : mChannel,
354 : NS_HTTP_ACTIVITY_TYPE_HTTP_TRANSACTION,
355 0 : NS_HTTP_ACTIVITY_SUBTYPE_REQUEST_HEADER,
356 0 : PR_Now(), 0,
357 0 : mReqHeaderBuf);
358 : if (NS_FAILED(rv)) {
359 : LOG3(("ObserveActivity failed (%08x)", static_cast<uint32_t>(rv)));
360 : }
361 : }
362 :
363 : // Create a string stream for the request header buf (the stream holds
364 0 : // a non-owning reference to the request header data, so we MUST keep
365 0 : // mReqHeaderBuf around).
366 : nsCOMPtr<nsIInputStream> headers;
367 0 : rv = NS_NewByteInputStream(getter_AddRefs(headers),
368 0 : mReqHeaderBuf.get(),
369 : mReqHeaderBuf.Length());
370 0 : if (NS_FAILED(rv)) return rv;
371 0 :
372 0 : mHasRequestBody = !!requestBody;
373 : if (mHasRequestBody && !requestContentLength) {
374 : mHasRequestBody = false;
375 0 : }
376 :
377 0 : requestContentLength += mReqHeaderBuf.Length();
378 :
379 : if (mHasRequestBody) {
380 0 : // wrap the headers and request body in a multiplexed input stream.
381 0 : nsCOMPtr<nsIMultiplexInputStream> multi =
382 : do_CreateInstance(kMultiplexInputStream, &rv);
383 0 : if (NS_FAILED(rv)) return rv;
384 0 :
385 : rv = multi->AppendStream(headers);
386 0 : if (NS_FAILED(rv)) return rv;
387 0 :
388 : rv = multi->AppendStream(requestBody);
389 : if (NS_FAILED(rv)) return rv;
390 :
391 : // wrap the multiplexed input stream with a buffered input stream, so
392 0 : // that we write data in the largest chunks possible. this is actually
393 0 : // necessary to workaround some common server bugs (see bug 137155).
394 0 : nsCOMPtr<nsIInputStream> stream(do_QueryInterface(multi));
395 : rv = NS_NewBufferedInputStream(getter_AddRefs(mRequestStream),
396 0 : stream.forget(),
397 : nsIOService::gDefaultSegmentSize);
398 0 : if (NS_FAILED(rv)) return rv;
399 : } else {
400 : mRequestStream = headers;
401 0 : }
402 :
403 0 : nsCOMPtr<nsIThrottledInputChannel> throttled = do_QueryInterface(mChannel);
404 0 : nsIInputChannelThrottleQueue* queue;
405 : if (throttled) {
406 0 : rv = throttled->GetThrottleQueue(&queue);
407 0 : // In case of failure, just carry on without throttling.
408 0 : if (NS_SUCCEEDED(rv) && queue) {
409 : nsCOMPtr<nsIAsyncInputStream> wrappedStream;
410 : rv = queue->WrapStream(mRequestStream, getter_AddRefs(wrappedStream));
411 0 : // Failure to throttle isn't sufficient reason to fail
412 0 : // initialization
413 0 : if (NS_SUCCEEDED(rv)) {
414 : MOZ_ASSERT(wrappedStream != nullptr);
415 0 : LOG(("nsHttpTransaction::Init %p wrapping input stream using throttle queue %p\n",
416 : this, queue));
417 : mRequestStream = do_QueryInterface(wrappedStream);
418 : }
419 : }
420 : }
421 0 :
422 : // make sure request content-length fits within js MAX_SAFE_INTEGER
423 : mRequestSize = InScriptableRange(requestContentLength) ? static_cast<int64_t>(requestContentLength) : -1;
424 0 :
425 0 : // create pipe for response stream
426 : rv = NS_NewPipe2(getter_AddRefs(mPipeIn),
427 : getter_AddRefs(mPipeOut),
428 : true, true,
429 0 : nsIOService::gDefaultSegmentSize,
430 : nsIOService::gDefaultSegmentCount);
431 : if (NS_FAILED(rv)) return rv;
432 :
433 : #ifdef WIN32 // bug 1153929
434 : MOZ_DIAGNOSTIC_ASSERT(mPipeOut);
435 : uint32_t * vtable = (uint32_t *) mPipeOut.get();
436 : MOZ_DIAGNOSTIC_ASSERT(*vtable != 0);
437 0 : #endif // WIN32
438 0 :
439 : nsCOMPtr<nsIAsyncInputStream> tmp(mPipeIn);
440 : tmp.forget(responseBody);
441 : return NS_OK;
442 : }
443 :
444 0 : // This method should only be used on the socket thread
445 : nsAHttpConnection *
446 0 : nsHttpTransaction::Connection()
447 0 : {
448 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
449 : return mConnection.get();
450 : }
451 0 :
452 : already_AddRefed<nsAHttpConnection>
453 0 : nsHttpTransaction::GetConnectionReference()
454 0 : {
455 0 : MutexAutoLock lock(mLock);
456 : RefPtr<nsAHttpConnection> connection(mConnection);
457 : return connection.forget();
458 : }
459 0 :
460 : nsHttpResponseHead *
461 0 : nsHttpTransaction::TakeResponseHead()
462 : {
463 : MOZ_ASSERT(!mResponseHeadTaken, "TakeResponseHead called 2x");
464 0 :
465 : // Lock TakeResponseHead() against main thread
466 0 : MutexAutoLock lock(*nsHttp::GetLock());
467 :
468 : mResponseHeadTaken = true;
469 :
470 0 : // Even in OnStartRequest() the headers won't be available if we were
471 0 : // canceled
472 0 : if (!mHaveAllHeaders) {
473 : NS_WARNING("response headers not available or incomplete");
474 : return nullptr;
475 0 : }
476 0 :
477 0 : nsHttpResponseHead *head = mResponseHead;
478 : mResponseHead = nullptr;
479 : return head;
480 : }
481 0 :
482 : nsHttpHeaderArray *
483 0 : nsHttpTransaction::TakeResponseTrailers()
484 : {
485 : MOZ_ASSERT(!mResponseTrailersTaken, "TakeResponseTrailers called 2x");
486 0 :
487 : // Lock TakeResponseTrailers() against main thread
488 0 : MutexAutoLock lock(*nsHttp::GetLock());
489 0 :
490 : mResponseTrailersTaken = true;
491 : return mForTakeResponseTrailers.forget();
492 : }
493 0 :
494 : void
495 0 : nsHttpTransaction::SetProxyConnectFailed()
496 0 : {
497 : mProxyConnectFailed = true;
498 : }
499 0 :
500 : nsHttpRequestHead *
501 0 : nsHttpTransaction::RequestHead()
502 : {
503 : return mRequestHead;
504 : }
505 0 :
506 : uint32_t
507 0 : nsHttpTransaction::Http1xTransactionCount()
508 : {
509 : return 1;
510 : }
511 0 :
512 : nsresult
513 : nsHttpTransaction::TakeSubTransactions(
514 0 : nsTArray<RefPtr<nsAHttpTransaction> > &outTransactions)
515 : {
516 : return NS_ERROR_NOT_IMPLEMENTED;
517 : }
518 :
519 : //----------------------------------------------------------------------------
520 : // nsHttpTransaction::nsAHttpTransaction
521 : //----------------------------------------------------------------------------
522 0 :
523 : void
524 : nsHttpTransaction::SetConnection(nsAHttpConnection *conn)
525 0 : {
526 0 : {
527 : MutexAutoLock lock(mLock);
528 0 : mConnection = conn;
529 : }
530 : }
531 0 :
532 : void
533 0 : nsHttpTransaction::OnActivated()
534 : {
535 0 : MOZ_ASSERT(OnSocketThread());
536 :
537 : if (mActivated) {
538 : return;
539 0 : }
540 :
541 : if (mConnection && mRequestHead && mConnection->Version() >= HttpVersion::v2_0) {
542 : // So this is fun. On http/2, we want to send TE: Trailers, to be
543 : // spec-compliant. So we add it to the request head here. The fun part
544 : // is that adding a header to the request head at this point has no
545 : // effect on what we send on the wire, as the headers are already
546 : // flattened (in Init()) by the time we get here. So the *real* adding
547 : // of the header happens in the h2 compression code. We still have to
548 0 : // add the header to the request head here, though, so that devtools can
549 : // show that we sent the header. FUN!
550 : Unused << mRequestHead->SetHeader(nsHttp::TE, NS_LITERAL_CSTRING("Trailers"));
551 0 : }
552 0 :
553 : mActivated = true;
554 : gHttpHandler->ConnMgr()->AddActiveTransaction(this);
555 : }
556 0 :
557 : void
558 18 : nsHttpTransaction::GetSecurityCallbacks(nsIInterfaceRequestor **cb)
559 18 : {
560 0 : MutexAutoLock lock(mLock);
561 0 : nsCOMPtr<nsIInterfaceRequestor> tmp(mCallbacks);
562 : tmp.forget(cb);
563 : }
564 0 :
565 : void
566 : nsHttpTransaction::SetSecurityCallbacks(nsIInterfaceRequestor* aCallbacks)
567 0 : {
568 0 : {
569 : MutexAutoLock lock(mLock);
570 : mCallbacks = aCallbacks;
571 0 : }
572 0 :
573 0 : if (gSocketTransportService) {
574 : RefPtr<UpdateSecurityCallbacks> event = new UpdateSecurityCallbacks(this, aCallbacks);
575 0 : gSocketTransportService->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL);
576 : }
577 : }
578 28 :
579 : void
580 : nsHttpTransaction::OnTransportStatus(nsITransport* transport,
581 0 : nsresult status, int64_t progress)
582 : {
583 : LOG(("nsHttpTransaction::OnSocketStatus [this=%p status=%" PRIx32 " progress=%" PRId64 "]\n",
584 0 : this, static_cast<uint32_t>(status), progress));
585 28 :
586 : if (status == NS_NET_STATUS_CONNECTED_TO ||
587 0 : status == NS_NET_STATUS_WAITING_FOR) {
588 7 : nsISocketTransport *socketTransport =
589 6 : mConnection ? mConnection->Transport() : nullptr;
590 0 : if (socketTransport) {
591 3 : MutexAutoLock lock(mLock);
592 : socketTransport->GetSelfAddr(&mSelfAddr);
593 : socketTransport->GetPeerAddr(&mPeerAddr);
594 : }
595 : }
596 :
597 : // If the timing is enabled, and we are not using a persistent connection
598 : // then the requestStart timestamp will be null, so we mark the timestamps
599 : // for domainLookupStart/End and connectStart/End
600 0 : // If we are using a persistent connection they will remain null,
601 0 : // and the correct value will be returned in Performance.
602 0 : if (TimingEnabled() && GetRequestStart().IsNull()) {
603 0 : if (status == NS_NET_STATUS_RESOLVING_HOST) {
604 0 : SetDomainLookupStart(TimeStamp::Now(), true);
605 0 : } else if (status == NS_NET_STATUS_RESOLVED_HOST) {
606 0 : SetDomainLookupEnd(TimeStamp::Now());
607 0 : } else if (status == NS_NET_STATUS_CONNECTING_TO) {
608 0 : SetConnectStart(TimeStamp::Now());
609 0 : } else if (status == NS_NET_STATUS_CONNECTED_TO) {
610 : TimeStamp tnow = TimeStamp::Now();
611 0 : SetConnectEnd(tnow, true);
612 0 : {
613 : MutexAutoLock lock(mLock);
614 : mTimings.tcpConnectEnd = tnow;
615 : // After a socket is connected we know for sure whether data
616 0 : // has been sent on SYN packet and if not we should update TLS
617 0 : // start timing.
618 0 : if ((mFastOpenStatus != TFO_DATA_SENT) &&
619 : !mTimings.secureConnectionStart.IsNull()) {
620 : mTimings.secureConnectionStart = tnow;
621 0 : }
622 : }
623 0 : } else if (status == NS_NET_STATUS_TLS_HANDSHAKE_STARTING) {
624 0 : {
625 : MutexAutoLock lock(mLock);
626 0 : mTimings.secureConnectionStart = TimeStamp::Now();
627 0 : }
628 0 : } else if (status == NS_NET_STATUS_TLS_HANDSHAKE_ENDED) {
629 : SetConnectEnd(TimeStamp::Now(), false);
630 0 : } else if (status == NS_NET_STATUS_SENDING_TO) {
631 : // Set the timestamp to Now(), only if it null
632 : SetRequestStart(TimeStamp::Now(), true);
633 : }
634 56 : }
635 :
636 : if (!mTransportSink)
637 0 : return;
638 :
639 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
640 :
641 56 : // Need to do this before the STATUS_RECEIVING_FROM check below, to make
642 : // sure that the activity distributor gets told about all status events.
643 0 : if (mActivityDistributor) {
644 : // upon STATUS_WAITING_FOR; report request body sent
645 0 : if ((mHasRequestBody) &&
646 : (status == NS_NET_STATUS_WAITING_FOR)) {
647 : nsresult rv = mActivityDistributor->ObserveActivity(
648 : mChannel,
649 0 : NS_HTTP_ACTIVITY_TYPE_HTTP_TRANSACTION,
650 0 : NS_HTTP_ACTIVITY_SUBTYPE_REQUEST_BODY_SENT,
651 0 : PR_Now(), 0, EmptyCString());
652 : if (NS_FAILED(rv)) {
653 : LOG3(("ObserveActivity failed (%08x)", static_cast<uint32_t>(rv)));
654 : }
655 : }
656 0 :
657 : // report the status and progress
658 : nsresult rv = mActivityDistributor->ObserveActivity(
659 : mChannel,
660 : NS_HTTP_ACTIVITY_TYPE_SOCKET_TRANSPORT,
661 : static_cast<uint32_t>(status),
662 0 : PR_Now(),
663 0 : progress,
664 0 : EmptyCString());
665 : if (NS_FAILED(rv)) {
666 : LOG3(("ObserveActivity failed (%08x)", static_cast<uint32_t>(rv)));
667 : }
668 : }
669 28 :
670 : // nsHttpChannel synthesizes progress events in OnDataAvailable
671 : if (status == NS_NET_STATUS_RECEIVING_FROM)
672 : return;
673 :
674 25 : int64_t progressMax;
675 :
676 5 : if (status == NS_NET_STATUS_SENDING_TO) {
677 1 : // suppress progress when only writing request headers
678 : if (!mHasRequestBody) {
679 3 : LOG(("nsHttpTransaction::OnTransportStatus %p "
680 : "SENDING_TO without request body\n", this));
681 : return;
682 4 : }
683 :
684 2 : if (mReader) {
685 : // A mRequestStream method is on the stack - wait.
686 : LOG(("nsHttpTransaction::OnSocketStatus [this=%p] "
687 2 : "Skipping Re-Entrant NS_NET_STATUS_SENDING_TO\n", this));
688 0 : // its ok to coalesce several of these into one deferred event
689 : mDeferredSendProgress = true;
690 : return;
691 0 : }
692 2 :
693 0 : nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mRequestStream);
694 : if (!seekable) {
695 : LOG(("nsHttpTransaction::OnTransportStatus %p "
696 : "SENDING_TO without seekable request stream\n", this));
697 0 : progress = 0;
698 2 : } else {
699 2 : int64_t prog = 0;
700 : seekable->Tell(&prog);
701 : progress = prog;
702 : }
703 :
704 2 : // when uploading, we include the request headers in the progress
705 : // notifications.
706 : progressMax = mRequestSize;
707 : }
708 : else {
709 : progress = 0;
710 : progressMax = 0;
711 22 : }
712 :
713 : mTransportSink->OnTransportStatus(transport, status, progress, progressMax);
714 : }
715 0 :
716 : bool
717 0 : nsHttpTransaction::IsDone()
718 : {
719 : return mTransactionDone;
720 : }
721 0 :
722 : nsresult
723 4 : nsHttpTransaction::Status()
724 : {
725 : return mStatus;
726 : }
727 42 :
728 : uint32_t
729 84 : nsHttpTransaction::Caps()
730 : {
731 : return mCaps & ~mCapsToClear;
732 : }
733 2 :
734 : void
735 2 : nsHttpTransaction::SetDNSWasRefreshed()
736 0 : {
737 2 : MOZ_ASSERT(NS_IsMainThread(), "SetDNSWasRefreshed on main thread only!");
738 : mCapsToClear |= NS_HTTP_REFRESH_DNS;
739 : }
740 3 :
741 : nsresult
742 : nsHttpTransaction::ReadRequestSegment(nsIInputStream *stream,
743 : void *closure,
744 : const char *buf,
745 : uint32_t offset,
746 : uint32_t count,
747 : uint32_t *countRead)
748 : {
749 : // For the tracking of sent bytes that we used to do for the networkstats
750 3 : // API, please see bug 1318883 where it was removed.
751 3 :
752 3 : nsHttpTransaction *trans = (nsHttpTransaction *) closure;
753 : nsresult rv = trans->mReader->OnReadSegment(buf, count, countRead);
754 3 : if (NS_FAILED(rv)) return rv;
755 :
756 3 : LOG(("nsHttpTransaction::ReadRequestSegment %p read=%u", trans, *countRead));
757 3 :
758 : trans->mSentData = true;
759 : return NS_OK;
760 : }
761 0 :
762 : nsresult
763 : nsHttpTransaction::ReadSegments(nsAHttpSegmentReader *reader,
764 6 : uint32_t count, uint32_t *countRead)
765 : {
766 0 : LOG(("nsHttpTransaction::ReadSegments %p", this));
767 :
768 6 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
769 0 :
770 0 : if (mTransactionDone) {
771 : *countRead = 0;
772 : return mStatus;
773 0 : }
774 3 :
775 0 : if (!mConnected && !m0RTTInProgress) {
776 : mConnected = true;
777 : mConnection->GetSecurityInfo(getter_AddRefs(mSecurityInfo));
778 1 : }
779 1 :
780 6 : mDeferredSendProgress = false;
781 6 : mReader = reader;
782 : nsresult rv = mRequestStream->ReadSegments(ReadRequestSegment, this, count, countRead);
783 0 : mReader = nullptr;
784 0 :
785 0 : if (m0RTTInProgress && (mEarlyDataDisposition == EARLY_NONE) &&
786 : NS_SUCCEEDED(rv) && (*countRead > 0)) {
787 : mEarlyDataDisposition = EARLY_SENT;
788 0 : }
789 :
790 : if (mDeferredSendProgress && mConnection && mConnection->Transport()) {
791 : // to avoid using mRequestStream concurrently, OnTransportStatus()
792 0 : // did not report upload status off the ReadSegments() stack from nsSocketTransport
793 : // do it now.
794 1 : OnTransportStatus(mConnection->Transport(), NS_NET_STATUS_SENDING_TO, 0);
795 : }
796 6 : mDeferredSendProgress = false;
797 :
798 : if (mForceRestart) {
799 : // The forceRestart condition was dealt with on the stack, but it did not
800 0 : // clear the flag because nsPipe in the readsegment stack clears out
801 0 : // return codes, so we need to use the flag here as a cue to return ERETARGETED
802 : if (NS_SUCCEEDED(rv)) {
803 0 : rv = NS_BINDING_RETARGETED;
804 : }
805 : mForceRestart = false;
806 : }
807 :
808 6 : // if read would block then we need to AsyncWait on the request stream.
809 : // have callback occur on socket thread so we stay synchronized.
810 0 : if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
811 0 : nsCOMPtr<nsIAsyncInputStream> asyncIn =
812 0 : do_QueryInterface(mRequestStream);
813 0 : if (asyncIn) {
814 0 : nsCOMPtr<nsIEventTarget> target;
815 0 : Unused << gHttpHandler->GetSocketThreadTarget(getter_AddRefs(target));
816 : if (target)
817 0 : asyncIn->AsyncWait(this, 0, 0, target);
818 0 : else {
819 : NS_ERROR("no socket thread event target");
820 : rv = NS_ERROR_UNEXPECTED;
821 : }
822 : }
823 : }
824 :
825 : return rv;
826 : }
827 1 :
828 : nsresult
829 : nsHttpTransaction::WritePipeSegment(nsIOutputStream *stream,
830 : void *closure,
831 : char *buf,
832 : uint32_t offset,
833 : uint32_t count,
834 6 : uint32_t *countWritten)
835 : {
836 0 : nsHttpTransaction *trans = (nsHttpTransaction *) closure;
837 :
838 : if (trans->mTransactionDone)
839 6 : return NS_BASE_STREAM_CLOSED; // stop iterating
840 :
841 0 : if (trans->TimingEnabled()) {
842 : // Set the timestamp to Now(), only if it null
843 : trans->SetResponseStart(TimeStamp::Now(), true);
844 : }
845 0 :
846 3 : // Bug 1153929 - add checks to fix windows crash
847 : MOZ_ASSERT(trans->mWriter);
848 : if (!trans->mWriter) {
849 : return NS_ERROR_UNEXPECTED;
850 : }
851 :
852 : nsresult rv;
853 : //
854 0 : // OK, now let the caller fill this segment with data.
855 0 : //
856 : rv = trans->mWriter->OnWriteSegment(buf, count, countWritten);
857 3 : if (NS_FAILED(rv)) return rv; // caller didn't want to write anything
858 :
859 3 : LOG(("nsHttpTransaction::WritePipeSegment %p written=%u", trans, *countWritten));
860 3 :
861 3 : MOZ_ASSERT(*countWritten > 0, "bad writer");
862 : trans->mReceivedData = true;
863 : trans->mTransferSize += *countWritten;
864 :
865 : // Let the transaction "play" with the buffer. It is free to modify
866 : // the contents of the buffer and/or modify countWritten.
867 : // - Bytes in HTTP headers don't count towards countWritten, so the input
868 : // side of pipe (aka nsHttpChannel's mTransactionPump) won't hit
869 0 : // OnInputStreamReady until all headers have been parsed.
870 0 : //
871 0 : rv = trans->ProcessData(buf, *countWritten, countWritten);
872 : if (NS_FAILED(rv))
873 : trans->Close(rv);
874 :
875 : return rv; // failure code only stops WriteSegments; it is not propagated.
876 3 : }
877 :
878 0 : bool nsHttpTransaction::ShouldThrottle()
879 : {
880 : if (mClassOfService & nsIClassOfService::DontThrottle) {
881 : // We deliberately don't touch the throttling window here since
882 : // DontThrottle requests are expected to be long-standing media
883 : // streams and would just unnecessarily block running downloads.
884 : // If we want to ballance bandwidth for media responses against
885 : // running downloads, we need to find something smarter like
886 : // changing the suspend/resume throttling intervals at-runtime.
887 : return false;
888 6 : }
889 :
890 : if (!gHttpHandler->ConnMgr()->ShouldThrottle(this)) {
891 : // We are not obligated to throttle
892 : return false;
893 0 : }
894 :
895 0 : if (mContentRead < 16000) {
896 : // Let the first bytes go, it may also well be all the content we get
897 : LOG(("nsHttpTransaction::ShouldThrottle too few content (%" PRIi64 ") this=%p", mContentRead, this));
898 : return false;
899 0 : }
900 0 :
901 0 : if (!(mClassOfService & nsIClassOfService::Throttleable) &&
902 : gHttpHandler->ConnMgr()->IsConnEntryUnderPressure(mConnInfo)) {
903 : LOG(("nsHttpTransaction::ShouldThrottle entry pressure this=%p", this));
904 : // This is expensive to check (two hashtable lookups) but may help
905 : // freeing connections for active tab transactions.
906 : // Checking this only for transactions that are not explicitly marked
907 : // as throttleable because trackers and (specially) downloads should
908 : // keep throttling even under pressure.
909 : return false;
910 : }
911 :
912 : return true;
913 : }
914 6 :
915 : nsresult
916 : nsHttpTransaction::WriteSegments(nsAHttpSegmentWriter *writer,
917 6 : uint32_t count, uint32_t *countWritten)
918 : {
919 6 : LOG(("nsHttpTransaction::WriteSegments %p", this));
920 :
921 6 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
922 3 :
923 : if (mTransactionDone) {
924 : return NS_SUCCEEDED(mStatus) ? NS_BASE_STREAM_CLOSED : mStatus;
925 3 : }
926 0 :
927 : if (ShouldThrottle()) {
928 0 : if (mThrottlingReadAllowance == THROTTLE_NO_LIMIT) { // no limit set
929 : // V1: ThrottlingReadLimit() returns 0
930 : mThrottlingReadAllowance = gHttpHandler->ThrottlingReadLimit();
931 0 : }
932 : } else {
933 : mThrottlingReadAllowance = THROTTLE_NO_LIMIT; // don't limit
934 0 : }
935 0 :
936 0 : if (mThrottlingReadAllowance == 0) { // depleted
937 0 : if (gHttpHandler->ConnMgr()->CurrentTopLevelOuterContentWindowId() !=
938 : mTopLevelOuterContentWindowId) {
939 : nsHttp::NotifyActiveTabLoadOptimization();
940 : }
941 :
942 0 : // Must remember that we have to call ResumeRecv() on our connection when
943 0 : // called back by the conn manager to resume reading.
944 : LOG(("nsHttpTransaction::WriteSegments %p response throttled", this));
945 : mReadingStopped = true;
946 : // This makes the underlaying connection or stream wait for explicit resume.
947 0 : // For h1 this means we stop reading from the socket.
948 : // For h2 this means we stop updating recv window for the stream.
949 : return NS_BASE_STREAM_WOULD_BLOCK;
950 3 : }
951 :
952 : mWriter = writer;
953 :
954 : #ifdef WIN32 // bug 1153929
955 : MOZ_DIAGNOSTIC_ASSERT(mPipeOut);
956 : uint32_t * vtable = (uint32_t *) mPipeOut.get();
957 : MOZ_DIAGNOSTIC_ASSERT(*vtable != 0);
958 6 : #endif // WIN32
959 :
960 : if (!mPipeOut) {
961 : return NS_ERROR_UNEXPECTED;
962 3 : }
963 0 :
964 : if (mThrottlingReadAllowance > 0) {
965 0 : LOG(("nsHttpTransaction::WriteSegments %p limiting read from %u to %d",
966 : this, count, mThrottlingReadAllowance));
967 : count = std::min(count, static_cast<uint32_t>(mThrottlingReadAllowance));
968 3 : }
969 :
970 3 : nsresult rv = mPipeOut->WriteSegments(WritePipeSegment, this, count, countWritten);
971 :
972 0 : mWriter = nullptr;
973 :
974 : if (mForceRestart) {
975 : // The forceRestart condition was dealt with on the stack, but it did not
976 0 : // clear the flag because nsPipe in the writesegment stack clears out
977 0 : // return codes, so we need to use the flag here as a cue to return ERETARGETED
978 : if (NS_SUCCEEDED(rv)) {
979 0 : rv = NS_BINDING_RETARGETED;
980 : }
981 : mForceRestart = false;
982 : }
983 :
984 3 : // if pipe would block then we need to AsyncWait on it. have callback
985 0 : // occur on socket thread so we stay synchronized.
986 0 : if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
987 0 : nsCOMPtr<nsIEventTarget> target;
988 0 : Unused << gHttpHandler->GetSocketThreadTarget(getter_AddRefs(target));
989 0 : if (target) {
990 : mPipeOut->AsyncWait(this, 0, 0, target);
991 0 : mWaitingOnPipeOut = true;
992 0 : } else {
993 : NS_ERROR("no socket thread event target");
994 0 : rv = NS_ERROR_UNEXPECTED;
995 0 : }
996 0 : } else if (mThrottlingReadAllowance > 0 && NS_SUCCEEDED(rv)) {
997 : MOZ_ASSERT(count >= *countWritten);
998 : mThrottlingReadAllowance -= *countWritten;
999 : }
1000 :
1001 : return rv;
1002 : }
1003 0 :
1004 : void
1005 0 : nsHttpTransaction::Close(nsresult reason)
1006 : {
1007 : LOG(("nsHttpTransaction::Close [this=%p reason=%" PRIx32 "]\n",
1008 3 : this, static_cast<uint32_t>(reason)));
1009 9 :
1010 3 : if (!mClosed) {
1011 : gHttpHandler->ConnMgr()->RemoveActiveTransaction(this);
1012 : mActivated = false;
1013 3 : }
1014 0 :
1015 0 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1016 : if (reason == NS_BINDING_RETARGETED) {
1017 : LOG((" close %p skipped due to ERETARGETED\n", this));
1018 : return;
1019 0 : }
1020 0 :
1021 : if (mClosed) {
1022 : LOG((" already closed\n"));
1023 : return;
1024 0 : }
1025 0 :
1026 0 : if (mTransactionObserver) {
1027 : mTransactionObserver->Complete(this, reason);
1028 : mTransactionObserver = nullptr;
1029 0 : }
1030 0 :
1031 0 : if (mTokenBucketCancel) {
1032 : mTokenBucketCancel->Cancel(reason);
1033 : mTokenBucketCancel = nullptr;
1034 0 : }
1035 :
1036 0 : if (mActivityDistributor) {
1037 0 : // report the reponse is complete if not already reported
1038 : if (!mResponseIsComplete) {
1039 : nsresult rv = mActivityDistributor->ObserveActivity(
1040 : mChannel,
1041 : NS_HTTP_ACTIVITY_TYPE_HTTP_TRANSACTION,
1042 0 : NS_HTTP_ACTIVITY_SUBTYPE_RESPONSE_COMPLETE,
1043 0 : PR_Now(),
1044 0 : static_cast<uint64_t>(mContentRead),
1045 0 : EmptyCString());
1046 : if (NS_FAILED(rv)) {
1047 : LOG3(("ObserveActivity failed (%08x)", static_cast<uint32_t>(rv)));
1048 : }
1049 : }
1050 0 :
1051 : // report that this transaction is closing
1052 : nsresult rv = mActivityDistributor->ObserveActivity(
1053 : mChannel,
1054 0 : NS_HTTP_ACTIVITY_TYPE_HTTP_TRANSACTION,
1055 0 : NS_HTTP_ACTIVITY_SUBTYPE_TRANSACTION_CLOSE,
1056 0 : PR_Now(), 0, EmptyCString());
1057 : if (NS_FAILED(rv)) {
1058 : LOG3(("ObserveActivity failed (%08x)", static_cast<uint32_t>(rv)));
1059 : }
1060 : }
1061 :
1062 3 : // we must no longer reference the connection! find out if the
1063 0 : // connection was being reused before letting it go.
1064 0 : bool connReused = false;
1065 : if (mConnection) {
1066 3 : connReused = mConnection->IsReused();
1067 3 : }
1068 : mConnected = false;
1069 : mTunnelProvider = nullptr;
1070 :
1071 : //
1072 : // if the connection was reset or closed before we wrote any part of the
1073 : // request or if we wrote the request but didn't receive any part of the
1074 : // response and the connection was being reused, then we can (and really
1075 : // should) assume that we wrote to a stale connection and we must therefore
1076 : // repeat the request over a new connection.
1077 : //
1078 : // We have decided to retry not only in case of the reused connections, but
1079 : // all safe methods(bug 1236277).
1080 : //
1081 : // NOTE: the conditions under which we will automatically retry the HTTP
1082 : // request have to be carefully selected to avoid duplication of the
1083 : // request from the point-of-view of the server. such duplication could
1084 : // have dire consequences including repeated purchases, etc.
1085 : //
1086 : // NOTE: because of the way SSL proxy CONNECT is implemented, it is
1087 : // possible that the transaction may have received data without having
1088 : // sent any data. for this reason, mSendData == FALSE does not imply
1089 : // mReceivedData == FALSE. (see bug 203057 for more info.)
1090 : //
1091 : // Never restart transactions that are marked as sticky to their conenction.
1092 : // We use that capability to identify transactions bound to connection based
1093 : // authentication. Reissuing them on a different connections will break
1094 : // this bondage. Major issue may arise when there is an NTLM message auth
1095 : // header on the transaction and we send it to a different NTLM authenticated
1096 : // connection. It will break that connection and also confuse the channel's
1097 9 : // auth provider, beliving the cached credentials are wrong and asking for
1098 3 : // the password mistakenly again from the user.
1099 6 : if ((reason == NS_ERROR_NET_RESET ||
1100 3 : reason == NS_OK ||
1101 : reason == psm::GetXPCOMFromNSSError(SSL_ERROR_DOWNGRADE_WITH_EARLY_DATA)) &&
1102 3 : (!(mCaps & NS_HTTP_STICKY_CONNECTION) || (mCaps & NS_HTTP_CONNECTION_RESTARTABLE))) {
1103 0 :
1104 0 : if (mForceRestart && NS_SUCCEEDED(Restart())) {
1105 : if (mResponseHead) {
1106 0 : mResponseHead->Reset();
1107 0 : }
1108 0 : mContentRead = 0;
1109 0 : mContentLength = -1;
1110 0 : delete mChunkedDecoder;
1111 0 : mChunkedDecoder = nullptr;
1112 0 : mHaveStatusLine = false;
1113 0 : mHaveAllHeaders = false;
1114 0 : mHttpResponseMatched = false;
1115 0 : mResponseIsComplete = false;
1116 0 : mDidContentStart = false;
1117 0 : mNoContent = false;
1118 0 : mSentData = false;
1119 : mReceivedData = false;
1120 : LOG(("transaction force restarted\n"));
1121 : return;
1122 : }
1123 :
1124 : // reallySentData is meant to separate the instances where data has
1125 : // been sent by this transaction but buffered at a higher level while
1126 0 : // a TLS session (perhaps via a tunnel) is setup.
1127 : bool reallySentData =
1128 9 : mSentData && (!mConnection || mConnection->BytesWritten());
1129 3 :
1130 0 : if (reason == psm::GetXPCOMFromNSSError(SSL_ERROR_DOWNGRADE_WITH_EARLY_DATA) ||
1131 0 : (!mReceivedData &&
1132 : ((mRequestHead && mRequestHead->IsSafeMethod()) ||
1133 : !reallySentData || connReused))) {
1134 : // if restarting fails, then we must proceed to close the pipe,
1135 0 : // which will notify the channel that the transaction failed.
1136 :
1137 : if (NS_SUCCEEDED(Restart()))
1138 : return;
1139 : }
1140 1 : }
1141 6 :
1142 : if ((mChunkedDecoder || (mContentLength >= int64_t(0))) &&
1143 0 : (NS_SUCCEEDED(reason) && !mResponseIsComplete)) {
1144 :
1145 0 : NS_WARNING("Partial transfer, incomplete HTTP response received");
1146 0 :
1147 0 : if ((mHttpResponseCode / 100 == 2) &&
1148 0 : (mHttpVersion >= HttpVersion::v1_1)) {
1149 0 : FrameCheckLevel clevel = gHttpHandler->GetEnforceH1Framing();
1150 0 : if (clevel >= FRAMECHECK_BARELY) {
1151 0 : if ((clevel == FRAMECHECK_STRICT) ||
1152 0 : (mChunkedDecoder && mChunkedDecoder->GetChunkRemaining()) ||
1153 0 : (!mChunkedDecoder && !mContentDecoding && mContentDecodingCheck) ) {
1154 : reason = NS_ERROR_NET_PARTIAL_TRANSFER;
1155 : LOG(("Partial transfer, incomplete HTTP response received: %s",
1156 : mChunkedDecoder ? "broken chunk" : "c-l underrun"));
1157 : }
1158 : }
1159 0 : }
1160 :
1161 : if (mConnection) {
1162 0 : // whether or not we generate an error for the transaction
1163 : // bad framing means we don't want a pconn
1164 : mConnection->DontReuse();
1165 : }
1166 3 : }
1167 3 :
1168 : bool relConn = true;
1169 : if (NS_SUCCEEDED(reason)) {
1170 :
1171 : // the server has not sent the final \r\n terminating the header
1172 : // section, and there may still be a header line unparsed. let's make
1173 3 : // sure we parse the remaining header line, and then hopefully, the
1174 0 : // response will be usable (see bug 88792).
1175 : if (!mHaveAllHeaders) {
1176 0 : char data = '\n';
1177 : uint32_t unused;
1178 0 : Unused << ParseHead(&data, 1, &unused);
1179 :
1180 0 : if (mResponseHead->Version() == HttpVersion::v0_9) {
1181 : // Reject 0 byte HTTP/0.9 Responses - bug 423506
1182 : LOG(("nsHttpTransaction::Close %p 0 Byte 0.9 Response", this));
1183 : reason = NS_ERROR_NET_RESET;
1184 : }
1185 : }
1186 3 :
1187 0 : // honor the sticky connection flag...
1188 : if (mCaps & NS_HTTP_STICKY_CONNECTION)
1189 : relConn = false;
1190 : }
1191 :
1192 : // mTimings.responseEnd is normally recorded based on the end of a
1193 6 : // HTTP delimiter such as chunked-encodings or content-length. However,
1194 0 : // EOF or an error still require an end time be recorded.
1195 0 : if (TimingEnabled()) {
1196 0 : const TimingStruct timings = Timings();
1197 : if (timings.responseEnd.IsNull() && !timings.responseStart.IsNull()) {
1198 : SetResponseEnd(TimeStamp::Now());
1199 : }
1200 6 : }
1201 6 :
1202 0 : if (relConn && mConnection) {
1203 : MutexAutoLock lock(mLock);
1204 : mConnection = nullptr;
1205 0 : }
1206 3 :
1207 3 : mStatus = reason;
1208 3 : mTransactionDone = true; // forcibly flag the transaction as complete
1209 : mClosed = true;
1210 : ReleaseBlockingTransaction();
1211 0 :
1212 3 : // release some resources that we no longer need
1213 3 : mRequestStream = nullptr;
1214 0 : mReqHeaderBuf.Truncate();
1215 0 : mLineBuf.Truncate();
1216 0 : if (mChunkedDecoder) {
1217 : delete mChunkedDecoder;
1218 : mChunkedDecoder = nullptr;
1219 : }
1220 0 :
1221 : // closing this pipe triggers the channel's OnStopRequest method.
1222 : mPipeOut->CloseWithStatus(reason);
1223 :
1224 : #ifdef WIN32 // bug 1153929
1225 : MOZ_DIAGNOSTIC_ASSERT(mPipeOut);
1226 : uint32_t * vtable = (uint32_t *) mPipeOut.get();
1227 : MOZ_DIAGNOSTIC_ASSERT(*vtable != 0);
1228 : mPipeOut = nullptr; // just in case
1229 : #endif // WIN32
1230 : }
1231 4 :
1232 : nsHttpConnectionInfo *
1233 16 : nsHttpTransaction::ConnectionInfo()
1234 : {
1235 : return mConnInfo.get();
1236 : }
1237 0 :
1238 : bool // NOTE BASE CLASS
1239 0 : nsAHttpTransaction::ResponseTimeoutEnabled() const
1240 : {
1241 : return false;
1242 : }
1243 0 :
1244 : PRIntervalTime // NOTE BASE CLASS
1245 0 : nsAHttpTransaction::ResponseTimeout()
1246 : {
1247 : return gHttpHandler->ResponseTimeout();
1248 : }
1249 0 :
1250 : bool
1251 0 : nsHttpTransaction::ResponseTimeoutEnabled() const
1252 : {
1253 : return mResponseTimeoutEnabled;
1254 : }
1255 :
1256 : //-----------------------------------------------------------------------------
1257 : // nsHttpTransaction <private>
1258 : //-----------------------------------------------------------------------------
1259 0 :
1260 : nsresult
1261 0 : nsHttpTransaction::Restart()
1262 : {
1263 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1264 0 :
1265 0 : // limit the number of restart attempts - bug 92224
1266 : if (++mRestartCount >= gHttpHandler->MaxRequestAttempts()) {
1267 : LOG(("reached max request attempts, failing transaction @%p\n", this));
1268 : return NS_ERROR_NET_RESET;
1269 0 : }
1270 0 :
1271 : LOG(("restarting transaction @%p\n", this));
1272 : mTunnelProvider = nullptr;
1273 0 :
1274 0 : // rewind streams in case we already wrote out the request
1275 0 : nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mRequestStream);
1276 : if (seekable)
1277 : seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
1278 0 :
1279 0 : // clear old connection state...
1280 0 : mSecurityInfo = nullptr;
1281 0 : if (mConnection) {
1282 : if (!mReuseOnRestart) {
1283 0 : mConnection->DontReuse();
1284 0 : }
1285 : MutexAutoLock lock(mLock);
1286 : mConnection = nullptr;
1287 : }
1288 :
1289 0 : // Reset this to our default state, since this may change from one restart
1290 : // to the next
1291 0 : mReuseOnRestart = false;
1292 0 :
1293 0 : if (!mConnInfo->GetRoutedHost().IsEmpty()) {
1294 0 : MutexAutoLock lock(*nsHttp::GetLock());
1295 0 : RefPtr<nsHttpConnectionInfo> ci;
1296 0 : mConnInfo->CloneAsDirectRoute(getter_AddRefs(ci));
1297 : mConnInfo = ci;
1298 0 : if (mRequestHead) {
1299 0 : DebugOnly<nsresult> rv =
1300 0 : mRequestHead->SetHeader(nsHttp::Alternate_Service_Used,
1301 : NS_LITERAL_CSTRING("0"));
1302 : MOZ_ASSERT(NS_SUCCEEDED(rv));
1303 : }
1304 0 : }
1305 :
1306 : return gHttpHandler->InitiateTransaction(this, mPriority);
1307 : }
1308 1 :
1309 : char *
1310 : nsHttpTransaction::LocateHttpStart(char *buf, uint32_t len,
1311 3 : bool aAllowPartialMatch)
1312 : {
1313 : MOZ_ASSERT(!aAllowPartialMatch || mLineBuf.IsEmpty());
1314 :
1315 : static const char HTTPHeader[] = "HTTP/1.";
1316 : static const uint32_t HTTPHeaderLen = sizeof(HTTPHeader) - 1;
1317 : static const char HTTP2Header[] = "HTTP/2.0";
1318 : static const uint32_t HTTP2HeaderLen = sizeof(HTTP2Header) - 1;
1319 : // ShoutCast ICY is treated as HTTP/1.0
1320 : static const char ICYHeader[] = "ICY ";
1321 3 : static const uint32_t ICYHeaderLen = sizeof(ICYHeader) - 1;
1322 0 :
1323 : if (aAllowPartialMatch && (len < HTTPHeaderLen))
1324 : return (PL_strncasecmp(buf, HTTPHeader, len) == 0) ? buf : nullptr;
1325 3 :
1326 0 : // mLineBuf can contain partial match from previous search
1327 0 : if (!mLineBuf.IsEmpty()) {
1328 0 : MOZ_ASSERT(mLineBuf.Length() < HTTPHeaderLen);
1329 : int32_t checkChars = std::min(len, HTTPHeaderLen - mLineBuf.Length());
1330 0 : if (PL_strncasecmp(buf, HTTPHeader + mLineBuf.Length(),
1331 0 : checkChars) == 0) {
1332 : mLineBuf.Append(buf, checkChars);
1333 : if (mLineBuf.Length() == HTTPHeaderLen) {
1334 0 : // We've found whole HTTPHeader sequence. Return pointer at the
1335 : // end of matched sequence since it is stored in mLineBuf.
1336 : return (buf + checkChars);
1337 : }
1338 : // Response matches pattern but is still incomplete.
1339 : return nullptr;
1340 : }
1341 0 : // Previous partial match together with new data doesn't match the
1342 : // pattern. Start the search again.
1343 : mLineBuf.Truncate();
1344 : }
1345 3 :
1346 3 : bool firstByte = true;
1347 3 : while (len > 0) {
1348 : if (PL_strncasecmp(buf, HTTPHeader, std::min<uint32_t>(len, HTTPHeaderLen)) == 0) {
1349 : if (len < HTTPHeaderLen) {
1350 0 : // partial HTTPHeader sequence found
1351 0 : // save partial match to mLineBuf
1352 : mLineBuf.Assign(buf, len);
1353 : return nullptr;
1354 : }
1355 :
1356 : // whole HTTPHeader sequence found
1357 : return buf;
1358 : }
1359 :
1360 : // At least "SmarterTools/2.0.3974.16813" generates nonsensical
1361 : // HTTP/2.0 responses to our HTTP/1 requests. Treat the minimal case of
1362 : // it as HTTP/1.1 to be compatible with old versions of ourselves and
1363 0 : // other browsers
1364 0 :
1365 0 : if (firstByte && !mInvalidResponseBytesRead && len >= HTTP2HeaderLen &&
1366 : (PL_strncasecmp(buf, HTTP2Header, HTTP2HeaderLen) == 0)) {
1367 : LOG(("nsHttpTransaction:: Identified HTTP/2.0 treating as 1.x\n"));
1368 : return buf;
1369 : }
1370 :
1371 : // Treat ICY (AOL/Nullsoft ShoutCast) non-standard header in same fashion
1372 : // as HTTP/2.0 is treated above. This will allow "ICY " to be interpretted
1373 0 : // as HTTP/1.0 in nsHttpResponseHead::ParseVersion
1374 0 :
1375 0 : if (firstByte && !mInvalidResponseBytesRead && len >= ICYHeaderLen &&
1376 : (PL_strncasecmp(buf, ICYHeader, ICYHeaderLen) == 0)) {
1377 : LOG(("nsHttpTransaction:: Identified ICY treating as HTTP/1.0\n"));
1378 : return buf;
1379 0 : }
1380 0 :
1381 0 : if (!nsCRT::IsAsciiSpace(*buf))
1382 0 : firstByte = false;
1383 : buf++;
1384 : len--;
1385 : }
1386 : return nullptr;
1387 : }
1388 0 :
1389 : nsresult
1390 0 : nsHttpTransaction::ParseLine(nsACString &line)
1391 0 : {
1392 : LOG(("nsHttpTransaction::ParseLine [%s]\n", PromiseFlatCString(line).get()));
1393 33 : nsresult rv = NS_OK;
1394 3 :
1395 3 : if (!mHaveStatusLine) {
1396 : mResponseHead->ParseStatusLine(line);
1397 0 : mHaveStatusLine = true;
1398 0 : // XXX this should probably never happen
1399 : if (mResponseHead->Version() == HttpVersion::v0_9)
1400 : mHaveAllHeaders = true;
1401 30 : }
1402 : else {
1403 0 : rv = mResponseHead->ParseHeaderLine(line);
1404 : }
1405 : return rv;
1406 : }
1407 1 :
1408 : nsresult
1409 36 : nsHttpTransaction::ParseLineSegment(char *segment, uint32_t len)
1410 : {
1411 36 : MOZ_ASSERT(!mHaveAllHeaders, "already have all headers");
1412 :
1413 : if (!mLineBuf.IsEmpty() && mLineBuf.Last() == '\n') {
1414 : // trim off the new line char, and if this segment is
1415 : // not a continuation of the previous or if we haven't
1416 0 : // parsed the status line yet, then parse the contents
1417 33 : // of mLineBuf.
1418 0 : mLineBuf.Truncate(mLineBuf.Length() - 1);
1419 33 : if (!mHaveStatusLine || (*segment != ' ' && *segment != '\t')) {
1420 0 : nsresult rv = ParseLine(mLineBuf);
1421 : mLineBuf.Truncate();
1422 : if (NS_FAILED(rv)) {
1423 : return rv;
1424 : }
1425 : }
1426 : }
1427 0 :
1428 : // append segment to mLineBuf...
1429 : mLineBuf.Append(segment, len);
1430 36 :
1431 3 : // a line buf with only a new line char signifies the end of headers.
1432 : if (mLineBuf.First() == '\n') {
1433 3 : mLineBuf.Truncate();
1434 3 : // discard this response if it is a 100 continue or other 1xx status.
1435 0 : uint16_t status = mResponseHead->Status();
1436 0 : if ((status != 101) && (status / 100 == 1)) {
1437 0 : LOG(("ignoring 1xx response\n"));
1438 0 : mHaveStatusLine = false;
1439 0 : mHttpResponseMatched = false;
1440 0 : mConnection->SetLastTransactionExpectedNoContent(true);
1441 : mResponseHead->Reset();
1442 0 : return NS_OK;
1443 : }
1444 : mHaveAllHeaders = true;
1445 : }
1446 : return NS_OK;
1447 : }
1448 1 :
1449 : nsresult
1450 : nsHttpTransaction::ParseHead(char *buf,
1451 : uint32_t count,
1452 : uint32_t *countRead)
1453 : {
1454 : nsresult rv;
1455 : uint32_t len;
1456 3 : char *eol;
1457 :
1458 3 : LOG(("nsHttpTransaction::ParseHead [count=%u]\n", count));
1459 :
1460 3 : *countRead = 0;
1461 :
1462 : MOZ_ASSERT(!mHaveAllHeaders, "oops");
1463 3 :
1464 3 : // allocate the response head object if necessary
1465 0 : if (!mResponseHead) {
1466 : mResponseHead = new nsHttpResponseHead();
1467 : if (!mResponseHead)
1468 : return NS_ERROR_OUT_OF_MEMORY;
1469 0 :
1470 0 : // report that we have a least some of the response
1471 0 : if (mActivityDistributor && !mReportedStart) {
1472 : mReportedStart = true;
1473 : rv = mActivityDistributor->ObserveActivity(
1474 : mChannel,
1475 0 : NS_HTTP_ACTIVITY_TYPE_HTTP_TRANSACTION,
1476 0 : NS_HTTP_ACTIVITY_SUBTYPE_RESPONSE_START,
1477 0 : PR_Now(), 0, EmptyCString());
1478 : if (NS_FAILED(rv)) {
1479 : LOG3(("ObserveActivity failed (%08x)",
1480 : static_cast<uint32_t>(rv)));
1481 : }
1482 : }
1483 3 : }
1484 :
1485 : if (!mHttpResponseMatched) {
1486 : // Normally we insist on seeing HTTP/1.x in the first few bytes,
1487 : // but if we are on a persistent connection and the previous transaction
1488 : // was not supposed to have any content then we need to be prepared
1489 6 : // to skip over a response body that the server may have sent even
1490 : // though it wasn't allowed.
1491 3 : if (!mConnection || !mConnection->LastTransactionExpectedNoContent()) {
1492 0 : // tolerate only minor junk before the status line
1493 3 : mHttpResponseMatched = true;
1494 : char *p = LocateHttpStart(buf, std::min<uint32_t>(count, 11), true);
1495 0 : if (!p) {
1496 : // Treat any 0.9 style response of a put as a failure.
1497 : if (mRequestHead->IsPut())
1498 0 : return NS_ERROR_ABORT;
1499 0 :
1500 0 : mResponseHead->ParseStatusLine(EmptyCString());
1501 0 : mHaveStatusLine = true;
1502 : mHaveAllHeaders = true;
1503 3 : return NS_OK;
1504 : }
1505 0 : if (p > buf) {
1506 0 : // skip over the junk
1507 0 : mInvalidResponseBytesRead += p - buf;
1508 : *countRead = p - buf;
1509 : buf = p;
1510 : }
1511 0 : }
1512 0 : else {
1513 0 : char *p = LocateHttpStart(buf, count, false);
1514 0 : if (p) {
1515 0 : mInvalidResponseBytesRead += p - buf;
1516 0 : *countRead = p - buf;
1517 : buf = p;
1518 0 : mHttpResponseMatched = true;
1519 0 : } else {
1520 0 : mInvalidResponseBytesRead += count;
1521 0 : *countRead = count;
1522 : if (mInvalidResponseBytesRead > MAX_INVALID_RESPONSE_BODY_SIZE) {
1523 : LOG(("nsHttpTransaction::ParseHead() "
1524 : "Cannot find Response Header\n"));
1525 : // cannot go back and call this 0.9 anymore as we
1526 : // have thrown away a lot of the leading junk
1527 : return NS_ERROR_ABORT;
1528 : }
1529 : return NS_OK;
1530 : }
1531 : }
1532 : }
1533 3 : // otherwise we can assume that we don't have a HTTP/0.9 response.
1534 72 :
1535 : MOZ_ASSERT (mHttpResponseMatched);
1536 36 : while ((eol = static_cast<char *>(memchr(buf, '\n', count - *countRead))) != nullptr) {
1537 : // found line in range [buf:eol]
1538 36 : len = eol - buf + 1;
1539 :
1540 : *countRead += len;
1541 36 :
1542 0 : // actually, the line is in the range [buf:eol-1]
1543 : if ((eol > buf) && (*(eol-1) == '\r'))
1544 36 : len--;
1545 0 :
1546 36 : buf[len-1] = '\n';
1547 : rv = ParseLineSegment(buf, len);
1548 : if (NS_FAILED(rv))
1549 36 : return rv;
1550 :
1551 : if (mHaveAllHeaders)
1552 : return NS_OK;
1553 0 :
1554 : // skip over line
1555 0 : buf = eol + 1;
1556 :
1557 : if (!mHttpResponseMatched) {
1558 : // a 100 class response has caused us to throw away that set of
1559 : // response headers and look for the next response
1560 : return NS_ERROR_NET_INTERRUPT;
1561 : }
1562 : }
1563 0 :
1564 0 : // do something about a partial header line
1565 : if (!mHaveAllHeaders && (len = count - *countRead)) {
1566 : *countRead = count;
1567 0 : // ignore a trailing carriage return, and don't bother calling
1568 : // ParseLineSegment if buf only contains a carriage return.
1569 0 : if ((buf[len-1] == '\r') && (--len == 0))
1570 0 : return NS_OK;
1571 : rv = ParseLineSegment(buf, len);
1572 : if (NS_FAILED(rv))
1573 : return rv;
1574 : }
1575 : return NS_OK;
1576 : }
1577 3 :
1578 : nsresult
1579 0 : nsHttpTransaction::HandleContentStart()
1580 3 : {
1581 : LOG(("nsHttpTransaction::HandleContentStart [this=%p]\n", this));
1582 3 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1583 3 :
1584 0 : if (mResponseHead) {
1585 : if (mEarlyDataDisposition == EARLY_ACCEPTED) {
1586 0 : if (mResponseHead->Status() == 425) {
1587 : // We will report this state when the final responce arrives.
1588 0 : mEarlyDataDisposition = EARLY_425;
1589 : } else {
1590 3 : Unused << mResponseHead->SetHeader(nsHttp::X_Firefox_Early_Data, NS_LITERAL_CSTRING("accepted"));
1591 0 : }
1592 0 : } else if (mEarlyDataDisposition == EARLY_SENT) {
1593 0 : Unused << mResponseHead->SetHeader(nsHttp::X_Firefox_Early_Data, NS_LITERAL_CSTRING("sent"));
1594 0 : } else if (mEarlyDataDisposition == EARLY_425) {
1595 : Unused << mResponseHead->SetHeader(nsHttp::X_Firefox_Early_Data, NS_LITERAL_CSTRING("received 425"));
1596 : mEarlyDataDisposition = EARLY_NONE;
1597 1 : } // no header on NONE case
1598 0 :
1599 0 : if (mFastOpenStatus == TFO_DATA_SENT) {
1600 0 : Unused << mResponseHead->SetHeader(nsHttp::X_Firefox_TCP_Fast_Open, NS_LITERAL_CSTRING("data sent"));
1601 0 : } else if (mFastOpenStatus == TFO_TRIED) {
1602 0 : Unused << mResponseHead->SetHeader(nsHttp::X_Firefox_TCP_Fast_Open, NS_LITERAL_CSTRING("tried negotiating"));
1603 : } else if (mFastOpenStatus == TFO_FAILED) {
1604 : Unused << mResponseHead->SetHeader(nsHttp::X_Firefox_TCP_Fast_Open, NS_LITERAL_CSTRING("failed"));
1605 3 : } // no header on TFO_NOT_TRIED case
1606 0 :
1607 0 : if (LOG3_ENABLED()) {
1608 0 : LOG3(("http response [\n"));
1609 0 : nsAutoCString headers;
1610 0 : mResponseHead->Flatten(headers, false);
1611 0 : headers.AppendLiteral(" OriginalHeaders");
1612 0 : headers.AppendLiteral("\r\n");
1613 0 : mResponseHead->FlattenNetworkOriginalHeaders(headers);
1614 : LogHeaders(headers.get());
1615 : LOG3(("]\n"));
1616 1 : }
1617 :
1618 : CheckForStickyAuthScheme();
1619 :
1620 1 : // Save http version, mResponseHead isn't available anymore after
1621 1 : // TakeResponseHead() is called
1622 : mHttpVersion = mResponseHead->Version();
1623 : mHttpResponseCode = mResponseHead->Status();
1624 3 :
1625 0 : // notify the connection, give it a chance to cause a reset.
1626 6 : bool reset = false;
1627 3 : nsresult rv = mConnection->OnHeadersAvailable(this, mRequestHead,
1628 : mResponseHead, &reset);
1629 : NS_ENSURE_SUCCESS(rv, rv);
1630 0 :
1631 0 : // looks like we should ignore this response, resetting...
1632 0 : if (reset) {
1633 0 : LOG(("resetting transaction's response head\n"));
1634 0 : mHaveAllHeaders = false;
1635 0 : mHaveStatusLine = false;
1636 0 : mReceivedData = false;
1637 0 : mSentData = false;
1638 : mHttpResponseMatched = false;
1639 0 : mResponseHead->Reset();
1640 : // wait to be called again...
1641 : return NS_OK;
1642 : }
1643 0 :
1644 : // check if this is a no-content response
1645 0 : switch (mResponseHead->Status()) {
1646 : case 101:
1647 : mPreserveStream = true;
1648 : MOZ_FALLTHROUGH; // to other no content cases:
1649 : case 204:
1650 0 : case 205:
1651 0 : case 304:
1652 : mNoContent = true;
1653 : LOG(("this response should not contain a body.\n"));
1654 0 : break;
1655 0 : case 421:
1656 : LOG(("Misdirected Request.\n"));
1657 : gHttpHandler->ConnMgr()->ClearHostMapping(mConnInfo);
1658 0 :
1659 0 : // retry on a new connection - just in case
1660 0 : if (!mRestartCount) {
1661 0 : mCaps &= ~NS_HTTP_ALLOW_KEEPALIVE;
1662 : mForceRestart = true; // force restart has built in loop protection
1663 : return NS_ERROR_NET_RESET;
1664 : }
1665 0 : break;
1666 0 : case 425:
1667 0 : LOG(("Too Early."));
1668 0 : if ((mEarlyDataDisposition == EARLY_425) && !mDoNotTryEarlyData) {
1669 0 : mDoNotTryEarlyData = true;
1670 0 : mForceRestart = true; // force restart has built in loop protection
1671 : if (mConnection->Version() == HttpVersion::v2_0) {
1672 : mReuseOnRestart = true;
1673 : }
1674 : return NS_ERROR_NET_RESET;
1675 : }
1676 : break;
1677 1 : }
1678 1 :
1679 : if (mResponseHead->Status() == 200 &&
1680 0 : mConnection->IsProxyConnectInProgress()) {
1681 : // successful CONNECTs do not have response bodies
1682 3 : mNoContent = true;
1683 : }
1684 3 : mConnection->SetLastTransactionExpectedNoContent(mNoContent);
1685 0 :
1686 : if (mNoContent) {
1687 : mContentLength = 0;
1688 3 : } else {
1689 : // grab the content-length from the response headers
1690 : mContentLength = mResponseHead->ContentLength();
1691 :
1692 : // handle chunked encoding here, so we'll know immediately when
1693 : // we're done with the socket. please note that _all_ other
1694 0 : // decoding is done when the channel receives the content data
1695 3 : // so as not to block the socket transport thread too much.
1696 : if (mResponseHead->Version() >= HttpVersion::v1_0 &&
1697 0 : mResponseHead->HasHeaderValue(nsHttp::Transfer_Encoding, "chunked")) {
1698 0 : // we only support the "chunked" transfer encoding right now.
1699 : mChunkedDecoder = new nsHttpChunkedDecoder();
1700 0 : LOG(("nsHttpTransaction %p chunked decoder created\n", this));
1701 0 : // Ignore server specified Content-Length.
1702 0 : if (mContentLength != int64_t(-1)) {
1703 0 : LOG(("nsHttpTransaction %p chunked with C-L ignores C-L\n", this));
1704 0 : mContentLength = -1;
1705 : if (mConnection) {
1706 : mConnection->DontReuse();
1707 : }
1708 3 : }
1709 0 : }
1710 : else if (mContentLength == int64_t(-1))
1711 : LOG(("waiting for the server to close the connection.\n"));
1712 : }
1713 0 : }
1714 3 :
1715 : mDidContentStart = true;
1716 : return NS_OK;
1717 : }
1718 :
1719 3 : // called on the socket thread
1720 : nsresult
1721 : nsHttpTransaction::HandleContent(char *buf,
1722 : uint32_t count,
1723 : uint32_t *contentRead,
1724 : uint32_t *contentRemaining)
1725 : {
1726 3 : nsresult rv;
1727 :
1728 0 : LOG(("nsHttpTransaction::HandleContent [this=%p count=%u]\n", this, count));
1729 3 :
1730 : *contentRead = 0;
1731 6 : *contentRemaining = 0;
1732 :
1733 3 : MOZ_ASSERT(mConnection);
1734 3 :
1735 0 : if (!mDidContentStart) {
1736 : rv = HandleContentStart();
1737 0 : if (NS_FAILED(rv)) return rv;
1738 : // Do not write content to the pipe if we haven't started streaming yet
1739 : if (!mDidContentStart)
1740 : return NS_OK;
1741 3 : }
1742 :
1743 : if (mChunkedDecoder) {
1744 0 : // give the buf over to the chunked decoder so it can reformat the
1745 0 : // data and tell us how much is really there.
1746 : rv = mChunkedDecoder->HandleChunkedContent(buf, count, contentRead, contentRemaining);
1747 3 : if (NS_FAILED(rv)) return rv;
1748 : }
1749 : else if (mContentLength >= int64_t(0)) {
1750 : // HTTP/1.0 servers have been known to send erroneous Content-Length
1751 : // headers. So, unless the connection is persistent, we must make
1752 3 : // allowances for a possibly invalid Content-Length header. Thus, if
1753 0 : // NOT persistent, we simply accept everything in |buf|.
1754 0 : if (mConnection->IsPersistent() || mPreserveStream ||
1755 6 : mHttpVersion >= HttpVersion::v1_1) {
1756 0 : int64_t remaining = mContentLength - mContentRead;
1757 : *contentRead = uint32_t(std::min<int64_t>(count, remaining));
1758 : *contentRemaining = count - *contentRead;
1759 0 : }
1760 : else {
1761 0 : *contentRead = count;
1762 0 : // mContentLength might need to be increased...
1763 0 : int64_t position = mContentRead + int64_t(count);
1764 : if (position > mContentLength) {
1765 : mContentLength = position;
1766 : //mResponseHead->SetContentLength(mContentLength);
1767 : }
1768 : }
1769 : }
1770 : else {
1771 0 : // when we are just waiting for the server to close the connection...
1772 : // (no explicit content-length given)
1773 : *contentRead = count;
1774 3 : }
1775 :
1776 3 : if (*contentRead) {
1777 : // update count of content bytes read and report progress...
1778 : mContentRead += *contentRead;
1779 3 : }
1780 :
1781 : LOG(("nsHttpTransaction::HandleContent [this=%p count=%u read=%u mContentRead=%" PRId64 " mContentLength=%" PRId64 "]\n",
1782 : this, count, *contentRead, mContentRead, mContentLength));
1783 0 :
1784 0 : // check for end-of-file
1785 0 : if ((mContentRead == mContentLength) ||
1786 3 : (mChunkedDecoder && mChunkedDecoder->ReachedEOF())) {
1787 0 : MutexAutoLock lock(*nsHttp::GetLock());
1788 : if (mChunkedDecoder) {
1789 : mForTakeResponseTrailers = mChunkedDecoder->TakeTrailers();
1790 : }
1791 3 :
1792 0 : // the transaction is done with a complete response.
1793 0 : mTransactionDone = true;
1794 : mResponseIsComplete = true;
1795 0 : ReleaseBlockingTransaction();
1796 0 :
1797 : if (TimingEnabled()) {
1798 : SetResponseEnd(TimeStamp::Now());
1799 : }
1800 0 :
1801 0 : // report the entire response has arrived
1802 : if (mActivityDistributor) {
1803 : rv = mActivityDistributor->ObserveActivity(
1804 : mChannel,
1805 : NS_HTTP_ACTIVITY_TYPE_HTTP_TRANSACTION,
1806 0 : NS_HTTP_ACTIVITY_SUBTYPE_RESPONSE_COMPLETE,
1807 0 : PR_Now(),
1808 0 : static_cast<uint64_t>(mContentRead),
1809 0 : EmptyCString());
1810 : if (NS_FAILED(rv)) {
1811 : LOG3(("ObserveActivity failed (%08x)",
1812 : static_cast<uint32_t>(rv)));
1813 : }
1814 : }
1815 : }
1816 :
1817 : return NS_OK;
1818 : }
1819 3 :
1820 : nsresult
1821 : nsHttpTransaction::ProcessData(char *buf, uint32_t count, uint32_t *countRead)
1822 : {
1823 3 : nsresult rv;
1824 :
1825 3 : LOG(("nsHttpTransaction::ProcessData [this=%p count=%u]\n", this, count));
1826 :
1827 : *countRead = 0;
1828 0 :
1829 : // we may not have read all of the headers yet...
1830 : if (!mHaveAllHeaders) {
1831 : uint32_t bytesConsumed = 0;
1832 0 :
1833 3 : do {
1834 0 : uint32_t localBytesConsumed = 0;
1835 : char *localBuf = buf + bytesConsumed;
1836 3 : uint32_t localCount = count - bytesConsumed;
1837 0 :
1838 0 : rv = ParseHead(localBuf, localCount, &localBytesConsumed);
1839 3 : if (NS_FAILED(rv) && rv != NS_ERROR_NET_INTERRUPT)
1840 3 : return rv;
1841 : bytesConsumed += localBytesConsumed;
1842 0 : } while (rv == NS_ERROR_NET_INTERRUPT);
1843 0 :
1844 3 : mCurrentHttpResponseHeaderSize += bytesConsumed;
1845 0 : if (mCurrentHttpResponseHeaderSize >
1846 : gHttpHandler->MaxHttpResponseHeaderSize()) {
1847 : LOG(("nsHttpTransaction %p The response header exceeds the limit.\n",
1848 : this));
1849 0 : return NS_ERROR_FILE_TOO_BIG;
1850 : }
1851 : count -= bytesConsumed;
1852 0 :
1853 0 : // if buf has some content in it, shift bytes to top of buf.
1854 : if (count && bytesConsumed)
1855 : memmove(buf, buf + bytesConsumed, count);
1856 6 :
1857 0 : // report the completed response header
1858 0 : if (mActivityDistributor && mResponseHead && mHaveAllHeaders &&
1859 0 : !mReportedResponseHeader) {
1860 0 : mReportedResponseHeader = true;
1861 0 : nsAutoCString completeResponseHeaders;
1862 0 : mResponseHead->Flatten(completeResponseHeaders, false);
1863 : completeResponseHeaders.AppendLiteral("\r\n");
1864 : rv = mActivityDistributor->ObserveActivity(
1865 : mChannel,
1866 : NS_HTTP_ACTIVITY_TYPE_HTTP_TRANSACTION,
1867 0 : NS_HTTP_ACTIVITY_SUBTYPE_RESPONSE_HEADER,
1868 0 : PR_Now(), 0,
1869 0 : completeResponseHeaders);
1870 : if (NS_FAILED(rv)) {
1871 : LOG3(("ObserveActivity failed (%08x)",
1872 : static_cast<uint32_t>(rv)));
1873 : }
1874 : }
1875 : }
1876 :
1877 0 : // even though count may be 0, we still want to call HandleContent
1878 1 : // so it can complete the transaction if this is a "no-content" response.
1879 : if (mHaveAllHeaders) {
1880 : uint32_t countRemaining = 0;
1881 : //
1882 : // buf layout:
1883 : //
1884 : // +--------------------------------------+----------------+-----+
1885 : // | countRead | countRemaining | |
1886 : // +--------------------------------------+----------------+-----+
1887 : //
1888 : // count : bytes read from the socket
1889 : // countRead : bytes corresponding to this transaction
1890 : // countRemaining : bytes corresponding to next transaction on conn
1891 : //
1892 : // NOTE:
1893 3 : // count > countRead + countRemaining <==> chunked transfer encoding
1894 3 : //
1895 : rv = HandleContent(buf, count, countRead, &countRemaining);
1896 : if (NS_FAILED(rv)) return rv;
1897 3 : // we may have read more than our share, in which case we must give
1898 0 : // the excess bytes back to the connection
1899 0 : if (mResponseIsComplete && countRemaining) {
1900 0 : MOZ_ASSERT(mConnection);
1901 : rv = mConnection->PushBack(buf + *countRead, countRemaining);
1902 : NS_ENSURE_SUCCESS(rv, rv);
1903 0 : }
1904 3 :
1905 3 : if (!mContentDecodingCheck && mResponseHead) {
1906 0 : mContentDecoding =
1907 : mResponseHead->HasHeader(nsHttp::Content_Encoding);
1908 : mContentDecodingCheck = true;
1909 : }
1910 : }
1911 :
1912 : return NS_OK;
1913 : }
1914 0 :
1915 : void
1916 0 : nsHttpTransaction::SetRequestContext(nsIRequestContext *aRequestContext)
1917 0 : {
1918 0 : LOG(("nsHttpTransaction %p SetRequestContext %p\n", this, aRequestContext));
1919 : mRequestContext = aRequestContext;
1920 : }
1921 :
1922 : // Called when the transaction marked for blocking is associated with a connection
1923 : // (i.e. added to a new h1 conn, an idle http connection, etc..)
1924 : // It is safe to call this multiple times with it only
1925 0 : // having an effect once.
1926 : void
1927 0 : nsHttpTransaction::DispatchedAsBlocking()
1928 : {
1929 : if (mDispatchedAsBlocking)
1930 0 : return;
1931 :
1932 0 : LOG(("nsHttpTransaction %p dispatched as blocking\n", this));
1933 :
1934 : if (!mRequestContext)
1935 0 : return;
1936 :
1937 : LOG(("nsHttpTransaction adding blocking transaction %p from "
1938 0 : "request context %p\n", this, mRequestContext.get()));
1939 0 :
1940 : mRequestContext->AddBlockingTransaction();
1941 : mDispatchedAsBlocking = true;
1942 : }
1943 7 :
1944 : void
1945 14 : nsHttpTransaction::RemoveDispatchedAsBlocking()
1946 7 : {
1947 : if (!mRequestContext || !mDispatchedAsBlocking) {
1948 0 : LOG(("nsHttpTransaction::RemoveDispatchedAsBlocking this=%p not blocking",
1949 : this));
1950 : return;
1951 0 : }
1952 0 :
1953 : uint32_t blockers = 0;
1954 0 : nsresult rv = mRequestContext->RemoveBlockingTransaction(&blockers);
1955 :
1956 : LOG(("nsHttpTransaction removing blocking transaction %p from "
1957 : "request context %p. %d blockers remain.\n", this,
1958 0 : mRequestContext.get(), blockers));
1959 0 :
1960 : if (NS_SUCCEEDED(rv) && !blockers) {
1961 0 : LOG(("nsHttpTransaction %p triggering release of blocked channels "
1962 0 : " with request context=%p\n", this, mRequestContext.get()));
1963 0 : rv = gHttpHandler->ConnMgr()->ProcessPendingQ();
1964 : if (NS_FAILED(rv)) {
1965 : LOG(("nsHttpTransaction::RemoveDispatchedAsBlocking\n"
1966 : " failed to process pending queue\n"));
1967 : }
1968 0 : }
1969 :
1970 : mDispatchedAsBlocking = false;
1971 : }
1972 1 :
1973 : void
1974 7 : nsHttpTransaction::ReleaseBlockingTransaction()
1975 7 : {
1976 : RemoveDispatchedAsBlocking();
1977 0 : LOG(("nsHttpTransaction %p request context set to null "
1978 7 : "in ReleaseBlockingTransaction() - was %p\n", this, mRequestContext.get()));
1979 : mRequestContext = nullptr;
1980 : }
1981 0 :
1982 : void
1983 0 : nsHttpTransaction::DisableSpdy()
1984 0 : {
1985 : mCaps |= NS_HTTP_DISALLOW_SPDY;
1986 : if (mConnInfo) {
1987 0 : // This is our clone of the connection info, not the persistent one that
1988 : // is owned by the connection manager, so we're safe to change this here
1989 0 : mConnInfo->SetNoSpdy(true);
1990 : }
1991 : }
1992 0 :
1993 : void
1994 3 : nsHttpTransaction::CheckForStickyAuthScheme()
1995 : {
1996 0 : LOG(("nsHttpTransaction::CheckForStickyAuthScheme this=%p", this));
1997 3 :
1998 0 : MOZ_ASSERT(mHaveAllHeaders);
1999 : MOZ_ASSERT(mResponseHead);
2000 3 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
2001 0 :
2002 3 : CheckForStickyAuthSchemeAt(nsHttp::WWW_Authenticate);
2003 : CheckForStickyAuthSchemeAt(nsHttp::Proxy_Authenticate);
2004 : }
2005 0 :
2006 : void
2007 0 : nsHttpTransaction::CheckForStickyAuthSchemeAt(nsHttpAtom const& header)
2008 0 : {
2009 0 : if (mCaps & NS_HTTP_STICKY_CONNECTION) {
2010 : LOG((" already sticky"));
2011 : return;
2012 6 : }
2013 6 :
2014 : nsAutoCString auth;
2015 : if (NS_FAILED(mResponseHead->GetHeader(header, auth))) {
2016 : return;
2017 0 : }
2018 0 :
2019 0 : Tokenizer p(auth);
2020 0 : nsAutoCString schema;
2021 : while (p.ReadWord(schema)) {
2022 0 : ToLowerCase(schema);
2023 0 :
2024 0 : nsAutoCString contractid;
2025 : contractid.AssignLiteral(NS_HTTP_AUTHENTICATOR_CONTRACTID_PREFIX);
2026 : contractid.Append(schema);
2027 0 :
2028 0 : // using a new instance because of thread safety of auth modules refcnt
2029 : nsCOMPtr<nsIHttpAuthenticator> authenticator(do_CreateInstance(contractid.get()));
2030 0 : if (authenticator) {
2031 0 : uint32_t flags;
2032 0 : nsresult rv = authenticator->GetAuthFlags(&flags);
2033 : if (NS_SUCCEEDED(rv) && (flags & nsIHttpAuthenticator::CONNECTION_BASED)) {
2034 : LOG((" connection made sticky, found %s auth shema", schema.get()));
2035 0 : // This is enough to make this transaction keep it's current connection,
2036 0 : // prevents the connection from being released back to the pool.
2037 : mCaps |= NS_HTTP_STICKY_CONNECTION;
2038 : break;
2039 : }
2040 : }
2041 0 :
2042 0 : // schemes are separated with LFs, nsHttpHeaderArray::MergeHeader
2043 : p.SkipUntil(Tokenizer::Token::NewLine());
2044 : p.SkipWhites(Tokenizer::INCLUDE_NEW_LINE);
2045 : }
2046 : }
2047 1 :
2048 : const TimingStruct
2049 2 : nsHttpTransaction::Timings()
2050 0 : {
2051 0 : mozilla::MutexAutoLock lock(mLock);
2052 : TimingStruct timings = mTimings;
2053 : return timings;
2054 : }
2055 0 :
2056 : void
2057 0 : nsHttpTransaction::BootstrapTimings(TimingStruct times)
2058 0 : {
2059 0 : mozilla::MutexAutoLock lock(mLock);
2060 : mTimings = times;
2061 : }
2062 0 :
2063 : void
2064 0 : nsHttpTransaction::SetDomainLookupStart(mozilla::TimeStamp timeStamp, bool onlyIfNull)
2065 0 : {
2066 0 : mozilla::MutexAutoLock lock(mLock);
2067 : if (onlyIfNull && !mTimings.domainLookupStart.IsNull()) {
2068 0 : return; // We only set the timestamp if it was previously null
2069 : }
2070 : mTimings.domainLookupStart = timeStamp;
2071 : }
2072 0 :
2073 : void
2074 0 : nsHttpTransaction::SetDomainLookupEnd(mozilla::TimeStamp timeStamp, bool onlyIfNull)
2075 0 : {
2076 0 : mozilla::MutexAutoLock lock(mLock);
2077 : if (onlyIfNull && !mTimings.domainLookupEnd.IsNull()) {
2078 0 : return; // We only set the timestamp if it was previously null
2079 : }
2080 : mTimings.domainLookupEnd = timeStamp;
2081 : }
2082 0 :
2083 : void
2084 0 : nsHttpTransaction::SetConnectStart(mozilla::TimeStamp timeStamp, bool onlyIfNull)
2085 0 : {
2086 0 : mozilla::MutexAutoLock lock(mLock);
2087 : if (onlyIfNull && !mTimings.connectStart.IsNull()) {
2088 0 : return; // We only set the timestamp if it was previously null
2089 : }
2090 : mTimings.connectStart = timeStamp;
2091 : }
2092 0 :
2093 : void
2094 0 : nsHttpTransaction::SetConnectEnd(mozilla::TimeStamp timeStamp, bool onlyIfNull)
2095 0 : {
2096 0 : mozilla::MutexAutoLock lock(mLock);
2097 : if (onlyIfNull && !mTimings.connectEnd.IsNull()) {
2098 0 : return; // We only set the timestamp if it was previously null
2099 : }
2100 : mTimings.connectEnd = timeStamp;
2101 : }
2102 0 :
2103 : void
2104 0 : nsHttpTransaction::SetRequestStart(mozilla::TimeStamp timeStamp, bool onlyIfNull)
2105 0 : {
2106 0 : mozilla::MutexAutoLock lock(mLock);
2107 : if (onlyIfNull && !mTimings.requestStart.IsNull()) {
2108 0 : return; // We only set the timestamp if it was previously null
2109 : }
2110 : mTimings.requestStart = timeStamp;
2111 : }
2112 0 :
2113 : void
2114 0 : nsHttpTransaction::SetResponseStart(mozilla::TimeStamp timeStamp, bool onlyIfNull)
2115 0 : {
2116 0 : mozilla::MutexAutoLock lock(mLock);
2117 : if (onlyIfNull && !mTimings.responseStart.IsNull()) {
2118 0 : return; // We only set the timestamp if it was previously null
2119 : }
2120 : mTimings.responseStart = timeStamp;
2121 : }
2122 0 :
2123 : void
2124 0 : nsHttpTransaction::SetResponseEnd(mozilla::TimeStamp timeStamp, bool onlyIfNull)
2125 0 : {
2126 0 : mozilla::MutexAutoLock lock(mLock);
2127 : if (onlyIfNull && !mTimings.responseEnd.IsNull()) {
2128 0 : return; // We only set the timestamp if it was previously null
2129 : }
2130 : mTimings.responseEnd = timeStamp;
2131 : }
2132 0 :
2133 : mozilla::TimeStamp
2134 0 : nsHttpTransaction::GetDomainLookupStart()
2135 0 : {
2136 : mozilla::MutexAutoLock lock(mLock);
2137 : return mTimings.domainLookupStart;
2138 : }
2139 0 :
2140 : mozilla::TimeStamp
2141 0 : nsHttpTransaction::GetDomainLookupEnd()
2142 0 : {
2143 : mozilla::MutexAutoLock lock(mLock);
2144 : return mTimings.domainLookupEnd;
2145 : }
2146 0 :
2147 : mozilla::TimeStamp
2148 0 : nsHttpTransaction::GetConnectStart()
2149 0 : {
2150 : mozilla::MutexAutoLock lock(mLock);
2151 : return mTimings.connectStart;
2152 : }
2153 0 :
2154 : mozilla::TimeStamp
2155 0 : nsHttpTransaction::GetTcpConnectEnd()
2156 0 : {
2157 : mozilla::MutexAutoLock lock(mLock);
2158 : return mTimings.tcpConnectEnd;
2159 : }
2160 0 :
2161 : mozilla::TimeStamp
2162 0 : nsHttpTransaction::GetSecureConnectionStart()
2163 0 : {
2164 : mozilla::MutexAutoLock lock(mLock);
2165 : return mTimings.secureConnectionStart;
2166 : }
2167 0 :
2168 : mozilla::TimeStamp
2169 0 : nsHttpTransaction::GetConnectEnd()
2170 0 : {
2171 : mozilla::MutexAutoLock lock(mLock);
2172 : return mTimings.connectEnd;
2173 : }
2174 0 :
2175 : mozilla::TimeStamp
2176 0 : nsHttpTransaction::GetRequestStart()
2177 0 : {
2178 : mozilla::MutexAutoLock lock(mLock);
2179 : return mTimings.requestStart;
2180 : }
2181 0 :
2182 : mozilla::TimeStamp
2183 0 : nsHttpTransaction::GetResponseStart()
2184 0 : {
2185 : mozilla::MutexAutoLock lock(mLock);
2186 : return mTimings.responseStart;
2187 : }
2188 0 :
2189 : mozilla::TimeStamp
2190 0 : nsHttpTransaction::GetResponseEnd()
2191 0 : {
2192 : mozilla::MutexAutoLock lock(mLock);
2193 : return mTimings.responseEnd;
2194 : }
2195 :
2196 : //-----------------------------------------------------------------------------
2197 : // nsHttpTransaction deletion event
2198 0 : //-----------------------------------------------------------------------------
2199 :
2200 0 : class DeleteHttpTransaction : public Runnable {
2201 0 : public:
2202 0 : explicit DeleteHttpTransaction(nsHttpTransaction* trans)
2203 : : Runnable("net::DeleteHttpTransaction")
2204 0 : , mTrans(trans)
2205 : {
2206 0 : }
2207 :
2208 0 : NS_IMETHOD Run() override
2209 0 : {
2210 : delete mTrans;
2211 : return NS_OK;
2212 : }
2213 : private:
2214 : nsHttpTransaction *mTrans;
2215 : };
2216 1 :
2217 : void
2218 0 : nsHttpTransaction::DeleteSelfOnConsumerThread()
2219 : {
2220 : LOG(("nsHttpTransaction::DeleteSelfOnConsumerThread [this=%p]\n", this));
2221 4 :
2222 2 : bool val;
2223 1 : if (!mConsumerTarget ||
2224 : (NS_SUCCEEDED(mConsumerTarget->IsOnCurrentThread(&val)) && val)) {
2225 0 : delete this;
2226 0 : } else {
2227 0 : LOG(("proxying delete to consumer thread...\n"));
2228 0 : nsCOMPtr<nsIRunnable> event = new DeleteHttpTransaction(this);
2229 : if (NS_FAILED(mConsumerTarget->Dispatch(event, NS_DISPATCH_NORMAL)))
2230 0 : NS_WARNING("failed to dispatch nsHttpDeleteTransaction event");
2231 : }
2232 : }
2233 4 :
2234 : bool
2235 0 : nsHttpTransaction::TryToRunPacedRequest()
2236 0 : {
2237 : if (mSubmittedRatePacing)
2238 4 : return mPassedRatePacing;
2239 0 :
2240 8 : mSubmittedRatePacing = true;
2241 4 : mSynchronousRatePaceRequest = true;
2242 0 : Unused << gHttpHandler->SubmitPacedRequest(this, getter_AddRefs(mTokenBucketCancel));
2243 : mSynchronousRatePaceRequest = false;
2244 : return mPassedRatePacing;
2245 : }
2246 4 :
2247 : void
2248 0 : nsHttpTransaction::OnTokenBucketAdmitted()
2249 0 : {
2250 : mPassedRatePacing = true;
2251 0 : mTokenBucketCancel = nullptr;
2252 0 :
2253 0 : if (!mSynchronousRatePaceRequest) {
2254 0 : nsresult rv = gHttpHandler->ConnMgr()->ProcessPendingQ(mConnInfo);
2255 : if (NS_FAILED(rv)) {
2256 : LOG(("nsHttpTransaction::OnTokenBucketAdmitted\n"
2257 : " failed to process pending queue\n"));
2258 0 : }
2259 : }
2260 : }
2261 0 :
2262 : void
2263 1 : nsHttpTransaction::CancelPacing(nsresult reason)
2264 0 : {
2265 0 : if (mTokenBucketCancel) {
2266 : mTokenBucketCancel->Cancel(reason);
2267 0 : mTokenBucketCancel = nullptr;
2268 : }
2269 : }
2270 :
2271 : //-----------------------------------------------------------------------------
2272 : // nsHttpTransaction::nsISupports
2273 0 : //-----------------------------------------------------------------------------
2274 :
2275 : NS_IMPL_ADDREF(nsHttpTransaction)
2276 0 :
2277 : NS_IMETHODIMP_(MozExternalRefCountType)
2278 : nsHttpTransaction::Release()
2279 46 : {
2280 23 : nsrefcnt count;
2281 23 : MOZ_ASSERT(0 != mRefCnt, "dup release");
2282 0 : count = --mRefCnt;
2283 1 : NS_LOG_RELEASE(this, count, "nsHttpTransaction");
2284 : if (0 == count) {
2285 : mRefCnt = 1; /* stablize */
2286 1 : // it is essential that the transaction be destroyed on the consumer
2287 1 : // thread (we could be holding the last reference to our consumer).
2288 : DeleteSelfOnConsumerThread();
2289 0 : return 0;
2290 : }
2291 : return count;
2292 0 : }
2293 :
2294 : NS_IMPL_QUERY_INTERFACE(nsHttpTransaction,
2295 : nsIInputStreamCallback,
2296 : nsIOutputStreamCallback)
2297 :
2298 : //-----------------------------------------------------------------------------
2299 : // nsHttpTransaction::nsIInputStreamCallback
2300 : //-----------------------------------------------------------------------------
2301 :
2302 0 : // called on the socket thread
2303 : NS_IMETHODIMP
2304 0 : nsHttpTransaction::OnInputStreamReady(nsIAsyncInputStream *out)
2305 0 : {
2306 0 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
2307 0 : if (mConnection) {
2308 0 : mConnection->TransactionHasDataToWrite(this);
2309 0 : nsresult rv = mConnection->ResumeSend();
2310 : if (NS_FAILED(rv))
2311 0 : NS_ERROR("ResumeSend failed");
2312 : }
2313 : return NS_OK;
2314 : }
2315 :
2316 : //-----------------------------------------------------------------------------
2317 : // nsHttpTransaction::nsIOutputStreamCallback
2318 : //-----------------------------------------------------------------------------
2319 :
2320 0 : // called on the socket thread
2321 : NS_IMETHODIMP
2322 0 : nsHttpTransaction::OnOutputStreamReady(nsIAsyncOutputStream *out)
2323 0 : {
2324 0 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
2325 0 : mWaitingOnPipeOut = false;
2326 0 : if (mConnection) {
2327 0 : mConnection->TransactionHasDataToRecv(this);
2328 0 : nsresult rv = mConnection->ResumeRecv();
2329 : if (NS_FAILED(rv))
2330 0 : NS_ERROR("ResumeRecv failed");
2331 : }
2332 : return NS_OK;
2333 : }
2334 0 :
2335 : void
2336 0 : nsHttpTransaction::GetNetworkAddresses(NetAddr &self, NetAddr &peer)
2337 1 : {
2338 3 : MutexAutoLock lock(mLock);
2339 0 : self = mSelfAddr;
2340 : peer = mPeerAddr;
2341 : }
2342 0 :
2343 : bool
2344 0 : nsHttpTransaction::CanDo0RTT()
2345 0 : {
2346 0 : if (mRequestHead->IsSafeMethod() &&
2347 0 : !mDoNotTryEarlyData &&
2348 : (!mConnection ||
2349 : !mConnection->IsProxyConnectInProgress())) {
2350 0 : return true;
2351 : }
2352 : return false;
2353 : }
2354 0 :
2355 : bool
2356 0 : nsHttpTransaction::Do0RTT()
2357 0 : {
2358 0 : if (mRequestHead->IsSafeMethod() &&
2359 0 : !mDoNotTryEarlyData &&
2360 0 : (!mConnection ||
2361 : !mConnection->IsProxyConnectInProgress())) {
2362 0 : m0RTTInProgress = true;
2363 : }
2364 : return m0RTTInProgress;
2365 : }
2366 0 :
2367 : nsresult
2368 0 : nsHttpTransaction::Finish0RTT(bool aRestart, bool aAlpnChanged /* ignored */)
2369 0 : {
2370 0 : LOG(("nsHttpTransaction::Finish0RTT %p %d %d\n", this, aRestart, aAlpnChanged));
2371 0 : MOZ_ASSERT(m0RTTInProgress);
2372 : m0RTTInProgress = false;
2373 : if (!aRestart && (mEarlyDataDisposition == EARLY_SENT)) {
2374 0 : // note that if this is invoked by a 3 param version of finish0rtt this
2375 : // disposition might be reverted
2376 0 : mEarlyDataDisposition = EARLY_ACCEPTED;
2377 : }
2378 : if (aRestart) {
2379 0 : // Reset request headers to be sent again.
2380 0 : nsCOMPtr<nsISeekableStream> seekable =
2381 0 : do_QueryInterface(mRequestStream);
2382 : if (seekable) {
2383 0 : seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
2384 : } else {
2385 0 : return NS_ERROR_FAILURE;
2386 : }
2387 0 : } else if (!mConnected) {
2388 0 : // this is code that was skipped in ::ReadSegments while in 0RTT
2389 : mConnected = true;
2390 : mConnection->GetSecurityInfo(getter_AddRefs(mSecurityInfo));
2391 : }
2392 : return NS_OK;
2393 : }
2394 0 :
2395 : nsresult
2396 : nsHttpTransaction::RestartOnFastOpenError()
2397 : {
2398 : // This will happen on connection error during Fast Open or if connect
2399 0 : // during Fast Open takes too long. So we should not have received any
2400 0 : // data!
2401 : MOZ_ASSERT(!mReceivedData);
2402 0 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
2403 :
2404 : LOG(("nsHttpTransaction::RestartOnFastOpenError - restarting transaction "
2405 : "%p\n", this));
2406 0 :
2407 0 : // rewind streams in case we already wrote out the request
2408 0 : nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mRequestStream);
2409 : if (seekable)
2410 0 : seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
2411 : // clear old connection state...
2412 0 : mSecurityInfo = nullptr;
2413 0 :
2414 0 : if (!mConnInfo->GetRoutedHost().IsEmpty()) {
2415 0 : MutexAutoLock lock(*nsHttp::GetLock());
2416 0 : RefPtr<nsHttpConnectionInfo> ci;
2417 : mConnInfo->CloneAsDirectRoute(getter_AddRefs(ci));
2418 0 : mConnInfo = ci;
2419 0 : }
2420 0 : mEarlyDataDisposition = EARLY_NONE;
2421 0 : m0RTTInProgress = false;
2422 0 : mFastOpenStatus = TFO_FAILED;
2423 : mTimings = TimingStruct();
2424 : return NS_OK;
2425 : }
2426 0 :
2427 : void
2428 0 : nsHttpTransaction::SetFastOpenStatus(uint8_t aStatus)
2429 : {
2430 0 : LOG(("nsHttpTransaction::SetFastOpenStatus %d [this=%p]\n",
2431 0 : aStatus, this));
2432 : mFastOpenStatus = aStatus;
2433 : }
2434 0 :
2435 : void
2436 0 : nsHttpTransaction::Refused0RTT()
2437 0 : {
2438 0 : LOG(("nsHttpTransaction::Refused0RTT %p\n", this));
2439 : if (mEarlyDataDisposition == EARLY_ACCEPTED) {
2440 0 : mEarlyDataDisposition = EARLY_SENT; // undo accepted state
2441 : }
2442 : }
2443 0 :
2444 : void
2445 0 : nsHttpTransaction::SetHttpTrailers(nsCString &aTrailers)
2446 0 : {
2447 0 : LOG(("nsHttpTransaction::SetHttpTrailers %p", this));
2448 0 : LOG(("[\n %s\n]", aTrailers.BeginReading()));
2449 : if (!mForTakeResponseTrailers) {
2450 : mForTakeResponseTrailers = new nsHttpHeaderArray();
2451 0 : }
2452 0 :
2453 0 : int32_t cur = 0;
2454 0 : int32_t len = aTrailers.Length();
2455 0 : while (cur < len) {
2456 0 : int32_t newline = aTrailers.FindCharInSet("\n", cur);
2457 : if (newline == -1) {
2458 : newline = len;
2459 0 : }
2460 0 :
2461 0 : int32_t end = aTrailers[newline - 1] == '\r' ? newline - 1 : newline;
2462 0 : nsDependentCSubstring line(aTrailers, cur, end);
2463 0 : nsHttpAtom hdr = {nullptr};
2464 0 : nsAutoCString hdrNameOriginal;
2465 0 : nsAutoCString val;
2466 0 : if (NS_SUCCEEDED(mForTakeResponseTrailers->ParseHeaderLine(line, &hdr, &hdrNameOriginal, &val))) {
2467 : if (hdr == nsHttp::Server_Timing) {
2468 : Unused << mForTakeResponseTrailers->SetHeaderFromNet(hdr, hdrNameOriginal, val, true);
2469 : }
2470 0 : }
2471 :
2472 : cur = newline + 1;
2473 0 : }
2474 :
2475 0 : if (mForTakeResponseTrailers->Count() == 0) {
2476 : // Didn't find a Server-Timing header, so get rid of this.
2477 0 : mForTakeResponseTrailers = nullptr;
2478 : }
2479 : }
2480 :
2481 : } // namespace net
2482 : } // namespace mozilla
|