netwerk/protocol/http/nsHttpTransaction.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     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/. */
     7 // HttpLog.h should generally be included first
     8 #include "HttpLog.h"
    10 #include "base/basictypes.h"
    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 "nsNetUtil.h"
    19 #include "nsCRT.h"
    21 #include "nsISeekableStream.h"
    22 #include "nsMultiplexInputStream.h"
    23 #include "nsStringStream.h"
    24 #include "mozilla/VisualEventTracer.h"
    26 #include "nsComponentManagerUtils.h" // do_CreateInstance
    27 #include "nsServiceManagerUtils.h"   // do_GetService
    28 #include "nsIHttpActivityObserver.h"
    29 #include "nsSocketTransportService2.h"
    30 #include "nsICancelable.h"
    31 #include "nsIEventTarget.h"
    32 #include "nsIHttpChannelInternal.h"
    33 #include "nsIInputStream.h"
    34 #include "nsITransport.h"
    35 #include "nsIOService.h"
    36 #include <algorithm>
    38 #ifdef MOZ_WIDGET_GONK
    39 #include "NetStatistics.h"
    40 #endif
    42 //-----------------------------------------------------------------------------
    44 #ifdef DEBUG
    45 // defined by the socket transport service while active
    46 extern PRThread *gSocketThread;
    47 #endif
    49 //-----------------------------------------------------------------------------
    51 static NS_DEFINE_CID(kMultiplexInputStream, NS_MULTIPLEXINPUTSTREAM_CID);
    53 // Place a limit on how much non-compliant HTTP can be skipped while
    54 // looking for a response header
    55 #define MAX_INVALID_RESPONSE_BODY_SIZE (1024 * 128)
    57 using namespace mozilla::net;
    59 namespace mozilla {
    60 namespace net {
    62 //-----------------------------------------------------------------------------
    63 // helpers
    64 //-----------------------------------------------------------------------------
    66 #if defined(PR_LOGGING)
    67 static void
    68 LogHeaders(const char *lineStart)
    69 {
    70     nsAutoCString buf;
    71     char *endOfLine;
    72     while ((endOfLine = PL_strstr(lineStart, "\r\n"))) {
    73         buf.Assign(lineStart, endOfLine - lineStart);
    74         if (PL_strcasestr(buf.get(), "authorization: ") ||
    75             PL_strcasestr(buf.get(), "proxy-authorization: ")) {
    76             char *p = PL_strchr(PL_strchr(buf.get(), ' ') + 1, ' ');
    77             while (p && *++p)
    78                 *p = '*';
    79         }
    80         LOG3(("  %s\n", buf.get()));
    81         lineStart = endOfLine + 2;
    82     }
    83 }
    84 #endif
    86 //-----------------------------------------------------------------------------
    87 // nsHttpTransaction <public>
    88 //-----------------------------------------------------------------------------
    90 nsHttpTransaction::nsHttpTransaction()
    91     : mLock("transaction lock")
    92     , mRequestSize(0)
    93     , mConnection(nullptr)
    94     , mConnInfo(nullptr)
    95     , mRequestHead(nullptr)
    96     , mResponseHead(nullptr)
    97     , mContentLength(-1)
    98     , mContentRead(0)
    99     , mInvalidResponseBytesRead(0)
   100     , mChunkedDecoder(nullptr)
   101     , mStatus(NS_OK)
   102     , mPriority(0)
   103     , mRestartCount(0)
   104     , mCaps(0)
   105     , mCapsToClear(0)
   106     , mClassification(CLASS_GENERAL)
   107     , mPipelinePosition(0)
   108     , mHttpVersion(NS_HTTP_VERSION_UNKNOWN)
   109     , mClosed(false)
   110     , mConnected(false)
   111     , mHaveStatusLine(false)
   112     , mHaveAllHeaders(false)
   113     , mTransactionDone(false)
   114     , mResponseIsComplete(false)
   115     , mDidContentStart(false)
   116     , mNoContent(false)
   117     , mSentData(false)
   118     , mReceivedData(false)
   119     , mStatusEventPending(false)
   120     , mHasRequestBody(false)
   121     , mProxyConnectFailed(false)
   122     , mHttpResponseMatched(false)
   123     , mPreserveStream(false)
   124     , mDispatchedAsBlocking(false)
   125     , mResponseTimeoutEnabled(true)
   126     , mReportedStart(false)
   127     , mReportedResponseHeader(false)
   128     , mForTakeResponseHead(nullptr)
   129     , mResponseHeadTaken(false)
   130     , mSubmittedRatePacing(false)
   131     , mPassedRatePacing(false)
   132     , mSynchronousRatePaceRequest(false)
   133     , mCountRecv(0)
   134     , mCountSent(0)
   135     , mAppId(NECKO_NO_APP_ID)
   136 {
   137     LOG(("Creating nsHttpTransaction @%p\n", this));
   138     gHttpHandler->GetMaxPipelineObjectSize(&mMaxPipelineObjectSize);
   139 }
   141 nsHttpTransaction::~nsHttpTransaction()
   142 {
   143     LOG(("Destroying nsHttpTransaction @%p\n", this));
   145     if (mTokenBucketCancel) {
   146         mTokenBucketCancel->Cancel(NS_ERROR_ABORT);
   147         mTokenBucketCancel = nullptr;
   148     }
   150     // Force the callbacks to be released right now
   151     mCallbacks = nullptr;
   153     NS_IF_RELEASE(mConnection);
   154     NS_IF_RELEASE(mConnInfo);
   156     delete mResponseHead;
   157     delete mForTakeResponseHead;
   158     delete mChunkedDecoder;
   159     ReleaseBlockingTransaction();
   160 }
   162 nsHttpTransaction::Classifier
   163 nsHttpTransaction::Classify()
   164 {
   165     if (!(mCaps & NS_HTTP_ALLOW_PIPELINING))
   166         return (mClassification = CLASS_SOLO);
   168     if (mRequestHead->PeekHeader(nsHttp::If_Modified_Since) ||
   169         mRequestHead->PeekHeader(nsHttp::If_None_Match))
   170         return (mClassification = CLASS_REVALIDATION);
   172     const char *accept = mRequestHead->PeekHeader(nsHttp::Accept);
   173     if (accept && !PL_strncmp(accept, "image/", 6))
   174         return (mClassification = CLASS_IMAGE);
   176     if (accept && !PL_strncmp(accept, "text/css", 8))
   177         return (mClassification = CLASS_SCRIPT);
   179     mClassification = CLASS_GENERAL;
   181     int32_t queryPos = mRequestHead->RequestURI().FindChar('?');
   182     if (queryPos == kNotFound) {
   183         if (StringEndsWith(mRequestHead->RequestURI(),
   184                            NS_LITERAL_CSTRING(".js")))
   185             mClassification = CLASS_SCRIPT;
   186     }
   187     else if (queryPos >= 3 &&
   188              Substring(mRequestHead->RequestURI(), queryPos - 3, 3).
   189              EqualsLiteral(".js")) {
   190         mClassification = CLASS_SCRIPT;
   191     }
   193     return mClassification;
   194 }
   196 nsresult
   197 nsHttpTransaction::Init(uint32_t caps,
   198                         nsHttpConnectionInfo *cinfo,
   199                         nsHttpRequestHead *requestHead,
   200                         nsIInputStream *requestBody,
   201                         bool requestBodyHasHeaders,
   202                         nsIEventTarget *target,
   203                         nsIInterfaceRequestor *callbacks,
   204                         nsITransportEventSink *eventsink,
   205                         nsIAsyncInputStream **responseBody)
   206 {
   207     MOZ_EVENT_TRACER_COMPOUND_NAME(static_cast<nsAHttpTransaction*>(this),
   208                                    requestHead->PeekHeader(nsHttp::Host),
   209                                    requestHead->RequestURI().BeginReading());
   211     MOZ_EVENT_TRACER_WAIT(static_cast<nsAHttpTransaction*>(this),
   212                           "net::http::transaction");
   213     nsresult rv;
   215     LOG(("nsHttpTransaction::Init [this=%p caps=%x]\n", this, caps));
   217     MOZ_ASSERT(cinfo);
   218     MOZ_ASSERT(requestHead);
   219     MOZ_ASSERT(target);
   221     mActivityDistributor = do_GetService(NS_HTTPACTIVITYDISTRIBUTOR_CONTRACTID, &rv);
   222     if (NS_FAILED(rv)) return rv;
   224     bool activityDistributorActive;
   225     rv = mActivityDistributor->GetIsActive(&activityDistributorActive);
   226     if (NS_SUCCEEDED(rv) && activityDistributorActive) {
   227         // there are some observers registered at activity distributor, gather
   228         // nsISupports for the channel that called Init()
   229         mChannel = do_QueryInterface(eventsink);
   230         LOG(("nsHttpTransaction::Init() " \
   231              "mActivityDistributor is active " \
   232              "this=%p", this));
   233     } else {
   234         // there is no observer, so don't use it
   235         activityDistributorActive = false;
   236         mActivityDistributor = nullptr;
   237     }
   239     nsCOMPtr<nsIChannel> channel = do_QueryInterface(eventsink);
   240     if (channel) {
   241         bool isInBrowser;
   242         NS_GetAppInfo(channel, &mAppId, &isInBrowser);
   243     }
   245 #ifdef MOZ_WIDGET_GONK
   246     if (mAppId != NECKO_NO_APP_ID) {
   247         nsCOMPtr<nsINetworkInterface> activeNetwork;
   248         GetActiveNetworkInterface(activeNetwork);
   249         mActiveNetwork =
   250             new nsMainThreadPtrHolder<nsINetworkInterface>(activeNetwork);
   251     }
   252 #endif
   254     nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal =
   255         do_QueryInterface(eventsink);
   256     if (httpChannelInternal) {
   257         rv = httpChannelInternal->GetResponseTimeoutEnabled(
   258             &mResponseTimeoutEnabled);
   259         if (NS_WARN_IF(NS_FAILED(rv))) {
   260             return rv;
   261         }
   262     }
   264     // create transport event sink proxy. it coalesces all events if and only
   265     // if the activity observer is not active. when the observer is active
   266     // we need not to coalesce any events to get all expected notifications
   267     // of the transaction state, necessary for correct debugging and logging.
   268     rv = net_NewTransportEventSinkProxy(getter_AddRefs(mTransportSink),
   269                                         eventsink, target,
   270                                         !activityDistributorActive);
   271     if (NS_FAILED(rv)) return rv;
   273     NS_ADDREF(mConnInfo = cinfo);
   274     mCallbacks = callbacks;
   275     mConsumerTarget = target;
   276     mCaps = caps;
   278     if (requestHead->IsHead()) {
   279         mNoContent = true;
   280     }
   282     // Make sure that there is "Content-Length: 0" header in the requestHead
   283     // in case of POST and PUT methods when there is no requestBody and
   284     // requestHead doesn't contain "Transfer-Encoding" header.
   285     //
   286     // RFC1945 section 7.2.2:
   287     //   HTTP/1.0 requests containing an entity body must include a valid
   288     //   Content-Length header field.
   289     //
   290     // RFC2616 section 4.4:
   291     //   For compatibility with HTTP/1.0 applications, HTTP/1.1 requests
   292     //   containing a message-body MUST include a valid Content-Length header
   293     //   field unless the server is known to be HTTP/1.1 compliant.
   294     if ((requestHead->IsPost() || requestHead->IsPut()) &&
   295         !requestBody && !requestHead->PeekHeader(nsHttp::Transfer_Encoding)) {
   296         requestHead->SetHeader(nsHttp::Content_Length, NS_LITERAL_CSTRING("0"));
   297     }
   299     // grab a weak reference to the request head
   300     mRequestHead = requestHead;
   302     // make sure we eliminate any proxy specific headers from
   303     // the request if we are using CONNECT
   304     bool pruneProxyHeaders = cinfo->UsingConnect();
   306     mReqHeaderBuf.Truncate();
   307     requestHead->Flatten(mReqHeaderBuf, pruneProxyHeaders);
   309 #if defined(PR_LOGGING)
   310     if (LOG3_ENABLED()) {
   311         LOG3(("http request [\n"));
   312         LogHeaders(mReqHeaderBuf.get());
   313         LOG3(("]\n"));
   314     }
   315 #endif
   317     // If the request body does not include headers or if there is no request
   318     // body, then we must add the header/body separator manually.
   319     if (!requestBodyHasHeaders || !requestBody)
   320         mReqHeaderBuf.AppendLiteral("\r\n");
   322     // report the request header
   323     if (mActivityDistributor)
   324         mActivityDistributor->ObserveActivity(
   325             mChannel,
   326             NS_HTTP_ACTIVITY_TYPE_HTTP_TRANSACTION,
   327             NS_HTTP_ACTIVITY_SUBTYPE_REQUEST_HEADER,
   328             PR_Now(), 0,
   329             mReqHeaderBuf);
   331     // Create a string stream for the request header buf (the stream holds
   332     // a non-owning reference to the request header data, so we MUST keep
   333     // mReqHeaderBuf around).
   334     nsCOMPtr<nsIInputStream> headers;
   335     rv = NS_NewByteInputStream(getter_AddRefs(headers),
   336                                mReqHeaderBuf.get(),
   337                                mReqHeaderBuf.Length());
   338     if (NS_FAILED(rv)) return rv;
   340     if (requestBody) {
   341         mHasRequestBody = true;
   343         // wrap the headers and request body in a multiplexed input stream.
   344         nsCOMPtr<nsIMultiplexInputStream> multi =
   345             do_CreateInstance(kMultiplexInputStream, &rv);
   346         if (NS_FAILED(rv)) return rv;
   348         rv = multi->AppendStream(headers);
   349         if (NS_FAILED(rv)) return rv;
   351         rv = multi->AppendStream(requestBody);
   352         if (NS_FAILED(rv)) return rv;
   354         // wrap the multiplexed input stream with a buffered input stream, so
   355         // that we write data in the largest chunks possible.  this is actually
   356         // necessary to workaround some common server bugs (see bug 137155).
   357         rv = NS_NewBufferedInputStream(getter_AddRefs(mRequestStream), multi,
   358                                        nsIOService::gDefaultSegmentSize);
   359         if (NS_FAILED(rv)) return rv;
   360     }
   361     else
   362         mRequestStream = headers;
   364     rv = mRequestStream->Available(&mRequestSize);
   365     if (NS_FAILED(rv)) return rv;
   367     // create pipe for response stream
   368     rv = NS_NewPipe2(getter_AddRefs(mPipeIn),
   369                      getter_AddRefs(mPipeOut),
   370                      true, true,
   371                      nsIOService::gDefaultSegmentSize,
   372                      nsIOService::gDefaultSegmentCount);
   373     if (NS_FAILED(rv)) return rv;
   375     Classify();
   377     NS_ADDREF(*responseBody = mPipeIn);
   378     return NS_OK;
   379 }
   381 // This method should only be used on the socket thread
   382 nsAHttpConnection *
   383 nsHttpTransaction::Connection()
   384 {
   385     return mConnection;
   386 }
   388 already_AddRefed<nsAHttpConnection>
   389 nsHttpTransaction::GetConnectionReference()
   390 {
   391     MutexAutoLock lock(mLock);
   392     nsRefPtr<nsAHttpConnection> connection = mConnection;
   393     return connection.forget();
   394 }
   396 nsHttpResponseHead *
   397 nsHttpTransaction::TakeResponseHead()
   398 {
   399     MOZ_ASSERT(!mResponseHeadTaken, "TakeResponseHead called 2x");
   401     // Lock RestartInProgress() and TakeResponseHead() against main thread
   402     MutexAutoLock lock(*nsHttp::GetLock());
   404     mResponseHeadTaken = true;
   406     // Prefer mForTakeResponseHead over mResponseHead. It is always a complete
   407     // set of headers.
   408     nsHttpResponseHead *head;
   409     if (mForTakeResponseHead) {
   410         head = mForTakeResponseHead;
   411         mForTakeResponseHead = nullptr;
   412         return head;
   413     }
   415     // Even in OnStartRequest() the headers won't be available if we were
   416     // canceled
   417     if (!mHaveAllHeaders) {
   418         NS_WARNING("response headers not available or incomplete");
   419         return nullptr;
   420     }
   422     head = mResponseHead;
   423     mResponseHead = nullptr;
   424     return head;
   425 }
   427 void
   428 nsHttpTransaction::SetProxyConnectFailed()
   429 {
   430     mProxyConnectFailed = true;
   431 }
   433 nsHttpRequestHead *
   434 nsHttpTransaction::RequestHead()
   435 {
   436     return mRequestHead;
   437 }
   439 uint32_t
   440 nsHttpTransaction::Http1xTransactionCount()
   441 {
   442   return 1;
   443 }
   445 nsresult
   446 nsHttpTransaction::TakeSubTransactions(
   447     nsTArray<nsRefPtr<nsAHttpTransaction> > &outTransactions)
   448 {
   449     return NS_ERROR_NOT_IMPLEMENTED;
   450 }
   452 //----------------------------------------------------------------------------
   453 // nsHttpTransaction::nsAHttpTransaction
   454 //----------------------------------------------------------------------------
   456 void
   457 nsHttpTransaction::SetConnection(nsAHttpConnection *conn)
   458 {
   459     {
   460         MutexAutoLock lock(mLock);
   461         NS_IF_RELEASE(mConnection);
   462         NS_IF_ADDREF(mConnection = conn);
   463     }
   465     if (conn) {
   466         MOZ_EVENT_TRACER_EXEC(static_cast<nsAHttpTransaction*>(this),
   467                               "net::http::transaction");
   468     }
   469 }
   471 void
   472 nsHttpTransaction::GetSecurityCallbacks(nsIInterfaceRequestor **cb)
   473 {
   474     MutexAutoLock lock(mLock);
   475     NS_IF_ADDREF(*cb = mCallbacks);
   476 }
   478 void
   479 nsHttpTransaction::SetSecurityCallbacks(nsIInterfaceRequestor* aCallbacks)
   480 {
   481     {
   482         MutexAutoLock lock(mLock);
   483         mCallbacks = aCallbacks;
   484     }
   486     if (gSocketTransportService) {
   487         nsRefPtr<UpdateSecurityCallbacks> event = new UpdateSecurityCallbacks(this, aCallbacks);
   488         gSocketTransportService->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL);
   489     }
   490 }
   492 void
   493 nsHttpTransaction::OnTransportStatus(nsITransport* transport,
   494                                      nsresult status, uint64_t progress)
   495 {
   496     LOG(("nsHttpTransaction::OnSocketStatus [this=%p status=%x progress=%llu]\n",
   497         this, status, progress));
   499     if (TimingEnabled()) {
   500         if (status == NS_NET_STATUS_RESOLVING_HOST) {
   501             mTimings.domainLookupStart = TimeStamp::Now();
   502         } else if (status == NS_NET_STATUS_RESOLVED_HOST) {
   503             mTimings.domainLookupEnd = TimeStamp::Now();
   504         } else if (status == NS_NET_STATUS_CONNECTING_TO) {
   505             mTimings.connectStart = TimeStamp::Now();
   506         } else if (status == NS_NET_STATUS_CONNECTED_TO) {
   507             mTimings.connectEnd = TimeStamp::Now();
   508         }
   509     }
   511     if (!mTransportSink)
   512         return;
   514     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
   516     // Need to do this before the STATUS_RECEIVING_FROM check below, to make
   517     // sure that the activity distributor gets told about all status events.
   518     if (mActivityDistributor) {
   519         // upon STATUS_WAITING_FOR; report request body sent
   520         if ((mHasRequestBody) &&
   521             (status == NS_NET_STATUS_WAITING_FOR))
   522             mActivityDistributor->ObserveActivity(
   523                 mChannel,
   524                 NS_HTTP_ACTIVITY_TYPE_HTTP_TRANSACTION,
   525                 NS_HTTP_ACTIVITY_SUBTYPE_REQUEST_BODY_SENT,
   526                 PR_Now(), 0, EmptyCString());
   528         // report the status and progress
   529         if (!mRestartInProgressVerifier.IsDiscardingContent())
   530             mActivityDistributor->ObserveActivity(
   531                 mChannel,
   532                 NS_HTTP_ACTIVITY_TYPE_SOCKET_TRANSPORT,
   533                 static_cast<uint32_t>(status),
   534                 PR_Now(),
   535                 progress,
   536                 EmptyCString());
   537     }
   539     // nsHttpChannel synthesizes progress events in OnDataAvailable
   540     if (status == NS_NET_STATUS_RECEIVING_FROM)
   541         return;
   543     uint64_t progressMax;
   545     if (status == NS_NET_STATUS_SENDING_TO) {
   546         // suppress progress when only writing request headers
   547         if (!mHasRequestBody)
   548             return;
   550         nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mRequestStream);
   551         MOZ_ASSERT(seekable, "Request stream isn't seekable?!?");
   553         int64_t prog = 0;
   554         seekable->Tell(&prog);
   555         progress = prog;
   557         // when uploading, we include the request headers in the progress
   558         // notifications.
   559         progressMax = mRequestSize; // XXX mRequestSize is 32-bit!
   560     }
   561     else {
   562         progress = 0;
   563         progressMax = 0;
   564     }
   566     mTransportSink->OnTransportStatus(transport, status, progress, progressMax);
   567 }
   569 bool
   570 nsHttpTransaction::IsDone()
   571 {
   572     return mTransactionDone;
   573 }
   575 nsresult
   576 nsHttpTransaction::Status()
   577 {
   578     return mStatus;
   579 }
   581 uint32_t
   582 nsHttpTransaction::Caps()
   583 {
   584     return mCaps & ~mCapsToClear;
   585 }
   587 void
   588 nsHttpTransaction::SetDNSWasRefreshed()
   589 {
   590     MOZ_ASSERT(NS_IsMainThread(), "SetDNSWasRefreshed on main thread only!");
   591     mCapsToClear |= NS_HTTP_REFRESH_DNS;
   592 }
   594 uint64_t
   595 nsHttpTransaction::Available()
   596 {
   597     uint64_t size;
   598     if (NS_FAILED(mRequestStream->Available(&size)))
   599         size = 0;
   600     return size;
   601 }
   603 NS_METHOD
   604 nsHttpTransaction::ReadRequestSegment(nsIInputStream *stream,
   605                                       void *closure,
   606                                       const char *buf,
   607                                       uint32_t offset,
   608                                       uint32_t count,
   609                                       uint32_t *countRead)
   610 {
   611     nsHttpTransaction *trans = (nsHttpTransaction *) closure;
   612     nsresult rv = trans->mReader->OnReadSegment(buf, count, countRead);
   613     if (NS_FAILED(rv)) return rv;
   615     if (trans->TimingEnabled() && trans->mTimings.requestStart.IsNull()) {
   616         // First data we're sending -> this is requestStart
   617         trans->mTimings.requestStart = TimeStamp::Now();
   618     }
   620     if (!trans->mSentData) {
   621         MOZ_EVENT_TRACER_MARK(static_cast<nsAHttpTransaction*>(trans),
   622                               "net::http::first-write");
   623     }
   625     trans->CountSentBytes(*countRead);
   626     trans->mSentData = true;
   627     return NS_OK;
   628 }
   630 nsresult
   631 nsHttpTransaction::ReadSegments(nsAHttpSegmentReader *reader,
   632                                 uint32_t count, uint32_t *countRead)
   633 {
   634     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
   636     if (mTransactionDone) {
   637         *countRead = 0;
   638         return mStatus;
   639     }
   641     if (!mConnected) {
   642         mConnected = true;
   643         mConnection->GetSecurityInfo(getter_AddRefs(mSecurityInfo));
   644     }
   646     mReader = reader;
   648     nsresult rv = mRequestStream->ReadSegments(ReadRequestSegment, this, count, countRead);
   650     mReader = nullptr;
   652     // if read would block then we need to AsyncWait on the request stream.
   653     // have callback occur on socket thread so we stay synchronized.
   654     if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
   655         nsCOMPtr<nsIAsyncInputStream> asyncIn =
   656                 do_QueryInterface(mRequestStream);
   657         if (asyncIn) {
   658             nsCOMPtr<nsIEventTarget> target;
   659             gHttpHandler->GetSocketThreadTarget(getter_AddRefs(target));
   660             if (target)
   661                 asyncIn->AsyncWait(this, 0, 0, target);
   662             else {
   663                 NS_ERROR("no socket thread event target");
   664                 rv = NS_ERROR_UNEXPECTED;
   665             }
   666         }
   667     }
   669     return rv;
   670 }
   672 NS_METHOD
   673 nsHttpTransaction::WritePipeSegment(nsIOutputStream *stream,
   674                                     void *closure,
   675                                     char *buf,
   676                                     uint32_t offset,
   677                                     uint32_t count,
   678                                     uint32_t *countWritten)
   679 {
   680     nsHttpTransaction *trans = (nsHttpTransaction *) closure;
   682     if (trans->mTransactionDone)
   683         return NS_BASE_STREAM_CLOSED; // stop iterating
   685     if (trans->TimingEnabled() && trans->mTimings.responseStart.IsNull()) {
   686         trans->mTimings.responseStart = TimeStamp::Now();
   687     }
   689     nsresult rv;
   690     //
   691     // OK, now let the caller fill this segment with data.
   692     //
   693     rv = trans->mWriter->OnWriteSegment(buf, count, countWritten);
   694     if (NS_FAILED(rv)) return rv; // caller didn't want to write anything
   696     if (!trans->mReceivedData) {
   697         MOZ_EVENT_TRACER_MARK(static_cast<nsAHttpTransaction*>(trans),
   698                               "net::http::first-read");
   699     }
   701     MOZ_ASSERT(*countWritten > 0, "bad writer");
   702     trans->CountRecvBytes(*countWritten);
   703     trans->mReceivedData = true;
   705     // Let the transaction "play" with the buffer.  It is free to modify
   706     // the contents of the buffer and/or modify countWritten.
   707     // - Bytes in HTTP headers don't count towards countWritten, so the input
   708     // side of pipe (aka nsHttpChannel's mTransactionPump) won't hit
   709     // OnInputStreamReady until all headers have been parsed.
   710     //
   711     rv = trans->ProcessData(buf, *countWritten, countWritten);
   712     if (NS_FAILED(rv))
   713         trans->Close(rv);
   715     return rv; // failure code only stops WriteSegments; it is not propagated.
   716 }
   718 nsresult
   719 nsHttpTransaction::WriteSegments(nsAHttpSegmentWriter *writer,
   720                                  uint32_t count, uint32_t *countWritten)
   721 {
   722     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
   724     if (mTransactionDone)
   725         return NS_SUCCEEDED(mStatus) ? NS_BASE_STREAM_CLOSED : mStatus;
   727     mWriter = writer;
   729     nsresult rv = mPipeOut->WriteSegments(WritePipeSegment, this, count, countWritten);
   731     mWriter = nullptr;
   733     // if pipe would block then we need to AsyncWait on it.  have callback
   734     // occur on socket thread so we stay synchronized.
   735     if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
   736         nsCOMPtr<nsIEventTarget> target;
   737         gHttpHandler->GetSocketThreadTarget(getter_AddRefs(target));
   738         if (target)
   739             mPipeOut->AsyncWait(this, 0, 0, target);
   740         else {
   741             NS_ERROR("no socket thread event target");
   742             rv = NS_ERROR_UNEXPECTED;
   743         }
   744     }
   746     return rv;
   747 }
   749 nsresult
   750 nsHttpTransaction::SaveNetworkStats(bool enforce)
   751 {
   752 #ifdef MOZ_WIDGET_GONK
   753     // Check if active network and appid are valid.
   754     if (!mActiveNetwork || mAppId == NECKO_NO_APP_ID) {
   755         return NS_OK;
   756     }
   758     if (mCountRecv <= 0 && mCountSent <= 0) {
   759         // There is no traffic, no need to save.
   760         return NS_OK;
   761     }
   763     // If |enforce| is false, the traffic amount is saved
   764     // only when the total amount exceeds the predefined
   765     // threshold.
   766     uint64_t totalBytes = mCountRecv + mCountSent;
   767     if (!enforce && totalBytes < NETWORK_STATS_THRESHOLD) {
   768         return NS_OK;
   769     }
   771     // Create the event to save the network statistics.
   772     // the event is then dispathed to the main thread.
   773     nsRefPtr<nsRunnable> event =
   774         new SaveNetworkStatsEvent(mAppId, mActiveNetwork,
   775                                   mCountRecv, mCountSent, false);
   776     NS_DispatchToMainThread(event);
   778     // Reset the counters after saving.
   779     mCountSent = 0;
   780     mCountRecv = 0;
   782     return NS_OK;
   783 #else
   784     return NS_ERROR_NOT_IMPLEMENTED;
   785 #endif
   786 }
   788 void
   789 nsHttpTransaction::Close(nsresult reason)
   790 {
   791     LOG(("nsHttpTransaction::Close [this=%p reason=%x]\n", this, reason));
   793     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
   795     if (mClosed) {
   796         LOG(("  already closed\n"));
   797         return;
   798     }
   800     if (mTokenBucketCancel) {
   801         mTokenBucketCancel->Cancel(reason);
   802         mTokenBucketCancel = nullptr;
   803     }
   805     if (mActivityDistributor) {
   806         // report the reponse is complete if not already reported
   807         if (!mResponseIsComplete)
   808             mActivityDistributor->ObserveActivity(
   809                 mChannel,
   810                 NS_HTTP_ACTIVITY_TYPE_HTTP_TRANSACTION,
   811                 NS_HTTP_ACTIVITY_SUBTYPE_RESPONSE_COMPLETE,
   812                 PR_Now(),
   813                 static_cast<uint64_t>(mContentRead),
   814                 EmptyCString());
   816         // report that this transaction is closing
   817         mActivityDistributor->ObserveActivity(
   818             mChannel,
   819             NS_HTTP_ACTIVITY_TYPE_HTTP_TRANSACTION,
   820             NS_HTTP_ACTIVITY_SUBTYPE_TRANSACTION_CLOSE,
   821             PR_Now(), 0, EmptyCString());
   822     }
   824     // we must no longer reference the connection!  find out if the
   825     // connection was being reused before letting it go.
   826     bool connReused = false;
   827     if (mConnection)
   828         connReused = mConnection->IsReused();
   829     mConnected = false;
   831     //
   832     // if the connection was reset or closed before we wrote any part of the
   833     // request or if we wrote the request but didn't receive any part of the
   834     // response and the connection was being reused, then we can (and really
   835     // should) assume that we wrote to a stale connection and we must therefore
   836     // repeat the request over a new connection.
   837     //
   838     // NOTE: the conditions under which we will automatically retry the HTTP
   839     // request have to be carefully selected to avoid duplication of the
   840     // request from the point-of-view of the server.  such duplication could
   841     // have dire consequences including repeated purchases, etc.
   842     //
   843     // NOTE: because of the way SSL proxy CONNECT is implemented, it is
   844     // possible that the transaction may have received data without having
   845     // sent any data.  for this reason, mSendData == FALSE does not imply
   846     // mReceivedData == FALSE.  (see bug 203057 for more info.)
   847     //
   848     if (reason == NS_ERROR_NET_RESET || reason == NS_OK) {
   850         // reallySentData is meant to separate the instances where data has
   851         // been sent by this transaction but buffered at a higher level while
   852         // a TLS session (perhaps via a tunnel) is setup.
   853         bool reallySentData =
   854             mSentData && (!mConnection || mConnection->BytesWritten());
   856         if (!mReceivedData &&
   857             (!reallySentData || connReused || mPipelinePosition)) {
   858             // if restarting fails, then we must proceed to close the pipe,
   859             // which will notify the channel that the transaction failed.
   861             if (mPipelinePosition) {
   862                 gHttpHandler->ConnMgr()->PipelineFeedbackInfo(
   863                     mConnInfo, nsHttpConnectionMgr::RedCanceledPipeline,
   864                     nullptr, 0);
   865             }
   866             if (NS_SUCCEEDED(Restart()))
   867                 return;
   868         }
   869         else if (!mResponseIsComplete && mPipelinePosition &&
   870                  reason == NS_ERROR_NET_RESET) {
   871             // due to unhandled rst on a pipeline - safe to
   872             // restart as only idempotent is found there
   874             gHttpHandler->ConnMgr()->PipelineFeedbackInfo(
   875                 mConnInfo, nsHttpConnectionMgr::RedCorruptedContent, nullptr, 0);
   876             if (NS_SUCCEEDED(RestartInProgress()))
   877                 return;
   878         }
   879     }
   881     bool relConn = true;
   882     if (NS_SUCCEEDED(reason)) {
   883         if (!mResponseIsComplete) {
   884             // The response has not been delimited with a high-confidence
   885             // algorithm like Content-Length or Chunked Encoding. We
   886             // need to use a strong framing mechanism to pipeline.
   887             gHttpHandler->ConnMgr()->PipelineFeedbackInfo(
   888                 mConnInfo, nsHttpConnectionMgr::BadInsufficientFraming,
   889                 nullptr, mClassification);
   890         }
   891         else if (mPipelinePosition) {
   892             // report this success as feedback
   893             gHttpHandler->ConnMgr()->PipelineFeedbackInfo(
   894                 mConnInfo, nsHttpConnectionMgr::GoodCompletedOK,
   895                 nullptr, mPipelinePosition);
   896         }
   898         // the server has not sent the final \r\n terminating the header
   899         // section, and there may still be a header line unparsed.  let's make
   900         // sure we parse the remaining header line, and then hopefully, the
   901         // response will be usable (see bug 88792).
   902         if (!mHaveAllHeaders) {
   903             char data = '\n';
   904             uint32_t unused;
   905             ParseHead(&data, 1, &unused);
   907             if (mResponseHead->Version() == NS_HTTP_VERSION_0_9) {
   908                 // Reject 0 byte HTTP/0.9 Responses - bug 423506
   909                 LOG(("nsHttpTransaction::Close %p 0 Byte 0.9 Response", this));
   910                 reason = NS_ERROR_NET_RESET;
   911             }
   912         }
   914         // honor the sticky connection flag...
   915         if (mCaps & NS_HTTP_STICKY_CONNECTION)
   916             relConn = false;
   917     }
   919     // mTimings.responseEnd is normally recorded based on the end of a
   920     // HTTP delimiter such as chunked-encodings or content-length. However,
   921     // EOF or an error still require an end time be recorded.
   922     if (TimingEnabled() &&
   923         mTimings.responseEnd.IsNull() && !mTimings.responseStart.IsNull())
   924         mTimings.responseEnd = TimeStamp::Now();
   926     if (relConn && mConnection) {
   927         MutexAutoLock lock(mLock);
   928         NS_RELEASE(mConnection);
   929     }
   931     // save network statistics in the end of transaction
   932     SaveNetworkStats(true);
   934     mStatus = reason;
   935     mTransactionDone = true; // forcibly flag the transaction as complete
   936     mClosed = true;
   937     ReleaseBlockingTransaction();
   939     // release some resources that we no longer need
   940     mRequestStream = nullptr;
   941     mReqHeaderBuf.Truncate();
   942     mLineBuf.Truncate();
   943     if (mChunkedDecoder) {
   944         delete mChunkedDecoder;
   945         mChunkedDecoder = nullptr;
   946     }
   948     // closing this pipe triggers the channel's OnStopRequest method.
   949     mPipeOut->CloseWithStatus(reason);
   951     MOZ_EVENT_TRACER_DONE(static_cast<nsAHttpTransaction*>(this),
   952                           "net::http::transaction");
   953 }
   955 nsresult
   956 nsHttpTransaction::AddTransaction(nsAHttpTransaction *trans)
   957 {
   958     return NS_ERROR_NOT_IMPLEMENTED;
   959 }
   961 uint32_t
   962 nsHttpTransaction::PipelineDepth()
   963 {
   964     return IsDone() ? 0 : 1;
   965 }
   967 nsresult
   968 nsHttpTransaction::SetPipelinePosition(int32_t position)
   969 {
   970     mPipelinePosition = position;
   971     return NS_OK;
   972 }
   974 int32_t
   975 nsHttpTransaction::PipelinePosition()
   976 {
   977     return mPipelinePosition;
   978 }
   980 bool // NOTE BASE CLASS
   981 nsAHttpTransaction::ResponseTimeoutEnabled() const
   982 {
   983     return false;
   984 }
   986 PRIntervalTime // NOTE BASE CLASS
   987 nsAHttpTransaction::ResponseTimeout()
   988 {
   989     return gHttpHandler->ResponseTimeout();
   990 }
   992 bool
   993 nsHttpTransaction::ResponseTimeoutEnabled() const
   994 {
   995     return mResponseTimeoutEnabled;
   996 }
   998 //-----------------------------------------------------------------------------
   999 // nsHttpTransaction <private>
  1000 //-----------------------------------------------------------------------------
  1002 nsresult
  1003 nsHttpTransaction::RestartInProgress()
  1005     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  1007     if ((mRestartCount + 1) >= gHttpHandler->MaxRequestAttempts()) {
  1008         LOG(("nsHttpTransaction::RestartInProgress() "
  1009              "reached max request attempts, failing transaction %p\n", this));
  1010         return NS_ERROR_NET_RESET;
  1013     // Lock RestartInProgress() and TakeResponseHead() against main thread
  1014     MutexAutoLock lock(*nsHttp::GetLock());
  1016     // Don't try and RestartInProgress() things that haven't gotten a response
  1017     // header yet. Those should be handled under the normal restart() path if
  1018     // they are eligible.
  1019     if (!mHaveAllHeaders)
  1020         return NS_ERROR_NET_RESET;
  1022     // don't try and restart 0.9 or non 200/Get HTTP/1
  1023     if (!mRestartInProgressVerifier.IsSetup())
  1024         return NS_ERROR_NET_RESET;
  1026     LOG(("Will restart transaction %p and skip first %lld bytes, "
  1027          "old Content-Length %lld",
  1028          this, mContentRead, mContentLength));
  1030     mRestartInProgressVerifier.SetAlreadyProcessed(
  1031         std::max(mRestartInProgressVerifier.AlreadyProcessed(), mContentRead));
  1033     if (!mResponseHeadTaken && !mForTakeResponseHead) {
  1034         // TakeResponseHeader() has not been called yet and this
  1035         // is the first restart. Store the resp headers exclusively
  1036         // for TakeResponseHead() which is called from the main thread and
  1037         // could happen at any time - so we can't continue to modify those
  1038         // headers (which restarting will effectively do)
  1039         mForTakeResponseHead = mResponseHead;
  1040         mResponseHead = nullptr;
  1043     if (mResponseHead) {
  1044         mResponseHead->Reset();
  1047     mContentRead = 0;
  1048     mContentLength = -1;
  1049     delete mChunkedDecoder;
  1050     mChunkedDecoder = nullptr;
  1051     mHaveStatusLine = false;
  1052     mHaveAllHeaders = false;
  1053     mHttpResponseMatched = false;
  1054     mResponseIsComplete = false;
  1055     mDidContentStart = false;
  1056     mNoContent = false;
  1057     mSentData = false;
  1058     mReceivedData = false;
  1060     return Restart();
  1063 nsresult
  1064 nsHttpTransaction::Restart()
  1066     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  1068     // limit the number of restart attempts - bug 92224
  1069     if (++mRestartCount >= gHttpHandler->MaxRequestAttempts()) {
  1070         LOG(("reached max request attempts, failing transaction @%p\n", this));
  1071         return NS_ERROR_NET_RESET;
  1074     LOG(("restarting transaction @%p\n", this));
  1076     // rewind streams in case we already wrote out the request
  1077     nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mRequestStream);
  1078     if (seekable)
  1079         seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
  1081     // clear old connection state...
  1082     mSecurityInfo = 0;
  1083     if (mConnection) {
  1084         MutexAutoLock lock(mLock);
  1085         NS_RELEASE(mConnection);
  1088     // disable pipelining for the next attempt in case pipelining caused the
  1089     // reset.  this is being overly cautious since we don't know if pipelining
  1090     // was the problem here.
  1091     mCaps &= ~NS_HTTP_ALLOW_PIPELINING;
  1092     SetPipelinePosition(0);
  1094     return gHttpHandler->InitiateTransaction(this, mPriority);
  1097 char *
  1098 nsHttpTransaction::LocateHttpStart(char *buf, uint32_t len,
  1099                                    bool aAllowPartialMatch)
  1101     MOZ_ASSERT(!aAllowPartialMatch || mLineBuf.IsEmpty());
  1103     static const char HTTPHeader[] = "HTTP/1.";
  1104     static const uint32_t HTTPHeaderLen = sizeof(HTTPHeader) - 1;
  1105     static const char HTTP2Header[] = "HTTP/2.0";
  1106     static const uint32_t HTTP2HeaderLen = sizeof(HTTP2Header) - 1;
  1107     // ShoutCast ICY is treated as HTTP/1.0
  1108     static const char ICYHeader[] = "ICY ";
  1109     static const uint32_t ICYHeaderLen = sizeof(ICYHeader) - 1;
  1111     if (aAllowPartialMatch && (len < HTTPHeaderLen))
  1112         return (PL_strncasecmp(buf, HTTPHeader, len) == 0) ? buf : nullptr;
  1114     // mLineBuf can contain partial match from previous search
  1115     if (!mLineBuf.IsEmpty()) {
  1116         MOZ_ASSERT(mLineBuf.Length() < HTTPHeaderLen);
  1117         int32_t checkChars = std::min(len, HTTPHeaderLen - mLineBuf.Length());
  1118         if (PL_strncasecmp(buf, HTTPHeader + mLineBuf.Length(),
  1119                            checkChars) == 0) {
  1120             mLineBuf.Append(buf, checkChars);
  1121             if (mLineBuf.Length() == HTTPHeaderLen) {
  1122                 // We've found whole HTTPHeader sequence. Return pointer at the
  1123                 // end of matched sequence since it is stored in mLineBuf.
  1124                 return (buf + checkChars);
  1126             // Response matches pattern but is still incomplete.
  1127             return 0;
  1129         // Previous partial match together with new data doesn't match the
  1130         // pattern. Start the search again.
  1131         mLineBuf.Truncate();
  1134     bool firstByte = true;
  1135     while (len > 0) {
  1136         if (PL_strncasecmp(buf, HTTPHeader, std::min<uint32_t>(len, HTTPHeaderLen)) == 0) {
  1137             if (len < HTTPHeaderLen) {
  1138                 // partial HTTPHeader sequence found
  1139                 // save partial match to mLineBuf
  1140                 mLineBuf.Assign(buf, len);
  1141                 return 0;
  1144             // whole HTTPHeader sequence found
  1145             return buf;
  1148         // At least "SmarterTools/2.0.3974.16813" generates nonsensical
  1149         // HTTP/2.0 responses to our HTTP/1 requests. Treat the minimal case of
  1150         // it as HTTP/1.1 to be compatible with old versions of ourselves and
  1151         // other browsers
  1153         if (firstByte && !mInvalidResponseBytesRead && len >= HTTP2HeaderLen &&
  1154             (PL_strncasecmp(buf, HTTP2Header, HTTP2HeaderLen) == 0)) {
  1155             LOG(("nsHttpTransaction:: Identified HTTP/2.0 treating as 1.x\n"));
  1156             return buf;
  1159         // Treat ICY (AOL/Nullsoft ShoutCast) non-standard header in same fashion
  1160         // as HTTP/2.0 is treated above. This will allow "ICY " to be interpretted
  1161         // as HTTP/1.0 in nsHttpResponseHead::ParseVersion
  1163         if (firstByte && !mInvalidResponseBytesRead && len >= ICYHeaderLen &&
  1164             (PL_strncasecmp(buf, ICYHeader, ICYHeaderLen) == 0)) {
  1165             LOG(("nsHttpTransaction:: Identified ICY treating as HTTP/1.0\n"));
  1166             return buf;
  1169         if (!nsCRT::IsAsciiSpace(*buf))
  1170             firstByte = false;
  1171         buf++;
  1172         len--;
  1174     return 0;
  1177 nsresult
  1178 nsHttpTransaction::ParseLine(char *line)
  1180     LOG(("nsHttpTransaction::ParseLine [%s]\n", line));
  1181     nsresult rv = NS_OK;
  1183     if (!mHaveStatusLine) {
  1184         mResponseHead->ParseStatusLine(line);
  1185         mHaveStatusLine = true;
  1186         // XXX this should probably never happen
  1187         if (mResponseHead->Version() == NS_HTTP_VERSION_0_9)
  1188             mHaveAllHeaders = true;
  1190     else {
  1191         rv = mResponseHead->ParseHeaderLine(line);
  1193     return rv;
  1196 nsresult
  1197 nsHttpTransaction::ParseLineSegment(char *segment, uint32_t len)
  1199     NS_PRECONDITION(!mHaveAllHeaders, "already have all headers");
  1201     if (!mLineBuf.IsEmpty() && mLineBuf.Last() == '\n') {
  1202         // trim off the new line char, and if this segment is
  1203         // not a continuation of the previous or if we haven't
  1204         // parsed the status line yet, then parse the contents
  1205         // of mLineBuf.
  1206         mLineBuf.Truncate(mLineBuf.Length() - 1);
  1207         if (!mHaveStatusLine || (*segment != ' ' && *segment != '\t')) {
  1208             nsresult rv = ParseLine(mLineBuf.BeginWriting());
  1209             mLineBuf.Truncate();
  1210             if (NS_FAILED(rv)) {
  1211                 gHttpHandler->ConnMgr()->PipelineFeedbackInfo(
  1212                     mConnInfo, nsHttpConnectionMgr::RedCorruptedContent,
  1213                     nullptr, 0);
  1214                 return rv;
  1219     // append segment to mLineBuf...
  1220     mLineBuf.Append(segment, len);
  1222     // a line buf with only a new line char signifies the end of headers.
  1223     if (mLineBuf.First() == '\n') {
  1224         mLineBuf.Truncate();
  1225         // discard this response if it is a 100 continue or other 1xx status.
  1226         uint16_t status = mResponseHead->Status();
  1227         if ((status != 101) && (status / 100 == 1)) {
  1228             LOG(("ignoring 1xx response\n"));
  1229             mHaveStatusLine = false;
  1230             mHttpResponseMatched = false;
  1231             mConnection->SetLastTransactionExpectedNoContent(true);
  1232             mResponseHead->Reset();
  1233             return NS_OK;
  1235         mHaveAllHeaders = true;
  1237     return NS_OK;
  1240 nsresult
  1241 nsHttpTransaction::ParseHead(char *buf,
  1242                              uint32_t count,
  1243                              uint32_t *countRead)
  1245     nsresult rv;
  1246     uint32_t len;
  1247     char *eol;
  1249     LOG(("nsHttpTransaction::ParseHead [count=%u]\n", count));
  1251     *countRead = 0;
  1253     NS_PRECONDITION(!mHaveAllHeaders, "oops");
  1255     // allocate the response head object if necessary
  1256     if (!mResponseHead) {
  1257         mResponseHead = new nsHttpResponseHead();
  1258         if (!mResponseHead)
  1259             return NS_ERROR_OUT_OF_MEMORY;
  1261         // report that we have a least some of the response
  1262         if (mActivityDistributor && !mReportedStart) {
  1263             mReportedStart = true;
  1264             mActivityDistributor->ObserveActivity(
  1265                 mChannel,
  1266                 NS_HTTP_ACTIVITY_TYPE_HTTP_TRANSACTION,
  1267                 NS_HTTP_ACTIVITY_SUBTYPE_RESPONSE_START,
  1268                 PR_Now(), 0, EmptyCString());
  1272     if (!mHttpResponseMatched) {
  1273         // Normally we insist on seeing HTTP/1.x in the first few bytes,
  1274         // but if we are on a persistent connection and the previous transaction
  1275         // was not supposed to have any content then we need to be prepared
  1276         // to skip over a response body that the server may have sent even
  1277         // though it wasn't allowed.
  1278         if (!mConnection || !mConnection->LastTransactionExpectedNoContent()) {
  1279             // tolerate only minor junk before the status line
  1280             mHttpResponseMatched = true;
  1281             char *p = LocateHttpStart(buf, std::min<uint32_t>(count, 11), true);
  1282             if (!p) {
  1283                 // Treat any 0.9 style response of a put as a failure.
  1284                 if (mRequestHead->IsPut())
  1285                     return NS_ERROR_ABORT;
  1287                 mResponseHead->ParseStatusLine("");
  1288                 mHaveStatusLine = true;
  1289                 mHaveAllHeaders = true;
  1290                 return NS_OK;
  1292             if (p > buf) {
  1293                 // skip over the junk
  1294                 mInvalidResponseBytesRead += p - buf;
  1295                 *countRead = p - buf;
  1296                 buf = p;
  1299         else {
  1300             char *p = LocateHttpStart(buf, count, false);
  1301             if (p) {
  1302                 mInvalidResponseBytesRead += p - buf;
  1303                 *countRead = p - buf;
  1304                 buf = p;
  1305                 mHttpResponseMatched = true;
  1306             } else {
  1307                 mInvalidResponseBytesRead += count;
  1308                 *countRead = count;
  1309                 if (mInvalidResponseBytesRead > MAX_INVALID_RESPONSE_BODY_SIZE) {
  1310                     LOG(("nsHttpTransaction::ParseHead() "
  1311                          "Cannot find Response Header\n"));
  1312                     // cannot go back and call this 0.9 anymore as we
  1313                     // have thrown away a lot of the leading junk
  1314                     return NS_ERROR_ABORT;
  1316                 return NS_OK;
  1320     // otherwise we can assume that we don't have a HTTP/0.9 response.
  1322     MOZ_ASSERT (mHttpResponseMatched);
  1323     while ((eol = static_cast<char *>(memchr(buf, '\n', count - *countRead))) != nullptr) {
  1324         // found line in range [buf:eol]
  1325         len = eol - buf + 1;
  1327         *countRead += len;
  1329         // actually, the line is in the range [buf:eol-1]
  1330         if ((eol > buf) && (*(eol-1) == '\r'))
  1331             len--;
  1333         buf[len-1] = '\n';
  1334         rv = ParseLineSegment(buf, len);
  1335         if (NS_FAILED(rv))
  1336             return rv;
  1338         if (mHaveAllHeaders)
  1339             return NS_OK;
  1341         // skip over line
  1342         buf = eol + 1;
  1344         if (!mHttpResponseMatched) {
  1345             // a 100 class response has caused us to throw away that set of
  1346             // response headers and look for the next response
  1347             return NS_ERROR_NET_INTERRUPT;
  1351     // do something about a partial header line
  1352     if (!mHaveAllHeaders && (len = count - *countRead)) {
  1353         *countRead = count;
  1354         // ignore a trailing carriage return, and don't bother calling
  1355         // ParseLineSegment if buf only contains a carriage return.
  1356         if ((buf[len-1] == '\r') && (--len == 0))
  1357             return NS_OK;
  1358         rv = ParseLineSegment(buf, len);
  1359         if (NS_FAILED(rv))
  1360             return rv;
  1362     return NS_OK;
  1365 // called on the socket thread
  1366 nsresult
  1367 nsHttpTransaction::HandleContentStart()
  1369     LOG(("nsHttpTransaction::HandleContentStart [this=%p]\n", this));
  1371     if (mResponseHead) {
  1372 #if defined(PR_LOGGING)
  1373         if (LOG3_ENABLED()) {
  1374             LOG3(("http response [\n"));
  1375             nsAutoCString headers;
  1376             mResponseHead->Flatten(headers, false);
  1377             LogHeaders(headers.get());
  1378             LOG3(("]\n"));
  1380 #endif
  1381         // Save http version, mResponseHead isn't available anymore after
  1382         // TakeResponseHead() is called
  1383         mHttpVersion = mResponseHead->Version();
  1385         // notify the connection, give it a chance to cause a reset.
  1386         bool reset = false;
  1387         if (!mRestartInProgressVerifier.IsSetup())
  1388             mConnection->OnHeadersAvailable(this, mRequestHead, mResponseHead, &reset);
  1390         // looks like we should ignore this response, resetting...
  1391         if (reset) {
  1392             LOG(("resetting transaction's response head\n"));
  1393             mHaveAllHeaders = false;
  1394             mHaveStatusLine = false;
  1395             mReceivedData = false;
  1396             mSentData = false;
  1397             mHttpResponseMatched = false;
  1398             mResponseHead->Reset();
  1399             // wait to be called again...
  1400             return NS_OK;
  1403         // check if this is a no-content response
  1404         switch (mResponseHead->Status()) {
  1405         case 101:
  1406             mPreserveStream = true;    // fall through to other no content
  1407         case 204:
  1408         case 205:
  1409         case 304:
  1410             mNoContent = true;
  1411             LOG(("this response should not contain a body.\n"));
  1412             break;
  1415         if (mResponseHead->Status() == 200 &&
  1416             mConnection->IsProxyConnectInProgress()) {
  1417             // successful CONNECTs do not have response bodies
  1418             mNoContent = true;
  1420         mConnection->SetLastTransactionExpectedNoContent(mNoContent);
  1421         if (mInvalidResponseBytesRead)
  1422             gHttpHandler->ConnMgr()->PipelineFeedbackInfo(
  1423                 mConnInfo, nsHttpConnectionMgr::BadInsufficientFraming,
  1424                 nullptr, mClassification);
  1426         if (mNoContent)
  1427             mContentLength = 0;
  1428         else {
  1429             // grab the content-length from the response headers
  1430             mContentLength = mResponseHead->ContentLength();
  1432             if ((mClassification != CLASS_SOLO) &&
  1433                 (mContentLength > mMaxPipelineObjectSize))
  1434                 CancelPipeline(nsHttpConnectionMgr::BadUnexpectedLarge);
  1436             // handle chunked encoding here, so we'll know immediately when
  1437             // we're done with the socket.  please note that _all_ other
  1438             // decoding is done when the channel receives the content data
  1439             // so as not to block the socket transport thread too much.
  1440             // ignore chunked responses from HTTP/1.0 servers and proxies.
  1441             if (mResponseHead->Version() >= NS_HTTP_VERSION_1_1 &&
  1442                 mResponseHead->HasHeaderValue(nsHttp::Transfer_Encoding, "chunked")) {
  1443                 // we only support the "chunked" transfer encoding right now.
  1444                 mChunkedDecoder = new nsHttpChunkedDecoder();
  1445                 if (!mChunkedDecoder)
  1446                     return NS_ERROR_OUT_OF_MEMORY;
  1447                 LOG(("chunked decoder created\n"));
  1448                 // Ignore server specified Content-Length.
  1449                 mContentLength = -1;
  1451 #if defined(PR_LOGGING)
  1452             else if (mContentLength == int64_t(-1))
  1453                 LOG(("waiting for the server to close the connection.\n"));
  1454 #endif
  1456         if (mRestartInProgressVerifier.IsSetup() &&
  1457             !mRestartInProgressVerifier.Verify(mContentLength, mResponseHead)) {
  1458             LOG(("Restart in progress subsequent transaction failed to match"));
  1459             return NS_ERROR_ABORT;
  1463     mDidContentStart = true;
  1465     // The verifier only initializes itself once (from the first iteration of
  1466     // a transaction that gets far enough to have response headers)
  1467     if (mRequestHead->IsGet())
  1468         mRestartInProgressVerifier.Set(mContentLength, mResponseHead);
  1470     return NS_OK;
  1473 // called on the socket thread
  1474 nsresult
  1475 nsHttpTransaction::HandleContent(char *buf,
  1476                                  uint32_t count,
  1477                                  uint32_t *contentRead,
  1478                                  uint32_t *contentRemaining)
  1480     nsresult rv;
  1482     LOG(("nsHttpTransaction::HandleContent [this=%p count=%u]\n", this, count));
  1484     *contentRead = 0;
  1485     *contentRemaining = 0;
  1487     MOZ_ASSERT(mConnection);
  1489     if (!mDidContentStart) {
  1490         rv = HandleContentStart();
  1491         if (NS_FAILED(rv)) return rv;
  1492         // Do not write content to the pipe if we haven't started streaming yet
  1493         if (!mDidContentStart)
  1494             return NS_OK;
  1497     if (mChunkedDecoder) {
  1498         // give the buf over to the chunked decoder so it can reformat the
  1499         // data and tell us how much is really there.
  1500         rv = mChunkedDecoder->HandleChunkedContent(buf, count, contentRead, contentRemaining);
  1501         if (NS_FAILED(rv)) return rv;
  1503     else if (mContentLength >= int64_t(0)) {
  1504         // HTTP/1.0 servers have been known to send erroneous Content-Length
  1505         // headers. So, unless the connection is persistent, we must make
  1506         // allowances for a possibly invalid Content-Length header. Thus, if
  1507         // NOT persistent, we simply accept everything in |buf|.
  1508         if (mConnection->IsPersistent() || mPreserveStream ||
  1509             mHttpVersion >= NS_HTTP_VERSION_1_1) {
  1510             int64_t remaining = mContentLength - mContentRead;
  1511             *contentRead = uint32_t(std::min<int64_t>(count, remaining));
  1512             *contentRemaining = count - *contentRead;
  1514         else {
  1515             *contentRead = count;
  1516             // mContentLength might need to be increased...
  1517             int64_t position = mContentRead + int64_t(count);
  1518             if (position > mContentLength) {
  1519                 mContentLength = position;
  1520                 //mResponseHead->SetContentLength(mContentLength);
  1524     else {
  1525         // when we are just waiting for the server to close the connection...
  1526         // (no explicit content-length given)
  1527         *contentRead = count;
  1530     int64_t toReadBeforeRestart =
  1531         mRestartInProgressVerifier.ToReadBeforeRestart();
  1533     if (toReadBeforeRestart && *contentRead) {
  1534         uint32_t ignore =
  1535             static_cast<uint32_t>(std::min<int64_t>(toReadBeforeRestart, UINT32_MAX));
  1536         ignore = std::min(*contentRead, ignore);
  1537         LOG(("Due To Restart ignoring %d of remaining %ld",
  1538              ignore, toReadBeforeRestart));
  1539         *contentRead -= ignore;
  1540         mContentRead += ignore;
  1541         mRestartInProgressVerifier.HaveReadBeforeRestart(ignore);
  1542         memmove(buf, buf + ignore, *contentRead + *contentRemaining);
  1545     if (*contentRead) {
  1546         // update count of content bytes read and report progress...
  1547         mContentRead += *contentRead;
  1548         /* when uncommenting, take care of 64-bit integers w/ std::max...
  1549         if (mProgressSink)
  1550             mProgressSink->OnProgress(nullptr, nullptr, mContentRead, std::max(0, mContentLength));
  1551         */
  1554     LOG(("nsHttpTransaction::HandleContent [this=%p count=%u read=%u mContentRead=%lld mContentLength=%lld]\n",
  1555         this, count, *contentRead, mContentRead, mContentLength));
  1557     // Check the size of chunked responses. If we exceed the max pipeline size
  1558     // for this response reschedule the pipeline
  1559     if ((mClassification != CLASS_SOLO) &&
  1560         mChunkedDecoder &&
  1561         ((mContentRead + mChunkedDecoder->GetChunkRemaining()) >
  1562          mMaxPipelineObjectSize)) {
  1563         CancelPipeline(nsHttpConnectionMgr::BadUnexpectedLarge);
  1566     // check for end-of-file
  1567     if ((mContentRead == mContentLength) ||
  1568         (mChunkedDecoder && mChunkedDecoder->ReachedEOF())) {
  1569         // the transaction is done with a complete response.
  1570         mTransactionDone = true;
  1571         mResponseIsComplete = true;
  1572         ReleaseBlockingTransaction();
  1574         if (TimingEnabled())
  1575             mTimings.responseEnd = TimeStamp::Now();
  1577         // report the entire response has arrived
  1578         if (mActivityDistributor)
  1579             mActivityDistributor->ObserveActivity(
  1580                 mChannel,
  1581                 NS_HTTP_ACTIVITY_TYPE_HTTP_TRANSACTION,
  1582                 NS_HTTP_ACTIVITY_SUBTYPE_RESPONSE_COMPLETE,
  1583                 PR_Now(),
  1584                 static_cast<uint64_t>(mContentRead),
  1585                 EmptyCString());
  1588     return NS_OK;
  1591 nsresult
  1592 nsHttpTransaction::ProcessData(char *buf, uint32_t count, uint32_t *countRead)
  1594     nsresult rv;
  1596     LOG(("nsHttpTransaction::ProcessData [this=%p count=%u]\n", this, count));
  1598     *countRead = 0;
  1600     // we may not have read all of the headers yet...
  1601     if (!mHaveAllHeaders) {
  1602         uint32_t bytesConsumed = 0;
  1604         do {
  1605             uint32_t localBytesConsumed = 0;
  1606             char *localBuf = buf + bytesConsumed;
  1607             uint32_t localCount = count - bytesConsumed;
  1609             rv = ParseHead(localBuf, localCount, &localBytesConsumed);
  1610             if (NS_FAILED(rv) && rv != NS_ERROR_NET_INTERRUPT)
  1611                 return rv;
  1612             bytesConsumed += localBytesConsumed;
  1613         } while (rv == NS_ERROR_NET_INTERRUPT);
  1615         count -= bytesConsumed;
  1617         // if buf has some content in it, shift bytes to top of buf.
  1618         if (count && bytesConsumed)
  1619             memmove(buf, buf + bytesConsumed, count);
  1621         // report the completed response header
  1622         if (mActivityDistributor && mResponseHead && mHaveAllHeaders &&
  1623             !mReportedResponseHeader) {
  1624             mReportedResponseHeader = true;
  1625             nsAutoCString completeResponseHeaders;
  1626             mResponseHead->Flatten(completeResponseHeaders, false);
  1627             completeResponseHeaders.AppendLiteral("\r\n");
  1628             mActivityDistributor->ObserveActivity(
  1629                 mChannel,
  1630                 NS_HTTP_ACTIVITY_TYPE_HTTP_TRANSACTION,
  1631                 NS_HTTP_ACTIVITY_SUBTYPE_RESPONSE_HEADER,
  1632                 PR_Now(), 0,
  1633                 completeResponseHeaders);
  1637     // even though count may be 0, we still want to call HandleContent
  1638     // so it can complete the transaction if this is a "no-content" response.
  1639     if (mHaveAllHeaders) {
  1640         uint32_t countRemaining = 0;
  1641         //
  1642         // buf layout:
  1643         //
  1644         // +--------------------------------------+----------------+-----+
  1645         // |              countRead               | countRemaining |     |
  1646         // +--------------------------------------+----------------+-----+
  1647         //
  1648         // count          : bytes read from the socket
  1649         // countRead      : bytes corresponding to this transaction
  1650         // countRemaining : bytes corresponding to next pipelined transaction
  1651         //
  1652         // NOTE:
  1653         // count > countRead + countRemaining <==> chunked transfer encoding
  1654         //
  1655         rv = HandleContent(buf, count, countRead, &countRemaining);
  1656         if (NS_FAILED(rv)) return rv;
  1657         // we may have read more than our share, in which case we must give
  1658         // the excess bytes back to the connection
  1659         if (mResponseIsComplete && countRemaining) {
  1660             MOZ_ASSERT(mConnection);
  1661             mConnection->PushBack(buf + *countRead, countRemaining);
  1665     return NS_OK;
  1668 void
  1669 nsHttpTransaction::CancelPipeline(uint32_t reason)
  1671     // reason is casted through a uint to avoid compiler header deps
  1672     gHttpHandler->ConnMgr()->PipelineFeedbackInfo(
  1673         mConnInfo,
  1674         static_cast<nsHttpConnectionMgr::PipelineFeedbackInfoType>(reason),
  1675         nullptr, mClassification);
  1677     mConnection->CancelPipeline(NS_ERROR_ABORT);
  1679     // Avoid pipelining this transaction on restart by classifying it as solo.
  1680     // This also prevents BadUnexpectedLarge from being reported more
  1681     // than one time per transaction.
  1682     mClassification = CLASS_SOLO;
  1685 // Called when the transaction marked for blocking is associated with a connection
  1686 // (i.e. added to a spdy session, an idle http connection, or placed into
  1687 // a http pipeline). It is safe to call this multiple times with it only
  1688 // having an effect once.
  1689 void
  1690 nsHttpTransaction::DispatchedAsBlocking()
  1692     if (mDispatchedAsBlocking)
  1693         return;
  1695     LOG(("nsHttpTransaction %p dispatched as blocking\n", this));
  1697     if (!mLoadGroupCI)
  1698         return;
  1700     LOG(("nsHttpTransaction adding blocking channel %p from "
  1701          "loadgroup %p\n", this, mLoadGroupCI.get()));
  1703     mLoadGroupCI->AddBlockingTransaction();
  1704     mDispatchedAsBlocking = true;
  1707 void
  1708 nsHttpTransaction::RemoveDispatchedAsBlocking()
  1710     if (!mLoadGroupCI || !mDispatchedAsBlocking)
  1711         return;
  1713     uint32_t blockers = 0;
  1714     nsresult rv = mLoadGroupCI->RemoveBlockingTransaction(&blockers);
  1716     LOG(("nsHttpTransaction removing blocking channel %p from "
  1717          "loadgroup %p. %d blockers remain.\n", this,
  1718          mLoadGroupCI.get(), blockers));
  1720     if (NS_SUCCEEDED(rv) && !blockers) {
  1721         LOG(("nsHttpTransaction %p triggering release of blocked channels.\n",
  1722              this));
  1723         gHttpHandler->ConnMgr()->ProcessPendingQ();
  1726     mDispatchedAsBlocking = false;
  1729 void
  1730 nsHttpTransaction::ReleaseBlockingTransaction()
  1732     RemoveDispatchedAsBlocking();
  1733     mLoadGroupCI = nullptr;
  1736 //-----------------------------------------------------------------------------
  1737 // nsHttpTransaction deletion event
  1738 //-----------------------------------------------------------------------------
  1740 class nsDeleteHttpTransaction : public nsRunnable {
  1741 public:
  1742     nsDeleteHttpTransaction(nsHttpTransaction *trans)
  1743         : mTrans(trans)
  1744     {}
  1746     NS_IMETHOD Run()
  1748         delete mTrans;
  1749         return NS_OK;
  1751 private:
  1752     nsHttpTransaction *mTrans;
  1753 };
  1755 void
  1756 nsHttpTransaction::DeleteSelfOnConsumerThread()
  1758     LOG(("nsHttpTransaction::DeleteSelfOnConsumerThread [this=%p]\n", this));
  1760     bool val;
  1761     if (!mConsumerTarget ||
  1762         (NS_SUCCEEDED(mConsumerTarget->IsOnCurrentThread(&val)) && val)) {
  1763         delete this;
  1764     } else {
  1765         LOG(("proxying delete to consumer thread...\n"));
  1766         nsCOMPtr<nsIRunnable> event = new nsDeleteHttpTransaction(this);
  1767         if (NS_FAILED(mConsumerTarget->Dispatch(event, NS_DISPATCH_NORMAL)))
  1768             NS_WARNING("failed to dispatch nsHttpDeleteTransaction event");
  1772 bool
  1773 nsHttpTransaction::TryToRunPacedRequest()
  1775     if (mSubmittedRatePacing)
  1776         return mPassedRatePacing;
  1778     mSubmittedRatePacing = true;
  1779     mSynchronousRatePaceRequest = true;
  1780     gHttpHandler->SubmitPacedRequest(this, getter_AddRefs(mTokenBucketCancel));
  1781     mSynchronousRatePaceRequest = false;
  1782     return mPassedRatePacing;
  1785 void
  1786 nsHttpTransaction::OnTokenBucketAdmitted()
  1788     mPassedRatePacing = true;
  1789     mTokenBucketCancel = nullptr;
  1791     if (!mSynchronousRatePaceRequest)
  1792         gHttpHandler->ConnMgr()->ProcessPendingQ(mConnInfo);
  1795 void
  1796 nsHttpTransaction::CancelPacing(nsresult reason)
  1798     if (mTokenBucketCancel) {
  1799         mTokenBucketCancel->Cancel(reason);
  1800         mTokenBucketCancel = nullptr;
  1804 //-----------------------------------------------------------------------------
  1805 // nsHttpTransaction::nsISupports
  1806 //-----------------------------------------------------------------------------
  1808 NS_IMPL_ADDREF(nsHttpTransaction)
  1810 NS_IMETHODIMP_(MozExternalRefCountType)
  1811 nsHttpTransaction::Release()
  1813     nsrefcnt count;
  1814     NS_PRECONDITION(0 != mRefCnt, "dup release");
  1815     count = --mRefCnt;
  1816     NS_LOG_RELEASE(this, count, "nsHttpTransaction");
  1817     if (0 == count) {
  1818         mRefCnt = 1; /* stablize */
  1819         // it is essential that the transaction be destroyed on the consumer
  1820         // thread (we could be holding the last reference to our consumer).
  1821         DeleteSelfOnConsumerThread();
  1822         return 0;
  1824     return count;
  1827 NS_IMPL_QUERY_INTERFACE(nsHttpTransaction,
  1828                         nsIInputStreamCallback,
  1829                         nsIOutputStreamCallback)
  1831 //-----------------------------------------------------------------------------
  1832 // nsHttpTransaction::nsIInputStreamCallback
  1833 //-----------------------------------------------------------------------------
  1835 // called on the socket thread
  1836 NS_IMETHODIMP
  1837 nsHttpTransaction::OnInputStreamReady(nsIAsyncInputStream *out)
  1839     if (mConnection) {
  1840         mConnection->TransactionHasDataToWrite(this);
  1841         nsresult rv = mConnection->ResumeSend();
  1842         if (NS_FAILED(rv))
  1843             NS_ERROR("ResumeSend failed");
  1845     return NS_OK;
  1848 //-----------------------------------------------------------------------------
  1849 // nsHttpTransaction::nsIOutputStreamCallback
  1850 //-----------------------------------------------------------------------------
  1852 // called on the socket thread
  1853 NS_IMETHODIMP
  1854 nsHttpTransaction::OnOutputStreamReady(nsIAsyncOutputStream *out)
  1856     if (mConnection) {
  1857         nsresult rv = mConnection->ResumeRecv();
  1858         if (NS_FAILED(rv))
  1859             NS_ERROR("ResumeRecv failed");
  1861     return NS_OK;
  1864 // nsHttpTransaction::RestartVerifier
  1866 static bool
  1867 matchOld(nsHttpResponseHead *newHead, nsCString &old,
  1868          nsHttpAtom headerAtom)
  1870     const char *val;
  1872     val = newHead->PeekHeader(headerAtom);
  1873     if (val && old.IsEmpty())
  1874         return false;
  1875     if (!val && !old.IsEmpty())
  1876         return false;
  1877     if (val && !old.Equals(val))
  1878         return false;
  1879     return true;
  1882 bool
  1883 nsHttpTransaction::RestartVerifier::Verify(int64_t contentLength,
  1884                                            nsHttpResponseHead *newHead)
  1886     if (mContentLength != contentLength)
  1887         return false;
  1889     if (newHead->Status() != 200)
  1890         return false;
  1892     if (!matchOld(newHead, mContentRange, nsHttp::Content_Range))
  1893         return false;
  1895     if (!matchOld(newHead, mLastModified, nsHttp::Last_Modified))
  1896         return false;
  1898     if (!matchOld(newHead, mETag, nsHttp::ETag))
  1899         return false;
  1901     if (!matchOld(newHead, mContentEncoding, nsHttp::Content_Encoding))
  1902         return false;
  1904     if (!matchOld(newHead, mTransferEncoding, nsHttp::Transfer_Encoding))
  1905         return false;
  1907     return true;
  1910 void
  1911 nsHttpTransaction::RestartVerifier::Set(int64_t contentLength,
  1912                                         nsHttpResponseHead *head)
  1914     if (mSetup)
  1915         return;
  1917     // If mSetup does not transition to true RestartInPogress() is later
  1918     // forbidden
  1920     // Only RestartInProgress with 200 response code
  1921     if (head->Status() != 200)
  1922         return;
  1924     mContentLength = contentLength;
  1926     if (head) {
  1927         const char *val;
  1928         val = head->PeekHeader(nsHttp::ETag);
  1929         if (val)
  1930             mETag.Assign(val);
  1931         val = head->PeekHeader(nsHttp::Last_Modified);
  1932         if (val)
  1933             mLastModified.Assign(val);
  1934         val = head->PeekHeader(nsHttp::Content_Range);
  1935         if (val)
  1936             mContentRange.Assign(val);
  1937         val = head->PeekHeader(nsHttp::Content_Encoding);
  1938         if (val)
  1939             mContentEncoding.Assign(val);
  1940         val = head->PeekHeader(nsHttp::Transfer_Encoding);
  1941         if (val)
  1942             mTransferEncoding.Assign(val);
  1944         // We can only restart with any confidence if we have a stored etag or
  1945         // last-modified header
  1946         if (mETag.IsEmpty() && mLastModified.IsEmpty())
  1947             return;
  1949         mSetup = true;
  1953 } // namespace mozilla::net
  1954 } // namespace mozilla

mercurial