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