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