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