Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /* vim:set ts=4 sw=4 sts=4 et cin: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : // HttpLog.h should generally be included first
8 : #include "HttpLog.h"
9 :
10 : // Log on level :5, instead of default :4.
11 : #undef LOG
12 : #define LOG(args) LOG5(args)
13 : #undef LOG_ENABLED
14 : #define LOG_ENABLED() LOG5_ENABLED()
15 :
16 : #define TLS_EARLY_DATA_NOT_AVAILABLE 0
17 : #define TLS_EARLY_DATA_AVAILABLE_BUT_NOT_USED 1
18 : #define TLS_EARLY_DATA_AVAILABLE_AND_USED 2
19 :
20 : #include "ASpdySession.h"
21 : #include "mozilla/ChaosMode.h"
22 : #include "mozilla/Telemetry.h"
23 : #include "nsHttpConnection.h"
24 : #include "nsHttpHandler.h"
25 : #include "nsHttpRequestHead.h"
26 : #include "nsHttpResponseHead.h"
27 : #include "nsIClassOfService.h"
28 : #include "nsIOService.h"
29 : #include "nsISocketTransport.h"
30 : #include "nsSocketTransportService2.h"
31 : #include "nsISSLSocketControl.h"
32 : #include "nsISupportsPriority.h"
33 : #include "nsPreloadedStream.h"
34 : #include "nsProxyRelease.h"
35 : #include "nsSocketTransport2.h"
36 : #include "nsStringStream.h"
37 : #include "sslt.h"
38 : #include "TunnelUtils.h"
39 : #include "TCPFastOpenLayer.h"
40 :
41 : namespace mozilla {
42 : namespace net {
43 :
44 : //-----------------------------------------------------------------------------
45 : // nsHttpConnection <public>
46 : //-----------------------------------------------------------------------------
47 :
48 0 : nsHttpConnection::nsHttpConnection()
49 : : mSocketInCondition(NS_ERROR_NOT_INITIALIZED)
50 : , mSocketOutCondition(NS_ERROR_NOT_INITIALIZED)
51 : , mTransaction(nullptr)
52 : , mHttpHandler(gHttpHandler)
53 : , mCallbacksLock("nsHttpConnection::mCallbacksLock")
54 : , mLastReadTime(0)
55 : , mLastWriteTime(0)
56 : , mMaxHangTime(0)
57 : , mConsiderReusedAfterInterval(0)
58 : , mConsiderReusedAfterEpoch(0)
59 : , mCurrentBytesRead(0)
60 : , mMaxBytesRead(0)
61 : , mTotalBytesRead(0)
62 : , mTotalBytesWritten(0)
63 : , mContentBytesWritten(0)
64 : , mRtt(0)
65 : , mUrgentStartPreferred(false)
66 : , mUrgentStartPreferredKnown(false)
67 : , mConnectedTransport(false)
68 : , mKeepAlive(true) // assume to keep-alive by default
69 : , mKeepAliveMask(true)
70 : , mDontReuse(false)
71 : , mIsReused(false)
72 : , mCompletedProxyConnect(false)
73 : , mLastTransactionExpectedNoContent(false)
74 : , mIdleMonitoring(false)
75 : , mProxyConnectInProgress(false)
76 : , mExperienced(false)
77 : , mInSpdyTunnel(false)
78 : , mForcePlainText(false)
79 : , mTrafficCount(0)
80 : , mTrafficStamp(false)
81 : , mHttp1xTransactionCount(0)
82 : , mRemainingConnectionUses(0xffffffff)
83 : , mNPNComplete(false)
84 : , mSetupSSLCalled(false)
85 : , mUsingSpdyVersion(SpdyVersion::NONE)
86 : , mPriority(nsISupportsPriority::PRIORITY_NORMAL)
87 : , mReportedSpdy(false)
88 : , mEverUsedSpdy(false)
89 : , mLastHttpResponseVersion(HttpVersion::v1_1)
90 : , mTransactionCaps(0)
91 : , mDefaultTimeoutFactor(1)
92 : , mResponseTimeoutEnabled(false)
93 : , mTCPKeepaliveConfig(kTCPKeepaliveDisabled)
94 : , mForceSendPending(false)
95 : , m0RTTChecked(false)
96 : , mWaitingFor0RTTResponse(false)
97 : , mContentBytesWritten0RTT(0)
98 : , mEarlyDataNegotiated(false)
99 0 : , mDid0RTTSpdy(false)
100 : , mFastOpen(false)
101 0 : , mFastOpenStatus(TFO_NOT_SET)
102 : , mForceSendDuringFastOpenPending(false)
103 : , mReceivedSocketWouldBlockDuringFastOpen(false)
104 : , mCheckNetworkStallsWithTFO(false)
105 0 : , mLastRequestBytesSentTime(0)
106 0 : , mBootstrappedTimingsSet(false)
107 0 : {
108 0 : LOG(("Creating nsHttpConnection @%p\n", this));
109 :
110 0 : // the default timeout is for when this connection has not yet processed a
111 : // transaction
112 0 : static const PRIntervalTime k5Sec = PR_SecondsToInterval(5);
113 : mIdleTimeout =
114 0 : (k5Sec < gHttpHandler->IdleTimeout()) ? k5Sec : gHttpHandler->IdleTimeout();
115 0 : }
116 :
117 0 : nsHttpConnection::~nsHttpConnection()
118 0 : {
119 : LOG(("Destroying nsHttpConnection @%p\n", this));
120 :
121 0 : if (!mEverUsedSpdy) {
122 0 : LOG(("nsHttpConnection %p performed %d HTTP/1.x transactions\n",
123 0 : this, mHttp1xTransactionCount));
124 : Telemetry::Accumulate(Telemetry::HTTP_REQUEST_PER_CONN,
125 0 : mHttp1xTransactionCount);
126 : }
127 :
128 0 : if (mTotalBytesRead) {
129 : uint32_t totalKBRead = static_cast<uint32_t>(mTotalBytesRead >> 10);
130 0 : LOG(("nsHttpConnection %p read %dkb on connection spdy=%d\n",
131 0 : this, totalKBRead, mEverUsedSpdy));
132 0 : Telemetry::Accumulate(mEverUsedSpdy ?
133 : Telemetry::SPDY_KBREAD_PER_CONN :
134 : Telemetry::HTTP_KBREAD_PER_CONN,
135 0 : totalKBRead);
136 0 : }
137 0 : if (mForceSendTimer) {
138 0 : mForceSendTimer->Cancel();
139 : mForceSendTimer = nullptr;
140 : }
141 :
142 : if ((mFastOpenStatus != TFO_FAILED) &&
143 0 : (mFastOpenStatus != TFO_HTTP) &&
144 : (((mFastOpenStatus > TFO_DISABLED_CONNECT) && (mFastOpenStatus < TFO_BACKUP_CONN)) ||
145 0 : gHttpHandler->UseFastOpen())) {
146 : // TFO_FAILED will be reported in the replacement connection with more
147 : // details.
148 0 : // Otherwise report only if TFO is enabled and supported.
149 : // If TFO is disabled, report only connections ha cause it to be disabled, e.g. TFO_FAILED_NET_TIMEOUT, etc.
150 : Telemetry::Accumulate(Telemetry::TCP_FAST_OPEN_3, mFastOpenStatus);
151 : }
152 : }
153 :
154 : nsresult
155 : nsHttpConnection::Init(nsHttpConnectionInfo *info,
156 : uint16_t maxHangTime,
157 0 : nsISocketTransport *transport,
158 0 : nsIAsyncInputStream *instream,
159 0 : nsIAsyncOutputStream *outstream,
160 : bool connectedTransport,
161 0 : nsIInterfaceRequestor *callbacks,
162 0 : PRIntervalTime rtt)
163 0 : {
164 0 : LOG(("nsHttpConnection::Init this=%p sockettransport=%p", this, transport));
165 0 : NS_ENSURE_ARG_POINTER(info);
166 0 : NS_ENSURE_TRUE(!mConnInfo, NS_ERROR_ALREADY_INITIALIZED);
167 :
168 0 : mConnectedTransport = connectedTransport;
169 0 : mConnInfo = info;
170 0 : MOZ_ASSERT(mConnInfo);
171 : mLastWriteTime = mLastReadTime = PR_IntervalNow();
172 : mRtt = rtt;
173 : mMaxHangTime = PR_SecondsToInterval(maxHangTime);
174 0 :
175 : mSocketTransport = transport;
176 0 : mSocketIn = instream;
177 0 : mSocketOut = outstream;
178 :
179 0 : // See explanation for non-strictness of this operation in SetSecurityCallbacks.
180 : mCallbacks = new nsMainThreadPtrHolder<nsIInterfaceRequestor>(
181 : "nsHttpConnection::mCallbacks", callbacks, false);
182 :
183 0 : mSocketTransport->SetEventSink(this, nullptr);
184 : mSocketTransport->SetSecurityCallbacks(this);
185 0 :
186 : return NS_OK;
187 0 : }
188 :
189 0 : nsresult
190 : nsHttpConnection::TryTakeSubTransactions(nsTArray<RefPtr<nsAHttpTransaction> > &list)
191 0 : {
192 : nsresult rv = mTransaction->TakeSubTransactions(list);
193 :
194 : if (rv == NS_ERROR_ALREADY_OPENED) {
195 : // Has the interface for TakeSubTransactions() changed?
196 : LOG(("TakeSubTransactions somehow called after "
197 : "nsAHttpTransaction began processing\n"));
198 0 : MOZ_ASSERT(false,
199 : "TakeSubTransactions somehow called after "
200 0 : "nsAHttpTransaction began processing");
201 0 : mTransaction->Close(NS_ERROR_ABORT);
202 : return rv;
203 : }
204 :
205 : if (NS_FAILED(rv) && rv != NS_ERROR_NOT_IMPLEMENTED) {
206 : // Has the interface for TakeSubTransactions() changed?
207 : LOG(("unexpected rv from nnsAHttpTransaction::TakeSubTransactions()"));
208 : MOZ_ASSERT(false,
209 : "unexpected result from "
210 : "nsAHttpTransaction::TakeSubTransactions()");
211 : mTransaction->Close(NS_ERROR_ABORT);
212 0 : return rv;
213 : }
214 0 :
215 0 : return rv;
216 : }
217 :
218 : nsresult
219 : nsHttpConnection::MoveTransactionsToSpdy(nsresult status, nsTArray<RefPtr<nsAHttpTransaction> > &list)
220 0 : {
221 : if (NS_FAILED(status)) { // includes NS_ERROR_NOT_IMPLEMENTED
222 0 : MOZ_ASSERT(list.IsEmpty(), "sub transaction list not empty");
223 0 :
224 : // This is ok - treat mTransaction as a single real request.
225 : // Wrap the old http transaction into the new spdy session
226 : // as the first stream.
227 0 : LOG(("nsHttpConnection::MoveTransactionsToSpdy moves single transaction %p "
228 : "into SpdySession %p\n", mTransaction.get(), mSpdySession.get()));
229 0 : nsresult rv = AddTransaction(mTransaction, mPriority);
230 : if (NS_FAILED(rv)) {
231 : return rv;
232 0 : }
233 0 : } else {
234 0 : int32_t count = list.Length();
235 :
236 : LOG(("nsHttpConnection::MoveTransactionsToSpdy moving transaction list len=%d "
237 0 : "into SpdySession %p\n", count, mSpdySession.get()));
238 0 :
239 0 : if (!count) {
240 : mTransaction->Close(NS_ERROR_ABORT);
241 : return NS_ERROR_ABORT;
242 : }
243 :
244 : for (int32_t index = 0; index < count; ++index) {
245 : nsresult rv = AddTransaction(list[index], mPriority);
246 : if (NS_FAILED(rv)) {
247 : return rv;
248 : }
249 0 : }
250 : }
251 0 :
252 : return NS_OK;
253 0 : }
254 :
255 0 : void
256 0 : nsHttpConnection::Start0RTTSpdy(SpdyVersion spdyVersion)
257 : {
258 0 : LOG(("nsHttpConnection::Start0RTTSpdy [this=%p]", this));
259 :
260 0 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
261 0 :
262 0 : mDid0RTTSpdy = true;
263 0 : mUsingSpdyVersion = spdyVersion;
264 : mSpdySession = ASpdySession::NewSpdySession(spdyVersion, mSocketTransport,
265 0 : true);
266 :
267 : nsTArray<RefPtr<nsAHttpTransaction> > list;
268 0 : nsresult rv = TryTakeSubTransactions(list);
269 0 : if (NS_FAILED(rv) && rv != NS_ERROR_NOT_IMPLEMENTED) {
270 0 : LOG(("nsHttpConnection::Start0RTTSpdy [this=%p] failed taking "
271 : "subtransactions rv=%" PRIx32 , this, static_cast<uint32_t>(rv)));
272 : return;
273 : }
274 :
275 0 : rv = MoveTransactionsToSpdy(rv, list);
276 : if (NS_FAILED(rv)) {
277 : LOG(("nsHttpConnection::Start0RTTSpdy [this=%p] failed moving "
278 : "transactions rv=%" PRIx32 , this, static_cast<uint32_t>(rv)));
279 0 : return;
280 : }
281 0 :
282 : mTransaction = mSpdySession;
283 0 : }
284 0 :
285 : void
286 0 : nsHttpConnection::StartSpdy(nsISSLSocketControl *sslControl, SpdyVersion spdyVersion)
287 0 : {
288 0 : LOG(("nsHttpConnection::StartSpdy [this=%p, mDid0RTTSpdy=%d]\n", this, mDid0RTTSpdy));
289 0 :
290 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
291 : MOZ_ASSERT(!mSpdySession || mDid0RTTSpdy);
292 0 :
293 : mUsingSpdyVersion = spdyVersion;
294 0 : mEverUsedSpdy = true;
295 : if (sslControl) {
296 : sslControl->SetDenyClientCert(true);
297 0 : }
298 0 :
299 0 : if (!mDid0RTTSpdy) {
300 : mSpdySession = ASpdySession::NewSpdySession(spdyVersion, mSocketTransport,
301 : false);
302 : }
303 :
304 : if (!mReportedSpdy) {
305 : mReportedSpdy = true;
306 0 : gHttpHandler->ConnMgr()->ReportSpdyConnection(this, true);
307 : }
308 :
309 : // Setting the connection as reused allows some transactions that fail
310 : // with NS_ERROR_NET_RESET to be restarted and SPDY uses that code
311 : // to handle clean rejections (such as those that arrived after
312 0 : // a server goaway was generated).
313 0 : mIsReused = true;
314 0 :
315 0 : // If mTransaction is a muxed object it might represent
316 : // several requests. If so, we need to unpack that and
317 0 : // pack them all into a new spdy session.
318 0 :
319 : nsTArray<RefPtr<nsAHttpTransaction> > list;
320 : nsresult status = NS_OK;
321 : if (!mDid0RTTSpdy) {
322 0 : status = TryTakeSubTransactions(list);
323 0 :
324 : if (NS_FAILED(status) && status != NS_ERROR_NOT_IMPLEMENTED) {
325 0 : return;
326 : }
327 0 : }
328 0 :
329 0 : if (NeedSpdyTunnel()) {
330 : LOG3(("nsHttpConnection::StartSpdy %p Connecting To a HTTP/2 "
331 : "Proxy and Need Connect", this));
332 0 : MOZ_ASSERT(mProxyConnectStream);
333 0 :
334 0 : mProxyConnectStream = nullptr;
335 0 : mCompletedProxyConnect = true;
336 0 : mProxyConnectInProgress = false;
337 0 : }
338 0 :
339 0 : nsresult rv = NS_OK;
340 0 : bool spdyProxy = mConnInfo->UsingHttpsProxy() && !mTLSFilter;
341 0 : if (spdyProxy) {
342 : RefPtr<nsHttpConnectionInfo> wildCardProxyCi;
343 : rv = mConnInfo->CreateWildCard(getter_AddRefs(wildCardProxyCi));
344 0 : MOZ_ASSERT(NS_SUCCEEDED(rv));
345 0 : gHttpHandler->ConnMgr()->MoveToWildCardConnEntry(mConnInfo,
346 0 : wildCardProxyCi, this);
347 : mConnInfo = wildCardProxyCi;
348 : MOZ_ASSERT(mConnInfo);
349 : }
350 :
351 : if (!mDid0RTTSpdy) {
352 0 : rv = MoveTransactionsToSpdy(status, list);
353 0 : if (NS_FAILED(rv)) {
354 0 : return;
355 : }
356 : }
357 :
358 0 : // Disable TCP Keepalives - use SPDY ping instead.
359 : rv = DisableTCPKeepalives();
360 0 : if (NS_FAILED(rv)) {
361 0 : LOG(("nsHttpConnection::StartSpdy [%p] DisableTCPKeepalives failed "
362 : "rv[0x%" PRIx32 "]", this, static_cast<uint32_t>(rv)));
363 0 : }
364 0 :
365 0 : mIdleTimeout = gHttpHandler->SpdyTimeout() * mDefaultTimeoutFactor;
366 :
367 : if (!mTLSFilter) {
368 : mTransaction = mSpdySession;
369 0 : } else {
370 0 : rv = mTLSFilter->SetProxiedTransaction(mSpdySession);
371 : if (NS_FAILED(rv)) {
372 : LOG(("nsHttpConnection::StartSpdy [%p] SetProxiedTransaction failed"
373 : " rv[0x%x]", this, static_cast<uint32_t>(rv)));
374 : }
375 0 : }
376 : if (mDontReuse) {
377 : mSpdySession->DontReuse();
378 : }
379 : }
380 :
381 0 : bool
382 0 : nsHttpConnection::EnsureNPNComplete(nsresult &aOut0RTTWriteHandshakeValue,
383 : uint32_t &aOut0RTTBytesWritten)
384 0 : {
385 0 : // If for some reason the components to check on NPN aren't available,
386 : // this function will just return true to continue on and disable SPDY
387 0 :
388 0 : aOut0RTTWriteHandshakeValue = NS_OK;
389 : aOut0RTTBytesWritten = 0;
390 :
391 0 : MOZ_ASSERT(mSocketTransport);
392 : if (!mSocketTransport) {
393 : // this cannot happen
394 : mNPNComplete = true;
395 : return true;
396 0 : }
397 0 :
398 0 : if (mNPNComplete) {
399 : return true;
400 0 : }
401 0 :
402 : nsresult rv;
403 : nsCOMPtr<nsISupports> securityInfo;
404 : nsCOMPtr<nsISSLSocketControl> ssl;
405 0 : nsAutoCString negotiatedNPN;
406 0 :
407 : GetSecurityInfo(getter_AddRefs(securityInfo));
408 : if (!securityInfo) {
409 0 : goto npnComplete;
410 : }
411 0 :
412 : ssl = do_QueryInterface(securityInfo, &rv);
413 0 : if (NS_FAILED(rv))
414 : goto npnComplete;
415 :
416 0 : if (!m0RTTChecked) {
417 0 : // We reuse m0RTTChecked. We want to send this status only once.
418 0 : mTransaction->OnTransportStatus(mSocketTransport,
419 : NS_NET_STATUS_TLS_HANDSHAKE_STARTING,
420 : 0);
421 : }
422 :
423 0 : rv = ssl->GetNegotiatedNPN(negotiatedNPN);
424 0 : if (!m0RTTChecked && (rv == NS_ERROR_NOT_CONNECTED) &&
425 0 : !mConnInfo->UsingProxy()) {
426 : // There is no ALPN info (yet!). We need to consider doing 0RTT. We
427 : // will do so if there is ALPN information from a previous session
428 : // (AlpnEarlySelection), we are using HTTP/1, and the request data can
429 0 : // be safely retried.
430 : m0RTTChecked = true;
431 : nsresult rvEarlyAlpn = ssl->GetAlpnEarlySelection(mEarlyNegotiatedALPN);
432 : if (NS_FAILED(rvEarlyAlpn)) {
433 0 : // if ssl->DriveHandshake() has never been called the value
434 0 : // for AlpnEarlySelection is still not set. So call it here and
435 : // check again.
436 : LOG(("nsHttpConnection::EnsureNPNComplete %p - "
437 : "early selected alpn not available, we will try one more time.",
438 : this));
439 0 : // Let's do DriveHandshake again.
440 0 : rv = ssl->DriveHandshake();
441 0 : if (NS_FAILED(rv) && rv != NS_BASE_STREAM_WOULD_BLOCK) {
442 : goto npnComplete;
443 : }
444 :
445 0 : // Check NegotiatedNPN first.
446 0 : rv = ssl->GetNegotiatedNPN(negotiatedNPN);
447 : if (rv == NS_ERROR_NOT_CONNECTED) {
448 0 : rvEarlyAlpn = ssl->GetAlpnEarlySelection(mEarlyNegotiatedALPN);
449 : }
450 0 : }
451 :
452 : if (NS_FAILED(rvEarlyAlpn)) {
453 0 : LOG(("nsHttpConnection::EnsureNPNComplete %p - "
454 0 : "early selected alpn not available", this));
455 : mEarlyDataNegotiated = false;
456 : } else {
457 0 : LOG(("nsHttpConnection::EnsureNPNComplete %p -"
458 0 : "early selected alpn: %s", this, mEarlyNegotiatedALPN.get()));
459 : uint32_t infoIndex;
460 0 : const SpdyInformation *info = gHttpHandler->SpdyInfo();
461 : if (NS_FAILED(info->GetNPNIndex(mEarlyNegotiatedALPN, &infoIndex))) {
462 : // This is the HTTP/1 case.
463 : // Check if early-data is allowed for this transaction.
464 : if (mTransaction->Do0RTT()) {
465 0 : LOG(("nsHttpConnection::EnsureNPNComplete [this=%p] - We "
466 : "can do 0RTT (http/1)!", this));
467 0 : mWaitingFor0RTTResponse = true;
468 0 : }
469 : } else {
470 0 : // We have h2, we can at least 0-RTT the preamble and opening
471 : // SETTINGS, etc, and maybe some of the first request
472 : LOG(("nsHttpConnection::EnsureNPNComplete [this=%p] - Starting "
473 : "0RTT for h2!", this));
474 0 : mWaitingFor0RTTResponse = true;
475 0 : Start0RTTSpdy(info->Version[infoIndex]);
476 0 : }
477 0 : mEarlyDataNegotiated = true;
478 0 : }
479 : }
480 :
481 : if (rv == NS_ERROR_NOT_CONNECTED) {
482 0 : if (mWaitingFor0RTTResponse) {
483 : aOut0RTTWriteHandshakeValue = mTransaction->ReadSegments(this,
484 0 : nsIOService::gDefaultSegmentSize, &aOut0RTTBytesWritten);
485 0 : if (NS_FAILED(aOut0RTTWriteHandshakeValue) &&
486 0 : aOut0RTTWriteHandshakeValue != NS_BASE_STREAM_WOULD_BLOCK) {
487 : goto npnComplete;
488 : }
489 : LOG(("nsHttpConnection::EnsureNPNComplete [this=%p] - written %d "
490 0 : "bytes during 0RTT", this, aOut0RTTBytesWritten));
491 0 : mContentBytesWritten0RTT += aOut0RTTBytesWritten;
492 : if (mSocketOutCondition == NS_BASE_STREAM_WOULD_BLOCK) {
493 : mReceivedSocketWouldBlockDuringFastOpen = true;
494 : }
495 : }
496 :
497 : rv = ssl->DriveHandshake();
498 0 : if (NS_FAILED(rv) && rv != NS_BASE_STREAM_WOULD_BLOCK) {
499 0 : goto npnComplete;
500 : }
501 :
502 : return false;
503 0 : }
504 0 :
505 : if (NS_SUCCEEDED(rv)) {
506 0 : LOG(("nsHttpConnection::EnsureNPNComplete %p [%s] negotiated to '%s'%s\n",
507 0 : this, mConnInfo->HashKey().get(), negotiatedNPN.get(),
508 : mTLSFilter ? " [Double Tunnel]" : ""));
509 :
510 : bool earlyDataAccepted = false;
511 0 : if (mWaitingFor0RTTResponse) {
512 0 : // Check if early data has been accepted.
513 0 : rv = ssl->GetEarlyDataAccepted(&earlyDataAccepted);
514 0 : LOG(("nsHttpConnection::EnsureNPNComplete [this=%p] - early data "
515 0 : "that was sent during 0RTT %s been accepted [rv=%" PRIx32 "].",
516 : this, earlyDataAccepted ? "has" : "has not", static_cast<uint32_t>(rv)));
517 :
518 : if (NS_FAILED(rv) ||
519 : NS_FAILED(mTransaction->Finish0RTT(!earlyDataAccepted, negotiatedNPN != mEarlyNegotiatedALPN))) {
520 0 : LOG(("nsHttpConection::EnsureNPNComplete [this=%p] closing transaction %p", this, mTransaction.get()));
521 : mTransaction->Close(NS_ERROR_NET_RESET);
522 0 : goto npnComplete;
523 0 : }
524 0 : }
525 0 :
526 0 : int16_t tlsVersion;
527 0 : ssl->GetSSLVersionUsed(&tlsVersion);
528 0 : // Send the 0RTT telemetry only for tls1.3
529 0 : if (tlsVersion > nsISSLSocketControl::TLS_VERSION_1_2) {
530 : Telemetry::Accumulate(Telemetry::TLS_EARLY_DATA_NEGOTIATED,
531 0 : (!mEarlyDataNegotiated) ? TLS_EARLY_DATA_NOT_AVAILABLE
532 0 : : ((mWaitingFor0RTTResponse) ? TLS_EARLY_DATA_AVAILABLE_AND_USED
533 0 : : TLS_EARLY_DATA_AVAILABLE_BUT_NOT_USED));
534 : if (mWaitingFor0RTTResponse) {
535 : Telemetry::Accumulate(Telemetry::TLS_EARLY_DATA_ACCEPTED,
536 0 : earlyDataAccepted);
537 : }
538 0 : if (earlyDataAccepted) {
539 0 : Telemetry::Accumulate(Telemetry::TLS_EARLY_DATA_BYTES_WRITTEN,
540 0 : mContentBytesWritten0RTT);
541 0 : }
542 0 : }
543 : mWaitingFor0RTTResponse = false;
544 :
545 : if (!earlyDataAccepted) {
546 : LOG(("nsHttpConnection::EnsureNPNComplete [this=%p] early data not accepted", this));
547 0 : if (mTransaction->QueryNullTransaction() &&
548 : (mBootstrappedTimings.secureConnectionStart.IsNull() ||
549 0 : mBootstrappedTimings.tcpConnectEnd.IsNull())) {
550 : // if TFO is used some socket event will be sent after
551 : // mBootstrappedTimings has been set. therefore we should
552 0 : // update them.
553 0 : mBootstrappedTimings.secureConnectionStart =
554 0 : mTransaction->QueryNullTransaction()->GetSecureConnectionStart();
555 : mBootstrappedTimings.tcpConnectEnd =
556 : mTransaction->QueryNullTransaction()->GetTcpConnectEnd();
557 0 : }
558 : uint32_t infoIndex;
559 0 : const SpdyInformation *info = gHttpHandler->SpdyInfo();
560 0 : if (NS_SUCCEEDED(info->GetNPNIndex(negotiatedNPN, &infoIndex))) {
561 : StartSpdy(ssl, info->Version[infoIndex]);
562 : }
563 0 : } else {
564 : LOG(("nsHttpConnection::EnsureNPNComplete [this=%p] - %" PRId64 " bytes "
565 0 : "has been sent during 0RTT.", this, mContentBytesWritten0RTT));
566 : mContentBytesWritten = mContentBytesWritten0RTT;
567 : if (mSpdySession) {
568 : // We had already started 0RTT-spdy, now we need to fully set up
569 0 : // spdy, since we know we're sticking with it.
570 : LOG(("nsHttpConnection::EnsureNPNComplete [this=%p] - finishing "
571 : "StartSpdy for 0rtt spdy session %p", this, mSpdySession.get()));
572 : StartSpdy(ssl, mSpdySession->SpdyVersion());
573 0 : }
574 0 : }
575 :
576 0 : Telemetry::Accumulate(Telemetry::SPDY_NPN_CONNECT, UsingSpdy());
577 0 : }
578 :
579 : npnComplete:
580 0 : LOG(("nsHttpConnection::EnsureNPNComplete [this=%p] setting complete to true", this));
581 0 : mNPNComplete = true;
582 0 :
583 : mTransaction->OnTransportStatus(mSocketTransport,
584 : NS_NET_STATUS_TLS_HANDSHAKE_ENDED, 0);
585 :
586 : // this is happening after the bootstrap was originally written to. so update it.
587 0 : if (mTransaction->QueryNullTransaction() &&
588 : (mBootstrappedTimings.secureConnectionStart.IsNull() ||
589 0 : mBootstrappedTimings.tcpConnectEnd.IsNull())) {
590 : // if TFO is used some socket event will be sent after
591 : // mBootstrappedTimings has been set. therefore we should
592 0 : // update them.
593 0 : mBootstrappedTimings.secureConnectionStart =
594 : mTransaction->QueryNullTransaction()->GetSecureConnectionStart();
595 : mBootstrappedTimings.tcpConnectEnd =
596 0 : mTransaction->QueryNullTransaction()->GetTcpConnectEnd();
597 : }
598 0 :
599 0 : if (securityInfo) {
600 0 : mBootstrappedTimings.connectEnd = TimeStamp::Now();
601 0 : }
602 :
603 0 : if (mWaitingFor0RTTResponse) {
604 : // Didn't get 0RTT OK, back out of the "attempting 0RTT" state
605 : mWaitingFor0RTTResponse = false;
606 0 : LOG(("nsHttpConnection::EnsureNPNComplete [this=%p] 0rtt failed", this));
607 : if (NS_FAILED(mTransaction->Finish0RTT(true, negotiatedNPN != mEarlyNegotiatedALPN))) {
608 0 : mTransaction->Close(NS_ERROR_NET_RESET);
609 0 : }
610 0 : mContentBytesWritten0RTT = 0;
611 0 : }
612 :
613 : if (mDid0RTTSpdy && negotiatedNPN != mEarlyNegotiatedALPN) {
614 0 : // Reset the work done by Start0RTTSpdy
615 : LOG(("nsHttpConnection::EnsureNPNComplete [this=%p] resetting Start0RTTSpdy", this));
616 : mUsingSpdyVersion = SpdyVersion::NONE;
617 : mTransaction = nullptr;
618 : mSpdySession = nullptr;
619 : // We have to reset this here, just in case we end up starting spdy again,
620 0 : // so it can actually do everything it needs to do.
621 : mDid0RTTSpdy = false;
622 0 : }
623 0 : return true;
624 0 : }
625 :
626 : void
627 0 : nsHttpConnection::OnTunnelNudged(TLSFilterTransaction *trans)
628 0 : {
629 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
630 : LOG(("nsHttpConnection::OnTunnelNudged %p\n", this));
631 : if (trans != mTLSFilter) {
632 : return;
633 0 : }
634 : LOG(("nsHttpConnection::OnTunnelNudged %p Calling OnSocketWritable\n", this));
635 0 : Unused << OnSocketWritable();
636 0 : }
637 :
638 : // called on the socket thread
639 0 : nsresult
640 0 : nsHttpConnection::Activate(nsAHttpTransaction *trans, uint32_t caps, int32_t pri)
641 0 : {
642 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
643 0 : LOG(("nsHttpConnection::Activate [this=%p trans=%p caps=%x]\n",
644 0 : this, trans, caps));
645 0 :
646 0 : if (!mExperienced && !trans->IsNullTransaction()) {
647 0 : if (!mFastOpen) {
648 0 : mExperienced = true;
649 : }
650 : if (mBootstrappedTimingsSet) {
651 0 : mBootstrappedTimingsSet = false;
652 : nsHttpTransaction *hTrans = trans->QueryHttpTransaction();
653 : if (hTrans) {
654 0 : hTrans->BootstrapTimings(mBootstrappedTimings);
655 0 : SetUrgentStartPreferred(hTrans->ClassOfService() & nsIClassOfService::UrgentStart);
656 : }
657 : }
658 0 : mBootstrappedTimings = TimingStruct();
659 0 : }
660 0 :
661 0 : if (caps & NS_HTTP_LARGE_KEEPALIVE) {
662 : mDefaultTimeoutFactor = 10; // don't ever lower
663 : }
664 0 :
665 0 : mTransactionCaps = caps;
666 : mPriority = pri;
667 : if (mTransaction && (mUsingSpdyVersion != SpdyVersion::NONE)) {
668 : return AddTransaction(trans, pri);
669 0 : }
670 0 :
671 0 : NS_ENSURE_ARG_POINTER(trans);
672 : NS_ENSURE_TRUE(!mTransaction, NS_ERROR_IN_PROGRESS);
673 :
674 0 : // If TCP fast Open has been used and conection was idle for some time
675 : // we will be cautious and watch out for bug 1395494.
676 : if (mNPNComplete && (mFastOpenStatus == TFO_DATA_SENT) &&
677 0 : gHttpHandler->CheckIfConnectionIsStalledOnlyIfIdleForThisAmountOfSeconds() &&
678 : IdleTime() >= gHttpHandler->CheckIfConnectionIsStalledOnlyIfIdleForThisAmountOfSeconds()) {
679 : // If a connection was using the TCP FastOpen and it was idle for a
680 0 : // long time we should check for stalls like bug 1395494.
681 : mCheckNetworkStallsWithTFO = true;
682 : // Also reset last write. We should start measuring a stall time only
683 : // after we really write a request to the network.
684 : mLastRequestBytesSentTime = 0;
685 0 : }
686 : // reset the read timers to wash away any idle time
687 0 : mLastWriteTime = mLastReadTime = PR_IntervalNow();
688 0 :
689 0 : // Connection failures are Activated() just like regular transacions.
690 : // If we don't have a confirmation of a connected socket then test it
691 0 : // with a write() to get relevant error code.
692 : if (!mConnectedTransport) {
693 0 : uint32_t count;
694 : mSocketOutCondition = NS_ERROR_FAILURE;
695 0 : if (mSocketOut) {
696 0 : mSocketOutCondition = mSocketOut->Write("", 0, &count);
697 0 : }
698 0 : if (NS_FAILED(mSocketOutCondition) &&
699 : mSocketOutCondition != NS_BASE_STREAM_WOULD_BLOCK) {
700 : LOG(("nsHttpConnection::Activate [this=%p] Bad Socket %" PRIx32 "\n",
701 : this, static_cast<uint32_t>(mSocketOutCondition)));
702 : mSocketOut->AsyncWait(nullptr, 0, 0, nullptr);
703 0 : mTransaction = trans;
704 0 : CloseTransaction(mTransaction, mSocketOutCondition);
705 0 : return mSocketOutCondition;
706 0 : }
707 : }
708 :
709 0 : // Update security callbacks
710 : nsCOMPtr<nsIInterfaceRequestor> callbacks;
711 0 : trans->GetSecurityCallbacks(getter_AddRefs(callbacks));
712 0 : SetSecurityCallbacks(callbacks);
713 : SetupSSL();
714 :
715 0 : // take ownership of the transaction
716 : mTransaction = trans;
717 :
718 : MOZ_ASSERT(!mIdleMonitoring, "Activating a connection with an Idle Monitor");
719 0 : mIdleMonitoring = false;
720 0 :
721 0 : // set mKeepAlive according to what will be requested
722 0 : mKeepAliveMask = mKeepAlive = (caps & NS_HTTP_ALLOW_KEEPALIVE);
723 :
724 0 : // need to handle HTTP CONNECT tunnels if this is the first time if
725 : // we are tunneling through a proxy
726 : nsresult rv = NS_OK;
727 : if (mTransaction->ConnectionInfo()->UsingConnect() && !mCompletedProxyConnect) {
728 0 : rv = SetupProxyConnect();
729 : if (NS_FAILED(rv))
730 : goto failed_activation;
731 0 : mProxyConnectInProgress = true;
732 : }
733 0 :
734 0 : // Clear the per activation counter
735 0 : mCurrentBytesRead = 0;
736 :
737 0 : // The overflow state is not needed between activations
738 0 : mInputOverflow = nullptr;
739 0 :
740 : mResponseTimeoutEnabled = gHttpHandler->ResponseTimeoutEnabled() &&
741 : mTransaction->ResponseTimeout() > 0 &&
742 : mTransaction->ResponseTimeoutEnabled();
743 :
744 0 : rv = StartShortLivedTCPKeepalives();
745 0 : if (NS_FAILED(rv)) {
746 0 : LOG(("nsHttpConnection::Activate [%p] "
747 0 : "StartShortLivedTCPKeepalives failed rv[0x%" PRIx32 "]",
748 : this, static_cast<uint32_t>(rv)));
749 : }
750 0 :
751 : if (mTLSFilter) {
752 0 : rv = mTLSFilter->SetProxiedTransaction(trans);
753 : NS_ENSURE_SUCCESS(rv, rv);
754 : mTransaction = mTLSFilter;
755 0 : }
756 0 :
757 : trans->OnActivated();
758 :
759 : rv = OnOutputStreamReady(mSocketOut);
760 :
761 : failed_activation:
762 : if (NS_FAILED(rv)) {
763 0 : mTransaction = nullptr;
764 : }
765 0 :
766 : return rv;
767 : }
768 0 :
769 : void
770 0 : nsHttpConnection::SetupSSL()
771 : {
772 0 : LOG(("nsHttpConnection::SetupSSL %p caps=0x%X %s\n",
773 : this, mTransactionCaps, mConnInfo->HashKey().get()));
774 :
775 : if (mSetupSSLCalled) // do only once
776 : return;
777 0 : mSetupSSLCalled = true;
778 :
779 0 : if (mNPNComplete)
780 : return;
781 :
782 : // we flip this back to false if SetNPNList succeeds at the end
783 : // of this function
784 : mNPNComplete = true;
785 0 :
786 0 : if (!mConnInfo->FirstHopSSL() || mForcePlainText) {
787 0 : return;
788 : }
789 0 :
790 0 : // if we are connected to the proxy with TLS, start the TLS
791 : // flow immediately without waiting for a CONNECT sequence.
792 0 : DebugOnly<nsresult> rv;
793 : if (mInSpdyTunnel) {
794 : rv = InitSSLParams(false, true);
795 : } else {
796 : bool usingHttpsProxy = mConnInfo->UsingHttpsProxy();
797 : rv = InitSSLParams(usingHttpsProxy, usingHttpsProxy);
798 : }
799 : MOZ_ASSERT(NS_SUCCEEDED(rv));
800 0 : }
801 :
802 0 : // The naming of NPN is historical - this function creates the basic
803 : // offer list for both NPN and ALPN. ALPN validation callbacks are made
804 0 : // now before the handshake is complete, and NPN validation callbacks
805 0 : // are made during the handshake.
806 : nsresult
807 : nsHttpConnection::SetupNPNList(nsISSLSocketControl *ssl, uint32_t caps)
808 : {
809 : nsTArray<nsCString> protocolArray;
810 :
811 : nsCString npnToken = mConnInfo->GetNPNToken();
812 : if (npnToken.IsEmpty()) {
813 0 : // The first protocol is used as the fallback if none of the
814 : // protocols supported overlap with the server's list.
815 0 : // When using ALPN the advertised preferences are protocolArray indicies
816 0 : // {1, .., N, 0} in decreasing order.
817 0 : // For NPN, In the case of overlap, matching priority is driven by
818 0 : // the order of the server's advertisement - with index 0 used when
819 0 : // there is no match.
820 0 : protocolArray.AppendElement(NS_LITERAL_CSTRING("http/1.1"));
821 0 :
822 0 : if (gHttpHandler->IsSpdyEnabled() &&
823 : !(caps & NS_HTTP_DISALLOW_SPDY)) {
824 : LOG(("nsHttpConnection::SetupSSL Allow SPDY NPN selection"));
825 : const SpdyInformation *info = gHttpHandler->SpdyInfo();
826 : for (uint32_t index = SpdyInformation::kCount; index > 0; --index) {
827 0 : if (info->ProtocolEnabled(index - 1) &&
828 : info->ALPNCallbacks[index - 1](ssl)) {
829 0 : protocolArray.AppendElement(info->VersionString[index - 1]);
830 : }
831 : }
832 0 : }
833 0 : } else {
834 : LOG(("nsHttpConnection::SetupSSL limiting NPN selection to %s",
835 0 : npnToken.get()));
836 : protocolArray.AppendElement(npnToken);
837 : }
838 :
839 0 : nsresult rv = ssl->SetNPNList(protocolArray);
840 : LOG(("nsHttpConnection::SetupNPNList %p %" PRIx32 "\n",
841 : this, static_cast<uint32_t>(rv)));
842 0 : return rv;
843 0 : }
844 :
845 : nsresult
846 : nsHttpConnection::AddTransaction(nsAHttpTransaction *httpTransaction,
847 : int32_t priority)
848 : {
849 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
850 0 : MOZ_ASSERT(mSpdySession && (mUsingSpdyVersion != SpdyVersion::NONE),
851 : "AddTransaction to live http connection without spdy");
852 0 :
853 0 : // If this is a wild card nshttpconnection (i.e. a spdy proxy) then
854 0 : // it is important to start the stream using the specific connection
855 0 : // info of the transaction to ensure it is routed on the right tunnel
856 :
857 0 : nsHttpConnectionInfo *transCI = httpTransaction->ConnectionInfo();
858 :
859 : bool needTunnel = transCI->UsingHttpsProxy();
860 0 : needTunnel = needTunnel && !mTLSFilter;
861 0 : needTunnel = needTunnel && transCI->UsingConnect();
862 0 : needTunnel = needTunnel && httpTransaction->QueryHttpTransaction();
863 :
864 : LOG(("nsHttpConnection::AddTransaction for SPDY%s",
865 : needTunnel ? " over tunnel" : ""));
866 :
867 0 : if (!mSpdySession->AddStream(httpTransaction, priority,
868 : needTunnel, mCallbacks)) {
869 : MOZ_ASSERT(false); // this cannot happen!
870 : httpTransaction->Close(NS_ERROR_ABORT);
871 : return NS_ERROR_FAILURE;
872 0 : }
873 :
874 0 : Unused << ResumeSend();
875 : return NS_OK;
876 : }
877 0 :
878 : void
879 : nsHttpConnection::Close(nsresult reason, bool aIsShutdown)
880 0 : {
881 0 : LOG(("nsHttpConnection::Close [this=%p reason=%" PRIx32 "]\n",
882 0 : this, static_cast<uint32_t>(reason)));
883 :
884 0 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
885 0 :
886 0 : // Ensure TCP keepalive timer is stopped.
887 : if (mTCPKeepaliveTransitionTimer) {
888 : mTCPKeepaliveTransitionTimer->Cancel();
889 0 : mTCPKeepaliveTransitionTimer = nullptr;
890 0 : }
891 0 : if (mForceSendTimer) {
892 : mForceSendTimer->Cancel();
893 0 : mForceSendTimer = nullptr;
894 : }
895 :
896 : if (NS_FAILED(reason)) {
897 0 : if (mIdleMonitoring)
898 0 : EndIdleMonitoring();
899 0 :
900 0 : mTLSFilter = nullptr;
901 :
902 : // The connection and security errors clear out alt-svc mappings
903 0 : // in case any previously validated ones are now invalid
904 0 : if (((reason == NS_ERROR_NET_RESET) ||
905 : (NS_ERROR_GET_MODULE(reason) == NS_ERROR_MODULE_SECURITY))
906 : && mConnInfo && !(mTransactionCaps & NS_HTTP_ERROR_SOFTLY)) {
907 : gHttpHandler->ConnMgr()->ClearHostMapping(mConnInfo);
908 : }
909 :
910 : if (mSocketTransport) {
911 : mSocketTransport->SetEventSink(nullptr, nullptr);
912 0 :
913 : // If there are bytes sitting in the input queue then read them
914 : // into a junk buffer to avoid generating a tcp rst by closing a
915 : // socket with data pending. TLS is a classic case of this where
916 0 : // a Alert record might be superfulous to a clean HTTP/SPDY shutdown.
917 0 : // Never block to do this and limit it to a small amount of data.
918 0 : // During shutdown just be fast!
919 0 : if (mSocketIn && !aIsShutdown) {
920 : char buffer[4000];
921 0 : uint32_t count, total = 0;
922 0 : nsresult rv;
923 : do {
924 : rv = mSocketIn->Read(buffer, 4000, &count);
925 0 : if (NS_SUCCEEDED(rv))
926 0 : total += count;
927 0 : }
928 0 : while (NS_SUCCEEDED(rv) && count > 0 && total < 64000);
929 : LOG(("nsHttpConnection::Close drained %d bytes\n", total));
930 0 : }
931 :
932 0 : mSocketTransport->SetSecurityCallbacks(nullptr);
933 : mSocketTransport->Close(reason);
934 : if (mSocketOut)
935 : mSocketOut->AsyncWait(nullptr, 0, 0, nullptr);
936 0 : }
937 : mKeepAlive = false;
938 0 : }
939 : }
940 0 :
941 : // called on the socket thread
942 : nsresult
943 0 : nsHttpConnection::InitSSLParams(bool connectingToProxy, bool proxyStartSSL)
944 0 : {
945 0 : LOG(("nsHttpConnection::InitSSLParams [this=%p] connectingToProxy=%d\n",
946 : this, connectingToProxy));
947 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
948 :
949 0 : nsresult rv;
950 0 : nsCOMPtr<nsISupports> securityInfo;
951 : GetSecurityInfo(getter_AddRefs(securityInfo));
952 : if (!securityInfo) {
953 : return NS_ERROR_FAILURE;
954 0 : }
955 0 :
956 0 : nsCOMPtr<nsISSLSocketControl> ssl = do_QueryInterface(securityInfo, &rv);
957 : if (NS_FAILED(rv)){
958 : return rv;
959 : }
960 :
961 0 : if (proxyStartSSL) {
962 0 : rv = ssl->ProxyStartSSL();
963 0 : if (NS_FAILED(rv)){
964 : return rv;
965 : }
966 : }
967 :
968 : if (NS_SUCCEEDED(SetupNPNList(ssl, mTransactionCaps))) {
969 : LOG(("InitSSLParams Setting up SPDY Negotiation OK"));
970 0 : mNPNComplete = false;
971 : }
972 0 :
973 0 : return NS_OK;
974 0 : }
975 0 :
976 0 : void
977 0 : nsHttpConnection::DontReuse()
978 0 : {
979 0 : LOG(("nsHttpConnection::DontReuse %p spdysession=%p\n", this, mSpdySession.get()));
980 : mKeepAliveMask = false;
981 : mKeepAlive = false;
982 0 : mDontReuse = true;
983 : mIdleTimeout = 0;
984 0 : if (mSpdySession)
985 0 : mSpdySession->DontReuse();
986 : }
987 :
988 : bool
989 : nsHttpConnection::TestJoinConnection(const nsACString &hostname, int32_t port)
990 : {
991 0 : if (mSpdySession && CanDirectlyActivate()) {
992 : return mSpdySession->TestJoinConnection(hostname, port);
993 0 : }
994 0 : return false;
995 : }
996 :
997 : bool
998 : nsHttpConnection::JoinConnection(const nsACString &hostname, int32_t port)
999 : {
1000 0 : if (mSpdySession && CanDirectlyActivate()) {
1001 : return mSpdySession->JoinConnection(hostname, port);
1002 0 : }
1003 : return false;
1004 : }
1005 :
1006 0 : bool
1007 0 : nsHttpConnection::CanReuse()
1008 : {
1009 : if (mDontReuse || !mRemainingConnectionUses) {
1010 : return false;
1011 : }
1012 0 :
1013 0 : if ((mTransaction ? (mTransaction->IsDone() ? 0U : 1U) : 0U) >=
1014 : mRemainingConnectionUses) {
1015 0 : return false;
1016 : }
1017 0 :
1018 : bool canReuse;
1019 : if (mSpdySession) {
1020 : canReuse = mSpdySession->CanReuse();
1021 : } else {
1022 : canReuse = IsKeepAlive();
1023 : }
1024 : canReuse = canReuse && (IdleTime() < mIdleTimeout) && IsAlive();
1025 0 :
1026 0 : // An idle persistent connection should not have data waiting to be read
1027 0 : // before a request is sent. Data here is likely a 408 timeout response
1028 0 : // which we would deal with later on through the restart logic, but that
1029 : // path is more expensive than just closing the socket now.
1030 :
1031 : uint64_t dataSize;
1032 : if (canReuse && mSocketIn && (mUsingSpdyVersion == SpdyVersion::NONE) &&
1033 : mHttp1xTransactionCount &&
1034 : NS_SUCCEEDED(mSocketIn->Available(&dataSize)) && dataSize) {
1035 : LOG(("nsHttpConnection::CanReuse %p %s"
1036 : "Socket not reusable because read data pending (%" PRIu64 ") on it.\n",
1037 0 : this, mConnInfo->Origin(), dataSize));
1038 : canReuse = false;
1039 : }
1040 : return canReuse;
1041 : }
1042 :
1043 0 : bool
1044 0 : nsHttpConnection::CanDirectlyActivate()
1045 : {
1046 : // return true if a new transaction can be addded to ths connection at any
1047 : // time through Activate(). In practice this means this is a healthy SPDY
1048 6 : // connection with room for more concurrent streams.
1049 :
1050 0 : return UsingSpdy() && CanReuse() &&
1051 24 : mSpdySession && mSpdySession->RoomForMoreStreams();
1052 : }
1053 :
1054 : PRIntervalTime
1055 : nsHttpConnection::IdleTime()
1056 : {
1057 2 : return mSpdySession ?
1058 : mSpdySession->IdleTime() : (PR_IntervalNow() - mLastReadTime);
1059 2 : }
1060 :
1061 : // returns the number of seconds left before the allowable idle period
1062 2 : // expires, or 0 if the period has already expied.
1063 : uint32_t
1064 : nsHttpConnection::TimeToLive()
1065 : {
1066 2 : LOG(("nsHttpConnection::TTL: %p %s idle %d timeout %d\n",
1067 : this, mConnInfo->Origin(), IdleTime(), mIdleTimeout));
1068 :
1069 : if (IdleTime() >= mIdleTimeout) {
1070 0 : return 0;
1071 0 : }
1072 :
1073 : uint32_t timeToLive = PR_IntervalToSeconds(mIdleTimeout - IdleTime());
1074 :
1075 : // a positive amount of time can be rounded to 0. Because 0 is used
1076 : // as the expiration signal, round all values from 0 to 1 up to 1.
1077 2 : if (!timeToLive) {
1078 : timeToLive = 1;
1079 4 : }
1080 : return timeToLive;
1081 : }
1082 :
1083 : bool
1084 2 : nsHttpConnection::IsAlive()
1085 : {
1086 : if (!mSocketTransport || !mConnectedTransport)
1087 0 : return false;
1088 1 :
1089 0 : // SocketTransport::IsAlive can run the SSL state machine, so make sure
1090 : // the NPN options are set before that happens.
1091 : SetupSSL();
1092 :
1093 : bool alive;
1094 : nsresult rv = mSocketTransport->IsAlive(&alive);
1095 : if (NS_FAILED(rv))
1096 : alive = false;
1097 :
1098 : //#define TEST_RESTART_LOGIC
1099 2 : #ifdef TEST_RESTART_LOGIC
1100 : if (!alive) {
1101 : LOG(("pretending socket is still alive to test restart logic\n"));
1102 : alive = true;
1103 0 : }
1104 : #endif
1105 0 :
1106 : return alive;
1107 0 : }
1108 0 :
1109 0 : void
1110 : nsHttpConnection::SetUrgentStartPreferred(bool urgent)
1111 0 : {
1112 : if (mExperienced && !mUrgentStartPreferredKnown) {
1113 : // Set only according the first ever dispatched non-null transaction
1114 : mUrgentStartPreferredKnown = true;
1115 : mUrgentStartPreferred = urgent;
1116 : LOG(("nsHttpConnection::SetUrgentStartPreferred [this=%p urgent=%d]", this, urgent));
1117 : }
1118 2 : }
1119 :
1120 : //----------------------------------------------------------------------------
1121 : // nsHttpConnection::nsAHttpConnection compatible methods
1122 : //----------------------------------------------------------------------------
1123 2 :
1124 : nsresult
1125 : nsHttpConnection::OnHeadersAvailable(nsAHttpTransaction *trans,
1126 0 : nsHttpRequestHead *requestHead,
1127 0 : nsHttpResponseHead *responseHead,
1128 2 : bool *reset)
1129 : {
1130 2 : LOG(("nsHttpConnection::OnHeadersAvailable [this=%p trans=%p response-head=%p]\n",
1131 : this, trans, responseHead));
1132 0 :
1133 0 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1134 0 : NS_ENSURE_ARG_POINTER(trans);
1135 : MOZ_ASSERT(responseHead, "No response head?");
1136 :
1137 : if (mInSpdyTunnel) {
1138 : DebugOnly<nsresult> rv =
1139 : responseHead->SetHeader(nsHttp::X_Firefox_Spdy_Proxy,
1140 : NS_LITERAL_CSTRING("true"));
1141 : MOZ_ASSERT(NS_SUCCEEDED(rv));
1142 : }
1143 :
1144 0 : // we won't change our keep-alive policy unless the server has explicitly
1145 0 : // told us to do so.
1146 0 :
1147 0 : // inspect the connection headers for keep-alive info provided the
1148 0 : // transaction completed successfully. In the case of a non-sensical close
1149 1 : // and keep-alive favor the close out of conservatism.
1150 :
1151 : bool explicitKeepAlive = false;
1152 0 : bool explicitClose = responseHead->HasHeaderValue(nsHttp::Connection, "close") ||
1153 0 : responseHead->HasHeaderValue(nsHttp::Proxy_Connection, "close");
1154 2 : if (!explicitClose)
1155 : explicitKeepAlive = responseHead->HasHeaderValue(nsHttp::Connection, "keep-alive") ||
1156 : responseHead->HasHeaderValue(nsHttp::Proxy_Connection, "keep-alive");
1157 :
1158 : // deal with 408 Server Timeouts
1159 0 : uint16_t responseStatus = responseHead->Status();
1160 0 : static const PRIntervalTime k1000ms = PR_MillisecondsToInterval(1000);
1161 0 : if (responseStatus == 408) {
1162 0 : // If this error could be due to a persistent connection reuse then
1163 : // we pass an error code of NS_ERROR_NET_RESET to
1164 : // trigger the transaction 'restart' mechanism. We tell it to reset its
1165 : // response headers so that it will be ready to receive the new response.
1166 : if (mIsReused && ((PR_IntervalNow() - mLastWriteTime) < k1000ms)) {
1167 : Close(NS_ERROR_NET_RESET);
1168 : *reset = true;
1169 : return NS_OK;
1170 : }
1171 :
1172 0 : // timeouts that are not caused by persistent connection reuse should
1173 2 : // not be retried for browser compatibility reasons. bug 907800. The
1174 : // server driven close is implicit in the 408.
1175 0 : explicitClose = true;
1176 0 : explicitKeepAlive = false;
1177 : }
1178 0 :
1179 : if ((responseHead->Version() < HttpVersion::v1_1) ||
1180 : (requestHead->Version() < HttpVersion::v1_1)) {
1181 : // HTTP/1.0 connections are by default NOT persistent
1182 2 : if (explicitKeepAlive)
1183 : mKeepAlive = true;
1184 2 : else
1185 : mKeepAlive = false;
1186 : }
1187 : else {
1188 : // HTTP/1.1 connections are by default persistent
1189 : mKeepAlive = !explicitClose;
1190 : }
1191 : mKeepAliveMask = mKeepAlive;
1192 :
1193 0 : // if this connection is persistent, then the server may send a "Keep-Alive"
1194 0 : // header specifying the maximum number of times the connection can be
1195 0 : // reused as well as the maximum amount of time the connection can be idle
1196 2 : // before the server will close it. we ignore the max reuse count, because
1197 : // a "keep-alive" connection is by definition capable of being reused, and
1198 0 : // we only care about being able to reuse it once. if a timeout is not
1199 0 : // specified then we use our advertized timeout value.
1200 0 : bool foundKeepAliveMax = false;
1201 0 : if (mKeepAlive) {
1202 : nsAutoCString keepAlive;
1203 2 : Unused << responseHead->GetHeader(nsHttp::Keep_Alive, keepAlive);
1204 :
1205 0 : if (mUsingSpdyVersion == SpdyVersion::NONE) {
1206 0 : const char *cp = PL_strcasestr(keepAlive.get(), "timeout=");
1207 0 : if (cp)
1208 0 : mIdleTimeout = PR_SecondsToInterval((uint32_t) atoi(cp + 8));
1209 0 : else
1210 0 : mIdleTimeout = gHttpHandler->IdleTimeout() * mDefaultTimeoutFactor;
1211 :
1212 : cp = PL_strcasestr(keepAlive.get(), "max=");
1213 : if (cp) {
1214 : int maxUses = atoi(cp + 4);
1215 2 : if (maxUses > 0) {
1216 : foundKeepAliveMax = true;
1217 : mRemainingConnectionUses = static_cast<uint32_t>(maxUses);
1218 : }
1219 0 : }
1220 2 : }
1221 :
1222 : LOG(("Connection can be reused [this=%p idle-timeout=%usec]\n",
1223 : this, PR_IntervalToSeconds(mIdleTimeout)));
1224 : }
1225 :
1226 0 : if (!foundKeepAliveMax && mRemainingConnectionUses && (mUsingSpdyVersion == SpdyVersion::NONE))
1227 0 : --mRemainingConnectionUses;
1228 :
1229 0 : // If we're doing a proxy connect, we need to check whether or not
1230 : // it was successful. If so, we have to reset the transaction and step-up
1231 0 : // the socket connection if using SSL. Finally, we have to wake up the
1232 0 : // socket write request.
1233 : if (mProxyConnectStream) {
1234 0 : MOZ_ASSERT(mUsingSpdyVersion == SpdyVersion::NONE,
1235 0 : "SPDY NPN Complete while using proxy connect stream");
1236 0 : mProxyConnectStream = nullptr;
1237 : bool isHttps =
1238 0 : mTransaction ? mTransaction->ConnectionInfo()->EndToEndSSL() :
1239 0 : mConnInfo->EndToEndSSL();
1240 0 :
1241 : if (responseStatus == 200) {
1242 0 : LOG(("proxy CONNECT succeeded! endtoendssl=%d\n", isHttps));
1243 : *reset = true;
1244 : nsresult rv;
1245 0 : if (isHttps) {
1246 0 : if (mConnInfo->UsingHttpsProxy()) {
1247 : LOG(("%p new TLSFilterTransaction %s %d\n",
1248 0 : this, mConnInfo->Origin(), mConnInfo->OriginPort()));
1249 0 : SetupSecondaryTLS();
1250 0 : }
1251 :
1252 0 : rv = InitSSLParams(false, true);
1253 : LOG(("InitSSLParams [rv=%" PRIx32 "]\n", static_cast<uint32_t>(rv)));
1254 : }
1255 0 : mCompletedProxyConnect = true;
1256 0 : mProxyConnectInProgress = false;
1257 : rv = mSocketOut->AsyncWait(this, 0, 0, nullptr);
1258 : // XXX what if this fails -- need to handle this error
1259 : MOZ_ASSERT(NS_SUCCEEDED(rv), "mSocketOut->AsyncWait failed");
1260 0 : }
1261 2 : else {
1262 : LOG(("proxy CONNECT failed! endtoendssl=%d\n", isHttps));
1263 : mTransaction->SetProxyConnectFailed();
1264 : }
1265 0 : }
1266 0 :
1267 0 : nsAutoCString upgradeReq;
1268 : bool hasUpgradeReq = NS_SUCCEEDED(requestHead->GetHeader(nsHttp::Upgrade,
1269 : upgradeReq));
1270 0 : // Don't use persistent connection for Upgrade unless there's an auth failure:
1271 0 : // some proxies expect to see auth response on persistent connection.
1272 0 : if (hasUpgradeReq && responseStatus != 401 && responseStatus != 407) {
1273 : LOG(("HTTP Upgrade in play - disable keepalive\n"));
1274 : DontReuse();
1275 0 : }
1276 0 :
1277 : if (responseStatus == 101) {
1278 0 : nsAutoCString upgradeResp;
1279 : bool hasUpgradeResp = NS_SUCCEEDED(responseHead->GetHeader(
1280 : nsHttp::Upgrade,
1281 : upgradeResp));
1282 0 : if (!hasUpgradeReq || !hasUpgradeResp ||
1283 : !nsHttp::FindToken(upgradeResp.get(), upgradeReq.get(),
1284 : HTTP_HEADER_VALUE_SEPS)) {
1285 0 : LOG(("HTTP 101 Upgrade header mismatch req = %s, resp = %s\n",
1286 : upgradeReq.get(),
1287 : !upgradeResp.IsEmpty() ? upgradeResp.get() :
1288 : "RESPONSE's nsHttp::Upgrade is empty"));
1289 2 : Close(NS_ERROR_ABORT);
1290 : }
1291 2 : else {
1292 : LOG(("HTTP Upgrade Response to %s\n", upgradeResp.get()));
1293 : }
1294 : }
1295 2 :
1296 : mLastHttpResponseVersion = responseHead->Version();
1297 2 :
1298 : return NS_OK;
1299 2 : }
1300 :
1301 : bool
1302 : nsHttpConnection::IsReused()
1303 : {
1304 0 : if (mIsReused)
1305 0 : return true;
1306 : if (!mConsiderReusedAfterInterval)
1307 : return false;
1308 :
1309 0 : // ReusedAfter allows a socket to be consider reused only after a certain
1310 : // interval of time has passed
1311 0 : return (PR_IntervalNow() - mConsiderReusedAfterEpoch) >=
1312 0 : mConsiderReusedAfterInterval;
1313 0 : }
1314 :
1315 : void
1316 0 : nsHttpConnection::SetIsReusedAfter(uint32_t afterMilliseconds)
1317 : {
1318 : mConsiderReusedAfterEpoch = PR_IntervalNow();
1319 : mConsiderReusedAfterInterval = PR_MillisecondsToInterval(afterMilliseconds);
1320 0 : }
1321 :
1322 0 : nsresult
1323 : nsHttpConnection::TakeTransport(nsISocketTransport **aTransport,
1324 0 : nsIAsyncInputStream **aInputStream,
1325 : nsIAsyncOutputStream **aOutputStream)
1326 : {
1327 0 : if (mUsingSpdyVersion != SpdyVersion::NONE)
1328 0 : return NS_ERROR_FAILURE;
1329 : if (mTransaction && !mTransaction->IsDone())
1330 : return NS_ERROR_IN_PROGRESS;
1331 0 : if (!(mSocketTransport && mSocketIn && mSocketOut))
1332 0 : return NS_ERROR_NOT_INITIALIZED;
1333 0 :
1334 0 : if (mInputOverflow)
1335 : mSocketIn = mInputOverflow.forget();
1336 0 :
1337 0 : // Change TCP Keepalive frequency to long-lived if currently short-lived.
1338 : if (mTCPKeepaliveConfig == kTCPKeepaliveShortLivedConfig) {
1339 0 : if (mTCPKeepaliveTransitionTimer) {
1340 0 : mTCPKeepaliveTransitionTimer->Cancel();
1341 : mTCPKeepaliveTransitionTimer = nullptr;
1342 : }
1343 : nsresult rv = StartLongLivedTCPKeepalives();
1344 : LOG(("nsHttpConnection::TakeTransport [%p] calling "
1345 : "StartLongLivedTCPKeepalives", this));
1346 0 : if (NS_FAILED(rv)) {
1347 0 : LOG(("nsHttpConnection::TakeTransport [%p] "
1348 : "StartLongLivedTCPKeepalives failed rv[0x%" PRIx32 "]",
1349 : this, static_cast<uint32_t>(rv)));
1350 : }
1351 : }
1352 :
1353 0 : mSocketTransport->SetSecurityCallbacks(nullptr);
1354 0 : mSocketTransport->SetEventSink(nullptr, nullptr);
1355 0 :
1356 0 : // The nsHttpConnection will go away soon, so if there is a TLS Filter
1357 0 : // being used (e.g. for wss CONNECT tunnel from a proxy connected to
1358 0 : // via https) that filter needs to take direct control of the
1359 0 : // streams
1360 : if (mTLSFilter) {
1361 : nsCOMPtr<nsIAsyncInputStream> ref1(mSocketIn);
1362 0 : nsCOMPtr<nsIAsyncOutputStream> ref2(mSocketOut);
1363 0 : mTLSFilter->newIODriver(ref1, ref2,
1364 0 : getter_AddRefs(mSocketIn),
1365 : getter_AddRefs(mSocketOut));
1366 0 : mTLSFilter = nullptr;
1367 : }
1368 :
1369 : mSocketTransport.forget(aTransport);
1370 0 : mSocketIn.forget(aInputStream);
1371 : mSocketOut.forget(aOutputStream);
1372 0 :
1373 : return NS_OK;
1374 : }
1375 0 :
1376 : uint32_t
1377 : nsHttpConnection::ReadTimeoutTick(PRIntervalTime now)
1378 : {
1379 0 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1380 0 :
1381 : // make sure timer didn't tick before Activate()
1382 : if (!mTransaction)
1383 0 : return UINT32_MAX;
1384 :
1385 0 : // Spdy implements some timeout handling using the SPDY ping frame.
1386 0 : if (mSpdySession) {
1387 : return mSpdySession->ReadTimeoutTick(now);
1388 : }
1389 :
1390 0 : uint32_t nextTickAfter = UINT32_MAX;
1391 : // Timeout if the response is taking too long to arrive.
1392 0 : if (mResponseTimeoutEnabled) {
1393 0 : NS_WARNING_ASSERTION(
1394 : gHttpHandler->ResponseTimeoutEnabled(),
1395 : "Timing out a response, but response timeout is disabled!");
1396 :
1397 0 : PRIntervalTime initialResponseDelta = now - mLastWriteTime;
1398 :
1399 : if (initialResponseDelta > mTransaction->ResponseTimeout()) {
1400 0 : LOG(("canceling transaction: no response for %ums: timeout is %dms\n",
1401 0 : PR_IntervalToMilliseconds(initialResponseDelta),
1402 : PR_IntervalToMilliseconds(mTransaction->ResponseTimeout())));
1403 0 :
1404 0 : mResponseTimeoutEnabled = false;
1405 0 :
1406 : // This will also close the connection
1407 : CloseTransaction(mTransaction, NS_ERROR_NET_TIMEOUT);
1408 : return UINT32_MAX;
1409 0 : }
1410 0 : nextTickAfter = PR_IntervalToSeconds(mTransaction->ResponseTimeout()) -
1411 0 : PR_IntervalToSeconds(initialResponseDelta);
1412 0 : nextTickAfter = std::max(nextTickAfter, 1U);
1413 0 : }
1414 :
1415 0 : // Check for the TCP Fast Open related stalls.
1416 0 : if (mCheckNetworkStallsWithTFO && mLastRequestBytesSentTime) {
1417 0 : PRIntervalTime initialResponseDelta = now - mLastRequestBytesSentTime;
1418 : if (initialResponseDelta >= gHttpHandler->FastOpenStallsTimeout()) {
1419 : gHttpHandler->IncrementFastOpenStallsCounter();
1420 : mCheckNetworkStallsWithTFO = false;
1421 0 : } else {
1422 : uint32_t next = PR_IntervalToSeconds(gHttpHandler->FastOpenStallsTimeout()) -
1423 : PR_IntervalToSeconds(initialResponseDelta);
1424 : nextTickAfter = std::min(nextTickAfter, next);
1425 : }
1426 0 : }
1427 0 :
1428 0 : if (!mNPNComplete) {
1429 : // We can reuse mLastWriteTime here, because it is set when the
1430 : // connection is activated and only change when a transaction
1431 : // succesfullu write to the socket and this can only happen after
1432 : // the TLS handshake is done.
1433 : PRIntervalTime initialTLSDelta = now - mLastWriteTime;
1434 0 : if (initialTLSDelta > gHttpHandler->TLSHandshakeTimeout()) {
1435 0 : LOG(("canceling transaction: tls handshake takes too long: tls handshake "
1436 : "last %ums, timeout is %dms.",
1437 : PR_IntervalToMilliseconds(initialTLSDelta),
1438 : gHttpHandler->TLSHandshakeTimeout()));
1439 0 :
1440 : // This will also close the connection
1441 : CloseTransaction(mTransaction, NS_ERROR_NET_TIMEOUT);
1442 : return UINT32_MAX;
1443 0 : }
1444 : }
1445 0 :
1446 0 : return nextTickAfter;
1447 : }
1448 0 :
1449 : void
1450 0 : nsHttpConnection::UpdateTCPKeepalive(nsITimer *aTimer, void *aClosure)
1451 : {
1452 : MOZ_ASSERT(aTimer);
1453 : MOZ_ASSERT(aClosure);
1454 :
1455 0 : nsHttpConnection *self = static_cast<nsHttpConnection*>(aClosure);
1456 :
1457 : if (NS_WARN_IF(self->mUsingSpdyVersion != SpdyVersion::NONE)) {
1458 : return;
1459 0 : }
1460 0 :
1461 0 : // Do not reduce keepalive probe frequency for idle connections.
1462 : if (self->mIdleMonitoring) {
1463 : return;
1464 : }
1465 :
1466 : nsresult rv = self->StartLongLivedTCPKeepalives();
1467 : if (NS_FAILED(rv)) {
1468 8 : LOG(("nsHttpConnection::UpdateTCPKeepalive [%p] "
1469 : "StartLongLivedTCPKeepalives failed rv[0x%" PRIx32 "]",
1470 0 : self, static_cast<uint32_t>(rv)));
1471 8 : }
1472 : }
1473 :
1474 0 : void
1475 23 : nsHttpConnection::GetSecurityInfo(nsISupports **secinfo)
1476 : {
1477 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1478 : LOG(("nsHttpConnection::GetSecurityInfo trans=%p tlsfilter=%p socket=%p\n",
1479 0 : mTransaction.get(), mTLSFilter.get(), mSocketTransport.get()));
1480 16 :
1481 : if (mTransaction &&
1482 : NS_SUCCEEDED(mTransaction->GetTransactionSecurityInfo(secinfo))) {
1483 : return;
1484 0 : }
1485 24 :
1486 : if (mTLSFilter &&
1487 : NS_SUCCEEDED(mTLSFilter->GetTransactionSecurityInfo(secinfo))) {
1488 : return;
1489 0 : }
1490 :
1491 : if (mSocketTransport &&
1492 : NS_SUCCEEDED(mSocketTransport->GetSecurityInfo(secinfo))) {
1493 3 : return;
1494 : }
1495 6 :
1496 : *secinfo = nullptr;
1497 : }
1498 :
1499 : void
1500 : nsHttpConnection::SetSecurityCallbacks(nsIInterfaceRequestor* aCallbacks)
1501 0 : {
1502 3 : MutexAutoLock lock(mCallbacksLock);
1503 : // This is called both on and off the main thread. For JS-implemented
1504 : // callbacks, we requires that the call happen on the main thread, but
1505 0 : // for C++-implemented callbacks we don't care. Use a pointer holder with
1506 : // strict checking disabled.
1507 0 : mCallbacks = new nsMainThreadPtrHolder<nsIInterfaceRequestor>(
1508 : "nsHttpConnection::mCallbacks", aCallbacks, false);
1509 0 : }
1510 0 :
1511 0 : nsresult
1512 : nsHttpConnection::PushBack(const char *data, uint32_t length)
1513 : {
1514 0 : LOG(("nsHttpConnection::PushBack [this=%p, length=%d]\n", this, length));
1515 0 :
1516 : if (mInputOverflow) {
1517 : NS_ERROR("nsHttpConnection::PushBack only one buffer supported");
1518 0 : return NS_ERROR_UNEXPECTED;
1519 : }
1520 :
1521 0 : mInputOverflow = new nsPreloadedStream(mSocketIn, data, length);
1522 : return NS_OK;
1523 : }
1524 0 :
1525 : class HttpConnectionForceIO : public Runnable
1526 : {
1527 0 : public:
1528 : HttpConnectionForceIO(nsHttpConnection* aConn,
1529 0 : bool doRecv,
1530 : bool isFastOpenForce)
1531 0 : : Runnable("net::HttpConnectionForceIO")
1532 : , mConn(aConn)
1533 0 : , mDoRecv(doRecv)
1534 : , mIsFastOpenForce(isFastOpenForce)
1535 0 : {
1536 0 : }
1537 :
1538 0 : NS_IMETHOD Run() override
1539 : {
1540 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1541 :
1542 : if (mDoRecv) {
1543 : if (!mConn->mSocketIn)
1544 0 : return NS_OK;
1545 : return mConn->OnInputStreamReady(mConn->mSocketIn);
1546 : }
1547 :
1548 : // This runnable will be called when the ForceIO timer expires
1549 0 : // (mIsFastOpenForce==false) or during the TCP Fast Open to force
1550 0 : // writes (mIsFastOpenForce==true).
1551 0 : if (mIsFastOpenForce && !mConn->mWaitingFor0RTTResponse) {
1552 : // If we have exit the TCP Fast Open in the meantime we can skip
1553 : // this.
1554 0 : return NS_OK;
1555 : }
1556 : if (!mIsFastOpenForce) {
1557 0 : MOZ_ASSERT(mConn->mForceSendPending);
1558 : mConn->mForceSendPending = false;
1559 : }
1560 :
1561 : if (!mConn->mSocketOut) {
1562 : return NS_OK;
1563 : }
1564 : return mConn->OnOutputStreamReady(mConn->mSocketOut);
1565 : }
1566 0 : private:
1567 : RefPtr<nsHttpConnection> mConn;
1568 0 : bool mDoRecv;
1569 : bool mIsFastOpenForce;
1570 0 : };
1571 :
1572 0 : nsresult
1573 0 : nsHttpConnection::ResumeSend()
1574 0 : {
1575 : LOG(("nsHttpConnection::ResumeSend [this=%p]\n", this));
1576 :
1577 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1578 :
1579 0 : if (mSocketOut) {
1580 0 : nsresult rv = mSocketOut->AsyncWait(this, 0, 0, nullptr);
1581 0 : LOG(("nsHttpConnection::ResumeSend [this=%p] "
1582 : "mWaitingFor0RTTResponse=%d mForceSendDuringFastOpenPending=%d "
1583 : "mReceivedSocketWouldBlockDuringFastOpen=%d\n",
1584 0 : this, mWaitingFor0RTTResponse, mForceSendDuringFastOpenPending,
1585 0 : mReceivedSocketWouldBlockDuringFastOpen));
1586 : if (mWaitingFor0RTTResponse && !mForceSendDuringFastOpenPending &&
1587 : !mReceivedSocketWouldBlockDuringFastOpen &&
1588 : NS_SUCCEEDED(rv)) {
1589 : // During TCP Fast Open, poll does not work properly so we will
1590 0 : // trigger writes manually.
1591 0 : mForceSendDuringFastOpenPending = true;
1592 : NS_DispatchToCurrentThread(new HttpConnectionForceIO(this, false, true));
1593 : }
1594 : return rv;
1595 2 : }
1596 :
1597 2 : NS_NOTREACHED("no socket output stream");
1598 : return NS_ERROR_UNEXPECTED;
1599 2 : }
1600 :
1601 1 : nsresult
1602 0 : nsHttpConnection::ResumeRecv()
1603 : {
1604 : LOG(("nsHttpConnection::ResumeRecv [this=%p]\n", this));
1605 :
1606 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1607 :
1608 : if (mFastOpen) {
1609 : LOG(("nsHttpConnection::ResumeRecv - do not waiting for read during "
1610 : "fast open! [this=%p]\n", this));
1611 : return NS_OK;
1612 : }
1613 2 :
1614 : // the mLastReadTime timestamp is used for finding slowish readers
1615 0 : // and can be pretty sensitive. For that reason we actually reset it
1616 2 : // when we ask to read (resume recv()) so that when we get called back
1617 : // with actual read data in OnSocketReadable() we are only measuring
1618 0 : // the latency between those two acts and not all the processing that
1619 0 : // may get done before the ResumeRecv() call
1620 : mLastReadTime = PR_IntervalNow();
1621 :
1622 : if (mSocketIn)
1623 0 : return mSocketIn->AsyncWait(this, 0, 0, nullptr);
1624 :
1625 0 : NS_NOTREACHED("no socket input stream");
1626 0 : return NS_ERROR_UNEXPECTED;
1627 0 : }
1628 0 :
1629 0 : void
1630 0 : nsHttpConnection::ForceSendIO(nsITimer *aTimer, void *aClosure)
1631 : {
1632 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1633 0 : nsHttpConnection *self = static_cast<nsHttpConnection *>(aClosure);
1634 : MOZ_ASSERT(aTimer == self->mForceSendTimer);
1635 0 : self->mForceSendTimer = nullptr;
1636 : NS_DispatchToCurrentThread(new HttpConnectionForceIO(self, false, false));
1637 : }
1638 :
1639 : nsresult
1640 : nsHttpConnection::MaybeForceSendIO()
1641 : {
1642 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1643 0 : // due to bug 1213084 sometimes real I/O events do not get serviced when
1644 : // NSPR derived I/O events are ready and this can cause a deadlock with
1645 : // https over https proxying. Normally we would expect the write callback to
1646 0 : // be invoked before this timer goes off, but set it at the old windows
1647 0 : // tick interval (kForceDelay) as a backup for those circumstances.
1648 0 : static const uint32_t kForceDelay = 17; //ms
1649 0 :
1650 : if (mForceSendPending) {
1651 : return NS_OK;
1652 : }
1653 : MOZ_ASSERT(!mForceSendTimer);
1654 0 : mForceSendPending = true;
1655 : return NS_NewTimerWithFuncCallback(
1656 : getter_AddRefs(mForceSendTimer),
1657 : nsHttpConnection::ForceSendIO,
1658 : this,
1659 0 : kForceDelay,
1660 : nsITimer::TYPE_ONE_SHOT,
1661 0 : "net::nsHttpConnection::MaybeForceSendIO");
1662 0 : }
1663 :
1664 0 : // trigger an asynchronous read
1665 : nsresult
1666 : nsHttpConnection::ForceRecv()
1667 : {
1668 : LOG(("nsHttpConnection::ForceRecv [this=%p]\n", this));
1669 0 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1670 :
1671 0 : return NS_DispatchToCurrentThread(new HttpConnectionForceIO(this, true, false));
1672 0 : }
1673 :
1674 0 : // trigger an asynchronous write
1675 0 : nsresult
1676 : nsHttpConnection::ForceSend()
1677 0 : {
1678 : LOG(("nsHttpConnection::ForceSend [this=%p]\n", this));
1679 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1680 :
1681 2 : if (mTLSFilter) {
1682 : return mTLSFilter->NudgeTunnel(this);
1683 0 : }
1684 0 : return MaybeForceSendIO();
1685 0 : }
1686 2 :
1687 : void
1688 0 : nsHttpConnection::BeginIdleMonitoring()
1689 0 : {
1690 0 : LOG(("nsHttpConnection::BeginIdleMonitoring [this=%p]\n", this));
1691 0 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1692 2 : MOZ_ASSERT(!mTransaction, "BeginIdleMonitoring() while active");
1693 : MOZ_ASSERT(mUsingSpdyVersion == SpdyVersion::NONE, "Idle monitoring of spdy not allowed");
1694 :
1695 0 : LOG(("Entering Idle Monitoring Mode [this=%p]", this));
1696 : mIdleMonitoring = true;
1697 0 : if (mSocketIn)
1698 0 : mSocketIn->AsyncWait(this, 0, 0, nullptr);
1699 0 : }
1700 :
1701 0 : void
1702 0 : nsHttpConnection::EndIdleMonitoring()
1703 0 : {
1704 0 : LOG(("nsHttpConnection::EndIdleMonitoring [this=%p]\n", this));
1705 0 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1706 : MOZ_ASSERT(!mTransaction, "EndIdleMonitoring() while active");
1707 0 :
1708 : if (mIdleMonitoring) {
1709 : LOG(("Leaving Idle Monitoring Mode [this=%p]", this));
1710 3 : mIdleMonitoring = false;
1711 : if (mSocketIn)
1712 3 : mSocketIn->AsyncWait(nullptr, 0, 0, nullptr);
1713 0 : }
1714 : }
1715 3 :
1716 : HttpVersion
1717 : nsHttpConnection::Version()
1718 : {
1719 : if (mUsingSpdyVersion != SpdyVersion::NONE) {
1720 : return nsHttp::GetHttpVersionFromSpdy(mUsingSpdyVersion);
1721 : }
1722 : return mLastHttpResponseVersion;
1723 2 : }
1724 :
1725 : //-----------------------------------------------------------------------------
1726 2 : // nsHttpConnection <private>
1727 : //-----------------------------------------------------------------------------
1728 :
1729 0 : void
1730 : nsHttpConnection::CloseTransaction(nsAHttpTransaction *trans, nsresult reason,
1731 2 : bool aIsShutdown)
1732 : {
1733 0 : LOG(("nsHttpConnection::CloseTransaction[this=%p trans=%p reason=%" PRIx32 "]\n",
1734 0 : this, trans, static_cast<uint32_t>(reason)));
1735 :
1736 : MOZ_ASSERT((trans == mTransaction) ||
1737 0 : (mTLSFilter && mTLSFilter->Transaction() == trans));
1738 2 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1739 :
1740 0 : if (mCurrentBytesRead > mMaxBytesRead)
1741 0 : mMaxBytesRead = mCurrentBytesRead;
1742 :
1743 0 : // mask this error code because its not a real error.
1744 0 : if (reason == NS_BASE_STREAM_CLOSED)
1745 : reason = NS_OK;
1746 :
1747 0 : if (mUsingSpdyVersion != SpdyVersion::NONE) {
1748 2 : DontReuse();
1749 : // if !mSpdySession then mUsingSpdyVersion must be false for canreuse()
1750 2 : mUsingSpdyVersion = SpdyVersion::NONE;
1751 0 : mSpdySession = nullptr;
1752 : }
1753 :
1754 : if (mTransaction) {
1755 0 : mHttp1xTransactionCount += mTransaction->Http1xTransactionCount();
1756 0 :
1757 : mTransaction->Close(reason);
1758 : mTransaction = nullptr;
1759 2 : }
1760 0 :
1761 : {
1762 : MutexAutoLock lock(mCallbacksLock);
1763 : mCallbacks = nullptr;
1764 : }
1765 0 :
1766 2 : if (NS_FAILED(reason) && (reason != NS_BINDING_RETARGETED)) {
1767 : Close(reason, aIsShutdown);
1768 : }
1769 0 :
1770 : // flag the connection as reused here for convenience sake. certainly
1771 : // it might be going away instead ;-)
1772 : mIsReused = true;
1773 : }
1774 :
1775 : nsresult
1776 : nsHttpConnection::ReadFromStream(nsIInputStream *input,
1777 0 : void *closure,
1778 0 : const char *buf,
1779 : uint32_t offset,
1780 : uint32_t count,
1781 : uint32_t *countRead)
1782 0 : {
1783 : // thunk for nsIInputStream instance
1784 : nsHttpConnection *conn = (nsHttpConnection *) closure;
1785 : return conn->OnReadSegment(buf, count, countRead);
1786 1 : }
1787 :
1788 : nsresult
1789 : nsHttpConnection::OnReadSegment(const char *buf,
1790 0 : uint32_t count,
1791 0 : uint32_t *countRead)
1792 : {
1793 : if (count == 0) {
1794 1 : // some ReadSegments implementations will erroneously call the writer
1795 2 : // to consume 0 bytes worth of data. we must protect against this case
1796 0 : // or else we'd end up closing the socket prematurely.
1797 0 : NS_ERROR("bad ReadSegments implementation");
1798 0 : return NS_ERROR_FAILURE; // stop iterating
1799 : }
1800 2 :
1801 2 : nsresult rv = mSocketOut->Write(buf, count, countRead);
1802 0 : if (NS_FAILED(rv))
1803 2 : mSocketOutCondition = rv;
1804 : else if (*countRead == 0)
1805 : mSocketOutCondition = NS_BASE_STREAM_CLOSED;
1806 0 : else {
1807 : mLastWriteTime = PR_IntervalNow();
1808 : mSocketOutCondition = NS_OK; // reset condition
1809 : if (!mProxyConnectInProgress)
1810 7 : mTotalBytesWritten += *countRead;
1811 : }
1812 7 :
1813 : return mSocketOutCondition;
1814 : }
1815 :
1816 : nsresult
1817 0 : nsHttpConnection::OnSocketWritable()
1818 : {
1819 : LOG(("nsHttpConnection::OnSocketWritable [this=%p] host=%s\n",
1820 7 : this, mConnInfo->Origin()));
1821 0 :
1822 : nsresult rv;
1823 0 : uint32_t transactionBytes;
1824 : bool again = true;
1825 9 :
1826 9 : // Prevent STS thread from being blocked by single OnOutputStreamReady callback.
1827 9 : const uint32_t maxWriteAttempts = 128;
1828 9 : uint32_t writeAttempts = 0;
1829 :
1830 : mForceSendDuringFastOpenPending = false;
1831 :
1832 : do {
1833 : ++writeAttempts;
1834 : rv = mSocketOutCondition = NS_OK;
1835 0 : transactionBytes = 0;
1836 0 :
1837 0 : // The SSL handshake must be completed before the transaction->readsegments()
1838 0 : // processing can proceed because we need to know how to format the
1839 0 : // request differently for http/1, http/2, spdy, etc.. and that is
1840 : // negotiated with NPN/ALPN in the SSL handshake.
1841 :
1842 0 : if (mConnInfo->UsingHttpsProxy() &&
1843 0 : !EnsureNPNComplete(rv, transactionBytes)) {
1844 : MOZ_ASSERT(!transactionBytes);
1845 0 : mSocketOutCondition = NS_BASE_STREAM_WOULD_BLOCK;
1846 9 : } else if (mProxyConnectStream) {
1847 0 : // If we're need an HTTP/1 CONNECT tunnel through a proxy
1848 1 : // send it before doing the SSL handshake
1849 1 : LOG((" writing CONNECT request stream\n"));
1850 : rv = mProxyConnectStream->ReadSegments(ReadFromStream, this,
1851 8 : nsIOService::gDefaultSegmentSize,
1852 0 : &transactionBytes);
1853 0 : } else if (!EnsureNPNComplete(rv, transactionBytes)) {
1854 0 : if (NS_SUCCEEDED(rv) && !transactionBytes &&
1855 : NS_SUCCEEDED(mSocketOutCondition)) {
1856 : mSocketOutCondition = NS_BASE_STREAM_WOULD_BLOCK;
1857 4 : }
1858 2 : } else if (!mTransaction) {
1859 0 : rv = NS_ERROR_FAILURE;
1860 0 : LOG((" No Transaction In OnSocketWritable\n"));
1861 : } else if (NS_SUCCEEDED(rv)) {
1862 :
1863 0 : // for non spdy sessions let the connection manager know
1864 4 : if (!mReportedSpdy) {
1865 8 : mReportedSpdy = true;
1866 0 : MOZ_ASSERT(!mEverUsedSpdy);
1867 4 : gHttpHandler->ConnMgr()->ReportSpdyConnection(this, false);
1868 : }
1869 :
1870 9 : LOG((" writing transaction request stream\n"));
1871 : mProxyConnectInProgress = false;
1872 : rv = mTransaction->ReadSegmentsAgain(this, nsIOService::gDefaultSegmentSize,
1873 : &transactionBytes, &again);
1874 : mContentBytesWritten += transactionBytes;
1875 : }
1876 :
1877 9 : LOG(("nsHttpConnection::OnSocketWritable %p "
1878 0 : "ReadSegments returned [rv=%" PRIx32 " read=%u "
1879 0 : "sock-cond=%" PRIx32 " again=%d]\n",
1880 : this, static_cast<uint32_t>(rv), transactionBytes,
1881 : static_cast<uint32_t>(mSocketOutCondition), again));
1882 0 :
1883 : // XXX some streams return NS_BASE_STREAM_CLOSED to indicate EOF.
1884 0 : if (rv == NS_BASE_STREAM_CLOSED && !mTransaction->IsDone()) {
1885 : rv = NS_OK;
1886 0 : transactionBytes = 0;
1887 : }
1888 :
1889 0 : if (!again && (mFastOpen || mWaitingFor0RTTResponse)) {
1890 0 : // Continue waiting;
1891 0 : rv = mSocketOut->AsyncWait(this, 0, 0, nullptr);
1892 : }
1893 0 : if (NS_FAILED(rv)) {
1894 : // if the transaction didn't want to write any more data, then
1895 : // wait for the transaction to call ResumeSend.
1896 0 : if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
1897 1 : rv = NS_OK;
1898 5 : if (mFastOpen || mWaitingFor0RTTResponse) {
1899 0 : // Continue waiting;
1900 0 : rv = mSocketOut->AsyncWait(this, 0, 0, nullptr);
1901 0 : }
1902 : }
1903 5 : again = false;
1904 : } else if (NS_FAILED(mSocketOutCondition)) {
1905 : if (mSocketOutCondition == NS_BASE_STREAM_WOULD_BLOCK) {
1906 0 : if (mTLSFilter) {
1907 : LOG((" blocked tunnel (handshake?)\n"));
1908 0 : rv = mTLSFilter->NudgeTunnel(this);
1909 4 : } else {
1910 1 : rv = mSocketOut->AsyncWait(this, 0, 0, nullptr); // continue writing
1911 : }
1912 2 : } else {
1913 : rv = mSocketOutCondition;
1914 0 : }
1915 4 : again = false;
1916 : } else if (!transactionBytes) {
1917 : rv = NS_OK;
1918 :
1919 : if (mWaitingFor0RTTResponse || mFastOpen) {
1920 : // Wait for tls handshake to finish or waiting for connect.
1921 : rv = mSocketOut->AsyncWait(this, 0, 0, nullptr);
1922 1 : } else if (mTransaction) { // in case the ReadSegments stack called CloseTransaction()
1923 : //
1924 4 : // at this point we've written out the entire transaction, and now we
1925 0 : // must wait for the server's response. we manufacture a status message
1926 0 : // here to reflect the fact that we are waiting. this message will be
1927 : // trumped (overwritten) if the server responds quickly.
1928 : //
1929 1 : mTransaction->OnTransportStatus(mSocketTransport,
1930 : NS_NET_STATUS_WAITING_FOR,
1931 1 : 0);
1932 2 : if (mCheckNetworkStallsWithTFO) {
1933 0 : mLastRequestBytesSentTime = PR_IntervalNow();
1934 0 : }
1935 0 :
1936 : rv = ResumeRecv(); // start reading
1937 : }
1938 11 : again = false;
1939 : } else if (writeAttempts >= maxWriteAttempts) {
1940 0 : LOG((" yield for other transactions\n"));
1941 : rv = mSocketOut->AsyncWait(this, 0, 0, nullptr); // continue writing
1942 : again = false;
1943 : }
1944 0 : // write more to the socket until error or end-of-request...
1945 : } while (again && gHttpHandler->Active());
1946 :
1947 : return rv;
1948 1 : }
1949 :
1950 : nsresult
1951 : nsHttpConnection::OnWriteSegment(char *buf,
1952 0 : uint32_t count,
1953 0 : uint32_t *countWritten)
1954 : {
1955 : if (count == 0) {
1956 2 : // some WriteSegments implementations will erroneously call the reader
1957 0 : // to provide 0 bytes worth of data. we must protect against this case
1958 : // or else we'd end up closing the socket prematurely.
1959 0 : NS_ERROR("bad WriteSegments implementation");
1960 : return NS_ERROR_FAILURE; // stop iterating
1961 : }
1962 0 :
1963 2 : if (ChaosMode::isActive(ChaosFeature::IOAmounts) &&
1964 0 : ChaosMode::randomUint32LessThan(2)) {
1965 2 : // read 1...count bytes
1966 0 : count = ChaosMode::randomUint32LessThan(count) + 1;
1967 : }
1968 0 :
1969 : nsresult rv = mSocketIn->Read(buf, count, countWritten);
1970 2 : if (NS_FAILED(rv))
1971 : mSocketInCondition = rv;
1972 0 : else if (*countWritten == 0)
1973 : mSocketInCondition = NS_BASE_STREAM_CLOSED;
1974 : else
1975 : mSocketInCondition = NS_OK; // reset condition
1976 0 :
1977 : mCheckNetworkStallsWithTFO = false;
1978 2 :
1979 : return mSocketInCondition;
1980 0 : }
1981 2 :
1982 : nsresult
1983 : nsHttpConnection::OnSocketReadable()
1984 2 : {
1985 : LOG(("nsHttpConnection::OnSocketReadable [this=%p]\n", this));
1986 0 :
1987 0 : PRIntervalTime now = PR_IntervalNow();
1988 : PRIntervalTime delta = now - mLastReadTime;
1989 :
1990 0 : // Reset mResponseTimeoutEnabled to stop response timeout checks.
1991 0 : mResponseTimeoutEnabled = false;
1992 :
1993 : if (mKeepAliveMask && (delta >= mMaxHangTime)) {
1994 : LOG(("max hang time exceeded!\n"));
1995 : // give the handler a chance to create a new persistent connection to
1996 0 : // this host if we've been busy for too long.
1997 : mKeepAliveMask = false;
1998 : Unused << gHttpHandler->ProcessPendingQ(mConnInfo);
1999 : }
2000 2 :
2001 : // Reduce the estimate of the time since last read by up to 1 RTT to
2002 4 : // accommodate exhausted sender TCP congestion windows or minor I/O delays.
2003 4 : mLastReadTime = now;
2004 :
2005 : nsresult rv;
2006 : uint32_t n;
2007 : bool again = true;
2008 :
2009 : do {
2010 : if (!mProxyConnectInProgress && !mNPNComplete) {
2011 0 : // Unless we are setting up a tunnel via CONNECT, prevent reading
2012 : // from the socket until the results of NPN
2013 : // negotiation are known (which is determined from the write path).
2014 : // If the server speaks SPDY it is likely the readable data here is
2015 : // a spdy settings frame and without NPN it would be misinterpreted
2016 : // as HTTP/*
2017 4 :
2018 4 : LOG(("nsHttpConnection::OnSocketReadable %p return due to inactive "
2019 0 : "tunnel setup but incomplete NPN state\n", this));
2020 4 : rv = NS_OK;
2021 : break;
2022 : }
2023 0 :
2024 : mSocketInCondition = NS_OK;
2025 : rv = mTransaction->
2026 2 : WriteSegmentsAgain(this, nsIOService::gDefaultSegmentSize, &n, &again);
2027 0 : LOG(("nsHttpConnection::OnSocketReadable %p trans->ws rv=%" PRIx32
2028 : " n=%d socketin=%" PRIx32 "\n",
2029 0 : this, static_cast<uint32_t>(rv), n, static_cast<uint32_t>(mSocketInCondition)));
2030 : if (NS_FAILED(rv)) {
2031 0 : // if the transaction didn't want to take any more data, then
2032 0 : // wait for the transaction to call ResumeRecv.
2033 2 : if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
2034 : rv = NS_OK;
2035 0 : }
2036 0 : again = false;
2037 : } else {
2038 : mCurrentBytesRead += n;
2039 : mTotalBytesRead += n;
2040 0 : if (NS_FAILED(mSocketInCondition)) {
2041 : // continue waiting for the socket if necessary...
2042 : if (mSocketInCondition == NS_BASE_STREAM_WOULD_BLOCK) {
2043 : rv = ResumeRecv();
2044 6 : } else {
2045 : rv = mSocketInCondition;
2046 1 : }
2047 : again = false;
2048 : }
2049 : }
2050 0 : // read more from the socket until error...
2051 : } while (again && gHttpHandler->Active());
2052 0 :
2053 0 : return rv;
2054 0 : }
2055 :
2056 : void
2057 0 : nsHttpConnection::SetupSecondaryTLS()
2058 0 : {
2059 0 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
2060 : MOZ_ASSERT(!mTLSFilter);
2061 0 : LOG(("nsHttpConnection %p SetupSecondaryTLS %s %d\n",
2062 0 : this, mConnInfo->Origin(), mConnInfo->OriginPort()));
2063 :
2064 0 : nsHttpConnectionInfo *ci = nullptr;
2065 : if (mTransaction) {
2066 : ci = mTransaction->ConnectionInfo();
2067 0 : }
2068 : if (!ci) {
2069 0 : ci = mConnInfo;
2070 0 : }
2071 : MOZ_ASSERT(ci);
2072 0 :
2073 : mTLSFilter = new TLSFilterTransaction(mTransaction,
2074 : ci->Origin(), ci->OriginPort(), this, this);
2075 0 :
2076 : if (mTransaction) {
2077 0 : mTransaction = mTLSFilter;
2078 0 : }
2079 : }
2080 :
2081 0 : void
2082 0 : nsHttpConnection::SetInSpdyTunnel(bool arg)
2083 0 : {
2084 0 : MOZ_ASSERT(mTLSFilter);
2085 : mInSpdyTunnel = arg;
2086 :
2087 0 : // don't setup another tunnel :)
2088 : mProxyConnectStream = nullptr;
2089 : mCompletedProxyConnect = true;
2090 : mProxyConnectInProgress = false;
2091 0 : }
2092 0 :
2093 : nsresult
2094 : nsHttpConnection::MakeConnectString(nsAHttpTransaction *trans,
2095 : nsHttpRequestHead *request,
2096 0 : nsACString &result)
2097 : {
2098 0 : result.Truncate();
2099 0 : if (!trans->ConnectionInfo()) {
2100 0 : return NS_ERROR_NOT_INITIALIZED;
2101 0 : }
2102 :
2103 : DebugOnly<nsresult> rv;
2104 0 :
2105 0 : rv = nsHttpHandler::GenerateHostPort(
2106 0 : nsDependentCString(trans->ConnectionInfo()->Origin()),
2107 0 : trans->ConnectionInfo()->OriginPort(), result);
2108 0 : MOZ_ASSERT(NS_SUCCEEDED(rv));
2109 :
2110 : // CONNECT host:port HTTP/1.1
2111 0 : request->SetMethod(NS_LITERAL_CSTRING("CONNECT"));
2112 0 : request->SetVersion(gHttpHandler->HttpVersion());
2113 0 : request->SetRequestURI(result);
2114 0 : rv = request->SetHeader(nsHttp::User_Agent, gHttpHandler->UserAgent());
2115 : MOZ_ASSERT(NS_SUCCEEDED(rv));
2116 :
2117 : // a CONNECT is always persistent
2118 0 : rv = request->SetHeader(nsHttp::Proxy_Connection, NS_LITERAL_CSTRING("keep-alive"));
2119 0 : MOZ_ASSERT(NS_SUCCEEDED(rv));
2120 : rv = request->SetHeader(nsHttp::Connection, NS_LITERAL_CSTRING("keep-alive"));
2121 0 : MOZ_ASSERT(NS_SUCCEEDED(rv));
2122 0 :
2123 : // all HTTP/1.1 requests must include a Host header (even though it
2124 : // may seem redundant in this case; see bug 82388).
2125 : rv = request->SetHeader(nsHttp::Host, result);
2126 : MOZ_ASSERT(NS_SUCCEEDED(rv));
2127 0 :
2128 0 : nsAutoCString val;
2129 : if (NS_SUCCEEDED(trans->RequestHead()->GetHeader(
2130 : nsHttp::Proxy_Authorization,
2131 0 : val))) {
2132 0 : // we don't know for sure if this authorization is intended for the
2133 0 : // SSL proxy, so we add it just in case.
2134 0 : rv = request->SetHeader(nsHttp::Proxy_Authorization, val);
2135 : MOZ_ASSERT(NS_SUCCEEDED(rv));
2136 : }
2137 :
2138 0 : result.Truncate();
2139 : request->Flatten(result, false);
2140 0 : result.AppendLiteral("\r\n");
2141 0 : return NS_OK;
2142 0 : }
2143 :
2144 : nsresult
2145 0 : nsHttpConnection::SetupProxyConnect()
2146 0 : {
2147 0 : LOG(("nsHttpConnection::SetupProxyConnect [this=%p]\n", this));
2148 0 : NS_ENSURE_TRUE(!mProxyConnectStream, NS_ERROR_ALREADY_INITIALIZED);
2149 : MOZ_ASSERT(mUsingSpdyVersion == SpdyVersion::NONE,
2150 : "SPDY NPN Complete while using proxy connect stream");
2151 0 :
2152 : nsAutoCString buf;
2153 : nsHttpRequestHead request;
2154 : nsresult rv = MakeConnectString(mTransaction, &request, buf);
2155 3 : if (NS_FAILED(rv)) {
2156 : return rv;
2157 0 : }
2158 : return NS_NewCStringInputStream(getter_AddRefs(mProxyConnectStream), std::move(buf));
2159 : }
2160 6 :
2161 0 : nsresult
2162 : nsHttpConnection::StartShortLivedTCPKeepalives()
2163 : {
2164 : if (mUsingSpdyVersion != SpdyVersion::NONE) {
2165 3 : return NS_OK;
2166 0 : }
2167 0 : MOZ_ASSERT(mSocketTransport);
2168 3 : if (!mSocketTransport) {
2169 : return NS_ERROR_NOT_INITIALIZED;
2170 0 : }
2171 0 :
2172 : nsresult rv = NS_OK;
2173 : int32_t idleTimeS = -1;
2174 3 : int32_t retryIntervalS = -1;
2175 9 : if (gHttpHandler->TCPKeepaliveEnabledForShortLivedConns()) {
2176 0 : // Set the idle time.
2177 0 : idleTimeS = gHttpHandler->GetTCPKeepaliveShortLivedIdleTime();
2178 : LOG(("nsHttpConnection::StartShortLivedTCPKeepalives[%p] "
2179 : "idle time[%ds].", this, idleTimeS));
2180 0 :
2181 3 : retryIntervalS =
2182 : std::max<int32_t>((int32_t)PR_IntervalToSeconds(mRtt), 1);
2183 0 : rv = mSocketTransport->SetKeepaliveVals(idleTimeS, retryIntervalS);
2184 0 : if (NS_FAILED(rv)) {
2185 : return rv;
2186 3 : }
2187 : rv = mSocketTransport->SetKeepaliveEnabled(true);
2188 : mTCPKeepaliveConfig = kTCPKeepaliveShortLivedConfig;
2189 : } else {
2190 : rv = mSocketTransport->SetKeepaliveEnabled(false);
2191 6 : mTCPKeepaliveConfig = kTCPKeepaliveDisabled;
2192 : }
2193 0 : if (NS_FAILED(rv)) {
2194 : return rv;
2195 : }
2196 6 :
2197 0 : // Start a timer to move to long-lived keepalive config.
2198 : if(!mTCPKeepaliveTransitionTimer) {
2199 : mTCPKeepaliveTransitionTimer =
2200 : NS_NewTimer();
2201 0 : }
2202 0 :
2203 0 : if (mTCPKeepaliveTransitionTimer) {
2204 : int32_t time = gHttpHandler->GetTCPKeepaliveShortLivedTime();
2205 3 :
2206 0 : // Adjust |time| to ensure a full set of keepalive probes can be sent
2207 3 : // at the end of the short-lived phase.
2208 : if (gHttpHandler->TCPKeepaliveEnabledForShortLivedConns()) {
2209 : if (NS_WARN_IF(!gSocketTransportService)) {
2210 0 : return NS_ERROR_NOT_INITIALIZED;
2211 : }
2212 : int32_t probeCount = -1;
2213 : rv = gSocketTransportService->GetKeepaliveProbeCount(&probeCount);
2214 3 : if (NS_WARN_IF(NS_FAILED(rv))) {
2215 : return rv;
2216 6 : }
2217 : if (NS_WARN_IF(probeCount <= 0)) {
2218 : return NS_ERROR_UNEXPECTED;
2219 3 : }
2220 : // Add time for final keepalive probes, and 2 seconds for a buffer.
2221 6 : time += ((probeCount) * retryIntervalS) - (time % idleTimeS) + 2;
2222 : }
2223 : mTCPKeepaliveTransitionTimer->InitWithNamedFuncCallback(
2224 0 : nsHttpConnection::UpdateTCPKeepalive,
2225 : this,
2226 : (uint32_t)time * 1000,
2227 : nsITimer::TYPE_ONE_SHOT,
2228 : "net::nsHttpConnection::StartShortLivedTCPKeepalives");
2229 : } else {
2230 : NS_WARNING("nsHttpConnection::StartShortLivedTCPKeepalives failed to "
2231 0 : "create timer.");
2232 : }
2233 0 :
2234 0 : return NS_OK;
2235 : }
2236 :
2237 0 : nsresult
2238 0 : nsHttpConnection::StartLongLivedTCPKeepalives()
2239 : {
2240 : MOZ_ASSERT(mUsingSpdyVersion == SpdyVersion::NONE, "Don't use TCP Keepalive with SPDY!");
2241 : if (NS_WARN_IF(mUsingSpdyVersion != SpdyVersion::NONE)) {
2242 0 : return NS_OK;
2243 0 : }
2244 : MOZ_ASSERT(mSocketTransport);
2245 0 : if (!mSocketTransport) {
2246 0 : return NS_ERROR_NOT_INITIALIZED;
2247 : }
2248 :
2249 : nsresult rv = NS_OK;
2250 0 : if (gHttpHandler->TCPKeepaliveEnabledForLongLivedConns()) {
2251 0 : // Increase the idle time.
2252 0 : int32_t idleTimeS = gHttpHandler->GetTCPKeepaliveLongLivedIdleTime();
2253 : LOG(("nsHttpConnection::StartLongLivedTCPKeepalives[%p] idle time[%ds]",
2254 : this, idleTimeS));
2255 :
2256 : int32_t retryIntervalS =
2257 0 : std::max<int32_t>((int32_t)PR_IntervalToSeconds(mRtt), 1);
2258 0 : rv = mSocketTransport->SetKeepaliveVals(idleTimeS, retryIntervalS);
2259 0 : if (NS_FAILED(rv)) {
2260 : return rv;
2261 : }
2262 :
2263 0 : // Ensure keepalive is enabled, if current status is disabled.
2264 : if (mTCPKeepaliveConfig == kTCPKeepaliveDisabled) {
2265 0 : rv = mSocketTransport->SetKeepaliveEnabled(true);
2266 0 : if (NS_FAILED(rv)) {
2267 : return rv;
2268 : }
2269 0 : }
2270 : mTCPKeepaliveConfig = kTCPKeepaliveLongLivedConfig;
2271 : } else {
2272 0 : rv = mSocketTransport->SetKeepaliveEnabled(false);
2273 : mTCPKeepaliveConfig = kTCPKeepaliveDisabled;
2274 : }
2275 :
2276 0 : if (NS_FAILED(rv)) {
2277 : return rv;
2278 0 : }
2279 0 : return NS_OK;
2280 : }
2281 :
2282 : nsresult
2283 0 : nsHttpConnection::DisableTCPKeepalives()
2284 0 : {
2285 0 : MOZ_ASSERT(mSocketTransport);
2286 0 : if (!mSocketTransport) {
2287 : return NS_ERROR_NOT_INITIALIZED;
2288 : }
2289 0 :
2290 : LOG(("nsHttpConnection::DisableTCPKeepalives [%p]", this));
2291 0 : if (mTCPKeepaliveConfig != kTCPKeepaliveDisabled) {
2292 0 : nsresult rv = mSocketTransport->SetKeepaliveEnabled(false);
2293 0 : if (NS_FAILED(rv)) {
2294 : return rv;
2295 : }
2296 : mTCPKeepaliveConfig = kTCPKeepaliveDisabled;
2297 : }
2298 : if (mTCPKeepaliveTransitionTimer) {
2299 : mTCPKeepaliveTransitionTimer->Cancel();
2300 : mTCPKeepaliveTransitionTimer = nullptr;
2301 : }
2302 0 : return NS_OK;
2303 0 : }
2304 :
2305 0 : //-----------------------------------------------------------------------------
2306 0 : // nsHttpConnection::nsISupports
2307 0 : //-----------------------------------------------------------------------------
2308 0 :
2309 6 : NS_IMPL_ADDREF(nsHttpConnection)
2310 3 : NS_IMPL_RELEASE(nsHttpConnection)
2311 0 :
2312 21 : NS_INTERFACE_MAP_BEGIN(nsHttpConnection)
2313 : NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
2314 : NS_INTERFACE_MAP_ENTRY(nsIInputStreamCallback)
2315 : NS_INTERFACE_MAP_ENTRY(nsIOutputStreamCallback)
2316 : NS_INTERFACE_MAP_ENTRY(nsITransportEventSink)
2317 : NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
2318 : NS_INTERFACE_MAP_ENTRY_CONCRETE(nsHttpConnection)
2319 : NS_INTERFACE_MAP_END
2320 2 :
2321 : //-----------------------------------------------------------------------------
2322 0 : // nsHttpConnection::nsIInputStreamCallback
2323 2 : //-----------------------------------------------------------------------------
2324 :
2325 2 : // called on the socket transport thread
2326 0 : NS_IMETHODIMP
2327 : nsHttpConnection::OnInputStreamReady(nsIAsyncInputStream *in)
2328 : {
2329 : MOZ_ASSERT(in == mSocketIn, "unexpected stream");
2330 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
2331 :
2332 : if (mIdleMonitoring) {
2333 : MOZ_ASSERT(!mTransaction, "Idle Input Event While Active");
2334 0 :
2335 0 : // The only read event that is protocol compliant for an idle connection
2336 0 : // is an EOF, which we check for with CanReuse(). If the data is
2337 0 : // something else then just ignore it and suspend checking for EOF -
2338 : // our normal timers or protocol stack are the place to deal with
2339 : // any exception logic.
2340 0 :
2341 : if (!CanReuse()) {
2342 : LOG(("Server initiated close of idle conn %p\n", this));
2343 : Unused << gHttpHandler->ConnMgr()->CloseIdleConnection(this);
2344 : return NS_OK;
2345 4 : }
2346 0 :
2347 : LOG(("Input data on idle conn %p, but not closing yet\n", this));
2348 : return NS_OK;
2349 : }
2350 2 :
2351 2 : // if the transaction was dropped...
2352 4 : if (!mTransaction) {
2353 : LOG((" no transaction; ignoring event\n"));
2354 : return NS_OK;
2355 : }
2356 :
2357 : nsresult rv = OnSocketReadable();
2358 : if (NS_FAILED(rv))
2359 : CloseTransaction(mTransaction, rv);
2360 :
2361 : return NS_OK;
2362 7 : }
2363 :
2364 1 : //-----------------------------------------------------------------------------
2365 14 : // nsHttpConnection::nsIOutputStreamCallback
2366 : //-----------------------------------------------------------------------------
2367 14 :
2368 0 : NS_IMETHODIMP
2369 : nsHttpConnection::OnOutputStreamReady(nsIAsyncOutputStream *out)
2370 : {
2371 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
2372 7 : MOZ_ASSERT(out == mSocketOut, "unexpected socket");
2373 7 : // if the transaction was dropped...
2374 0 : if (!mTransaction) {
2375 : LOG((" no transaction; ignoring event\n"));
2376 : return NS_OK;
2377 : }
2378 :
2379 : nsresult rv = OnSocketWritable();
2380 : if (NS_FAILED(rv))
2381 : CloseTransaction(mTransaction, rv);
2382 :
2383 : return NS_OK;
2384 4 : }
2385 :
2386 : //-----------------------------------------------------------------------------
2387 : // nsHttpConnection::nsITransportEventSink
2388 : //-----------------------------------------------------------------------------
2389 8 :
2390 4 : NS_IMETHODIMP
2391 4 : nsHttpConnection::OnTransportStatus(nsITransport *trans,
2392 : nsresult status,
2393 : int64_t progress,
2394 : int64_t progressMax)
2395 : {
2396 : if (mTransaction)
2397 : mTransaction->OnTransportStatus(trans, status, progress);
2398 : return NS_OK;
2399 : }
2400 0 :
2401 : //-----------------------------------------------------------------------------
2402 : // nsHttpConnection::nsIInterfaceRequestor
2403 : //-----------------------------------------------------------------------------
2404 :
2405 : // not called on the socket transport thread
2406 : NS_IMETHODIMP
2407 : nsHttpConnection::GetInterface(const nsIID &iid, void **result)
2408 : {
2409 : // NOTE: This function is only called on the UI thread via sync proxy from
2410 : // the socket transport thread. If that weren't the case, then we'd
2411 0 : // have to worry about the possibility of mTransaction going away
2412 : // part-way through this function call. See CloseTransaction.
2413 0 :
2414 : // NOTE - there is a bug here, the call to getinterface is proxied off the
2415 0 : // nss thread, not the ui thread as the above comment says. So there is
2416 0 : // indeed a chance of mTransaction going away. bug 615342
2417 :
2418 0 : MOZ_ASSERT(!OnSocketThread(), "on socket thread");
2419 0 :
2420 : nsCOMPtr<nsIInterfaceRequestor> callbacks;
2421 : {
2422 : MutexAutoLock lock(mCallbacksLock);
2423 : callbacks = mCallbacks;
2424 0 : }
2425 : if (callbacks)
2426 0 : return callbacks->GetInterface(iid, result);
2427 0 : return NS_ERROR_NO_INTERFACE;
2428 0 : }
2429 0 :
2430 : void
2431 : nsHttpConnection::CheckForTraffic(bool check)
2432 : {
2433 0 : if (check) {
2434 0 : LOG((" CheckForTraffic conn %p\n", this));
2435 : if (mSpdySession) {
2436 0 : if (PR_IntervalToMilliseconds(IdleTime()) >= 500) {
2437 : // Send a ping to verify it is still alive if it has been idle
2438 : // more than half a second, the network changed events are
2439 : // rate-limited to one per 1000 ms.
2440 0 : LOG((" SendPing\n"));
2441 0 : mSpdySession->SendPing();
2442 : } else {
2443 : LOG((" SendPing skipped due to network activity\n"));
2444 : }
2445 0 : } else {
2446 : // If not SPDY, Store snapshot amount of data right now
2447 0 : mTrafficCount = mTotalBytesWritten + mTotalBytesRead;
2448 : mTrafficStamp = true;
2449 : }
2450 0 : } else {
2451 : // mark it as not checked
2452 0 : mTrafficStamp = false;
2453 0 : }
2454 : }
2455 0 :
2456 0 : nsAHttpTransaction *
2457 : nsHttpConnection::CloseConnectionFastOpenTakesTooLongOrError(bool aCloseSocketTransport)
2458 0 : {
2459 : MOZ_ASSERT(!mCurrentBytesRead);
2460 0 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
2461 :
2462 : mFastOpenStatus = TFO_FAILED;
2463 : RefPtr<nsAHttpTransaction> trans;
2464 0 :
2465 0 : DontReuse();
2466 0 :
2467 0 : if (mUsingSpdyVersion != SpdyVersion::NONE) {
2468 : // If we have a http2 connection just restart it as if 0rtt failed.
2469 0 : // For http2 we do not need to do similar thing as for http1 because
2470 : // backup connection will pick immediately all this transaction anyway.
2471 : mUsingSpdyVersion = SpdyVersion::NONE;
2472 : if (mSpdySession) {
2473 0 : mTransaction->SetFastOpenStatus(TFO_FAILED);
2474 0 : Unused << mSpdySession->Finish0RTT(true, true);
2475 : }
2476 0 : mSpdySession = nullptr;
2477 : } else {
2478 : // For http1 we want to make this transaction an absolute priority to
2479 : // get the backup connection so we will return it from here.
2480 0 : if (NS_SUCCEEDED(mTransaction->RestartOnFastOpenError())) {
2481 0 : trans = mTransaction;
2482 : }
2483 : mTransaction->SetConnection(nullptr);
2484 0 : }
2485 0 :
2486 : {
2487 : MutexAutoLock lock(mCallbacksLock);
2488 0 : mCallbacks = nullptr;
2489 0 : }
2490 0 :
2491 0 : if (mSocketIn) {
2492 : mSocketIn->AsyncWait(nullptr, 0, 0, nullptr);
2493 0 : }
2494 0 :
2495 0 : mTransaction = nullptr;
2496 : if (!aCloseSocketTransport) {
2497 0 : if (mSocketOut) {
2498 0 : mSocketOut->AsyncWait(nullptr, 0, 0, nullptr);
2499 : }
2500 : mSocketTransport->SetEventSink(nullptr, nullptr);
2501 : mSocketTransport->SetSecurityCallbacks(nullptr);
2502 0 : mSocketTransport = nullptr;
2503 : }
2504 0 : Close(NS_ERROR_NET_RESET);
2505 0 : return trans;
2506 0 : }
2507 0 :
2508 : void
2509 0 : nsHttpConnection::SetFastOpen(bool aFastOpen)
2510 : {
2511 0 : mFastOpen = aFastOpen;
2512 0 : if (!mFastOpen &&
2513 0 : mTransaction &&
2514 : !mTransaction->IsNullTransaction()) {
2515 :
2516 0 : mExperienced = true;
2517 :
2518 : nsHttpTransaction *hTrans = mTransaction->QueryHttpTransaction();
2519 1 : if (hTrans) {
2520 3 : SetUrgentStartPreferred(hTrans->ClassOfService() & nsIClassOfService::UrgentStart);
2521 1 : }
2522 1 : }
2523 0 : }
2524 :
2525 0 : void
2526 0 : nsHttpConnection::SetFastOpenStatus(uint8_t tfoStatus) {
2527 0 : mFastOpenStatus = tfoStatus;
2528 : if ((mFastOpenStatus >= TFO_FAILED_CONNECTION_REFUSED) &&
2529 0 : (mFastOpenStatus <= TFO_FAILED_BACKUP_CONNECTION_TFO_DATA_COOKIE_NOT_ACCEPTED) &&
2530 : mSocketTransport) {
2531 : nsresult firstRetryError;
2532 : if (NS_SUCCEEDED(mSocketTransport->GetFirstRetryError(&firstRetryError)) &&
2533 : (NS_FAILED(firstRetryError))) {
2534 0 : if ((mFastOpenStatus >= TFO_FAILED_BACKUP_CONNECTION_TFO_NOT_TRIED) &&
2535 : (mFastOpenStatus <= TFO_FAILED_BACKUP_CONNECTION_TFO_DATA_COOKIE_NOT_ACCEPTED)) {
2536 : mFastOpenStatus = TFO_FAILED_BACKUP_CONNECTION_NO_TFO_FAILED_TOO;
2537 : } else {
2538 3 : // We add +7 to tranform TFO_FAILED_CONNECTION_REFUSED into
2539 : // TFO_FAILED_CONNECTION_REFUSED_NO_TFO_FAILED_TOO, etc.
2540 : // If the list in TCPFastOpenLayer.h changes please addapt +7.
2541 0 : mFastOpenStatus = tfoStatus + 7;
2542 : }
2543 0 : }
2544 0 : }
2545 0 : }
2546 :
2547 : void
2548 0 : nsHttpConnection::BootstrapTimings(TimingStruct times)
2549 : {
2550 0 : mBootstrappedTimingsSet = true;
2551 : mBootstrappedTimings = times;
2552 0 : }
2553 0 :
2554 : void
2555 0 : nsHttpConnection::SetEvent(nsresult aStatus)
2556 0 : {
2557 : switch (aStatus) {
2558 0 : case NS_NET_STATUS_RESOLVING_HOST:
2559 0 : mBootstrappedTimings.domainLookupStart = TimeStamp::Now();
2560 : break;
2561 : case NS_NET_STATUS_RESOLVED_HOST:
2562 0 : mBootstrappedTimings.domainLookupEnd = TimeStamp::Now();
2563 0 : break;
2564 0 : case NS_NET_STATUS_CONNECTING_TO:
2565 0 : mBootstrappedTimings.connectStart = TimeStamp::Now();
2566 0 : break;
2567 0 : case NS_NET_STATUS_CONNECTED_TO:
2568 : {
2569 : TimeStamp tnow = TimeStamp::Now();
2570 : mBootstrappedTimings.tcpConnectEnd = tnow;
2571 : mBootstrappedTimings.connectEnd = tnow;
2572 0 : if ((mFastOpenStatus != TFO_DATA_SENT) &&
2573 0 : !mBootstrappedTimings.secureConnectionStart.IsNull()) {
2574 : mBootstrappedTimings.secureConnectionStart = tnow;
2575 0 : }
2576 0 : break;
2577 : }
2578 : case NS_NET_STATUS_TLS_HANDSHAKE_STARTING:
2579 : mBootstrappedTimings.secureConnectionStart = TimeStamp::Now();
2580 0 : break;
2581 : case NS_NET_STATUS_TLS_HANDSHAKE_ENDED:
2582 : mBootstrappedTimings.connectEnd = TimeStamp::Now();
2583 0 : break;
2584 : default:
2585 0 : break;
2586 : }
2587 : }
2588 :
2589 0 : bool
2590 0 : nsHttpConnection::NoClientCertAuth() const
2591 0 : {
2592 : if (!mSocketTransport) {
2593 : return false;
2594 : }
2595 0 :
2596 0 : nsCOMPtr<nsISupports> secInfo;
2597 : mSocketTransport->GetSecurityInfo(getter_AddRefs(secInfo));
2598 : if (!secInfo) {
2599 : return false;
2600 : }
2601 :
2602 : nsCOMPtr<nsISSLSocketControl> ssc(do_QueryInterface(secInfo));
2603 : if (!ssc) {
2604 : return false;
2605 : }
2606 :
2607 : return !ssc->GetClientCertSent();
2608 : }
2609 :
2610 : } // namespace net
2611 : } // namespace mozilla
|