netwerk/protocol/http/nsHttpTransaction.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/netwerk/protocol/http/nsHttpTransaction.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1954 @@
     1.4 +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
     1.5 +/* vim:set ts=4 sw=4 sts=4 et cin: */
     1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +// HttpLog.h should generally be included first
    1.11 +#include "HttpLog.h"
    1.12 +
    1.13 +#include "base/basictypes.h"
    1.14 +
    1.15 +#include "nsHttpHandler.h"
    1.16 +#include "nsHttpTransaction.h"
    1.17 +#include "nsHttpRequestHead.h"
    1.18 +#include "nsHttpResponseHead.h"
    1.19 +#include "nsHttpChunkedDecoder.h"
    1.20 +#include "nsTransportUtils.h"
    1.21 +#include "nsNetUtil.h"
    1.22 +#include "nsCRT.h"
    1.23 +
    1.24 +#include "nsISeekableStream.h"
    1.25 +#include "nsMultiplexInputStream.h"
    1.26 +#include "nsStringStream.h"
    1.27 +#include "mozilla/VisualEventTracer.h"
    1.28 +
    1.29 +#include "nsComponentManagerUtils.h" // do_CreateInstance
    1.30 +#include "nsServiceManagerUtils.h"   // do_GetService
    1.31 +#include "nsIHttpActivityObserver.h"
    1.32 +#include "nsSocketTransportService2.h"
    1.33 +#include "nsICancelable.h"
    1.34 +#include "nsIEventTarget.h"
    1.35 +#include "nsIHttpChannelInternal.h"
    1.36 +#include "nsIInputStream.h"
    1.37 +#include "nsITransport.h"
    1.38 +#include "nsIOService.h"
    1.39 +#include <algorithm>
    1.40 +
    1.41 +#ifdef MOZ_WIDGET_GONK
    1.42 +#include "NetStatistics.h"
    1.43 +#endif
    1.44 +
    1.45 +//-----------------------------------------------------------------------------
    1.46 +
    1.47 +#ifdef DEBUG
    1.48 +// defined by the socket transport service while active
    1.49 +extern PRThread *gSocketThread;
    1.50 +#endif
    1.51 +
    1.52 +//-----------------------------------------------------------------------------
    1.53 +
    1.54 +static NS_DEFINE_CID(kMultiplexInputStream, NS_MULTIPLEXINPUTSTREAM_CID);
    1.55 +
    1.56 +// Place a limit on how much non-compliant HTTP can be skipped while
    1.57 +// looking for a response header
    1.58 +#define MAX_INVALID_RESPONSE_BODY_SIZE (1024 * 128)
    1.59 +
    1.60 +using namespace mozilla::net;
    1.61 +
    1.62 +namespace mozilla {
    1.63 +namespace net {
    1.64 +
    1.65 +//-----------------------------------------------------------------------------
    1.66 +// helpers
    1.67 +//-----------------------------------------------------------------------------
    1.68 +
    1.69 +#if defined(PR_LOGGING)
    1.70 +static void
    1.71 +LogHeaders(const char *lineStart)
    1.72 +{
    1.73 +    nsAutoCString buf;
    1.74 +    char *endOfLine;
    1.75 +    while ((endOfLine = PL_strstr(lineStart, "\r\n"))) {
    1.76 +        buf.Assign(lineStart, endOfLine - lineStart);
    1.77 +        if (PL_strcasestr(buf.get(), "authorization: ") ||
    1.78 +            PL_strcasestr(buf.get(), "proxy-authorization: ")) {
    1.79 +            char *p = PL_strchr(PL_strchr(buf.get(), ' ') + 1, ' ');
    1.80 +            while (p && *++p)
    1.81 +                *p = '*';
    1.82 +        }
    1.83 +        LOG3(("  %s\n", buf.get()));
    1.84 +        lineStart = endOfLine + 2;
    1.85 +    }
    1.86 +}
    1.87 +#endif
    1.88 +
    1.89 +//-----------------------------------------------------------------------------
    1.90 +// nsHttpTransaction <public>
    1.91 +//-----------------------------------------------------------------------------
    1.92 +
    1.93 +nsHttpTransaction::nsHttpTransaction()
    1.94 +    : mLock("transaction lock")
    1.95 +    , mRequestSize(0)
    1.96 +    , mConnection(nullptr)
    1.97 +    , mConnInfo(nullptr)
    1.98 +    , mRequestHead(nullptr)
    1.99 +    , mResponseHead(nullptr)
   1.100 +    , mContentLength(-1)
   1.101 +    , mContentRead(0)
   1.102 +    , mInvalidResponseBytesRead(0)
   1.103 +    , mChunkedDecoder(nullptr)
   1.104 +    , mStatus(NS_OK)
   1.105 +    , mPriority(0)
   1.106 +    , mRestartCount(0)
   1.107 +    , mCaps(0)
   1.108 +    , mCapsToClear(0)
   1.109 +    , mClassification(CLASS_GENERAL)
   1.110 +    , mPipelinePosition(0)
   1.111 +    , mHttpVersion(NS_HTTP_VERSION_UNKNOWN)
   1.112 +    , mClosed(false)
   1.113 +    , mConnected(false)
   1.114 +    , mHaveStatusLine(false)
   1.115 +    , mHaveAllHeaders(false)
   1.116 +    , mTransactionDone(false)
   1.117 +    , mResponseIsComplete(false)
   1.118 +    , mDidContentStart(false)
   1.119 +    , mNoContent(false)
   1.120 +    , mSentData(false)
   1.121 +    , mReceivedData(false)
   1.122 +    , mStatusEventPending(false)
   1.123 +    , mHasRequestBody(false)
   1.124 +    , mProxyConnectFailed(false)
   1.125 +    , mHttpResponseMatched(false)
   1.126 +    , mPreserveStream(false)
   1.127 +    , mDispatchedAsBlocking(false)
   1.128 +    , mResponseTimeoutEnabled(true)
   1.129 +    , mReportedStart(false)
   1.130 +    , mReportedResponseHeader(false)
   1.131 +    , mForTakeResponseHead(nullptr)
   1.132 +    , mResponseHeadTaken(false)
   1.133 +    , mSubmittedRatePacing(false)
   1.134 +    , mPassedRatePacing(false)
   1.135 +    , mSynchronousRatePaceRequest(false)
   1.136 +    , mCountRecv(0)
   1.137 +    , mCountSent(0)
   1.138 +    , mAppId(NECKO_NO_APP_ID)
   1.139 +{
   1.140 +    LOG(("Creating nsHttpTransaction @%p\n", this));
   1.141 +    gHttpHandler->GetMaxPipelineObjectSize(&mMaxPipelineObjectSize);
   1.142 +}
   1.143 +
   1.144 +nsHttpTransaction::~nsHttpTransaction()
   1.145 +{
   1.146 +    LOG(("Destroying nsHttpTransaction @%p\n", this));
   1.147 +
   1.148 +    if (mTokenBucketCancel) {
   1.149 +        mTokenBucketCancel->Cancel(NS_ERROR_ABORT);
   1.150 +        mTokenBucketCancel = nullptr;
   1.151 +    }
   1.152 +
   1.153 +    // Force the callbacks to be released right now
   1.154 +    mCallbacks = nullptr;
   1.155 +
   1.156 +    NS_IF_RELEASE(mConnection);
   1.157 +    NS_IF_RELEASE(mConnInfo);
   1.158 +
   1.159 +    delete mResponseHead;
   1.160 +    delete mForTakeResponseHead;
   1.161 +    delete mChunkedDecoder;
   1.162 +    ReleaseBlockingTransaction();
   1.163 +}
   1.164 +
   1.165 +nsHttpTransaction::Classifier
   1.166 +nsHttpTransaction::Classify()
   1.167 +{
   1.168 +    if (!(mCaps & NS_HTTP_ALLOW_PIPELINING))
   1.169 +        return (mClassification = CLASS_SOLO);
   1.170 +
   1.171 +    if (mRequestHead->PeekHeader(nsHttp::If_Modified_Since) ||
   1.172 +        mRequestHead->PeekHeader(nsHttp::If_None_Match))
   1.173 +        return (mClassification = CLASS_REVALIDATION);
   1.174 +
   1.175 +    const char *accept = mRequestHead->PeekHeader(nsHttp::Accept);
   1.176 +    if (accept && !PL_strncmp(accept, "image/", 6))
   1.177 +        return (mClassification = CLASS_IMAGE);
   1.178 +
   1.179 +    if (accept && !PL_strncmp(accept, "text/css", 8))
   1.180 +        return (mClassification = CLASS_SCRIPT);
   1.181 +
   1.182 +    mClassification = CLASS_GENERAL;
   1.183 +
   1.184 +    int32_t queryPos = mRequestHead->RequestURI().FindChar('?');
   1.185 +    if (queryPos == kNotFound) {
   1.186 +        if (StringEndsWith(mRequestHead->RequestURI(),
   1.187 +                           NS_LITERAL_CSTRING(".js")))
   1.188 +            mClassification = CLASS_SCRIPT;
   1.189 +    }
   1.190 +    else if (queryPos >= 3 &&
   1.191 +             Substring(mRequestHead->RequestURI(), queryPos - 3, 3).
   1.192 +             EqualsLiteral(".js")) {
   1.193 +        mClassification = CLASS_SCRIPT;
   1.194 +    }
   1.195 +
   1.196 +    return mClassification;
   1.197 +}
   1.198 +
   1.199 +nsresult
   1.200 +nsHttpTransaction::Init(uint32_t caps,
   1.201 +                        nsHttpConnectionInfo *cinfo,
   1.202 +                        nsHttpRequestHead *requestHead,
   1.203 +                        nsIInputStream *requestBody,
   1.204 +                        bool requestBodyHasHeaders,
   1.205 +                        nsIEventTarget *target,
   1.206 +                        nsIInterfaceRequestor *callbacks,
   1.207 +                        nsITransportEventSink *eventsink,
   1.208 +                        nsIAsyncInputStream **responseBody)
   1.209 +{
   1.210 +    MOZ_EVENT_TRACER_COMPOUND_NAME(static_cast<nsAHttpTransaction*>(this),
   1.211 +                                   requestHead->PeekHeader(nsHttp::Host),
   1.212 +                                   requestHead->RequestURI().BeginReading());
   1.213 +
   1.214 +    MOZ_EVENT_TRACER_WAIT(static_cast<nsAHttpTransaction*>(this),
   1.215 +                          "net::http::transaction");
   1.216 +    nsresult rv;
   1.217 +
   1.218 +    LOG(("nsHttpTransaction::Init [this=%p caps=%x]\n", this, caps));
   1.219 +
   1.220 +    MOZ_ASSERT(cinfo);
   1.221 +    MOZ_ASSERT(requestHead);
   1.222 +    MOZ_ASSERT(target);
   1.223 +
   1.224 +    mActivityDistributor = do_GetService(NS_HTTPACTIVITYDISTRIBUTOR_CONTRACTID, &rv);
   1.225 +    if (NS_FAILED(rv)) return rv;
   1.226 +
   1.227 +    bool activityDistributorActive;
   1.228 +    rv = mActivityDistributor->GetIsActive(&activityDistributorActive);
   1.229 +    if (NS_SUCCEEDED(rv) && activityDistributorActive) {
   1.230 +        // there are some observers registered at activity distributor, gather
   1.231 +        // nsISupports for the channel that called Init()
   1.232 +        mChannel = do_QueryInterface(eventsink);
   1.233 +        LOG(("nsHttpTransaction::Init() " \
   1.234 +             "mActivityDistributor is active " \
   1.235 +             "this=%p", this));
   1.236 +    } else {
   1.237 +        // there is no observer, so don't use it
   1.238 +        activityDistributorActive = false;
   1.239 +        mActivityDistributor = nullptr;
   1.240 +    }
   1.241 +
   1.242 +    nsCOMPtr<nsIChannel> channel = do_QueryInterface(eventsink);
   1.243 +    if (channel) {
   1.244 +        bool isInBrowser;
   1.245 +        NS_GetAppInfo(channel, &mAppId, &isInBrowser);
   1.246 +    }
   1.247 +
   1.248 +#ifdef MOZ_WIDGET_GONK
   1.249 +    if (mAppId != NECKO_NO_APP_ID) {
   1.250 +        nsCOMPtr<nsINetworkInterface> activeNetwork;
   1.251 +        GetActiveNetworkInterface(activeNetwork);
   1.252 +        mActiveNetwork =
   1.253 +            new nsMainThreadPtrHolder<nsINetworkInterface>(activeNetwork);
   1.254 +    }
   1.255 +#endif
   1.256 +
   1.257 +    nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal =
   1.258 +        do_QueryInterface(eventsink);
   1.259 +    if (httpChannelInternal) {
   1.260 +        rv = httpChannelInternal->GetResponseTimeoutEnabled(
   1.261 +            &mResponseTimeoutEnabled);
   1.262 +        if (NS_WARN_IF(NS_FAILED(rv))) {
   1.263 +            return rv;
   1.264 +        }
   1.265 +    }
   1.266 +
   1.267 +    // create transport event sink proxy. it coalesces all events if and only
   1.268 +    // if the activity observer is not active. when the observer is active
   1.269 +    // we need not to coalesce any events to get all expected notifications
   1.270 +    // of the transaction state, necessary for correct debugging and logging.
   1.271 +    rv = net_NewTransportEventSinkProxy(getter_AddRefs(mTransportSink),
   1.272 +                                        eventsink, target,
   1.273 +                                        !activityDistributorActive);
   1.274 +    if (NS_FAILED(rv)) return rv;
   1.275 +
   1.276 +    NS_ADDREF(mConnInfo = cinfo);
   1.277 +    mCallbacks = callbacks;
   1.278 +    mConsumerTarget = target;
   1.279 +    mCaps = caps;
   1.280 +
   1.281 +    if (requestHead->IsHead()) {
   1.282 +        mNoContent = true;
   1.283 +    }
   1.284 +
   1.285 +    // Make sure that there is "Content-Length: 0" header in the requestHead
   1.286 +    // in case of POST and PUT methods when there is no requestBody and
   1.287 +    // requestHead doesn't contain "Transfer-Encoding" header.
   1.288 +    //
   1.289 +    // RFC1945 section 7.2.2:
   1.290 +    //   HTTP/1.0 requests containing an entity body must include a valid
   1.291 +    //   Content-Length header field.
   1.292 +    //
   1.293 +    // RFC2616 section 4.4:
   1.294 +    //   For compatibility with HTTP/1.0 applications, HTTP/1.1 requests
   1.295 +    //   containing a message-body MUST include a valid Content-Length header
   1.296 +    //   field unless the server is known to be HTTP/1.1 compliant.
   1.297 +    if ((requestHead->IsPost() || requestHead->IsPut()) &&
   1.298 +        !requestBody && !requestHead->PeekHeader(nsHttp::Transfer_Encoding)) {
   1.299 +        requestHead->SetHeader(nsHttp::Content_Length, NS_LITERAL_CSTRING("0"));
   1.300 +    }
   1.301 +
   1.302 +    // grab a weak reference to the request head
   1.303 +    mRequestHead = requestHead;
   1.304 +
   1.305 +    // make sure we eliminate any proxy specific headers from
   1.306 +    // the request if we are using CONNECT
   1.307 +    bool pruneProxyHeaders = cinfo->UsingConnect();
   1.308 +
   1.309 +    mReqHeaderBuf.Truncate();
   1.310 +    requestHead->Flatten(mReqHeaderBuf, pruneProxyHeaders);
   1.311 +
   1.312 +#if defined(PR_LOGGING)
   1.313 +    if (LOG3_ENABLED()) {
   1.314 +        LOG3(("http request [\n"));
   1.315 +        LogHeaders(mReqHeaderBuf.get());
   1.316 +        LOG3(("]\n"));
   1.317 +    }
   1.318 +#endif
   1.319 +
   1.320 +    // If the request body does not include headers or if there is no request
   1.321 +    // body, then we must add the header/body separator manually.
   1.322 +    if (!requestBodyHasHeaders || !requestBody)
   1.323 +        mReqHeaderBuf.AppendLiteral("\r\n");
   1.324 +
   1.325 +    // report the request header
   1.326 +    if (mActivityDistributor)
   1.327 +        mActivityDistributor->ObserveActivity(
   1.328 +            mChannel,
   1.329 +            NS_HTTP_ACTIVITY_TYPE_HTTP_TRANSACTION,
   1.330 +            NS_HTTP_ACTIVITY_SUBTYPE_REQUEST_HEADER,
   1.331 +            PR_Now(), 0,
   1.332 +            mReqHeaderBuf);
   1.333 +
   1.334 +    // Create a string stream for the request header buf (the stream holds
   1.335 +    // a non-owning reference to the request header data, so we MUST keep
   1.336 +    // mReqHeaderBuf around).
   1.337 +    nsCOMPtr<nsIInputStream> headers;
   1.338 +    rv = NS_NewByteInputStream(getter_AddRefs(headers),
   1.339 +                               mReqHeaderBuf.get(),
   1.340 +                               mReqHeaderBuf.Length());
   1.341 +    if (NS_FAILED(rv)) return rv;
   1.342 +
   1.343 +    if (requestBody) {
   1.344 +        mHasRequestBody = true;
   1.345 +
   1.346 +        // wrap the headers and request body in a multiplexed input stream.
   1.347 +        nsCOMPtr<nsIMultiplexInputStream> multi =
   1.348 +            do_CreateInstance(kMultiplexInputStream, &rv);
   1.349 +        if (NS_FAILED(rv)) return rv;
   1.350 +
   1.351 +        rv = multi->AppendStream(headers);
   1.352 +        if (NS_FAILED(rv)) return rv;
   1.353 +
   1.354 +        rv = multi->AppendStream(requestBody);
   1.355 +        if (NS_FAILED(rv)) return rv;
   1.356 +
   1.357 +        // wrap the multiplexed input stream with a buffered input stream, so
   1.358 +        // that we write data in the largest chunks possible.  this is actually
   1.359 +        // necessary to workaround some common server bugs (see bug 137155).
   1.360 +        rv = NS_NewBufferedInputStream(getter_AddRefs(mRequestStream), multi,
   1.361 +                                       nsIOService::gDefaultSegmentSize);
   1.362 +        if (NS_FAILED(rv)) return rv;
   1.363 +    }
   1.364 +    else
   1.365 +        mRequestStream = headers;
   1.366 +
   1.367 +    rv = mRequestStream->Available(&mRequestSize);
   1.368 +    if (NS_FAILED(rv)) return rv;
   1.369 +
   1.370 +    // create pipe for response stream
   1.371 +    rv = NS_NewPipe2(getter_AddRefs(mPipeIn),
   1.372 +                     getter_AddRefs(mPipeOut),
   1.373 +                     true, true,
   1.374 +                     nsIOService::gDefaultSegmentSize,
   1.375 +                     nsIOService::gDefaultSegmentCount);
   1.376 +    if (NS_FAILED(rv)) return rv;
   1.377 +
   1.378 +    Classify();
   1.379 +
   1.380 +    NS_ADDREF(*responseBody = mPipeIn);
   1.381 +    return NS_OK;
   1.382 +}
   1.383 +
   1.384 +// This method should only be used on the socket thread
   1.385 +nsAHttpConnection *
   1.386 +nsHttpTransaction::Connection()
   1.387 +{
   1.388 +    return mConnection;
   1.389 +}
   1.390 +
   1.391 +already_AddRefed<nsAHttpConnection>
   1.392 +nsHttpTransaction::GetConnectionReference()
   1.393 +{
   1.394 +    MutexAutoLock lock(mLock);
   1.395 +    nsRefPtr<nsAHttpConnection> connection = mConnection;
   1.396 +    return connection.forget();
   1.397 +}
   1.398 +
   1.399 +nsHttpResponseHead *
   1.400 +nsHttpTransaction::TakeResponseHead()
   1.401 +{
   1.402 +    MOZ_ASSERT(!mResponseHeadTaken, "TakeResponseHead called 2x");
   1.403 +
   1.404 +    // Lock RestartInProgress() and TakeResponseHead() against main thread
   1.405 +    MutexAutoLock lock(*nsHttp::GetLock());
   1.406 +
   1.407 +    mResponseHeadTaken = true;
   1.408 +
   1.409 +    // Prefer mForTakeResponseHead over mResponseHead. It is always a complete
   1.410 +    // set of headers.
   1.411 +    nsHttpResponseHead *head;
   1.412 +    if (mForTakeResponseHead) {
   1.413 +        head = mForTakeResponseHead;
   1.414 +        mForTakeResponseHead = nullptr;
   1.415 +        return head;
   1.416 +    }
   1.417 +
   1.418 +    // Even in OnStartRequest() the headers won't be available if we were
   1.419 +    // canceled
   1.420 +    if (!mHaveAllHeaders) {
   1.421 +        NS_WARNING("response headers not available or incomplete");
   1.422 +        return nullptr;
   1.423 +    }
   1.424 +
   1.425 +    head = mResponseHead;
   1.426 +    mResponseHead = nullptr;
   1.427 +    return head;
   1.428 +}
   1.429 +
   1.430 +void
   1.431 +nsHttpTransaction::SetProxyConnectFailed()
   1.432 +{
   1.433 +    mProxyConnectFailed = true;
   1.434 +}
   1.435 +
   1.436 +nsHttpRequestHead *
   1.437 +nsHttpTransaction::RequestHead()
   1.438 +{
   1.439 +    return mRequestHead;
   1.440 +}
   1.441 +
   1.442 +uint32_t
   1.443 +nsHttpTransaction::Http1xTransactionCount()
   1.444 +{
   1.445 +  return 1;
   1.446 +}
   1.447 +
   1.448 +nsresult
   1.449 +nsHttpTransaction::TakeSubTransactions(
   1.450 +    nsTArray<nsRefPtr<nsAHttpTransaction> > &outTransactions)
   1.451 +{
   1.452 +    return NS_ERROR_NOT_IMPLEMENTED;
   1.453 +}
   1.454 +
   1.455 +//----------------------------------------------------------------------------
   1.456 +// nsHttpTransaction::nsAHttpTransaction
   1.457 +//----------------------------------------------------------------------------
   1.458 +
   1.459 +void
   1.460 +nsHttpTransaction::SetConnection(nsAHttpConnection *conn)
   1.461 +{
   1.462 +    {
   1.463 +        MutexAutoLock lock(mLock);
   1.464 +        NS_IF_RELEASE(mConnection);
   1.465 +        NS_IF_ADDREF(mConnection = conn);
   1.466 +    }
   1.467 +
   1.468 +    if (conn) {
   1.469 +        MOZ_EVENT_TRACER_EXEC(static_cast<nsAHttpTransaction*>(this),
   1.470 +                              "net::http::transaction");
   1.471 +    }
   1.472 +}
   1.473 +
   1.474 +void
   1.475 +nsHttpTransaction::GetSecurityCallbacks(nsIInterfaceRequestor **cb)
   1.476 +{
   1.477 +    MutexAutoLock lock(mLock);
   1.478 +    NS_IF_ADDREF(*cb = mCallbacks);
   1.479 +}
   1.480 +
   1.481 +void
   1.482 +nsHttpTransaction::SetSecurityCallbacks(nsIInterfaceRequestor* aCallbacks)
   1.483 +{
   1.484 +    {
   1.485 +        MutexAutoLock lock(mLock);
   1.486 +        mCallbacks = aCallbacks;
   1.487 +    }
   1.488 +
   1.489 +    if (gSocketTransportService) {
   1.490 +        nsRefPtr<UpdateSecurityCallbacks> event = new UpdateSecurityCallbacks(this, aCallbacks);
   1.491 +        gSocketTransportService->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL);
   1.492 +    }
   1.493 +}
   1.494 +
   1.495 +void
   1.496 +nsHttpTransaction::OnTransportStatus(nsITransport* transport,
   1.497 +                                     nsresult status, uint64_t progress)
   1.498 +{
   1.499 +    LOG(("nsHttpTransaction::OnSocketStatus [this=%p status=%x progress=%llu]\n",
   1.500 +        this, status, progress));
   1.501 +
   1.502 +    if (TimingEnabled()) {
   1.503 +        if (status == NS_NET_STATUS_RESOLVING_HOST) {
   1.504 +            mTimings.domainLookupStart = TimeStamp::Now();
   1.505 +        } else if (status == NS_NET_STATUS_RESOLVED_HOST) {
   1.506 +            mTimings.domainLookupEnd = TimeStamp::Now();
   1.507 +        } else if (status == NS_NET_STATUS_CONNECTING_TO) {
   1.508 +            mTimings.connectStart = TimeStamp::Now();
   1.509 +        } else if (status == NS_NET_STATUS_CONNECTED_TO) {
   1.510 +            mTimings.connectEnd = TimeStamp::Now();
   1.511 +        }
   1.512 +    }
   1.513 +
   1.514 +    if (!mTransportSink)
   1.515 +        return;
   1.516 +
   1.517 +    MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
   1.518 +
   1.519 +    // Need to do this before the STATUS_RECEIVING_FROM check below, to make
   1.520 +    // sure that the activity distributor gets told about all status events.
   1.521 +    if (mActivityDistributor) {
   1.522 +        // upon STATUS_WAITING_FOR; report request body sent
   1.523 +        if ((mHasRequestBody) &&
   1.524 +            (status == NS_NET_STATUS_WAITING_FOR))
   1.525 +            mActivityDistributor->ObserveActivity(
   1.526 +                mChannel,
   1.527 +                NS_HTTP_ACTIVITY_TYPE_HTTP_TRANSACTION,
   1.528 +                NS_HTTP_ACTIVITY_SUBTYPE_REQUEST_BODY_SENT,
   1.529 +                PR_Now(), 0, EmptyCString());
   1.530 +
   1.531 +        // report the status and progress
   1.532 +        if (!mRestartInProgressVerifier.IsDiscardingContent())
   1.533 +            mActivityDistributor->ObserveActivity(
   1.534 +                mChannel,
   1.535 +                NS_HTTP_ACTIVITY_TYPE_SOCKET_TRANSPORT,
   1.536 +                static_cast<uint32_t>(status),
   1.537 +                PR_Now(),
   1.538 +                progress,
   1.539 +                EmptyCString());
   1.540 +    }
   1.541 +
   1.542 +    // nsHttpChannel synthesizes progress events in OnDataAvailable
   1.543 +    if (status == NS_NET_STATUS_RECEIVING_FROM)
   1.544 +        return;
   1.545 +
   1.546 +    uint64_t progressMax;
   1.547 +
   1.548 +    if (status == NS_NET_STATUS_SENDING_TO) {
   1.549 +        // suppress progress when only writing request headers
   1.550 +        if (!mHasRequestBody)
   1.551 +            return;
   1.552 +
   1.553 +        nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mRequestStream);
   1.554 +        MOZ_ASSERT(seekable, "Request stream isn't seekable?!?");
   1.555 +
   1.556 +        int64_t prog = 0;
   1.557 +        seekable->Tell(&prog);
   1.558 +        progress = prog;
   1.559 +
   1.560 +        // when uploading, we include the request headers in the progress
   1.561 +        // notifications.
   1.562 +        progressMax = mRequestSize; // XXX mRequestSize is 32-bit!
   1.563 +    }
   1.564 +    else {
   1.565 +        progress = 0;
   1.566 +        progressMax = 0;
   1.567 +    }
   1.568 +
   1.569 +    mTransportSink->OnTransportStatus(transport, status, progress, progressMax);
   1.570 +}
   1.571 +
   1.572 +bool
   1.573 +nsHttpTransaction::IsDone()
   1.574 +{
   1.575 +    return mTransactionDone;
   1.576 +}
   1.577 +
   1.578 +nsresult
   1.579 +nsHttpTransaction::Status()
   1.580 +{
   1.581 +    return mStatus;
   1.582 +}
   1.583 +
   1.584 +uint32_t
   1.585 +nsHttpTransaction::Caps()
   1.586 +{
   1.587 +    return mCaps & ~mCapsToClear;
   1.588 +}
   1.589 +
   1.590 +void
   1.591 +nsHttpTransaction::SetDNSWasRefreshed()
   1.592 +{
   1.593 +    MOZ_ASSERT(NS_IsMainThread(), "SetDNSWasRefreshed on main thread only!");
   1.594 +    mCapsToClear |= NS_HTTP_REFRESH_DNS;
   1.595 +}
   1.596 +
   1.597 +uint64_t
   1.598 +nsHttpTransaction::Available()
   1.599 +{
   1.600 +    uint64_t size;
   1.601 +    if (NS_FAILED(mRequestStream->Available(&size)))
   1.602 +        size = 0;
   1.603 +    return size;
   1.604 +}
   1.605 +
   1.606 +NS_METHOD
   1.607 +nsHttpTransaction::ReadRequestSegment(nsIInputStream *stream,
   1.608 +                                      void *closure,
   1.609 +                                      const char *buf,
   1.610 +                                      uint32_t offset,
   1.611 +                                      uint32_t count,
   1.612 +                                      uint32_t *countRead)
   1.613 +{
   1.614 +    nsHttpTransaction *trans = (nsHttpTransaction *) closure;
   1.615 +    nsresult rv = trans->mReader->OnReadSegment(buf, count, countRead);
   1.616 +    if (NS_FAILED(rv)) return rv;
   1.617 +
   1.618 +    if (trans->TimingEnabled() && trans->mTimings.requestStart.IsNull()) {
   1.619 +        // First data we're sending -> this is requestStart
   1.620 +        trans->mTimings.requestStart = TimeStamp::Now();
   1.621 +    }
   1.622 +
   1.623 +    if (!trans->mSentData) {
   1.624 +        MOZ_EVENT_TRACER_MARK(static_cast<nsAHttpTransaction*>(trans),
   1.625 +                              "net::http::first-write");
   1.626 +    }
   1.627 +
   1.628 +    trans->CountSentBytes(*countRead);
   1.629 +    trans->mSentData = true;
   1.630 +    return NS_OK;
   1.631 +}
   1.632 +
   1.633 +nsresult
   1.634 +nsHttpTransaction::ReadSegments(nsAHttpSegmentReader *reader,
   1.635 +                                uint32_t count, uint32_t *countRead)
   1.636 +{
   1.637 +    MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
   1.638 +
   1.639 +    if (mTransactionDone) {
   1.640 +        *countRead = 0;
   1.641 +        return mStatus;
   1.642 +    }
   1.643 +
   1.644 +    if (!mConnected) {
   1.645 +        mConnected = true;
   1.646 +        mConnection->GetSecurityInfo(getter_AddRefs(mSecurityInfo));
   1.647 +    }
   1.648 +
   1.649 +    mReader = reader;
   1.650 +
   1.651 +    nsresult rv = mRequestStream->ReadSegments(ReadRequestSegment, this, count, countRead);
   1.652 +
   1.653 +    mReader = nullptr;
   1.654 +
   1.655 +    // if read would block then we need to AsyncWait on the request stream.
   1.656 +    // have callback occur on socket thread so we stay synchronized.
   1.657 +    if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
   1.658 +        nsCOMPtr<nsIAsyncInputStream> asyncIn =
   1.659 +                do_QueryInterface(mRequestStream);
   1.660 +        if (asyncIn) {
   1.661 +            nsCOMPtr<nsIEventTarget> target;
   1.662 +            gHttpHandler->GetSocketThreadTarget(getter_AddRefs(target));
   1.663 +            if (target)
   1.664 +                asyncIn->AsyncWait(this, 0, 0, target);
   1.665 +            else {
   1.666 +                NS_ERROR("no socket thread event target");
   1.667 +                rv = NS_ERROR_UNEXPECTED;
   1.668 +            }
   1.669 +        }
   1.670 +    }
   1.671 +
   1.672 +    return rv;
   1.673 +}
   1.674 +
   1.675 +NS_METHOD
   1.676 +nsHttpTransaction::WritePipeSegment(nsIOutputStream *stream,
   1.677 +                                    void *closure,
   1.678 +                                    char *buf,
   1.679 +                                    uint32_t offset,
   1.680 +                                    uint32_t count,
   1.681 +                                    uint32_t *countWritten)
   1.682 +{
   1.683 +    nsHttpTransaction *trans = (nsHttpTransaction *) closure;
   1.684 +
   1.685 +    if (trans->mTransactionDone)
   1.686 +        return NS_BASE_STREAM_CLOSED; // stop iterating
   1.687 +
   1.688 +    if (trans->TimingEnabled() && trans->mTimings.responseStart.IsNull()) {
   1.689 +        trans->mTimings.responseStart = TimeStamp::Now();
   1.690 +    }
   1.691 +
   1.692 +    nsresult rv;
   1.693 +    //
   1.694 +    // OK, now let the caller fill this segment with data.
   1.695 +    //
   1.696 +    rv = trans->mWriter->OnWriteSegment(buf, count, countWritten);
   1.697 +    if (NS_FAILED(rv)) return rv; // caller didn't want to write anything
   1.698 +
   1.699 +    if (!trans->mReceivedData) {
   1.700 +        MOZ_EVENT_TRACER_MARK(static_cast<nsAHttpTransaction*>(trans),
   1.701 +                              "net::http::first-read");
   1.702 +    }
   1.703 +
   1.704 +    MOZ_ASSERT(*countWritten > 0, "bad writer");
   1.705 +    trans->CountRecvBytes(*countWritten);
   1.706 +    trans->mReceivedData = true;
   1.707 +
   1.708 +    // Let the transaction "play" with the buffer.  It is free to modify
   1.709 +    // the contents of the buffer and/or modify countWritten.
   1.710 +    // - Bytes in HTTP headers don't count towards countWritten, so the input
   1.711 +    // side of pipe (aka nsHttpChannel's mTransactionPump) won't hit
   1.712 +    // OnInputStreamReady until all headers have been parsed.
   1.713 +    //
   1.714 +    rv = trans->ProcessData(buf, *countWritten, countWritten);
   1.715 +    if (NS_FAILED(rv))
   1.716 +        trans->Close(rv);
   1.717 +
   1.718 +    return rv; // failure code only stops WriteSegments; it is not propagated.
   1.719 +}
   1.720 +
   1.721 +nsresult
   1.722 +nsHttpTransaction::WriteSegments(nsAHttpSegmentWriter *writer,
   1.723 +                                 uint32_t count, uint32_t *countWritten)
   1.724 +{
   1.725 +    MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
   1.726 +
   1.727 +    if (mTransactionDone)
   1.728 +        return NS_SUCCEEDED(mStatus) ? NS_BASE_STREAM_CLOSED : mStatus;
   1.729 +
   1.730 +    mWriter = writer;
   1.731 +
   1.732 +    nsresult rv = mPipeOut->WriteSegments(WritePipeSegment, this, count, countWritten);
   1.733 +
   1.734 +    mWriter = nullptr;
   1.735 +
   1.736 +    // if pipe would block then we need to AsyncWait on it.  have callback
   1.737 +    // occur on socket thread so we stay synchronized.
   1.738 +    if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
   1.739 +        nsCOMPtr<nsIEventTarget> target;
   1.740 +        gHttpHandler->GetSocketThreadTarget(getter_AddRefs(target));
   1.741 +        if (target)
   1.742 +            mPipeOut->AsyncWait(this, 0, 0, target);
   1.743 +        else {
   1.744 +            NS_ERROR("no socket thread event target");
   1.745 +            rv = NS_ERROR_UNEXPECTED;
   1.746 +        }
   1.747 +    }
   1.748 +
   1.749 +    return rv;
   1.750 +}
   1.751 +
   1.752 +nsresult
   1.753 +nsHttpTransaction::SaveNetworkStats(bool enforce)
   1.754 +{
   1.755 +#ifdef MOZ_WIDGET_GONK
   1.756 +    // Check if active network and appid are valid.
   1.757 +    if (!mActiveNetwork || mAppId == NECKO_NO_APP_ID) {
   1.758 +        return NS_OK;
   1.759 +    }
   1.760 +
   1.761 +    if (mCountRecv <= 0 && mCountSent <= 0) {
   1.762 +        // There is no traffic, no need to save.
   1.763 +        return NS_OK;
   1.764 +    }
   1.765 +
   1.766 +    // If |enforce| is false, the traffic amount is saved
   1.767 +    // only when the total amount exceeds the predefined
   1.768 +    // threshold.
   1.769 +    uint64_t totalBytes = mCountRecv + mCountSent;
   1.770 +    if (!enforce && totalBytes < NETWORK_STATS_THRESHOLD) {
   1.771 +        return NS_OK;
   1.772 +    }
   1.773 +
   1.774 +    // Create the event to save the network statistics.
   1.775 +    // the event is then dispathed to the main thread.
   1.776 +    nsRefPtr<nsRunnable> event =
   1.777 +        new SaveNetworkStatsEvent(mAppId, mActiveNetwork,
   1.778 +                                  mCountRecv, mCountSent, false);
   1.779 +    NS_DispatchToMainThread(event);
   1.780 +
   1.781 +    // Reset the counters after saving.
   1.782 +    mCountSent = 0;
   1.783 +    mCountRecv = 0;
   1.784 +
   1.785 +    return NS_OK;
   1.786 +#else
   1.787 +    return NS_ERROR_NOT_IMPLEMENTED;
   1.788 +#endif
   1.789 +}
   1.790 +
   1.791 +void
   1.792 +nsHttpTransaction::Close(nsresult reason)
   1.793 +{
   1.794 +    LOG(("nsHttpTransaction::Close [this=%p reason=%x]\n", this, reason));
   1.795 +
   1.796 +    MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
   1.797 +
   1.798 +    if (mClosed) {
   1.799 +        LOG(("  already closed\n"));
   1.800 +        return;
   1.801 +    }
   1.802 +
   1.803 +    if (mTokenBucketCancel) {
   1.804 +        mTokenBucketCancel->Cancel(reason);
   1.805 +        mTokenBucketCancel = nullptr;
   1.806 +    }
   1.807 +
   1.808 +    if (mActivityDistributor) {
   1.809 +        // report the reponse is complete if not already reported
   1.810 +        if (!mResponseIsComplete)
   1.811 +            mActivityDistributor->ObserveActivity(
   1.812 +                mChannel,
   1.813 +                NS_HTTP_ACTIVITY_TYPE_HTTP_TRANSACTION,
   1.814 +                NS_HTTP_ACTIVITY_SUBTYPE_RESPONSE_COMPLETE,
   1.815 +                PR_Now(),
   1.816 +                static_cast<uint64_t>(mContentRead),
   1.817 +                EmptyCString());
   1.818 +
   1.819 +        // report that this transaction is closing
   1.820 +        mActivityDistributor->ObserveActivity(
   1.821 +            mChannel,
   1.822 +            NS_HTTP_ACTIVITY_TYPE_HTTP_TRANSACTION,
   1.823 +            NS_HTTP_ACTIVITY_SUBTYPE_TRANSACTION_CLOSE,
   1.824 +            PR_Now(), 0, EmptyCString());
   1.825 +    }
   1.826 +
   1.827 +    // we must no longer reference the connection!  find out if the
   1.828 +    // connection was being reused before letting it go.
   1.829 +    bool connReused = false;
   1.830 +    if (mConnection)
   1.831 +        connReused = mConnection->IsReused();
   1.832 +    mConnected = false;
   1.833 +
   1.834 +    //
   1.835 +    // if the connection was reset or closed before we wrote any part of the
   1.836 +    // request or if we wrote the request but didn't receive any part of the
   1.837 +    // response and the connection was being reused, then we can (and really
   1.838 +    // should) assume that we wrote to a stale connection and we must therefore
   1.839 +    // repeat the request over a new connection.
   1.840 +    //
   1.841 +    // NOTE: the conditions under which we will automatically retry the HTTP
   1.842 +    // request have to be carefully selected to avoid duplication of the
   1.843 +    // request from the point-of-view of the server.  such duplication could
   1.844 +    // have dire consequences including repeated purchases, etc.
   1.845 +    //
   1.846 +    // NOTE: because of the way SSL proxy CONNECT is implemented, it is
   1.847 +    // possible that the transaction may have received data without having
   1.848 +    // sent any data.  for this reason, mSendData == FALSE does not imply
   1.849 +    // mReceivedData == FALSE.  (see bug 203057 for more info.)
   1.850 +    //
   1.851 +    if (reason == NS_ERROR_NET_RESET || reason == NS_OK) {
   1.852 +
   1.853 +        // reallySentData is meant to separate the instances where data has
   1.854 +        // been sent by this transaction but buffered at a higher level while
   1.855 +        // a TLS session (perhaps via a tunnel) is setup.
   1.856 +        bool reallySentData =
   1.857 +            mSentData && (!mConnection || mConnection->BytesWritten());
   1.858 +
   1.859 +        if (!mReceivedData &&
   1.860 +            (!reallySentData || connReused || mPipelinePosition)) {
   1.861 +            // if restarting fails, then we must proceed to close the pipe,
   1.862 +            // which will notify the channel that the transaction failed.
   1.863 +
   1.864 +            if (mPipelinePosition) {
   1.865 +                gHttpHandler->ConnMgr()->PipelineFeedbackInfo(
   1.866 +                    mConnInfo, nsHttpConnectionMgr::RedCanceledPipeline,
   1.867 +                    nullptr, 0);
   1.868 +            }
   1.869 +            if (NS_SUCCEEDED(Restart()))
   1.870 +                return;
   1.871 +        }
   1.872 +        else if (!mResponseIsComplete && mPipelinePosition &&
   1.873 +                 reason == NS_ERROR_NET_RESET) {
   1.874 +            // due to unhandled rst on a pipeline - safe to
   1.875 +            // restart as only idempotent is found there
   1.876 +
   1.877 +            gHttpHandler->ConnMgr()->PipelineFeedbackInfo(
   1.878 +                mConnInfo, nsHttpConnectionMgr::RedCorruptedContent, nullptr, 0);
   1.879 +            if (NS_SUCCEEDED(RestartInProgress()))
   1.880 +                return;
   1.881 +        }
   1.882 +    }
   1.883 +
   1.884 +    bool relConn = true;
   1.885 +    if (NS_SUCCEEDED(reason)) {
   1.886 +        if (!mResponseIsComplete) {
   1.887 +            // The response has not been delimited with a high-confidence
   1.888 +            // algorithm like Content-Length or Chunked Encoding. We
   1.889 +            // need to use a strong framing mechanism to pipeline.
   1.890 +            gHttpHandler->ConnMgr()->PipelineFeedbackInfo(
   1.891 +                mConnInfo, nsHttpConnectionMgr::BadInsufficientFraming,
   1.892 +                nullptr, mClassification);
   1.893 +        }
   1.894 +        else if (mPipelinePosition) {
   1.895 +            // report this success as feedback
   1.896 +            gHttpHandler->ConnMgr()->PipelineFeedbackInfo(
   1.897 +                mConnInfo, nsHttpConnectionMgr::GoodCompletedOK,
   1.898 +                nullptr, mPipelinePosition);
   1.899 +        }
   1.900 +
   1.901 +        // the server has not sent the final \r\n terminating the header
   1.902 +        // section, and there may still be a header line unparsed.  let's make
   1.903 +        // sure we parse the remaining header line, and then hopefully, the
   1.904 +        // response will be usable (see bug 88792).
   1.905 +        if (!mHaveAllHeaders) {
   1.906 +            char data = '\n';
   1.907 +            uint32_t unused;
   1.908 +            ParseHead(&data, 1, &unused);
   1.909 +
   1.910 +            if (mResponseHead->Version() == NS_HTTP_VERSION_0_9) {
   1.911 +                // Reject 0 byte HTTP/0.9 Responses - bug 423506
   1.912 +                LOG(("nsHttpTransaction::Close %p 0 Byte 0.9 Response", this));
   1.913 +                reason = NS_ERROR_NET_RESET;
   1.914 +            }
   1.915 +        }
   1.916 +
   1.917 +        // honor the sticky connection flag...
   1.918 +        if (mCaps & NS_HTTP_STICKY_CONNECTION)
   1.919 +            relConn = false;
   1.920 +    }
   1.921 +
   1.922 +    // mTimings.responseEnd is normally recorded based on the end of a
   1.923 +    // HTTP delimiter such as chunked-encodings or content-length. However,
   1.924 +    // EOF or an error still require an end time be recorded.
   1.925 +    if (TimingEnabled() &&
   1.926 +        mTimings.responseEnd.IsNull() && !mTimings.responseStart.IsNull())
   1.927 +        mTimings.responseEnd = TimeStamp::Now();
   1.928 +
   1.929 +    if (relConn && mConnection) {
   1.930 +        MutexAutoLock lock(mLock);
   1.931 +        NS_RELEASE(mConnection);
   1.932 +    }
   1.933 +
   1.934 +    // save network statistics in the end of transaction
   1.935 +    SaveNetworkStats(true);
   1.936 +
   1.937 +    mStatus = reason;
   1.938 +    mTransactionDone = true; // forcibly flag the transaction as complete
   1.939 +    mClosed = true;
   1.940 +    ReleaseBlockingTransaction();
   1.941 +
   1.942 +    // release some resources that we no longer need
   1.943 +    mRequestStream = nullptr;
   1.944 +    mReqHeaderBuf.Truncate();
   1.945 +    mLineBuf.Truncate();
   1.946 +    if (mChunkedDecoder) {
   1.947 +        delete mChunkedDecoder;
   1.948 +        mChunkedDecoder = nullptr;
   1.949 +    }
   1.950 +
   1.951 +    // closing this pipe triggers the channel's OnStopRequest method.
   1.952 +    mPipeOut->CloseWithStatus(reason);
   1.953 +
   1.954 +    MOZ_EVENT_TRACER_DONE(static_cast<nsAHttpTransaction*>(this),
   1.955 +                          "net::http::transaction");
   1.956 +}
   1.957 +
   1.958 +nsresult
   1.959 +nsHttpTransaction::AddTransaction(nsAHttpTransaction *trans)
   1.960 +{
   1.961 +    return NS_ERROR_NOT_IMPLEMENTED;
   1.962 +}
   1.963 +
   1.964 +uint32_t
   1.965 +nsHttpTransaction::PipelineDepth()
   1.966 +{
   1.967 +    return IsDone() ? 0 : 1;
   1.968 +}
   1.969 +
   1.970 +nsresult
   1.971 +nsHttpTransaction::SetPipelinePosition(int32_t position)
   1.972 +{
   1.973 +    mPipelinePosition = position;
   1.974 +    return NS_OK;
   1.975 +}
   1.976 +
   1.977 +int32_t
   1.978 +nsHttpTransaction::PipelinePosition()
   1.979 +{
   1.980 +    return mPipelinePosition;
   1.981 +}
   1.982 +
   1.983 +bool // NOTE BASE CLASS
   1.984 +nsAHttpTransaction::ResponseTimeoutEnabled() const
   1.985 +{
   1.986 +    return false;
   1.987 +}
   1.988 +
   1.989 +PRIntervalTime // NOTE BASE CLASS
   1.990 +nsAHttpTransaction::ResponseTimeout()
   1.991 +{
   1.992 +    return gHttpHandler->ResponseTimeout();
   1.993 +}
   1.994 +
   1.995 +bool
   1.996 +nsHttpTransaction::ResponseTimeoutEnabled() const
   1.997 +{
   1.998 +    return mResponseTimeoutEnabled;
   1.999 +}
  1.1000 +
  1.1001 +//-----------------------------------------------------------------------------
  1.1002 +// nsHttpTransaction <private>
  1.1003 +//-----------------------------------------------------------------------------
  1.1004 +
  1.1005 +nsresult
  1.1006 +nsHttpTransaction::RestartInProgress()
  1.1007 +{
  1.1008 +    MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  1.1009 +
  1.1010 +    if ((mRestartCount + 1) >= gHttpHandler->MaxRequestAttempts()) {
  1.1011 +        LOG(("nsHttpTransaction::RestartInProgress() "
  1.1012 +             "reached max request attempts, failing transaction %p\n", this));
  1.1013 +        return NS_ERROR_NET_RESET;
  1.1014 +    }
  1.1015 +
  1.1016 +    // Lock RestartInProgress() and TakeResponseHead() against main thread
  1.1017 +    MutexAutoLock lock(*nsHttp::GetLock());
  1.1018 +
  1.1019 +    // Don't try and RestartInProgress() things that haven't gotten a response
  1.1020 +    // header yet. Those should be handled under the normal restart() path if
  1.1021 +    // they are eligible.
  1.1022 +    if (!mHaveAllHeaders)
  1.1023 +        return NS_ERROR_NET_RESET;
  1.1024 +
  1.1025 +    // don't try and restart 0.9 or non 200/Get HTTP/1
  1.1026 +    if (!mRestartInProgressVerifier.IsSetup())
  1.1027 +        return NS_ERROR_NET_RESET;
  1.1028 +
  1.1029 +    LOG(("Will restart transaction %p and skip first %lld bytes, "
  1.1030 +         "old Content-Length %lld",
  1.1031 +         this, mContentRead, mContentLength));
  1.1032 +
  1.1033 +    mRestartInProgressVerifier.SetAlreadyProcessed(
  1.1034 +        std::max(mRestartInProgressVerifier.AlreadyProcessed(), mContentRead));
  1.1035 +
  1.1036 +    if (!mResponseHeadTaken && !mForTakeResponseHead) {
  1.1037 +        // TakeResponseHeader() has not been called yet and this
  1.1038 +        // is the first restart. Store the resp headers exclusively
  1.1039 +        // for TakeResponseHead() which is called from the main thread and
  1.1040 +        // could happen at any time - so we can't continue to modify those
  1.1041 +        // headers (which restarting will effectively do)
  1.1042 +        mForTakeResponseHead = mResponseHead;
  1.1043 +        mResponseHead = nullptr;
  1.1044 +    }
  1.1045 +
  1.1046 +    if (mResponseHead) {
  1.1047 +        mResponseHead->Reset();
  1.1048 +    }
  1.1049 +
  1.1050 +    mContentRead = 0;
  1.1051 +    mContentLength = -1;
  1.1052 +    delete mChunkedDecoder;
  1.1053 +    mChunkedDecoder = nullptr;
  1.1054 +    mHaveStatusLine = false;
  1.1055 +    mHaveAllHeaders = false;
  1.1056 +    mHttpResponseMatched = false;
  1.1057 +    mResponseIsComplete = false;
  1.1058 +    mDidContentStart = false;
  1.1059 +    mNoContent = false;
  1.1060 +    mSentData = false;
  1.1061 +    mReceivedData = false;
  1.1062 +
  1.1063 +    return Restart();
  1.1064 +}
  1.1065 +
  1.1066 +nsresult
  1.1067 +nsHttpTransaction::Restart()
  1.1068 +{
  1.1069 +    MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  1.1070 +
  1.1071 +    // limit the number of restart attempts - bug 92224
  1.1072 +    if (++mRestartCount >= gHttpHandler->MaxRequestAttempts()) {
  1.1073 +        LOG(("reached max request attempts, failing transaction @%p\n", this));
  1.1074 +        return NS_ERROR_NET_RESET;
  1.1075 +    }
  1.1076 +
  1.1077 +    LOG(("restarting transaction @%p\n", this));
  1.1078 +
  1.1079 +    // rewind streams in case we already wrote out the request
  1.1080 +    nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mRequestStream);
  1.1081 +    if (seekable)
  1.1082 +        seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
  1.1083 +
  1.1084 +    // clear old connection state...
  1.1085 +    mSecurityInfo = 0;
  1.1086 +    if (mConnection) {
  1.1087 +        MutexAutoLock lock(mLock);
  1.1088 +        NS_RELEASE(mConnection);
  1.1089 +    }
  1.1090 +
  1.1091 +    // disable pipelining for the next attempt in case pipelining caused the
  1.1092 +    // reset.  this is being overly cautious since we don't know if pipelining
  1.1093 +    // was the problem here.
  1.1094 +    mCaps &= ~NS_HTTP_ALLOW_PIPELINING;
  1.1095 +    SetPipelinePosition(0);
  1.1096 +
  1.1097 +    return gHttpHandler->InitiateTransaction(this, mPriority);
  1.1098 +}
  1.1099 +
  1.1100 +char *
  1.1101 +nsHttpTransaction::LocateHttpStart(char *buf, uint32_t len,
  1.1102 +                                   bool aAllowPartialMatch)
  1.1103 +{
  1.1104 +    MOZ_ASSERT(!aAllowPartialMatch || mLineBuf.IsEmpty());
  1.1105 +
  1.1106 +    static const char HTTPHeader[] = "HTTP/1.";
  1.1107 +    static const uint32_t HTTPHeaderLen = sizeof(HTTPHeader) - 1;
  1.1108 +    static const char HTTP2Header[] = "HTTP/2.0";
  1.1109 +    static const uint32_t HTTP2HeaderLen = sizeof(HTTP2Header) - 1;
  1.1110 +    // ShoutCast ICY is treated as HTTP/1.0
  1.1111 +    static const char ICYHeader[] = "ICY ";
  1.1112 +    static const uint32_t ICYHeaderLen = sizeof(ICYHeader) - 1;
  1.1113 +
  1.1114 +    if (aAllowPartialMatch && (len < HTTPHeaderLen))
  1.1115 +        return (PL_strncasecmp(buf, HTTPHeader, len) == 0) ? buf : nullptr;
  1.1116 +
  1.1117 +    // mLineBuf can contain partial match from previous search
  1.1118 +    if (!mLineBuf.IsEmpty()) {
  1.1119 +        MOZ_ASSERT(mLineBuf.Length() < HTTPHeaderLen);
  1.1120 +        int32_t checkChars = std::min(len, HTTPHeaderLen - mLineBuf.Length());
  1.1121 +        if (PL_strncasecmp(buf, HTTPHeader + mLineBuf.Length(),
  1.1122 +                           checkChars) == 0) {
  1.1123 +            mLineBuf.Append(buf, checkChars);
  1.1124 +            if (mLineBuf.Length() == HTTPHeaderLen) {
  1.1125 +                // We've found whole HTTPHeader sequence. Return pointer at the
  1.1126 +                // end of matched sequence since it is stored in mLineBuf.
  1.1127 +                return (buf + checkChars);
  1.1128 +            }
  1.1129 +            // Response matches pattern but is still incomplete.
  1.1130 +            return 0;
  1.1131 +        }
  1.1132 +        // Previous partial match together with new data doesn't match the
  1.1133 +        // pattern. Start the search again.
  1.1134 +        mLineBuf.Truncate();
  1.1135 +    }
  1.1136 +
  1.1137 +    bool firstByte = true;
  1.1138 +    while (len > 0) {
  1.1139 +        if (PL_strncasecmp(buf, HTTPHeader, std::min<uint32_t>(len, HTTPHeaderLen)) == 0) {
  1.1140 +            if (len < HTTPHeaderLen) {
  1.1141 +                // partial HTTPHeader sequence found
  1.1142 +                // save partial match to mLineBuf
  1.1143 +                mLineBuf.Assign(buf, len);
  1.1144 +                return 0;
  1.1145 +            }
  1.1146 +
  1.1147 +            // whole HTTPHeader sequence found
  1.1148 +            return buf;
  1.1149 +        }
  1.1150 +
  1.1151 +        // At least "SmarterTools/2.0.3974.16813" generates nonsensical
  1.1152 +        // HTTP/2.0 responses to our HTTP/1 requests. Treat the minimal case of
  1.1153 +        // it as HTTP/1.1 to be compatible with old versions of ourselves and
  1.1154 +        // other browsers
  1.1155 +
  1.1156 +        if (firstByte && !mInvalidResponseBytesRead && len >= HTTP2HeaderLen &&
  1.1157 +            (PL_strncasecmp(buf, HTTP2Header, HTTP2HeaderLen) == 0)) {
  1.1158 +            LOG(("nsHttpTransaction:: Identified HTTP/2.0 treating as 1.x\n"));
  1.1159 +            return buf;
  1.1160 +        }
  1.1161 +
  1.1162 +        // Treat ICY (AOL/Nullsoft ShoutCast) non-standard header in same fashion
  1.1163 +        // as HTTP/2.0 is treated above. This will allow "ICY " to be interpretted
  1.1164 +        // as HTTP/1.0 in nsHttpResponseHead::ParseVersion
  1.1165 +
  1.1166 +        if (firstByte && !mInvalidResponseBytesRead && len >= ICYHeaderLen &&
  1.1167 +            (PL_strncasecmp(buf, ICYHeader, ICYHeaderLen) == 0)) {
  1.1168 +            LOG(("nsHttpTransaction:: Identified ICY treating as HTTP/1.0\n"));
  1.1169 +            return buf;
  1.1170 +        }
  1.1171 +
  1.1172 +        if (!nsCRT::IsAsciiSpace(*buf))
  1.1173 +            firstByte = false;
  1.1174 +        buf++;
  1.1175 +        len--;
  1.1176 +    }
  1.1177 +    return 0;
  1.1178 +}
  1.1179 +
  1.1180 +nsresult
  1.1181 +nsHttpTransaction::ParseLine(char *line)
  1.1182 +{
  1.1183 +    LOG(("nsHttpTransaction::ParseLine [%s]\n", line));
  1.1184 +    nsresult rv = NS_OK;
  1.1185 +
  1.1186 +    if (!mHaveStatusLine) {
  1.1187 +        mResponseHead->ParseStatusLine(line);
  1.1188 +        mHaveStatusLine = true;
  1.1189 +        // XXX this should probably never happen
  1.1190 +        if (mResponseHead->Version() == NS_HTTP_VERSION_0_9)
  1.1191 +            mHaveAllHeaders = true;
  1.1192 +    }
  1.1193 +    else {
  1.1194 +        rv = mResponseHead->ParseHeaderLine(line);
  1.1195 +    }
  1.1196 +    return rv;
  1.1197 +}
  1.1198 +
  1.1199 +nsresult
  1.1200 +nsHttpTransaction::ParseLineSegment(char *segment, uint32_t len)
  1.1201 +{
  1.1202 +    NS_PRECONDITION(!mHaveAllHeaders, "already have all headers");
  1.1203 +
  1.1204 +    if (!mLineBuf.IsEmpty() && mLineBuf.Last() == '\n') {
  1.1205 +        // trim off the new line char, and if this segment is
  1.1206 +        // not a continuation of the previous or if we haven't
  1.1207 +        // parsed the status line yet, then parse the contents
  1.1208 +        // of mLineBuf.
  1.1209 +        mLineBuf.Truncate(mLineBuf.Length() - 1);
  1.1210 +        if (!mHaveStatusLine || (*segment != ' ' && *segment != '\t')) {
  1.1211 +            nsresult rv = ParseLine(mLineBuf.BeginWriting());
  1.1212 +            mLineBuf.Truncate();
  1.1213 +            if (NS_FAILED(rv)) {
  1.1214 +                gHttpHandler->ConnMgr()->PipelineFeedbackInfo(
  1.1215 +                    mConnInfo, nsHttpConnectionMgr::RedCorruptedContent,
  1.1216 +                    nullptr, 0);
  1.1217 +                return rv;
  1.1218 +            }
  1.1219 +        }
  1.1220 +    }
  1.1221 +
  1.1222 +    // append segment to mLineBuf...
  1.1223 +    mLineBuf.Append(segment, len);
  1.1224 +
  1.1225 +    // a line buf with only a new line char signifies the end of headers.
  1.1226 +    if (mLineBuf.First() == '\n') {
  1.1227 +        mLineBuf.Truncate();
  1.1228 +        // discard this response if it is a 100 continue or other 1xx status.
  1.1229 +        uint16_t status = mResponseHead->Status();
  1.1230 +        if ((status != 101) && (status / 100 == 1)) {
  1.1231 +            LOG(("ignoring 1xx response\n"));
  1.1232 +            mHaveStatusLine = false;
  1.1233 +            mHttpResponseMatched = false;
  1.1234 +            mConnection->SetLastTransactionExpectedNoContent(true);
  1.1235 +            mResponseHead->Reset();
  1.1236 +            return NS_OK;
  1.1237 +        }
  1.1238 +        mHaveAllHeaders = true;
  1.1239 +    }
  1.1240 +    return NS_OK;
  1.1241 +}
  1.1242 +
  1.1243 +nsresult
  1.1244 +nsHttpTransaction::ParseHead(char *buf,
  1.1245 +                             uint32_t count,
  1.1246 +                             uint32_t *countRead)
  1.1247 +{
  1.1248 +    nsresult rv;
  1.1249 +    uint32_t len;
  1.1250 +    char *eol;
  1.1251 +
  1.1252 +    LOG(("nsHttpTransaction::ParseHead [count=%u]\n", count));
  1.1253 +
  1.1254 +    *countRead = 0;
  1.1255 +
  1.1256 +    NS_PRECONDITION(!mHaveAllHeaders, "oops");
  1.1257 +
  1.1258 +    // allocate the response head object if necessary
  1.1259 +    if (!mResponseHead) {
  1.1260 +        mResponseHead = new nsHttpResponseHead();
  1.1261 +        if (!mResponseHead)
  1.1262 +            return NS_ERROR_OUT_OF_MEMORY;
  1.1263 +
  1.1264 +        // report that we have a least some of the response
  1.1265 +        if (mActivityDistributor && !mReportedStart) {
  1.1266 +            mReportedStart = true;
  1.1267 +            mActivityDistributor->ObserveActivity(
  1.1268 +                mChannel,
  1.1269 +                NS_HTTP_ACTIVITY_TYPE_HTTP_TRANSACTION,
  1.1270 +                NS_HTTP_ACTIVITY_SUBTYPE_RESPONSE_START,
  1.1271 +                PR_Now(), 0, EmptyCString());
  1.1272 +        }
  1.1273 +    }
  1.1274 +
  1.1275 +    if (!mHttpResponseMatched) {
  1.1276 +        // Normally we insist on seeing HTTP/1.x in the first few bytes,
  1.1277 +        // but if we are on a persistent connection and the previous transaction
  1.1278 +        // was not supposed to have any content then we need to be prepared
  1.1279 +        // to skip over a response body that the server may have sent even
  1.1280 +        // though it wasn't allowed.
  1.1281 +        if (!mConnection || !mConnection->LastTransactionExpectedNoContent()) {
  1.1282 +            // tolerate only minor junk before the status line
  1.1283 +            mHttpResponseMatched = true;
  1.1284 +            char *p = LocateHttpStart(buf, std::min<uint32_t>(count, 11), true);
  1.1285 +            if (!p) {
  1.1286 +                // Treat any 0.9 style response of a put as a failure.
  1.1287 +                if (mRequestHead->IsPut())
  1.1288 +                    return NS_ERROR_ABORT;
  1.1289 +
  1.1290 +                mResponseHead->ParseStatusLine("");
  1.1291 +                mHaveStatusLine = true;
  1.1292 +                mHaveAllHeaders = true;
  1.1293 +                return NS_OK;
  1.1294 +            }
  1.1295 +            if (p > buf) {
  1.1296 +                // skip over the junk
  1.1297 +                mInvalidResponseBytesRead += p - buf;
  1.1298 +                *countRead = p - buf;
  1.1299 +                buf = p;
  1.1300 +            }
  1.1301 +        }
  1.1302 +        else {
  1.1303 +            char *p = LocateHttpStart(buf, count, false);
  1.1304 +            if (p) {
  1.1305 +                mInvalidResponseBytesRead += p - buf;
  1.1306 +                *countRead = p - buf;
  1.1307 +                buf = p;
  1.1308 +                mHttpResponseMatched = true;
  1.1309 +            } else {
  1.1310 +                mInvalidResponseBytesRead += count;
  1.1311 +                *countRead = count;
  1.1312 +                if (mInvalidResponseBytesRead > MAX_INVALID_RESPONSE_BODY_SIZE) {
  1.1313 +                    LOG(("nsHttpTransaction::ParseHead() "
  1.1314 +                         "Cannot find Response Header\n"));
  1.1315 +                    // cannot go back and call this 0.9 anymore as we
  1.1316 +                    // have thrown away a lot of the leading junk
  1.1317 +                    return NS_ERROR_ABORT;
  1.1318 +                }
  1.1319 +                return NS_OK;
  1.1320 +            }
  1.1321 +        }
  1.1322 +    }
  1.1323 +    // otherwise we can assume that we don't have a HTTP/0.9 response.
  1.1324 +
  1.1325 +    MOZ_ASSERT (mHttpResponseMatched);
  1.1326 +    while ((eol = static_cast<char *>(memchr(buf, '\n', count - *countRead))) != nullptr) {
  1.1327 +        // found line in range [buf:eol]
  1.1328 +        len = eol - buf + 1;
  1.1329 +
  1.1330 +        *countRead += len;
  1.1331 +
  1.1332 +        // actually, the line is in the range [buf:eol-1]
  1.1333 +        if ((eol > buf) && (*(eol-1) == '\r'))
  1.1334 +            len--;
  1.1335 +
  1.1336 +        buf[len-1] = '\n';
  1.1337 +        rv = ParseLineSegment(buf, len);
  1.1338 +        if (NS_FAILED(rv))
  1.1339 +            return rv;
  1.1340 +
  1.1341 +        if (mHaveAllHeaders)
  1.1342 +            return NS_OK;
  1.1343 +
  1.1344 +        // skip over line
  1.1345 +        buf = eol + 1;
  1.1346 +
  1.1347 +        if (!mHttpResponseMatched) {
  1.1348 +            // a 100 class response has caused us to throw away that set of
  1.1349 +            // response headers and look for the next response
  1.1350 +            return NS_ERROR_NET_INTERRUPT;
  1.1351 +        }
  1.1352 +    }
  1.1353 +
  1.1354 +    // do something about a partial header line
  1.1355 +    if (!mHaveAllHeaders && (len = count - *countRead)) {
  1.1356 +        *countRead = count;
  1.1357 +        // ignore a trailing carriage return, and don't bother calling
  1.1358 +        // ParseLineSegment if buf only contains a carriage return.
  1.1359 +        if ((buf[len-1] == '\r') && (--len == 0))
  1.1360 +            return NS_OK;
  1.1361 +        rv = ParseLineSegment(buf, len);
  1.1362 +        if (NS_FAILED(rv))
  1.1363 +            return rv;
  1.1364 +    }
  1.1365 +    return NS_OK;
  1.1366 +}
  1.1367 +
  1.1368 +// called on the socket thread
  1.1369 +nsresult
  1.1370 +nsHttpTransaction::HandleContentStart()
  1.1371 +{
  1.1372 +    LOG(("nsHttpTransaction::HandleContentStart [this=%p]\n", this));
  1.1373 +
  1.1374 +    if (mResponseHead) {
  1.1375 +#if defined(PR_LOGGING)
  1.1376 +        if (LOG3_ENABLED()) {
  1.1377 +            LOG3(("http response [\n"));
  1.1378 +            nsAutoCString headers;
  1.1379 +            mResponseHead->Flatten(headers, false);
  1.1380 +            LogHeaders(headers.get());
  1.1381 +            LOG3(("]\n"));
  1.1382 +        }
  1.1383 +#endif
  1.1384 +        // Save http version, mResponseHead isn't available anymore after
  1.1385 +        // TakeResponseHead() is called
  1.1386 +        mHttpVersion = mResponseHead->Version();
  1.1387 +
  1.1388 +        // notify the connection, give it a chance to cause a reset.
  1.1389 +        bool reset = false;
  1.1390 +        if (!mRestartInProgressVerifier.IsSetup())
  1.1391 +            mConnection->OnHeadersAvailable(this, mRequestHead, mResponseHead, &reset);
  1.1392 +
  1.1393 +        // looks like we should ignore this response, resetting...
  1.1394 +        if (reset) {
  1.1395 +            LOG(("resetting transaction's response head\n"));
  1.1396 +            mHaveAllHeaders = false;
  1.1397 +            mHaveStatusLine = false;
  1.1398 +            mReceivedData = false;
  1.1399 +            mSentData = false;
  1.1400 +            mHttpResponseMatched = false;
  1.1401 +            mResponseHead->Reset();
  1.1402 +            // wait to be called again...
  1.1403 +            return NS_OK;
  1.1404 +        }
  1.1405 +
  1.1406 +        // check if this is a no-content response
  1.1407 +        switch (mResponseHead->Status()) {
  1.1408 +        case 101:
  1.1409 +            mPreserveStream = true;    // fall through to other no content
  1.1410 +        case 204:
  1.1411 +        case 205:
  1.1412 +        case 304:
  1.1413 +            mNoContent = true;
  1.1414 +            LOG(("this response should not contain a body.\n"));
  1.1415 +            break;
  1.1416 +        }
  1.1417 +
  1.1418 +        if (mResponseHead->Status() == 200 &&
  1.1419 +            mConnection->IsProxyConnectInProgress()) {
  1.1420 +            // successful CONNECTs do not have response bodies
  1.1421 +            mNoContent = true;
  1.1422 +        }
  1.1423 +        mConnection->SetLastTransactionExpectedNoContent(mNoContent);
  1.1424 +        if (mInvalidResponseBytesRead)
  1.1425 +            gHttpHandler->ConnMgr()->PipelineFeedbackInfo(
  1.1426 +                mConnInfo, nsHttpConnectionMgr::BadInsufficientFraming,
  1.1427 +                nullptr, mClassification);
  1.1428 +
  1.1429 +        if (mNoContent)
  1.1430 +            mContentLength = 0;
  1.1431 +        else {
  1.1432 +            // grab the content-length from the response headers
  1.1433 +            mContentLength = mResponseHead->ContentLength();
  1.1434 +
  1.1435 +            if ((mClassification != CLASS_SOLO) &&
  1.1436 +                (mContentLength > mMaxPipelineObjectSize))
  1.1437 +                CancelPipeline(nsHttpConnectionMgr::BadUnexpectedLarge);
  1.1438 +
  1.1439 +            // handle chunked encoding here, so we'll know immediately when
  1.1440 +            // we're done with the socket.  please note that _all_ other
  1.1441 +            // decoding is done when the channel receives the content data
  1.1442 +            // so as not to block the socket transport thread too much.
  1.1443 +            // ignore chunked responses from HTTP/1.0 servers and proxies.
  1.1444 +            if (mResponseHead->Version() >= NS_HTTP_VERSION_1_1 &&
  1.1445 +                mResponseHead->HasHeaderValue(nsHttp::Transfer_Encoding, "chunked")) {
  1.1446 +                // we only support the "chunked" transfer encoding right now.
  1.1447 +                mChunkedDecoder = new nsHttpChunkedDecoder();
  1.1448 +                if (!mChunkedDecoder)
  1.1449 +                    return NS_ERROR_OUT_OF_MEMORY;
  1.1450 +                LOG(("chunked decoder created\n"));
  1.1451 +                // Ignore server specified Content-Length.
  1.1452 +                mContentLength = -1;
  1.1453 +            }
  1.1454 +#if defined(PR_LOGGING)
  1.1455 +            else if (mContentLength == int64_t(-1))
  1.1456 +                LOG(("waiting for the server to close the connection.\n"));
  1.1457 +#endif
  1.1458 +        }
  1.1459 +        if (mRestartInProgressVerifier.IsSetup() &&
  1.1460 +            !mRestartInProgressVerifier.Verify(mContentLength, mResponseHead)) {
  1.1461 +            LOG(("Restart in progress subsequent transaction failed to match"));
  1.1462 +            return NS_ERROR_ABORT;
  1.1463 +        }
  1.1464 +    }
  1.1465 +
  1.1466 +    mDidContentStart = true;
  1.1467 +
  1.1468 +    // The verifier only initializes itself once (from the first iteration of
  1.1469 +    // a transaction that gets far enough to have response headers)
  1.1470 +    if (mRequestHead->IsGet())
  1.1471 +        mRestartInProgressVerifier.Set(mContentLength, mResponseHead);
  1.1472 +
  1.1473 +    return NS_OK;
  1.1474 +}
  1.1475 +
  1.1476 +// called on the socket thread
  1.1477 +nsresult
  1.1478 +nsHttpTransaction::HandleContent(char *buf,
  1.1479 +                                 uint32_t count,
  1.1480 +                                 uint32_t *contentRead,
  1.1481 +                                 uint32_t *contentRemaining)
  1.1482 +{
  1.1483 +    nsresult rv;
  1.1484 +
  1.1485 +    LOG(("nsHttpTransaction::HandleContent [this=%p count=%u]\n", this, count));
  1.1486 +
  1.1487 +    *contentRead = 0;
  1.1488 +    *contentRemaining = 0;
  1.1489 +
  1.1490 +    MOZ_ASSERT(mConnection);
  1.1491 +
  1.1492 +    if (!mDidContentStart) {
  1.1493 +        rv = HandleContentStart();
  1.1494 +        if (NS_FAILED(rv)) return rv;
  1.1495 +        // Do not write content to the pipe if we haven't started streaming yet
  1.1496 +        if (!mDidContentStart)
  1.1497 +            return NS_OK;
  1.1498 +    }
  1.1499 +
  1.1500 +    if (mChunkedDecoder) {
  1.1501 +        // give the buf over to the chunked decoder so it can reformat the
  1.1502 +        // data and tell us how much is really there.
  1.1503 +        rv = mChunkedDecoder->HandleChunkedContent(buf, count, contentRead, contentRemaining);
  1.1504 +        if (NS_FAILED(rv)) return rv;
  1.1505 +    }
  1.1506 +    else if (mContentLength >= int64_t(0)) {
  1.1507 +        // HTTP/1.0 servers have been known to send erroneous Content-Length
  1.1508 +        // headers. So, unless the connection is persistent, we must make
  1.1509 +        // allowances for a possibly invalid Content-Length header. Thus, if
  1.1510 +        // NOT persistent, we simply accept everything in |buf|.
  1.1511 +        if (mConnection->IsPersistent() || mPreserveStream ||
  1.1512 +            mHttpVersion >= NS_HTTP_VERSION_1_1) {
  1.1513 +            int64_t remaining = mContentLength - mContentRead;
  1.1514 +            *contentRead = uint32_t(std::min<int64_t>(count, remaining));
  1.1515 +            *contentRemaining = count - *contentRead;
  1.1516 +        }
  1.1517 +        else {
  1.1518 +            *contentRead = count;
  1.1519 +            // mContentLength might need to be increased...
  1.1520 +            int64_t position = mContentRead + int64_t(count);
  1.1521 +            if (position > mContentLength) {
  1.1522 +                mContentLength = position;
  1.1523 +                //mResponseHead->SetContentLength(mContentLength);
  1.1524 +            }
  1.1525 +        }
  1.1526 +    }
  1.1527 +    else {
  1.1528 +        // when we are just waiting for the server to close the connection...
  1.1529 +        // (no explicit content-length given)
  1.1530 +        *contentRead = count;
  1.1531 +    }
  1.1532 +
  1.1533 +    int64_t toReadBeforeRestart =
  1.1534 +        mRestartInProgressVerifier.ToReadBeforeRestart();
  1.1535 +
  1.1536 +    if (toReadBeforeRestart && *contentRead) {
  1.1537 +        uint32_t ignore =
  1.1538 +            static_cast<uint32_t>(std::min<int64_t>(toReadBeforeRestart, UINT32_MAX));
  1.1539 +        ignore = std::min(*contentRead, ignore);
  1.1540 +        LOG(("Due To Restart ignoring %d of remaining %ld",
  1.1541 +             ignore, toReadBeforeRestart));
  1.1542 +        *contentRead -= ignore;
  1.1543 +        mContentRead += ignore;
  1.1544 +        mRestartInProgressVerifier.HaveReadBeforeRestart(ignore);
  1.1545 +        memmove(buf, buf + ignore, *contentRead + *contentRemaining);
  1.1546 +    }
  1.1547 +
  1.1548 +    if (*contentRead) {
  1.1549 +        // update count of content bytes read and report progress...
  1.1550 +        mContentRead += *contentRead;
  1.1551 +        /* when uncommenting, take care of 64-bit integers w/ std::max...
  1.1552 +        if (mProgressSink)
  1.1553 +            mProgressSink->OnProgress(nullptr, nullptr, mContentRead, std::max(0, mContentLength));
  1.1554 +        */
  1.1555 +    }
  1.1556 +
  1.1557 +    LOG(("nsHttpTransaction::HandleContent [this=%p count=%u read=%u mContentRead=%lld mContentLength=%lld]\n",
  1.1558 +        this, count, *contentRead, mContentRead, mContentLength));
  1.1559 +
  1.1560 +    // Check the size of chunked responses. If we exceed the max pipeline size
  1.1561 +    // for this response reschedule the pipeline
  1.1562 +    if ((mClassification != CLASS_SOLO) &&
  1.1563 +        mChunkedDecoder &&
  1.1564 +        ((mContentRead + mChunkedDecoder->GetChunkRemaining()) >
  1.1565 +         mMaxPipelineObjectSize)) {
  1.1566 +        CancelPipeline(nsHttpConnectionMgr::BadUnexpectedLarge);
  1.1567 +    }
  1.1568 +
  1.1569 +    // check for end-of-file
  1.1570 +    if ((mContentRead == mContentLength) ||
  1.1571 +        (mChunkedDecoder && mChunkedDecoder->ReachedEOF())) {
  1.1572 +        // the transaction is done with a complete response.
  1.1573 +        mTransactionDone = true;
  1.1574 +        mResponseIsComplete = true;
  1.1575 +        ReleaseBlockingTransaction();
  1.1576 +
  1.1577 +        if (TimingEnabled())
  1.1578 +            mTimings.responseEnd = TimeStamp::Now();
  1.1579 +
  1.1580 +        // report the entire response has arrived
  1.1581 +        if (mActivityDistributor)
  1.1582 +            mActivityDistributor->ObserveActivity(
  1.1583 +                mChannel,
  1.1584 +                NS_HTTP_ACTIVITY_TYPE_HTTP_TRANSACTION,
  1.1585 +                NS_HTTP_ACTIVITY_SUBTYPE_RESPONSE_COMPLETE,
  1.1586 +                PR_Now(),
  1.1587 +                static_cast<uint64_t>(mContentRead),
  1.1588 +                EmptyCString());
  1.1589 +    }
  1.1590 +
  1.1591 +    return NS_OK;
  1.1592 +}
  1.1593 +
  1.1594 +nsresult
  1.1595 +nsHttpTransaction::ProcessData(char *buf, uint32_t count, uint32_t *countRead)
  1.1596 +{
  1.1597 +    nsresult rv;
  1.1598 +
  1.1599 +    LOG(("nsHttpTransaction::ProcessData [this=%p count=%u]\n", this, count));
  1.1600 +
  1.1601 +    *countRead = 0;
  1.1602 +
  1.1603 +    // we may not have read all of the headers yet...
  1.1604 +    if (!mHaveAllHeaders) {
  1.1605 +        uint32_t bytesConsumed = 0;
  1.1606 +
  1.1607 +        do {
  1.1608 +            uint32_t localBytesConsumed = 0;
  1.1609 +            char *localBuf = buf + bytesConsumed;
  1.1610 +            uint32_t localCount = count - bytesConsumed;
  1.1611 +
  1.1612 +            rv = ParseHead(localBuf, localCount, &localBytesConsumed);
  1.1613 +            if (NS_FAILED(rv) && rv != NS_ERROR_NET_INTERRUPT)
  1.1614 +                return rv;
  1.1615 +            bytesConsumed += localBytesConsumed;
  1.1616 +        } while (rv == NS_ERROR_NET_INTERRUPT);
  1.1617 +
  1.1618 +        count -= bytesConsumed;
  1.1619 +
  1.1620 +        // if buf has some content in it, shift bytes to top of buf.
  1.1621 +        if (count && bytesConsumed)
  1.1622 +            memmove(buf, buf + bytesConsumed, count);
  1.1623 +
  1.1624 +        // report the completed response header
  1.1625 +        if (mActivityDistributor && mResponseHead && mHaveAllHeaders &&
  1.1626 +            !mReportedResponseHeader) {
  1.1627 +            mReportedResponseHeader = true;
  1.1628 +            nsAutoCString completeResponseHeaders;
  1.1629 +            mResponseHead->Flatten(completeResponseHeaders, false);
  1.1630 +            completeResponseHeaders.AppendLiteral("\r\n");
  1.1631 +            mActivityDistributor->ObserveActivity(
  1.1632 +                mChannel,
  1.1633 +                NS_HTTP_ACTIVITY_TYPE_HTTP_TRANSACTION,
  1.1634 +                NS_HTTP_ACTIVITY_SUBTYPE_RESPONSE_HEADER,
  1.1635 +                PR_Now(), 0,
  1.1636 +                completeResponseHeaders);
  1.1637 +        }
  1.1638 +    }
  1.1639 +
  1.1640 +    // even though count may be 0, we still want to call HandleContent
  1.1641 +    // so it can complete the transaction if this is a "no-content" response.
  1.1642 +    if (mHaveAllHeaders) {
  1.1643 +        uint32_t countRemaining = 0;
  1.1644 +        //
  1.1645 +        // buf layout:
  1.1646 +        //
  1.1647 +        // +--------------------------------------+----------------+-----+
  1.1648 +        // |              countRead               | countRemaining |     |
  1.1649 +        // +--------------------------------------+----------------+-----+
  1.1650 +        //
  1.1651 +        // count          : bytes read from the socket
  1.1652 +        // countRead      : bytes corresponding to this transaction
  1.1653 +        // countRemaining : bytes corresponding to next pipelined transaction
  1.1654 +        //
  1.1655 +        // NOTE:
  1.1656 +        // count > countRead + countRemaining <==> chunked transfer encoding
  1.1657 +        //
  1.1658 +        rv = HandleContent(buf, count, countRead, &countRemaining);
  1.1659 +        if (NS_FAILED(rv)) return rv;
  1.1660 +        // we may have read more than our share, in which case we must give
  1.1661 +        // the excess bytes back to the connection
  1.1662 +        if (mResponseIsComplete && countRemaining) {
  1.1663 +            MOZ_ASSERT(mConnection);
  1.1664 +            mConnection->PushBack(buf + *countRead, countRemaining);
  1.1665 +        }
  1.1666 +    }
  1.1667 +
  1.1668 +    return NS_OK;
  1.1669 +}
  1.1670 +
  1.1671 +void
  1.1672 +nsHttpTransaction::CancelPipeline(uint32_t reason)
  1.1673 +{
  1.1674 +    // reason is casted through a uint to avoid compiler header deps
  1.1675 +    gHttpHandler->ConnMgr()->PipelineFeedbackInfo(
  1.1676 +        mConnInfo,
  1.1677 +        static_cast<nsHttpConnectionMgr::PipelineFeedbackInfoType>(reason),
  1.1678 +        nullptr, mClassification);
  1.1679 +
  1.1680 +    mConnection->CancelPipeline(NS_ERROR_ABORT);
  1.1681 +
  1.1682 +    // Avoid pipelining this transaction on restart by classifying it as solo.
  1.1683 +    // This also prevents BadUnexpectedLarge from being reported more
  1.1684 +    // than one time per transaction.
  1.1685 +    mClassification = CLASS_SOLO;
  1.1686 +}
  1.1687 +
  1.1688 +// Called when the transaction marked for blocking is associated with a connection
  1.1689 +// (i.e. added to a spdy session, an idle http connection, or placed into
  1.1690 +// a http pipeline). It is safe to call this multiple times with it only
  1.1691 +// having an effect once.
  1.1692 +void
  1.1693 +nsHttpTransaction::DispatchedAsBlocking()
  1.1694 +{
  1.1695 +    if (mDispatchedAsBlocking)
  1.1696 +        return;
  1.1697 +
  1.1698 +    LOG(("nsHttpTransaction %p dispatched as blocking\n", this));
  1.1699 +
  1.1700 +    if (!mLoadGroupCI)
  1.1701 +        return;
  1.1702 +
  1.1703 +    LOG(("nsHttpTransaction adding blocking channel %p from "
  1.1704 +         "loadgroup %p\n", this, mLoadGroupCI.get()));
  1.1705 +
  1.1706 +    mLoadGroupCI->AddBlockingTransaction();
  1.1707 +    mDispatchedAsBlocking = true;
  1.1708 +}
  1.1709 +
  1.1710 +void
  1.1711 +nsHttpTransaction::RemoveDispatchedAsBlocking()
  1.1712 +{
  1.1713 +    if (!mLoadGroupCI || !mDispatchedAsBlocking)
  1.1714 +        return;
  1.1715 +
  1.1716 +    uint32_t blockers = 0;
  1.1717 +    nsresult rv = mLoadGroupCI->RemoveBlockingTransaction(&blockers);
  1.1718 +
  1.1719 +    LOG(("nsHttpTransaction removing blocking channel %p from "
  1.1720 +         "loadgroup %p. %d blockers remain.\n", this,
  1.1721 +         mLoadGroupCI.get(), blockers));
  1.1722 +
  1.1723 +    if (NS_SUCCEEDED(rv) && !blockers) {
  1.1724 +        LOG(("nsHttpTransaction %p triggering release of blocked channels.\n",
  1.1725 +             this));
  1.1726 +        gHttpHandler->ConnMgr()->ProcessPendingQ();
  1.1727 +    }
  1.1728 +
  1.1729 +    mDispatchedAsBlocking = false;
  1.1730 +}
  1.1731 +
  1.1732 +void
  1.1733 +nsHttpTransaction::ReleaseBlockingTransaction()
  1.1734 +{
  1.1735 +    RemoveDispatchedAsBlocking();
  1.1736 +    mLoadGroupCI = nullptr;
  1.1737 +}
  1.1738 +
  1.1739 +//-----------------------------------------------------------------------------
  1.1740 +// nsHttpTransaction deletion event
  1.1741 +//-----------------------------------------------------------------------------
  1.1742 +
  1.1743 +class nsDeleteHttpTransaction : public nsRunnable {
  1.1744 +public:
  1.1745 +    nsDeleteHttpTransaction(nsHttpTransaction *trans)
  1.1746 +        : mTrans(trans)
  1.1747 +    {}
  1.1748 +
  1.1749 +    NS_IMETHOD Run()
  1.1750 +    {
  1.1751 +        delete mTrans;
  1.1752 +        return NS_OK;
  1.1753 +    }
  1.1754 +private:
  1.1755 +    nsHttpTransaction *mTrans;
  1.1756 +};
  1.1757 +
  1.1758 +void
  1.1759 +nsHttpTransaction::DeleteSelfOnConsumerThread()
  1.1760 +{
  1.1761 +    LOG(("nsHttpTransaction::DeleteSelfOnConsumerThread [this=%p]\n", this));
  1.1762 +
  1.1763 +    bool val;
  1.1764 +    if (!mConsumerTarget ||
  1.1765 +        (NS_SUCCEEDED(mConsumerTarget->IsOnCurrentThread(&val)) && val)) {
  1.1766 +        delete this;
  1.1767 +    } else {
  1.1768 +        LOG(("proxying delete to consumer thread...\n"));
  1.1769 +        nsCOMPtr<nsIRunnable> event = new nsDeleteHttpTransaction(this);
  1.1770 +        if (NS_FAILED(mConsumerTarget->Dispatch(event, NS_DISPATCH_NORMAL)))
  1.1771 +            NS_WARNING("failed to dispatch nsHttpDeleteTransaction event");
  1.1772 +    }
  1.1773 +}
  1.1774 +
  1.1775 +bool
  1.1776 +nsHttpTransaction::TryToRunPacedRequest()
  1.1777 +{
  1.1778 +    if (mSubmittedRatePacing)
  1.1779 +        return mPassedRatePacing;
  1.1780 +
  1.1781 +    mSubmittedRatePacing = true;
  1.1782 +    mSynchronousRatePaceRequest = true;
  1.1783 +    gHttpHandler->SubmitPacedRequest(this, getter_AddRefs(mTokenBucketCancel));
  1.1784 +    mSynchronousRatePaceRequest = false;
  1.1785 +    return mPassedRatePacing;
  1.1786 +}
  1.1787 +
  1.1788 +void
  1.1789 +nsHttpTransaction::OnTokenBucketAdmitted()
  1.1790 +{
  1.1791 +    mPassedRatePacing = true;
  1.1792 +    mTokenBucketCancel = nullptr;
  1.1793 +
  1.1794 +    if (!mSynchronousRatePaceRequest)
  1.1795 +        gHttpHandler->ConnMgr()->ProcessPendingQ(mConnInfo);
  1.1796 +}
  1.1797 +
  1.1798 +void
  1.1799 +nsHttpTransaction::CancelPacing(nsresult reason)
  1.1800 +{
  1.1801 +    if (mTokenBucketCancel) {
  1.1802 +        mTokenBucketCancel->Cancel(reason);
  1.1803 +        mTokenBucketCancel = nullptr;
  1.1804 +    }
  1.1805 +}
  1.1806 +
  1.1807 +//-----------------------------------------------------------------------------
  1.1808 +// nsHttpTransaction::nsISupports
  1.1809 +//-----------------------------------------------------------------------------
  1.1810 +
  1.1811 +NS_IMPL_ADDREF(nsHttpTransaction)
  1.1812 +
  1.1813 +NS_IMETHODIMP_(MozExternalRefCountType)
  1.1814 +nsHttpTransaction::Release()
  1.1815 +{
  1.1816 +    nsrefcnt count;
  1.1817 +    NS_PRECONDITION(0 != mRefCnt, "dup release");
  1.1818 +    count = --mRefCnt;
  1.1819 +    NS_LOG_RELEASE(this, count, "nsHttpTransaction");
  1.1820 +    if (0 == count) {
  1.1821 +        mRefCnt = 1; /* stablize */
  1.1822 +        // it is essential that the transaction be destroyed on the consumer
  1.1823 +        // thread (we could be holding the last reference to our consumer).
  1.1824 +        DeleteSelfOnConsumerThread();
  1.1825 +        return 0;
  1.1826 +    }
  1.1827 +    return count;
  1.1828 +}
  1.1829 +
  1.1830 +NS_IMPL_QUERY_INTERFACE(nsHttpTransaction,
  1.1831 +                        nsIInputStreamCallback,
  1.1832 +                        nsIOutputStreamCallback)
  1.1833 +
  1.1834 +//-----------------------------------------------------------------------------
  1.1835 +// nsHttpTransaction::nsIInputStreamCallback
  1.1836 +//-----------------------------------------------------------------------------
  1.1837 +
  1.1838 +// called on the socket thread
  1.1839 +NS_IMETHODIMP
  1.1840 +nsHttpTransaction::OnInputStreamReady(nsIAsyncInputStream *out)
  1.1841 +{
  1.1842 +    if (mConnection) {
  1.1843 +        mConnection->TransactionHasDataToWrite(this);
  1.1844 +        nsresult rv = mConnection->ResumeSend();
  1.1845 +        if (NS_FAILED(rv))
  1.1846 +            NS_ERROR("ResumeSend failed");
  1.1847 +    }
  1.1848 +    return NS_OK;
  1.1849 +}
  1.1850 +
  1.1851 +//-----------------------------------------------------------------------------
  1.1852 +// nsHttpTransaction::nsIOutputStreamCallback
  1.1853 +//-----------------------------------------------------------------------------
  1.1854 +
  1.1855 +// called on the socket thread
  1.1856 +NS_IMETHODIMP
  1.1857 +nsHttpTransaction::OnOutputStreamReady(nsIAsyncOutputStream *out)
  1.1858 +{
  1.1859 +    if (mConnection) {
  1.1860 +        nsresult rv = mConnection->ResumeRecv();
  1.1861 +        if (NS_FAILED(rv))
  1.1862 +            NS_ERROR("ResumeRecv failed");
  1.1863 +    }
  1.1864 +    return NS_OK;
  1.1865 +}
  1.1866 +
  1.1867 +// nsHttpTransaction::RestartVerifier
  1.1868 +
  1.1869 +static bool
  1.1870 +matchOld(nsHttpResponseHead *newHead, nsCString &old,
  1.1871 +         nsHttpAtom headerAtom)
  1.1872 +{
  1.1873 +    const char *val;
  1.1874 +
  1.1875 +    val = newHead->PeekHeader(headerAtom);
  1.1876 +    if (val && old.IsEmpty())
  1.1877 +        return false;
  1.1878 +    if (!val && !old.IsEmpty())
  1.1879 +        return false;
  1.1880 +    if (val && !old.Equals(val))
  1.1881 +        return false;
  1.1882 +    return true;
  1.1883 +}
  1.1884 +
  1.1885 +bool
  1.1886 +nsHttpTransaction::RestartVerifier::Verify(int64_t contentLength,
  1.1887 +                                           nsHttpResponseHead *newHead)
  1.1888 +{
  1.1889 +    if (mContentLength != contentLength)
  1.1890 +        return false;
  1.1891 +
  1.1892 +    if (newHead->Status() != 200)
  1.1893 +        return false;
  1.1894 +
  1.1895 +    if (!matchOld(newHead, mContentRange, nsHttp::Content_Range))
  1.1896 +        return false;
  1.1897 +
  1.1898 +    if (!matchOld(newHead, mLastModified, nsHttp::Last_Modified))
  1.1899 +        return false;
  1.1900 +
  1.1901 +    if (!matchOld(newHead, mETag, nsHttp::ETag))
  1.1902 +        return false;
  1.1903 +
  1.1904 +    if (!matchOld(newHead, mContentEncoding, nsHttp::Content_Encoding))
  1.1905 +        return false;
  1.1906 +
  1.1907 +    if (!matchOld(newHead, mTransferEncoding, nsHttp::Transfer_Encoding))
  1.1908 +        return false;
  1.1909 +
  1.1910 +    return true;
  1.1911 +}
  1.1912 +
  1.1913 +void
  1.1914 +nsHttpTransaction::RestartVerifier::Set(int64_t contentLength,
  1.1915 +                                        nsHttpResponseHead *head)
  1.1916 +{
  1.1917 +    if (mSetup)
  1.1918 +        return;
  1.1919 +
  1.1920 +    // If mSetup does not transition to true RestartInPogress() is later
  1.1921 +    // forbidden
  1.1922 +
  1.1923 +    // Only RestartInProgress with 200 response code
  1.1924 +    if (head->Status() != 200)
  1.1925 +        return;
  1.1926 +
  1.1927 +    mContentLength = contentLength;
  1.1928 +
  1.1929 +    if (head) {
  1.1930 +        const char *val;
  1.1931 +        val = head->PeekHeader(nsHttp::ETag);
  1.1932 +        if (val)
  1.1933 +            mETag.Assign(val);
  1.1934 +        val = head->PeekHeader(nsHttp::Last_Modified);
  1.1935 +        if (val)
  1.1936 +            mLastModified.Assign(val);
  1.1937 +        val = head->PeekHeader(nsHttp::Content_Range);
  1.1938 +        if (val)
  1.1939 +            mContentRange.Assign(val);
  1.1940 +        val = head->PeekHeader(nsHttp::Content_Encoding);
  1.1941 +        if (val)
  1.1942 +            mContentEncoding.Assign(val);
  1.1943 +        val = head->PeekHeader(nsHttp::Transfer_Encoding);
  1.1944 +        if (val)
  1.1945 +            mTransferEncoding.Assign(val);
  1.1946 +
  1.1947 +        // We can only restart with any confidence if we have a stored etag or
  1.1948 +        // last-modified header
  1.1949 +        if (mETag.IsEmpty() && mLastModified.IsEmpty())
  1.1950 +            return;
  1.1951 +
  1.1952 +        mSetup = true;
  1.1953 +    }
  1.1954 +}
  1.1955 +
  1.1956 +} // namespace mozilla::net
  1.1957 +} // namespace mozilla

mercurial