mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-26 12:20:56 +00:00
Bug 1188435 - Implement fast open necko part. r=mcmanus
This commit is contained in:
parent
c6a90cc4b2
commit
accc6e95b1
@ -4624,6 +4624,9 @@ pref("network.tcp.keepalive.retry_interval", 1); // seconds
|
||||
pref("network.tcp.keepalive.probe_count", 4);
|
||||
#endif
|
||||
|
||||
pref("network.tcp.tcp_fastopen_enable", true);
|
||||
pref("network.tcp.tcp_fastopen_consecutive_failure_limit", 5);
|
||||
|
||||
// Whether to disable acceleration for all widgets.
|
||||
pref("layers.acceleration.disabled", false);
|
||||
// Preference that when switched at runtime will run a series of benchmarks
|
||||
|
46
netwerk/base/TCPFastOpen.h
Normal file
46
netwerk/base/TCPFastOpen.h
Normal file
@ -0,0 +1,46 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef TCPFASTOPEN_H__
|
||||
#define TCPFASTOPEN_H__
|
||||
|
||||
/**
|
||||
* This is an abstract class for TCP Fast Open - TFO (RFC7413).
|
||||
* It is not always safe to use Fast Open. It can be use for requests that
|
||||
* are replayable.
|
||||
* Middle boxes can block or reset connections that use TFO, therefore a
|
||||
* backup connection will be prepared with a delay.
|
||||
* In case of blocking such a connection tcp socket will terminate only after
|
||||
* a timeout, therefore a backup connection is needed. If connection is refuse
|
||||
* the same socketTransport will retry.
|
||||
*
|
||||
* This is implemented by nsHalfopenSocket.
|
||||
**/
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
class TCPFastOpen
|
||||
{
|
||||
public:
|
||||
|
||||
// Check if we have a transaction that is safe to be used with TFO.
|
||||
// Connections over TLS are always safe and some http requests (e.g. GET).
|
||||
virtual bool FastOpenEnabled() = 0;
|
||||
// To use TFO we need to have a transaction prepared, e.g. also have
|
||||
// nsHttpConnection ready. This functions is call by nsSocketTransport to
|
||||
// setup a connection.
|
||||
virtual nsresult StartFastOpen() = 0;
|
||||
// Inform nsHalfopenSocket whether a connection using TFO succeeded or not.
|
||||
// This will cancel the backup connection and in case of a failure rewind
|
||||
// the transaction.
|
||||
virtual void FastOpenConnected(nsresult error) = 0;
|
||||
virtual void FastOpenNotSupported() = 0;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif //TCPFASTOPEN_H__
|
303
netwerk/base/TCPFastOpenLayer.cpp
Normal file
303
netwerk/base/TCPFastOpenLayer.cpp
Normal file
@ -0,0 +1,303 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "TCPFastOpenLayer.h"
|
||||
#include "nsSocketTransportService2.h"
|
||||
#include "prmem.h"
|
||||
#include "prio.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
static PRDescIdentity sTCPFastOpenLayerIdentity;
|
||||
static PRIOMethods sTCPFastOpenLayerMethods;
|
||||
static PRIOMethods *sTCPFastOpenLayerMethodsPtr = nullptr;
|
||||
|
||||
class TCPFastOpenSecret
|
||||
{
|
||||
public:
|
||||
TCPFastOpenSecret()
|
||||
: mState(WAITING_FOR_CONNECT)
|
||||
, mConnectResult(0)
|
||||
, mFastOpenNotSupported(false)
|
||||
{}
|
||||
|
||||
enum {
|
||||
CONNECTED,
|
||||
WAITING_FOR_CONNECTCONTINUE,
|
||||
WAITING_FOR_FIRST_SEND,
|
||||
WAITING_FOR_CONNECT
|
||||
} mState;
|
||||
PRNetAddr mAddr;
|
||||
PRErrorCode mConnectResult;
|
||||
bool mFastOpenNotSupported;
|
||||
};
|
||||
|
||||
static PRStatus
|
||||
TCPFastOpenConnect(PRFileDesc *fd, const PRNetAddr *addr,
|
||||
PRIntervalTime timeout)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(fd->identity == sTCPFastOpenLayerIdentity);
|
||||
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
|
||||
|
||||
TCPFastOpenSecret *secret = reinterpret_cast<TCPFastOpenSecret *>(fd->secret);
|
||||
|
||||
SOCKET_LOG(("TCPFastOpenConnect state=%d.\n", secret->mState));
|
||||
|
||||
if (secret->mState != TCPFastOpenSecret::WAITING_FOR_CONNECT) {
|
||||
PR_SetError(PR_IS_CONNECTED_ERROR, 0);
|
||||
return PR_FAILURE;
|
||||
}
|
||||
|
||||
// Remember the address we will call PR_Sendto and for that we need
|
||||
// the address.
|
||||
memcpy(&secret->mAddr, addr, sizeof(secret->mAddr));
|
||||
secret->mState = TCPFastOpenSecret::WAITING_FOR_FIRST_SEND;
|
||||
|
||||
return PR_SUCCESS;
|
||||
}
|
||||
|
||||
static PRInt32
|
||||
TCPFastOpenSend(PRFileDesc *fd, const void *buf, PRInt32 amount,
|
||||
PRIntn flags, PRIntervalTime timeout)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(fd->identity == sTCPFastOpenLayerIdentity);
|
||||
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
|
||||
|
||||
TCPFastOpenSecret *secret = reinterpret_cast<TCPFastOpenSecret *>(fd->secret);
|
||||
|
||||
SOCKET_LOG(("TCPFastOpenSend state=%d.\n", secret->mState));
|
||||
|
||||
switch(secret->mState) {
|
||||
case TCPFastOpenSecret::CONNECTED:
|
||||
return (fd->lower->methods->send)(fd->lower, buf, amount, flags, timeout);
|
||||
case TCPFastOpenSecret::WAITING_FOR_CONNECTCONTINUE:
|
||||
PR_SetError(PR_WOULD_BLOCK_ERROR, 0);
|
||||
return -1;
|
||||
case TCPFastOpenSecret::WAITING_FOR_FIRST_SEND:
|
||||
{
|
||||
PRInt32 rv = (fd->lower->methods->sendto)(fd->lower, buf, amount, flags,
|
||||
&secret->mAddr, timeout);
|
||||
|
||||
SOCKET_LOG(("TCPFastOpenSend - sendto result=%d.\n", rv));
|
||||
if (rv > -1) {
|
||||
secret->mState = TCPFastOpenSecret::WAITING_FOR_CONNECTCONTINUE;
|
||||
secret->mConnectResult = PR_IN_PROGRESS_ERROR;
|
||||
return rv;
|
||||
}
|
||||
|
||||
secret->mConnectResult = PR_GetError();
|
||||
SOCKET_LOG(("TCPFastOpenSend - sendto error=%d.\n",
|
||||
secret->mConnectResult));
|
||||
|
||||
if (secret->mConnectResult == PR_IS_CONNECTED_ERROR) {
|
||||
secret->mState = TCPFastOpenSecret::CONNECTED;
|
||||
|
||||
} else if (secret->mConnectResult == PR_IN_PROGRESS_ERROR) {
|
||||
secret->mState = TCPFastOpenSecret::WAITING_FOR_CONNECTCONTINUE;
|
||||
|
||||
} else if (secret->mConnectResult == PR_NOT_IMPLEMENTED_ERROR || // When a windows version does not support Fast Open it will return this error.
|
||||
secret->mConnectResult == PR_NOT_TCP_SOCKET_ERROR) { // SendTo will return PR_NOT_TCP_SOCKET_ERROR if TCP Fast Open is turned off on Linux.
|
||||
// We can call connect again.
|
||||
secret->mFastOpenNotSupported = true;
|
||||
rv = (fd->lower->methods->connect)(fd->lower, &secret->mAddr, timeout);
|
||||
|
||||
if (rv == PR_SUCCESS) {
|
||||
secret->mConnectResult = PR_IS_CONNECTED_ERROR;
|
||||
secret->mState = TCPFastOpenSecret::CONNECTED;
|
||||
|
||||
} else {
|
||||
secret->mConnectResult = PR_GetError();
|
||||
secret->mState = TCPFastOpenSecret::WAITING_FOR_CONNECTCONTINUE;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Error will be picked up by TCPFastOpenConnectResult.
|
||||
PR_SetError(PR_WOULD_BLOCK_ERROR, 0);
|
||||
return -1;
|
||||
}
|
||||
case TCPFastOpenSecret::WAITING_FOR_CONNECT:
|
||||
PR_SetError(PR_NOT_CONNECTED_ERROR, 0);
|
||||
return -1;
|
||||
}
|
||||
PR_SetError(PR_WOULD_BLOCK_ERROR, 0);
|
||||
return PR_FAILURE;
|
||||
}
|
||||
|
||||
static PRInt32
|
||||
TCPFastOpenWrite(PRFileDesc *fd, const void *buf, PRInt32 amount)
|
||||
{
|
||||
return TCPFastOpenSend(fd, buf, amount, 0, PR_INTERVAL_NO_WAIT);
|
||||
}
|
||||
|
||||
static PRInt32
|
||||
TCPFastOpenRecv(PRFileDesc *fd, void *buf, PRInt32 amount,
|
||||
PRIntn flags, PRIntervalTime timeout)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(fd->identity == sTCPFastOpenLayerIdentity);
|
||||
|
||||
TCPFastOpenSecret *secret = reinterpret_cast<TCPFastOpenSecret *>(fd->secret);
|
||||
|
||||
PRInt32 rv = -1;
|
||||
switch(secret->mState) {
|
||||
case TCPFastOpenSecret::CONNECTED:
|
||||
rv = (fd->lower->methods->recv)(fd->lower, buf, amount, flags, timeout);
|
||||
break;
|
||||
case TCPFastOpenSecret::WAITING_FOR_CONNECTCONTINUE:
|
||||
case TCPFastOpenSecret::WAITING_FOR_FIRST_SEND:
|
||||
PR_SetError(PR_WOULD_BLOCK_ERROR, 0);
|
||||
break;
|
||||
case TCPFastOpenSecret::WAITING_FOR_CONNECT:
|
||||
PR_SetError(PR_NOT_CONNECTED_ERROR, 0);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
static PRInt32
|
||||
TCPFastOpenRead(PRFileDesc *fd, void *buf, PRInt32 amount)
|
||||
{
|
||||
return TCPFastOpenRecv(fd, buf, amount , 0, PR_INTERVAL_NO_WAIT);
|
||||
}
|
||||
|
||||
static PRStatus
|
||||
TCPFastOpenConnectContinue(PRFileDesc *fd, PRInt16 out_flags)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(fd->identity == sTCPFastOpenLayerIdentity);
|
||||
|
||||
TCPFastOpenSecret *secret = reinterpret_cast<TCPFastOpenSecret *>(fd->secret);
|
||||
|
||||
PRStatus rv = PR_FAILURE;
|
||||
switch(secret->mState) {
|
||||
case TCPFastOpenSecret::CONNECTED:
|
||||
rv = PR_SUCCESS;
|
||||
break;
|
||||
case TCPFastOpenSecret::WAITING_FOR_CONNECT:
|
||||
case TCPFastOpenSecret::WAITING_FOR_FIRST_SEND:
|
||||
PR_SetError(PR_NOT_CONNECTED_ERROR, 0);
|
||||
rv = PR_FAILURE;
|
||||
break;
|
||||
case TCPFastOpenSecret::WAITING_FOR_CONNECTCONTINUE:
|
||||
rv = (fd->lower->methods->connectcontinue)(fd->lower, out_flags);
|
||||
|
||||
SOCKET_LOG(("TCPFastOpenConnectContinue result=%d.\n", rv));
|
||||
if (rv == PR_SUCCESS) {
|
||||
secret->mState = TCPFastOpenSecret::CONNECTED;
|
||||
}
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
static PRStatus
|
||||
TCPFastOpenClose(PRFileDesc *fd)
|
||||
{
|
||||
if (!fd) {
|
||||
return PR_FAILURE;
|
||||
}
|
||||
|
||||
PRFileDesc* layer = PR_PopIOLayer(fd, PR_TOP_IO_LAYER);
|
||||
|
||||
MOZ_RELEASE_ASSERT(layer &&
|
||||
layer->identity == sTCPFastOpenLayerIdentity,
|
||||
"TCP Fast Open Layer not on top of stack");
|
||||
|
||||
TCPFastOpenSecret *secret = reinterpret_cast<TCPFastOpenSecret *>(layer->secret);
|
||||
layer->secret = nullptr;
|
||||
layer->dtor(layer);
|
||||
delete secret;
|
||||
return fd->methods->close(fd);
|
||||
}
|
||||
|
||||
static PRStatus
|
||||
TCPFastOpenGetpeername (PRFileDesc *fd, PRNetAddr *addr)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(fd);
|
||||
MOZ_RELEASE_ASSERT(addr);
|
||||
|
||||
MOZ_RELEASE_ASSERT(fd->identity == sTCPFastOpenLayerIdentity);
|
||||
|
||||
TCPFastOpenSecret *secret = reinterpret_cast<TCPFastOpenSecret *>(fd->secret);
|
||||
if (secret->mState == TCPFastOpenSecret::WAITING_FOR_CONNECT) {
|
||||
PR_SetError(PR_NOT_CONNECTED_ERROR, 0);
|
||||
return PR_FAILURE;
|
||||
}
|
||||
|
||||
memcpy(addr, &secret->mAddr, sizeof(secret->mAddr));
|
||||
return PR_SUCCESS;
|
||||
}
|
||||
|
||||
nsresult
|
||||
AttachTCPFastOpenIOLayer(PRFileDesc *fd)
|
||||
{
|
||||
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
|
||||
|
||||
if (!sTCPFastOpenLayerMethodsPtr) {
|
||||
sTCPFastOpenLayerIdentity = PR_GetUniqueIdentity("TCPFastOpen Layer");
|
||||
sTCPFastOpenLayerMethods = *PR_GetDefaultIOMethods();
|
||||
sTCPFastOpenLayerMethods.connect = TCPFastOpenConnect;
|
||||
sTCPFastOpenLayerMethods.send = TCPFastOpenSend;
|
||||
sTCPFastOpenLayerMethods.write = TCPFastOpenWrite;
|
||||
sTCPFastOpenLayerMethods.recv = TCPFastOpenRecv;
|
||||
sTCPFastOpenLayerMethods.read = TCPFastOpenRead;
|
||||
sTCPFastOpenLayerMethods.connectcontinue = TCPFastOpenConnectContinue;
|
||||
sTCPFastOpenLayerMethods.close = TCPFastOpenClose;
|
||||
sTCPFastOpenLayerMethods.getpeername = TCPFastOpenGetpeername;
|
||||
sTCPFastOpenLayerMethodsPtr = &sTCPFastOpenLayerMethods;
|
||||
}
|
||||
|
||||
PRFileDesc *layer = PR_CreateIOLayerStub(sTCPFastOpenLayerIdentity,
|
||||
sTCPFastOpenLayerMethodsPtr);
|
||||
|
||||
if (!layer) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
TCPFastOpenSecret *secret = new TCPFastOpenSecret();
|
||||
|
||||
layer->secret = reinterpret_cast<PRFilePrivate *>(secret);
|
||||
|
||||
PRStatus status = PR_PushIOLayer(fd, PR_NSPR_IO_LAYER, layer);
|
||||
|
||||
if (status == PR_FAILURE) {
|
||||
delete secret;
|
||||
PR_DELETE(layer);
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
TCPFastOpenConnectResult(PRFileDesc * fd, PRErrorCode *err,
|
||||
bool *fastOpenNotSupported)
|
||||
{
|
||||
PRFileDesc *tfoFd = PR_GetIdentitiesLayer(fd, sTCPFastOpenLayerIdentity);
|
||||
MOZ_RELEASE_ASSERT(tfoFd);
|
||||
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
|
||||
|
||||
TCPFastOpenSecret *secret = reinterpret_cast<TCPFastOpenSecret *>(tfoFd->secret);
|
||||
|
||||
MOZ_ASSERT(secret->mState != TCPFastOpenSecret::WAITING_FOR_CONNECT);
|
||||
|
||||
*fastOpenNotSupported = secret->mFastOpenNotSupported;
|
||||
|
||||
if (secret->mState == TCPFastOpenSecret::WAITING_FOR_FIRST_SEND) {
|
||||
// Because of the way our HttpTransaction dispatch work, it can happened
|
||||
// that connect is not called.
|
||||
PRInt32 rv = (tfoFd->lower->methods->connect)(tfoFd->lower, &secret->mAddr,
|
||||
PR_INTERVAL_NO_WAIT);
|
||||
if (rv == PR_SUCCESS) {
|
||||
secret->mConnectResult = PR_IS_CONNECTED_ERROR;
|
||||
secret->mState = TCPFastOpenSecret::CONNECTED;
|
||||
} else {
|
||||
secret->mConnectResult = PR_GetError();
|
||||
secret->mState = TCPFastOpenSecret::WAITING_FOR_CONNECTCONTINUE;
|
||||
}
|
||||
}
|
||||
*err = secret->mConnectResult;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
24
netwerk/base/TCPFastOpenLayer.h
Normal file
24
netwerk/base/TCPFastOpenLayer.h
Normal file
@ -0,0 +1,24 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef TCPFastOpenLayer_h__
|
||||
#define TCPFastOpenLayer_h__
|
||||
|
||||
#include "prerror.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
nsresult AttachTCPFastOpenIOLayer(PRFileDesc *fd);
|
||||
|
||||
// Get the result of TCP Fast Open.
|
||||
void TCPFastOpenConnectResult(PRFileDesc *fd, PRErrorCode *err,
|
||||
bool *fastOpenNotSupported);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif // TCPFastOpenLayer_h__
|
@ -178,6 +178,7 @@ EXPORTS.mozilla.net += [
|
||||
'MemoryDownloader.h',
|
||||
'Predictor.h',
|
||||
'ReferrerPolicy.h',
|
||||
'TCPFastOpen.h',
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
@ -247,6 +248,7 @@ UNIFIED_SOURCES += [
|
||||
'SimpleBuffer.cpp',
|
||||
'SimpleChannel.cpp',
|
||||
'StreamingProtocolService.cpp',
|
||||
'TCPFastOpenLayer.cpp',
|
||||
'ThrottleQueue.cpp',
|
||||
'ThrottlingService.cpp',
|
||||
'Tickler.cpp',
|
||||
|
@ -39,6 +39,7 @@
|
||||
#include "mozilla/Logging.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/net/HttpBaseChannel.h"
|
||||
#include "mozilla/ClearOnShutdown.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
@ -1016,5 +1017,7 @@ nsChannelClassifier::Observe(nsISupports *aSubject, const char *aTopic,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
#undef LOG_ENABLED
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
|
@ -5,6 +5,7 @@
|
||||
#ifndef nsChannelClassifier_h__
|
||||
#define nsChannelClassifier_h__
|
||||
|
||||
#include "nsIObserver.h"
|
||||
#include "nsIURIClassifier.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
|
@ -13,6 +13,7 @@ interface nsINetAddr;
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
union NetAddr;
|
||||
class TCPFastOpen;
|
||||
}
|
||||
}
|
||||
%}
|
||||
@ -20,6 +21,7 @@ native NetAddr(mozilla::net::NetAddr);
|
||||
[ptr] native NetAddrPtr(mozilla::net::NetAddr);
|
||||
native OriginAttributes(mozilla::OriginAttributes);
|
||||
[ref] native const_OriginAttributesRef(const mozilla::OriginAttributes);
|
||||
[ptr] native TCPFastOpenPtr(mozilla::net::TCPFastOpen);
|
||||
|
||||
/**
|
||||
* nsISocketTransport
|
||||
@ -258,4 +260,6 @@ interface nsISocketTransport : nsITransport
|
||||
attribute boolean keepaliveEnabled;
|
||||
void setKeepaliveVals(in long keepaliveIdleTime,
|
||||
in long keepaliveRetryInterval);
|
||||
|
||||
[noscript] void setFastOpenCallback(in TCPFastOpenPtr aFastOpen);
|
||||
};
|
||||
|
@ -34,6 +34,7 @@
|
||||
#include "nsIDNSService.h"
|
||||
#include "nsIDNSRecord.h"
|
||||
#include "nsICancelable.h"
|
||||
#include "TCPFastOpenLayer.h"
|
||||
#include <algorithm>
|
||||
|
||||
#include "nsPrintfCString.h"
|
||||
@ -605,7 +606,7 @@ nsSocketOutputStream::Write(const char *buf, uint32_t count, uint32_t *countWrit
|
||||
if (NS_FAILED(mCondition))
|
||||
return mCondition;
|
||||
|
||||
fd = mTransport->GetFD_Locked();
|
||||
fd = mTransport->GetFD_LockedAlsoDuringFastOpen();
|
||||
if (!fd)
|
||||
return NS_BASE_STREAM_WOULD_BLOCK;
|
||||
}
|
||||
@ -758,6 +759,7 @@ nsSocketTransport::nsSocketTransport()
|
||||
, mFD(this)
|
||||
, mFDref(0)
|
||||
, mFDconnected(false)
|
||||
, mFDFastOpenInProgress(false)
|
||||
, mSocketTransportService(gSocketTransportService)
|
||||
, mInput(this)
|
||||
, mOutput(this)
|
||||
@ -766,6 +768,7 @@ nsSocketTransport::nsSocketTransport()
|
||||
, mKeepaliveIdleTimeS(-1)
|
||||
, mKeepaliveRetryIntervalS(-1)
|
||||
, mKeepaliveProbeCount(-1)
|
||||
, mFastOpenCallback(nullptr)
|
||||
{
|
||||
SOCKET_LOG(("creating nsSocketTransport @%p\n", this));
|
||||
|
||||
@ -1496,10 +1499,58 @@ nsSocketTransport::InitiateSocket()
|
||||
connectStarted = PR_IntervalNow();
|
||||
}
|
||||
|
||||
bool tfo = false;
|
||||
if (fd && mFastOpenCallback &&
|
||||
mFastOpenCallback->FastOpenEnabled()) {
|
||||
if (NS_SUCCEEDED(AttachTCPFastOpenIOLayer(fd))) {
|
||||
tfo = true;
|
||||
SOCKET_LOG(("nsSocketTransport::InitiateSocket TCP Fast Open "
|
||||
"started [this=%p]\n", this));
|
||||
}
|
||||
}
|
||||
|
||||
bool connectCalled = true; // This is only needed for telemetry.
|
||||
status = PR_Connect(fd, &prAddr, NS_SOCKET_CONNECT_TIMEOUT);
|
||||
PRErrorCode code = PR_GetError();
|
||||
if ((status == PR_SUCCESS) && tfo) {
|
||||
{
|
||||
MutexAutoLock lock(mLock);
|
||||
mFDFastOpenInProgress = true;
|
||||
}
|
||||
SOCKET_LOG(("Using TCP Fast Open."));
|
||||
rv = mFastOpenCallback->StartFastOpen();
|
||||
status = PR_FAILURE;
|
||||
connectCalled = false;
|
||||
bool fastOpenNotSupported = false;
|
||||
|
||||
TCPFastOpenConnectResult(fd, &code, &fastOpenNotSupported);
|
||||
SOCKET_LOG(("called StartFastOpen - code=%d; fastOpen is %s "
|
||||
"supported.\n", code,
|
||||
fastOpenNotSupported ? "not" : ""));
|
||||
|
||||
if (fastOpenNotSupported) {
|
||||
// When TCP_FastOpen is turned off on the local host
|
||||
// SendTo will return PR_NOT_TCP_SOCKET_ERROR. This is only
|
||||
// on Linux.
|
||||
// If a windows version does not support Fast Open, the return value
|
||||
// will be PR_NOT_IMPLEMENTED_ERROR. This is only for windows 10
|
||||
// versions older than version 1607, because we do not have subverion
|
||||
// to check, we need to call PR_SendTo to check if it is supported.
|
||||
mFastOpenCallback->FastOpenNotSupported();
|
||||
mFastOpenCallback = nullptr;
|
||||
connectCalled = true;
|
||||
{
|
||||
MutexAutoLock lock(mLock);
|
||||
mFDFastOpenInProgress = false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
mFastOpenCallback = nullptr;
|
||||
}
|
||||
|
||||
|
||||
if (gSocketTransportService->IsTelemetryEnabledAndNotSleepPhase() &&
|
||||
connectStarted) {
|
||||
connectStarted && connectCalled) {
|
||||
SendPRBlockingTelemetry(connectStarted,
|
||||
Telemetry::PRCONNECT_BLOCKING_TIME_NORMAL,
|
||||
Telemetry::PRCONNECT_BLOCKING_TIME_SHUTDOWN,
|
||||
@ -1515,7 +1566,6 @@ nsSocketTransport::InitiateSocket()
|
||||
OnSocketConnected();
|
||||
}
|
||||
else {
|
||||
PRErrorCode code = PR_GetError();
|
||||
#if defined(TEST_CONNECT_ERRORS)
|
||||
code = RandomizeConnectError(code);
|
||||
#endif
|
||||
@ -1567,7 +1617,7 @@ nsSocketTransport::InitiateSocket()
|
||||
//
|
||||
else {
|
||||
if (gSocketTransportService->IsTelemetryEnabledAndNotSleepPhase() &&
|
||||
connectStarted) {
|
||||
connectStarted && connectStarted) {
|
||||
SendPRBlockingTelemetry(connectStarted,
|
||||
Telemetry::PRCONNECT_FAIL_BLOCKING_TIME_NORMAL,
|
||||
Telemetry::PRCONNECT_FAIL_BLOCKING_TIME_SHUTDOWN,
|
||||
@ -1610,7 +1660,8 @@ nsSocketTransport::RecoverFromError()
|
||||
|
||||
// all connection failures need to be reported to DNS so that the next
|
||||
// time we will use a different address if available.
|
||||
if (mState == STATE_CONNECTING && mDNSRecord) {
|
||||
if (!mFDFastOpenInProgress &&
|
||||
mState == STATE_CONNECTING && mDNSRecord) {
|
||||
mDNSRecord->ReportUnusable(SocketPort());
|
||||
}
|
||||
|
||||
@ -1623,45 +1674,57 @@ nsSocketTransport::RecoverFromError()
|
||||
return false;
|
||||
|
||||
bool tryAgain = false;
|
||||
|
||||
if ((mState == STATE_CONNECTING) && mDNSRecord &&
|
||||
mSocketTransportService->IsTelemetryEnabledAndNotSleepPhase()) {
|
||||
if (mNetAddr.raw.family == AF_INET) {
|
||||
Telemetry::Accumulate(Telemetry::IPV4_AND_IPV6_ADDRESS_CONNECTIVITY,
|
||||
UNSUCCESSFUL_CONNECTING_TO_IPV4_ADDRESS);
|
||||
} else if (mNetAddr.raw.family == AF_INET6) {
|
||||
Telemetry::Accumulate(Telemetry::IPV4_AND_IPV6_ADDRESS_CONNECTIVITY,
|
||||
UNSUCCESSFUL_CONNECTING_TO_IPV6_ADDRESS);
|
||||
}
|
||||
}
|
||||
|
||||
if (mConnectionFlags & (DISABLE_IPV6 | DISABLE_IPV4) &&
|
||||
mCondition == NS_ERROR_UNKNOWN_HOST &&
|
||||
mState == STATE_RESOLVING &&
|
||||
!mProxyTransparentResolvesHost) {
|
||||
SOCKET_LOG((" trying lookup again with both ipv4/ipv6 enabled\n"));
|
||||
mConnectionFlags &= ~(DISABLE_IPV6 | DISABLE_IPV4);
|
||||
if (mFDFastOpenInProgress &&
|
||||
((mCondition == NS_ERROR_CONNECTION_REFUSED) ||
|
||||
(mCondition == NS_ERROR_NET_TIMEOUT))) {
|
||||
// TCP Fast Open can be blocked by middle boxes so we will retry
|
||||
// without it.
|
||||
tryAgain = true;
|
||||
}
|
||||
MOZ_ASSERT(mFastOpenCallback);
|
||||
mFastOpenCallback->FastOpenConnected(mCondition);
|
||||
mFastOpenCallback = nullptr;
|
||||
} else {
|
||||
|
||||
// try next ip address only if past the resolver stage...
|
||||
if (mState == STATE_CONNECTING && mDNSRecord) {
|
||||
nsresult rv = mDNSRecord->GetNextAddr(SocketPort(), &mNetAddr);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
SOCKET_LOG((" trying again with next ip address\n"));
|
||||
tryAgain = true;
|
||||
if ((mState == STATE_CONNECTING) && mDNSRecord &&
|
||||
mSocketTransportService->IsTelemetryEnabledAndNotSleepPhase()) {
|
||||
if (mNetAddr.raw.family == AF_INET) {
|
||||
Telemetry::Accumulate(Telemetry::IPV4_AND_IPV6_ADDRESS_CONNECTIVITY,
|
||||
UNSUCCESSFUL_CONNECTING_TO_IPV4_ADDRESS);
|
||||
} else if (mNetAddr.raw.family == AF_INET6) {
|
||||
Telemetry::Accumulate(Telemetry::IPV4_AND_IPV6_ADDRESS_CONNECTIVITY,
|
||||
UNSUCCESSFUL_CONNECTING_TO_IPV6_ADDRESS);
|
||||
}
|
||||
}
|
||||
else if (mConnectionFlags & (DISABLE_IPV6 | DISABLE_IPV4)) {
|
||||
// Drop state to closed. This will trigger new round of DNS
|
||||
// resolving bellow.
|
||||
// XXX Could be optimized to only switch the flags to save duplicate
|
||||
// connection attempts.
|
||||
SOCKET_LOG((" failed to connect all ipv4-only or ipv6-only hosts,"
|
||||
" trying lookup/connect again with both ipv4/ipv6\n"));
|
||||
mState = STATE_CLOSED;
|
||||
|
||||
if (mConnectionFlags & (DISABLE_IPV6 | DISABLE_IPV4) &&
|
||||
mCondition == NS_ERROR_UNKNOWN_HOST &&
|
||||
mState == STATE_RESOLVING &&
|
||||
!mProxyTransparentResolvesHost) {
|
||||
SOCKET_LOG((" trying lookup again with both ipv4/ipv6 enabled\n"));
|
||||
mConnectionFlags &= ~(DISABLE_IPV6 | DISABLE_IPV4);
|
||||
tryAgain = true;
|
||||
}
|
||||
|
||||
// try next ip address only if past the resolver stage...
|
||||
if (mState == STATE_CONNECTING && mDNSRecord) {
|
||||
nsresult rv = mDNSRecord->GetNextAddr(SocketPort(), &mNetAddr);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
SOCKET_LOG((" trying again with next ip address\n"));
|
||||
tryAgain = true;
|
||||
}
|
||||
else if (mConnectionFlags & (DISABLE_IPV6 | DISABLE_IPV4)) {
|
||||
// Drop state to closed. This will trigger new round of DNS
|
||||
// resolving bellow.
|
||||
// XXX Could be optimized to only switch the flags to save
|
||||
// duplicate connection attempts.
|
||||
SOCKET_LOG((" failed to connect all ipv4-only or ipv6-only "
|
||||
"hosts, trying lookup/connect again with both "
|
||||
"ipv4/ipv6\n"));
|
||||
mState = STATE_CLOSED;
|
||||
mConnectionFlags &= ~(DISABLE_IPV6 | DISABLE_IPV4);
|
||||
tryAgain = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// prepare to try again.
|
||||
@ -1743,6 +1806,16 @@ nsSocketTransport::OnSocketConnected()
|
||||
// because we need to make sure its value does not change due to failover
|
||||
mNetAddrIsSet = true;
|
||||
|
||||
// If a Fast Open is not in progress, mFastOpenCallback must be null.
|
||||
MOZ_ASSERT(mFastOpenCallback || !mFastOpenCallback);
|
||||
if (mFDFastOpenInProgress && mFastOpenCallback) {
|
||||
// mFastOpenCallback can be null when for example h2 is negotiated on
|
||||
// another connection to the same host and all connections are
|
||||
// abandoned.
|
||||
mFastOpenCallback->FastOpenConnected(NS_OK);
|
||||
mFastOpenCallback = nullptr;
|
||||
}
|
||||
|
||||
// assign mFD (must do this within the transport lock), but take care not
|
||||
// to trample over mFDref if mFD is already set.
|
||||
{
|
||||
@ -1751,6 +1824,7 @@ nsSocketTransport::OnSocketConnected()
|
||||
NS_ASSERTION(mFDref == 1, "wrong socket ref count");
|
||||
SetSocketName(mFD);
|
||||
mFDconnected = true;
|
||||
mFDFastOpenInProgress = false;
|
||||
}
|
||||
|
||||
// Ensure keepalive is configured correctly if previously enabled.
|
||||
@ -1796,6 +1870,23 @@ nsSocketTransport::GetFD_Locked()
|
||||
return mFD;
|
||||
}
|
||||
|
||||
PRFileDesc *
|
||||
nsSocketTransport::GetFD_LockedAlsoDuringFastOpen()
|
||||
{
|
||||
mLock.AssertCurrentThreadOwns();
|
||||
|
||||
// mFD is not available to the streams while disconnected.
|
||||
if (!mFDconnected && !mFDFastOpenInProgress) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (mFD.IsInitialized()) {
|
||||
mFDref++;
|
||||
}
|
||||
|
||||
return mFD;
|
||||
}
|
||||
|
||||
class ThunkPRClose : public Runnable
|
||||
{
|
||||
public:
|
||||
@ -2135,6 +2226,17 @@ nsSocketTransport::OnSocketDetached(PRFileDesc *fd)
|
||||
if (secCtrl)
|
||||
secCtrl->SetNotificationCallbacks(nullptr);
|
||||
|
||||
// The error can happened before we start fast open. In that case do not
|
||||
// call mFastOpenCallback->FastOpenConnected; If error happends during
|
||||
// fast open, inform the halfOpenSocket.
|
||||
// If we cancel the connection because backup socket was successfully
|
||||
// connected, mFDFastOpenInProgress will be true but mFastOpenCallback
|
||||
// will be nullptr.
|
||||
if (mFDFastOpenInProgress && mFastOpenCallback) {
|
||||
mFastOpenCallback->FastOpenConnected(mCondition);
|
||||
}
|
||||
mFastOpenCallback = nullptr;
|
||||
|
||||
// finally, release our reference to the socket (must do this within
|
||||
// the transport lock) possibly closing the socket. Also release our
|
||||
// listeners to break potential refcount cycles.
|
||||
@ -2153,6 +2255,7 @@ nsSocketTransport::OnSocketDetached(PRFileDesc *fd)
|
||||
// flag mFD as unusable; this prevents other consumers from
|
||||
// acquiring a reference to mFD.
|
||||
mFDconnected = false;
|
||||
mFDFastOpenInProgress = false;
|
||||
}
|
||||
|
||||
// We must release mCallbacks and mEventSink to avoid memory leak
|
||||
@ -2369,8 +2472,14 @@ nsSocketTransport::IsAlive(bool *result)
|
||||
{
|
||||
*result = false;
|
||||
|
||||
// During Fast Open we need to return true here.
|
||||
if (mFDFastOpenInProgress) {
|
||||
*result = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult conditionWhileLocked = NS_OK;
|
||||
PRFileDescAutoLock fd(this, &conditionWhileLocked);
|
||||
PRFileDescAutoLock fd(this, false, &conditionWhileLocked);
|
||||
if (NS_FAILED(conditionWhileLocked) || !fd.IsInitialized()) {
|
||||
return NS_OK;
|
||||
}
|
||||
@ -2590,7 +2699,7 @@ nsSocketTransport::GetQoSBits(uint8_t *aQoSBits)
|
||||
NS_IMETHODIMP
|
||||
nsSocketTransport::GetRecvBufferSize(uint32_t *aSize)
|
||||
{
|
||||
PRFileDescAutoLock fd(this);
|
||||
PRFileDescAutoLock fd(this, false);
|
||||
if (!fd.IsInitialized())
|
||||
return NS_ERROR_NOT_CONNECTED;
|
||||
|
||||
@ -2608,7 +2717,7 @@ nsSocketTransport::GetRecvBufferSize(uint32_t *aSize)
|
||||
NS_IMETHODIMP
|
||||
nsSocketTransport::GetSendBufferSize(uint32_t *aSize)
|
||||
{
|
||||
PRFileDescAutoLock fd(this);
|
||||
PRFileDescAutoLock fd(this, false);
|
||||
if (!fd.IsInitialized())
|
||||
return NS_ERROR_NOT_CONNECTED;
|
||||
|
||||
@ -2626,7 +2735,7 @@ nsSocketTransport::GetSendBufferSize(uint32_t *aSize)
|
||||
NS_IMETHODIMP
|
||||
nsSocketTransport::SetRecvBufferSize(uint32_t aSize)
|
||||
{
|
||||
PRFileDescAutoLock fd(this);
|
||||
PRFileDescAutoLock fd(this, false);
|
||||
if (!fd.IsInitialized())
|
||||
return NS_ERROR_NOT_CONNECTED;
|
||||
|
||||
@ -2643,7 +2752,7 @@ nsSocketTransport::SetRecvBufferSize(uint32_t aSize)
|
||||
NS_IMETHODIMP
|
||||
nsSocketTransport::SetSendBufferSize(uint32_t aSize)
|
||||
{
|
||||
PRFileDescAutoLock fd(this);
|
||||
PRFileDescAutoLock fd(this, false);
|
||||
if (!fd.IsInitialized())
|
||||
return NS_ERROR_NOT_CONNECTED;
|
||||
|
||||
@ -2778,7 +2887,7 @@ nsSocketTransport::SetKeepaliveEnabledInternal(bool aEnable)
|
||||
MOZ_ASSERT(mKeepaliveProbeCount > 0 &&
|
||||
mKeepaliveProbeCount <= kMaxTCPKeepCount);
|
||||
|
||||
PRFileDescAutoLock fd(this);
|
||||
PRFileDescAutoLock fd(this, true);
|
||||
if (NS_WARN_IF(!fd.IsInitialized())) {
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
@ -2932,7 +3041,7 @@ nsSocketTransport::SetKeepaliveVals(int32_t aIdleTime,
|
||||
mKeepaliveIdleTimeS, mKeepaliveRetryIntervalS,
|
||||
mKeepaliveProbeCount));
|
||||
|
||||
PRFileDescAutoLock fd(this);
|
||||
PRFileDescAutoLock fd(this, true);
|
||||
if (NS_WARN_IF(!fd.IsInitialized())) {
|
||||
return NS_ERROR_NULL_POINTER;
|
||||
}
|
||||
@ -3261,5 +3370,12 @@ nsSocketTransport::SendPRBlockingTelemetry(PRIntervalTime aStart,
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsSocketTransport::SetFastOpenCallback(TCPFastOpen *aFastOpen)
|
||||
{
|
||||
mFastOpenCallback = aFastOpen;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include "nsIAsyncOutputStream.h"
|
||||
#include "nsIDNSListener.h"
|
||||
#include "nsIClassInfo.h"
|
||||
#include "TCPFastOpen.h"
|
||||
#include "mozilla/net/DNS.h"
|
||||
#include "nsASocketHandler.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
@ -209,6 +210,7 @@ private:
|
||||
{
|
||||
public:
|
||||
explicit PRFileDescAutoLock(nsSocketTransport *aSocketTransport,
|
||||
bool aAlsoDuringFastOpen,
|
||||
nsresult *aConditionWhileLocked = nullptr)
|
||||
: mSocketTransport(aSocketTransport)
|
||||
, mFd(nullptr)
|
||||
@ -221,7 +223,11 @@ private:
|
||||
return;
|
||||
}
|
||||
}
|
||||
mFd = mSocketTransport->GetFD_Locked();
|
||||
if (!aAlsoDuringFastOpen) {
|
||||
mFd = mSocketTransport->GetFD_Locked();
|
||||
} else {
|
||||
mFd = mSocketTransport->GetFD_LockedAlsoDuringFastOpen();
|
||||
}
|
||||
}
|
||||
~PRFileDescAutoLock() {
|
||||
MutexAutoLock lock(mSocketTransport->mLock);
|
||||
@ -379,6 +385,9 @@ private:
|
||||
LockedPRFileDesc mFD;
|
||||
nsrefcnt mFDref; // mFD is closed when mFDref goes to zero.
|
||||
bool mFDconnected; // mFD is available to consumer when TRUE.
|
||||
bool mFDFastOpenInProgress; // Fast Open is in progress, so
|
||||
// socket available for some
|
||||
// operations.
|
||||
|
||||
// A delete protector reference to gSocketTransportService held for lifetime
|
||||
// of 'this'. Sometimes used interchangably with gSocketTransportService due
|
||||
@ -405,6 +414,7 @@ private:
|
||||
// mFD access methods: called with mLock held.
|
||||
//
|
||||
PRFileDesc *GetFD_Locked();
|
||||
PRFileDesc *GetFD_LockedAlsoDuringFastOpen();
|
||||
void ReleaseFD_Locked(PRFileDesc *fd);
|
||||
|
||||
//
|
||||
@ -462,6 +472,9 @@ private:
|
||||
int32_t mKeepaliveIdleTimeS;
|
||||
int32_t mKeepaliveRetryIntervalS;
|
||||
int32_t mKeepaliveProbeCount;
|
||||
|
||||
// A Fast Open callback.
|
||||
TCPFastOpen *mFastOpenCallback;
|
||||
};
|
||||
|
||||
} // namespace net
|
||||
|
@ -15,7 +15,7 @@ nsSyncStreamListener::Init()
|
||||
{
|
||||
return NS_NewPipe(getter_AddRefs(mPipeIn),
|
||||
getter_AddRefs(mPipeOut),
|
||||
nsIOService::gDefaultSegmentSize,
|
||||
mozilla::net::nsIOService::gDefaultSegmentSize,
|
||||
UINT32_MAX, // no size limit
|
||||
false,
|
||||
false);
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "nsHttp.h"
|
||||
#include "nsHttpHandler.h"
|
||||
#include "nsHttpRequestHead.h"
|
||||
#include "TCPFastOpen.h"
|
||||
#include "nsISocketProvider.h"
|
||||
#include "nsISocketProviderService.h"
|
||||
#include "nsISSLSocketControl.h"
|
||||
@ -1627,6 +1628,12 @@ SocketTransportShim::SetQoSBits(uint8_t aQoSBits)
|
||||
return mWrapped->SetQoSBits(aQoSBits);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
SocketTransportShim::SetFastOpenCallback(TCPFastOpen *aFastOpen)
|
||||
{
|
||||
return mWrapped->SetFastOpenCallback(aFastOpen);
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS(TLSFilterTransaction, nsITimerCallback)
|
||||
NS_IMPL_ISUPPORTS(SocketTransportShim, nsISocketTransport, nsITransport)
|
||||
NS_IMPL_ISUPPORTS(InputStreamShim, nsIInputStream, nsIAsyncInputStream)
|
||||
|
@ -122,3 +122,9 @@ EXTRA_COMPONENTS += [
|
||||
'WellKnownOpportunisticUtils.js',
|
||||
'WellKnownOpportunisticUtils.manifest',
|
||||
]
|
||||
|
||||
if CONFIG['OS_TARGET'] == 'Darwin':
|
||||
if CONFIG['HOST_MAJOR_VERSION'] == '15':
|
||||
DEFINES.update(
|
||||
HAS_CONNECTX=True,
|
||||
)
|
||||
|
@ -165,7 +165,13 @@ public:
|
||||
virtual void DisableSpdy() { }
|
||||
virtual void ReuseConnectionOnRestartOK(bool) { }
|
||||
|
||||
// Returns true if early-data is possible.
|
||||
// Returns true if early-data or fast open is possible.
|
||||
virtual MOZ_MUST_USE bool CanDo0RTT() {
|
||||
return false;
|
||||
}
|
||||
// Returns true if early-data is possible and transaction will remember
|
||||
// that it is in 0RTT mode (to know should it rewide transaction or not
|
||||
// in the case of an error).
|
||||
virtual MOZ_MUST_USE bool Do0RTT() {
|
||||
return false;
|
||||
}
|
||||
@ -184,6 +190,10 @@ public:
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
virtual MOZ_MUST_USE nsresult RestartOnFastOpenError() {
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
virtual uint64_t TopLevelOuterContentWindowId() {
|
||||
MOZ_ASSERT(false);
|
||||
return 0;
|
||||
|
@ -87,6 +87,7 @@ nsHttpConnection::nsHttpConnection()
|
||||
, mDid0RTTSpdy(false)
|
||||
, mResponseThrottled(false)
|
||||
, mResumeRecvOnUnthrottle(false)
|
||||
, mFastOpen(false)
|
||||
{
|
||||
LOG(("Creating nsHttpConnection @%p\n", this));
|
||||
|
||||
@ -382,7 +383,9 @@ nsHttpConnection::EnsureNPNComplete(nsresult &aOut0RTTWriteHandshakeValue,
|
||||
}
|
||||
|
||||
rv = ssl->GetNegotiatedNPN(negotiatedNPN);
|
||||
if (!m0RTTChecked && (rv == NS_ERROR_NOT_CONNECTED) &&
|
||||
// Fast Open does not work well with TLS Early Data. TODO: dragana
|
||||
// fix this.
|
||||
if (!mFastOpen && !m0RTTChecked && (rv == NS_ERROR_NOT_CONNECTED) &&
|
||||
!mConnInfo->UsingProxy()) {
|
||||
// There is no ALPN info (yet!). We need to consider doing 0RTT. We
|
||||
// will do so if there is ALPN information from a previous session
|
||||
@ -1680,7 +1683,7 @@ nsHttpConnection::OnSocketWritable()
|
||||
} else if (!mTransaction) {
|
||||
rv = NS_ERROR_FAILURE;
|
||||
LOG((" No Transaction In OnSocketWritable\n"));
|
||||
} else {
|
||||
} else if (NS_SUCCEEDED(rv)) {
|
||||
|
||||
// for non spdy sessions let the connection manager know
|
||||
if (!mReportedSpdy) {
|
||||
@ -1725,6 +1728,9 @@ nsHttpConnection::OnSocketWritable()
|
||||
rv = mSocketOutCondition;
|
||||
}
|
||||
again = false;
|
||||
} else if (mFastOpen) {
|
||||
rv = mSocketOut->AsyncWait(this, 0, 0, nullptr); // wait for connected event.
|
||||
again = false;
|
||||
} else if (!transactionBytes) {
|
||||
rv = NS_OK;
|
||||
|
||||
@ -2287,5 +2293,29 @@ nsHttpConnection::CheckForTraffic(bool check)
|
||||
}
|
||||
}
|
||||
|
||||
nsAHttpTransaction *
|
||||
nsHttpConnection::CloseConnectionFastOpenTakesTooLongOrError()
|
||||
{
|
||||
MOZ_ASSERT(!mCurrentBytesRead);
|
||||
if (mUsingSpdyVersion) {
|
||||
DontReuse();
|
||||
mUsingSpdyVersion = 0;
|
||||
mSpdySession = nullptr;
|
||||
}
|
||||
|
||||
{
|
||||
MutexAutoLock lock(mCallbacksLock);
|
||||
mCallbacks = nullptr;
|
||||
}
|
||||
|
||||
RefPtr<nsAHttpTransaction> trans = nullptr;
|
||||
if (NS_SUCCEEDED(mTransaction->RestartOnFastOpenError())) {
|
||||
trans = mTransaction;
|
||||
}
|
||||
mTransaction = nullptr;
|
||||
Close(NS_ERROR_NET_RESET);
|
||||
return trans;
|
||||
}
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
|
@ -83,6 +83,12 @@ public:
|
||||
MOZ_MUST_USE nsresult Activate(nsAHttpTransaction *, uint32_t caps,
|
||||
int32_t pri);
|
||||
|
||||
void SetFastOpen(bool aFastOpen) { mFastOpen = aFastOpen; }
|
||||
// Close this connection and return the transaction. The transaction is
|
||||
// restarted as well. This will only happened before connection is
|
||||
// connected.
|
||||
nsAHttpTransaction * CloseConnectionFastOpenTakesTooLongOrError();
|
||||
|
||||
// Close the underlying socket transport.
|
||||
void Close(nsresult reason, bool aIsShutdown = false);
|
||||
|
||||
@ -392,6 +398,8 @@ private:
|
||||
// A read from the socket was requested while we where throttled, means
|
||||
// to ResumeRecv() when untrottled again. Only accessed on the socket thread.
|
||||
bool mResumeRecvOnUnthrottle;
|
||||
|
||||
bool mFastOpen;
|
||||
};
|
||||
|
||||
NS_DEFINE_STATIC_IID_ACCESSOR(nsHttpConnection, NS_HTTPCONNECTION_IID)
|
||||
|
@ -636,6 +636,7 @@ nsHttpConnectionMgr::RemoveIdleConnection(nsHttpConnection *conn)
|
||||
}
|
||||
|
||||
mNumIdleConns--;
|
||||
ConditionallyStopPruneDeadConnectionsTimer();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -2908,6 +2909,7 @@ nsHttpConnectionMgr::GetOrCreateConnectionEntry(nsHttpConnectionInfo *specificCI
|
||||
if (!specificEnt) {
|
||||
RefPtr<nsHttpConnectionInfo> clone(specificCI->Clone());
|
||||
specificEnt = new nsConnectionEntry(clone);
|
||||
specificEnt->mUseFastOpen = gHttpHandler->UseFastOpen();
|
||||
mCT.Put(clone->HashKey(), specificEnt);
|
||||
}
|
||||
return specificEnt;
|
||||
@ -3040,6 +3042,7 @@ nsHalfOpenSocket::nsHalfOpenSocket(nsConnectionEntry *ent,
|
||||
, mBackupConnectedOK(false)
|
||||
, mFreeToUse(true)
|
||||
, mPrimaryStreamStatus(NS_OK)
|
||||
, mUsingFastOpen(false)
|
||||
{
|
||||
MOZ_ASSERT(ent && trans, "constructor with null arguments");
|
||||
LOG(("Creating nsHalfOpenSocket [this=%p trans=%p ent=%s key=%s]\n",
|
||||
@ -3156,6 +3159,10 @@ nsHalfOpenSocket::SetupStreams(nsISocketTransport **transport,
|
||||
tmpFlags |= nsISocketTransport::DISABLE_RFC1918;
|
||||
}
|
||||
|
||||
if (!isBackup && mEnt->mUseFastOpen) {
|
||||
socketTransport->SetFastOpenCallback(this);
|
||||
}
|
||||
|
||||
socketTransport->SetConnectionFlags(tmpFlags);
|
||||
|
||||
const OriginAttributes& originAttributes = mEnt->mConnInfo->GetOriginAttributes();
|
||||
@ -3219,6 +3226,9 @@ nsHttpConnectionMgr::nsHalfOpenSocket::SetupPrimaryStreams()
|
||||
if (NS_FAILED(rv)) {
|
||||
if (mStreamOut)
|
||||
mStreamOut->AsyncWait(nullptr, 0, 0, nullptr);
|
||||
if (mSocketTransport) {
|
||||
mSocketTransport->SetFastOpenCallback(nullptr);
|
||||
}
|
||||
mStreamOut = nullptr;
|
||||
mStreamIn = nullptr;
|
||||
mSocketTransport = nullptr;
|
||||
@ -3253,7 +3263,13 @@ nsHttpConnectionMgr::nsHalfOpenSocket::SetupBackupTimer()
|
||||
{
|
||||
uint16_t timeout = gHttpHandler->GetIdleSynTimeout();
|
||||
MOZ_ASSERT(!mSynTimer, "timer already initd");
|
||||
if (timeout && !mSpeculative) {
|
||||
if (!timeout && mUsingFastOpen) {
|
||||
timeout = 250;
|
||||
}
|
||||
// When using Fast Open the correct transport will be setup for sure (it is
|
||||
// guaranteed), but it can be that it will happened a bit later.
|
||||
if (mUsingFastOpen ||
|
||||
(timeout && !mSpeculative)) {
|
||||
// Setup the timer that will establish a backup socket
|
||||
// if we do not get a writable event on the main one.
|
||||
// We do this because a lost SYN takes a very long time
|
||||
@ -3301,6 +3317,7 @@ nsHttpConnectionMgr::nsHalfOpenSocket::Abandon()
|
||||
if (mSocketTransport) {
|
||||
mSocketTransport->SetEventSink(nullptr, nullptr);
|
||||
mSocketTransport->SetSecurityCallbacks(nullptr);
|
||||
mSocketTransport->SetFastOpenCallback(nullptr);
|
||||
mSocketTransport = nullptr;
|
||||
}
|
||||
if (mBackupTransport) {
|
||||
@ -3395,15 +3412,168 @@ nsHalfOpenSocket::OnOutputStreamReady(nsIAsyncOutputStream *out)
|
||||
LOG(("nsHalfOpenSocket::OnOutputStreamReady [this=%p ent=%s %s]\n",
|
||||
this, mEnt->mConnInfo->Origin(),
|
||||
out == mStreamOut ? "primary" : "backup"));
|
||||
nsresult rv;
|
||||
|
||||
gHttpHandler->ConnMgr()->RecvdConnect();
|
||||
|
||||
CancelBackupTimer();
|
||||
|
||||
if (mConnectionNegotiatingFastOpen && mUsingFastOpen) {
|
||||
// If fast open is used, right after a socket for the primary stream is
|
||||
// created a nsHttpConnection is created for that socket. The connection
|
||||
// listens for OnOutputStreamReady not HalfOpenSocket. So this stream
|
||||
// cannot be mStreamOut.
|
||||
MOZ_ASSERT(out == mBackupStreamOut);
|
||||
MOZ_ASSERT(mTransaction->IsNullTransaction());
|
||||
// Here the backup, non-TFO connection has connected successfully,
|
||||
// before the TFO connection.
|
||||
//
|
||||
// The primary, TFO connection will be cancelled and the transaction
|
||||
// will be rewind. CloseConnectionFastOpenTakesTooLongOrError will
|
||||
// return the rewind transaction. The transaction will be put back to
|
||||
// the pending queue and as well connected to this halfOpenSocket.
|
||||
// SetupConn should set up a new nsHttpConnection with the backup
|
||||
// socketTransport and the rewind transaction.
|
||||
mSocketTransport->SetFastOpenCallback(nullptr);
|
||||
RefPtr<nsAHttpTransaction> trans =
|
||||
mConnectionNegotiatingFastOpen->CloseConnectionFastOpenTakesTooLongOrError();
|
||||
mConnectionNegotiatingFastOpen = nullptr;
|
||||
if (trans && trans->QueryHttpTransaction()) {
|
||||
mTransaction = trans;
|
||||
RefPtr<PendingTransactionInfo> pendingTransInfo =
|
||||
new PendingTransactionInfo(trans->QueryHttpTransaction());
|
||||
pendingTransInfo->mHalfOpen =
|
||||
do_GetWeakReference(static_cast<nsISupportsWeakReference*>(this));
|
||||
if (trans->Caps() & NS_HTTP_URGENT_START) {
|
||||
gHttpHandler->ConnMgr()->InsertTransactionSorted(mEnt->mUrgentStartQ,
|
||||
pendingTransInfo);
|
||||
} else {
|
||||
mEnt->InsertTransaction(pendingTransInfo);
|
||||
}
|
||||
}
|
||||
if (mEnt->mUseFastOpen) {
|
||||
gHttpHandler->IncrementFastOpenConsecutiveFailureCounter();
|
||||
mEnt->mUseFastOpen = false;
|
||||
}
|
||||
}
|
||||
|
||||
return SetupConn(out, false);
|
||||
}
|
||||
|
||||
bool
|
||||
nsHttpConnectionMgr::
|
||||
nsHalfOpenSocket::FastOpenEnabled()
|
||||
{
|
||||
LOG(("nsHalfOpenSocket::FastOpenEnabled [this=%p]\n", this));
|
||||
|
||||
if (!mEnt) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!gHttpHandler->UseFastOpen()) {
|
||||
// fast open was turned off.
|
||||
LOG(("nsHalfOpenSocket::FastEnabled - fast open was turned off.\n"));
|
||||
mEnt->mUseFastOpen = false;
|
||||
return false;
|
||||
}
|
||||
// We can use FastOpen if we have a transaction or if it is ssl
|
||||
// connection. For ssl we will use a null transaction to drive the SSL
|
||||
// handshake to completion if there is not a pending transaction. Afterwards
|
||||
// the connection will be 100% ready for the next transaction to use it.
|
||||
// Make an exception for SSL tunneled HTTP proxy as the NullHttpTransaction
|
||||
// does not know how to drive Connect.
|
||||
RefPtr<PendingTransactionInfo> info = FindTransactionHelper(false);
|
||||
|
||||
if ((!info) &&
|
||||
(!mEnt->mConnInfo->FirstHopSSL() || mEnt->mConnInfo->UsingConnect())) {
|
||||
LOG(("nsHalfOpenSocket::FastOpenEnabled - It is a connection without "
|
||||
"transaction and first hop is not ssl.\n"));
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((info) && !mEnt->mConnInfo->FirstHopSSL()) {
|
||||
// The following function call will check whether is possible to send
|
||||
// data during fast open
|
||||
if (!info->mTransaction->CanDo0RTT()) {
|
||||
LOG(("nsHalfOpenSocket::FastOpenEnabled - it is not safe to restart "
|
||||
"transaction.\n"));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHttpConnectionMgr::
|
||||
nsHalfOpenSocket::StartFastOpen()
|
||||
{
|
||||
MOZ_ASSERT(mStreamOut);
|
||||
MOZ_ASSERT(mEnt && !mBackupTransport);
|
||||
mUsingFastOpen = true;
|
||||
if (mEnt && !mBackupTransport && !mSynTimer) {
|
||||
// For Fast Open we will setup backup timer also for NullTransaction.
|
||||
// So maybe it is not set and we need to set it here.
|
||||
SetupBackupTimer();
|
||||
}
|
||||
mStreamOut->AsyncWait(nullptr, 0, 0, nullptr);
|
||||
mSocketTransport->SetEventSink(nullptr, nullptr);
|
||||
gHttpHandler->ConnMgr()->RecvdConnect();
|
||||
return SetupConn(mStreamOut, true);
|
||||
}
|
||||
|
||||
void
|
||||
nsHttpConnectionMgr::
|
||||
nsHalfOpenSocket::FastOpenConnected(nsresult aError)
|
||||
{
|
||||
RefPtr<nsHalfOpenSocket> deleteProtector(this);
|
||||
CancelBackupTimer();
|
||||
if (NS_SUCCEEDED(aError)) {
|
||||
NetAddr peeraddr;
|
||||
if (NS_SUCCEEDED(mSocketTransport->GetPeerAddr(&peeraddr))) {
|
||||
mEnt->RecordIPFamilyPreference(peeraddr.raw.family);
|
||||
}
|
||||
gHttpHandler->ResetFastOpenConsecutiveFailureCounter();
|
||||
} else if ((aError == NS_ERROR_CONNECTION_REFUSED) ||
|
||||
(aError == NS_ERROR_NET_TIMEOUT)) {
|
||||
if (mEnt->mUseFastOpen) {
|
||||
gHttpHandler->IncrementFastOpenConsecutiveFailureCounter();
|
||||
mEnt->mUseFastOpen = false;
|
||||
}
|
||||
// This is called from nsSocketTransport::RecoverFromError. The
|
||||
// socket will try connect and we need to rewind nsHttpTransaction
|
||||
MOZ_ASSERT(mConnectionNegotiatingFastOpen);
|
||||
DebugOnly<nsresult> rv =
|
||||
mConnectionNegotiatingFastOpen->Transaction()->RestartOnFastOpenError();
|
||||
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
||||
}
|
||||
if (mConnectionNegotiatingFastOpen) {
|
||||
mSocketTransport = nullptr;
|
||||
mConnectionNegotiatingFastOpen = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsHttpConnectionMgr::
|
||||
nsHalfOpenSocket::FastOpenNotSupported()
|
||||
{
|
||||
MOZ_ASSERT(mUsingFastOpen);
|
||||
RefPtr<nsHalfOpenSocket> deleteProtector(this);
|
||||
CancelBackupTimer();
|
||||
mUsingFastOpen = false;
|
||||
mConnectionNegotiatingFastOpen = nullptr;
|
||||
mSocketTransport = nullptr;
|
||||
gHttpHandler->SetFastOpenNotSupported();
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHttpConnectionMgr::
|
||||
nsHalfOpenSocket::SetupConn(nsIAsyncOutputStream *out,
|
||||
bool aFastOpen)
|
||||
{
|
||||
MOZ_ASSERT(!aFastOpen || (out == mStreamOut));
|
||||
// assign the new socket to the http connection
|
||||
RefPtr<nsHttpConnection> conn = new nsHttpConnection();
|
||||
LOG(("nsHalfOpenSocket::OnOutputStreamReady "
|
||||
LOG(("nsHalfOpenSocket::SetupConn "
|
||||
"Created new nshttpconnection %p\n", conn.get()));
|
||||
|
||||
// Some capabilities are needed before a transaciton actually gets
|
||||
@ -3413,22 +3583,28 @@ nsHalfOpenSocket::OnOutputStreamReady(nsIAsyncOutputStream *out)
|
||||
NetAddr peeraddr;
|
||||
nsCOMPtr<nsIInterfaceRequestor> callbacks;
|
||||
mTransaction->GetSecurityCallbacks(getter_AddRefs(callbacks));
|
||||
nsresult rv;
|
||||
if (out == mStreamOut) {
|
||||
TimeDuration rtt = TimeStamp::Now() - mPrimarySynStarted;
|
||||
rv = conn->Init(mEnt->mConnInfo,
|
||||
gHttpHandler->ConnMgr()->mMaxRequestDelay,
|
||||
mSocketTransport, mStreamIn, mStreamOut,
|
||||
mPrimaryConnectedOK, callbacks,
|
||||
mPrimaryConnectedOK || aFastOpen, callbacks,
|
||||
PR_MillisecondsToInterval(
|
||||
static_cast<uint32_t>(rtt.ToMilliseconds())));
|
||||
|
||||
if (NS_SUCCEEDED(mSocketTransport->GetPeerAddr(&peeraddr)))
|
||||
if (!aFastOpen &&
|
||||
NS_SUCCEEDED(mSocketTransport->GetPeerAddr(&peeraddr))) {
|
||||
mEnt->RecordIPFamilyPreference(peeraddr.raw.family);
|
||||
}
|
||||
|
||||
// The nsHttpConnection object now owns these streams and sockets
|
||||
mStreamOut = nullptr;
|
||||
mStreamIn = nullptr;
|
||||
mSocketTransport = nullptr;
|
||||
if (!aFastOpen) {
|
||||
mSocketTransport = nullptr;
|
||||
}
|
||||
conn->SetFastOpen(aFastOpen);
|
||||
} else if (out == mBackupStreamOut) {
|
||||
TimeDuration rtt = TimeStamp::Now() - mBackupSynStarted;
|
||||
rv = conn->Init(mEnt->mConnInfo,
|
||||
@ -3451,7 +3627,7 @@ nsHalfOpenSocket::OnOutputStreamReady(nsIAsyncOutputStream *out)
|
||||
}
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
LOG(("nsHalfOpenSocket::OnOutputStreamReady "
|
||||
LOG(("nsHalfOpenSocket::SetupConn "
|
||||
"conn->init (%p) failed %" PRIx32 "\n",
|
||||
conn.get(), static_cast<uint32_t>(rv)));
|
||||
return rv;
|
||||
@ -3459,7 +3635,9 @@ nsHalfOpenSocket::OnOutputStreamReady(nsIAsyncOutputStream *out)
|
||||
|
||||
// This half-open socket has created a connection. This flag excludes it
|
||||
// from counter of actual connections used for checking limits.
|
||||
mHasConnected = true;
|
||||
if (!aFastOpen) {
|
||||
mHasConnected = true;
|
||||
}
|
||||
|
||||
// if this is still in the pending list, remove it and dispatch it
|
||||
RefPtr<PendingTransactionInfo> pendingTransInfo = FindTransactionHelper(true);
|
||||
@ -3489,7 +3667,7 @@ nsHalfOpenSocket::OnOutputStreamReady(nsIAsyncOutputStream *out)
|
||||
!mEnt->mUrgentStartQ.Length() &&
|
||||
!mEnt->PendingQLength() &&
|
||||
!mEnt->mConnInfo->UsingConnect()) {
|
||||
LOG(("nsHalfOpenSocket::OnOutputStreamReady null transaction will "
|
||||
LOG(("nsHalfOpenSocket::SetupConn null transaction will "
|
||||
"be used to finish SSL handshake on conn %p\n", conn.get()));
|
||||
RefPtr<nsAHttpTransaction> trans;
|
||||
if (mTransaction->IsNullTransaction() && !mDispatchedMTransaction) {
|
||||
@ -3507,7 +3685,7 @@ nsHalfOpenSocket::OnOutputStreamReady(nsIAsyncOutputStream *out)
|
||||
DispatchAbstractTransaction(mEnt, trans, mCaps, conn, 0);
|
||||
} else {
|
||||
// otherwise just put this in the persistent connection pool
|
||||
LOG(("nsHalfOpenSocket::OnOutputStreamReady no transaction match "
|
||||
LOG(("nsHalfOpenSocket::SetupConn no transaction match "
|
||||
"returning conn %p to pool\n", conn.get()));
|
||||
gHttpHandler->ConnMgr()->OnMsgReclaimConnection(0, conn);
|
||||
|
||||
@ -3544,6 +3722,20 @@ nsHalfOpenSocket::OnOutputStreamReady(nsIAsyncOutputStream *out)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (aFastOpen) {
|
||||
// If it is fast open create a new tranaction for backup stream.
|
||||
mTransaction = new NullHttpTransaction(mEnt->mConnInfo,
|
||||
callbacks, mCaps);
|
||||
|
||||
conn->SetFastOpen(false);
|
||||
mConnectionNegotiatingFastOpen = conn;
|
||||
}
|
||||
|
||||
// If this halfOpenConn was speculative, but at the ende the conn got a
|
||||
// non-null transaction than this halfOpen is not speculative anymore!
|
||||
if (conn->Transaction() && conn->Transaction()->IsNullTransaction()) {
|
||||
mSpeculative = false;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
@ -3771,6 +3963,7 @@ nsConnectionEntry::nsConnectionEntry(nsHttpConnectionInfo *ci)
|
||||
, mUsedForConnection(false)
|
||||
{
|
||||
MOZ_COUNT_CTOR(nsConnectionEntry);
|
||||
mUseFastOpen = gHttpHandler->UseFastOpen();
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "AlternateServices.h"
|
||||
#include "ARefBase.h"
|
||||
#include "nsWeakReference.h"
|
||||
#include "TCPFastOpen.h"
|
||||
|
||||
#include "nsIObserver.h"
|
||||
#include "nsITimer.h"
|
||||
@ -276,6 +277,9 @@ private:
|
||||
// True if this connection entry has initiated a socket
|
||||
bool mUsedForConnection : 1;
|
||||
|
||||
// Try using TCP Fast Open.
|
||||
bool mUseFastOpen : 1;
|
||||
|
||||
// Set the IP family preference flags according the connected family
|
||||
void RecordIPFamilyPreference(uint16_t family);
|
||||
// Resets all flags to their default values
|
||||
@ -322,7 +326,8 @@ private:
|
||||
public nsITransportEventSink,
|
||||
public nsIInterfaceRequestor,
|
||||
public nsITimerCallback,
|
||||
public nsSupportsWeakReference
|
||||
public nsSupportsWeakReference,
|
||||
public TCPFastOpen
|
||||
{
|
||||
~nsHalfOpenSocket();
|
||||
|
||||
@ -368,7 +373,15 @@ private:
|
||||
|
||||
bool Claim();
|
||||
void Unclaim();
|
||||
|
||||
bool FastOpenEnabled() override;
|
||||
nsresult StartFastOpen() override;
|
||||
void FastOpenConnected(nsresult) override;
|
||||
void FastOpenNotSupported() override;
|
||||
private:
|
||||
nsresult SetupConn(nsIAsyncOutputStream *out,
|
||||
bool aFastOpen);
|
||||
|
||||
// To find out whether |mTransaction| is still in the connection entry's
|
||||
// pending queue. If the transaction is found and |removeWhenFound| is
|
||||
// true, the transaction will be removed from the pending queue.
|
||||
@ -421,6 +434,9 @@ private:
|
||||
// transactions.
|
||||
bool mFreeToUse;
|
||||
nsresult mPrimaryStreamStatus;
|
||||
|
||||
bool mUsingFastOpen;
|
||||
RefPtr<nsHttpConnection> mConnectionNegotiatingFastOpen;
|
||||
};
|
||||
friend class nsHalfOpenSocket;
|
||||
|
||||
|
@ -54,6 +54,7 @@
|
||||
#include "nsIThrottlingService.h"
|
||||
#include "nsISupportsPrimitives.h"
|
||||
#include "nsIXULRuntime.h"
|
||||
#include "nsCharSeparatedTokenizer.h"
|
||||
|
||||
#include "mozilla/net/NeckoChild.h"
|
||||
#include "mozilla/net/NeckoParent.h"
|
||||
@ -100,6 +101,9 @@
|
||||
#define SAFE_HINT_HEADER_VALUE "safeHint.enabled"
|
||||
#define SECURITY_PREFIX "security."
|
||||
|
||||
#define TCP_FAST_OPEN_ENABLE "network.tcp.tcp_fastopen_enable"
|
||||
#define TCP_FAST_OPEN_FAILURE_LIMIT "network.tcp.tcp_fastopen_consecutive_failure_limit"
|
||||
|
||||
#define UA_PREF(_pref) UA_PREF_PREFIX _pref
|
||||
#define HTTP_PREF(_pref) HTTP_PREF_PREFIX _pref
|
||||
#define BROWSER_PREF(_pref) BROWSER_PREF_PREFIX _pref
|
||||
@ -241,6 +245,9 @@ nsHttpHandler::nsHttpHandler()
|
||||
, mDefaultHpackBuffer(4096)
|
||||
, mMaxHttpResponseHeaderSize(393216)
|
||||
, mFocusedWindowTransactionRatio(0.9f)
|
||||
, mUseFastOpen(true)
|
||||
, mFastOpenConsecutiveFailureLimit(5)
|
||||
, mFastOpenConsecutiveFailureCounter(0)
|
||||
, mProcessId(0)
|
||||
, mNextChannelId(1)
|
||||
{
|
||||
@ -252,6 +259,71 @@ nsHttpHandler::nsHttpHandler()
|
||||
if (runtime) {
|
||||
runtime->GetProcessID(&mProcessId);
|
||||
}
|
||||
SetFastOpenOSSupport();
|
||||
}
|
||||
|
||||
void
|
||||
nsHttpHandler::SetFastOpenOSSupport()
|
||||
{
|
||||
mFastOpenSupported = false;
|
||||
#if !defined(XP_WIN) && !defined(XP_LINUX) && !defined(ANDROID) && !defined(HAS_CONNECTX)
|
||||
return;
|
||||
#else
|
||||
|
||||
nsCOMPtr<nsIPropertyBag2> infoService =
|
||||
do_GetService("@mozilla.org/system-info;1");
|
||||
MOZ_ASSERT(infoService, "Could not find a system info service");
|
||||
nsAutoCString version;
|
||||
nsresult rv;
|
||||
#ifdef ANDROID
|
||||
rv = infoService->GetPropertyAsACString(
|
||||
NS_LITERAL_STRING("sdk_version"), version);
|
||||
#else
|
||||
rv = infoService->GetPropertyAsACString(
|
||||
NS_LITERAL_STRING("version"), version);
|
||||
#endif
|
||||
|
||||
LOG(("nsHttpHandler::SetFastOpenOSSupport version %s", version.get()));
|
||||
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
// set min version minus 1.
|
||||
#ifdef XP_WIN
|
||||
int min_version[] = {10, 0};
|
||||
#elif XP_MACOSX
|
||||
int min_version[] = {15, 0};
|
||||
#elif ANDROID
|
||||
int min_version[] = {4, 4};
|
||||
#elif XP_LINUX
|
||||
int min_version[] = {3, 6};
|
||||
#endif
|
||||
int inx = 0;
|
||||
nsCCharSeparatedTokenizer tokenizer(version, '.');
|
||||
while ((inx < 2) && tokenizer.hasMoreTokens()) {
|
||||
nsAutoCString token(tokenizer.nextToken());
|
||||
const char* nondigit = NS_strspnp("0123456789", token.get());
|
||||
if (nondigit && *nondigit) {
|
||||
break;
|
||||
}
|
||||
nsresult rv;
|
||||
int32_t ver = token.ToInteger(&rv);
|
||||
if (NS_FAILED(rv)) {
|
||||
break;
|
||||
}
|
||||
if (ver > min_version[inx]) {
|
||||
mFastOpenSupported = true;
|
||||
break;
|
||||
} else if (ver == min_version[inx] && inx == 1) {
|
||||
mFastOpenSupported = true;
|
||||
} else if (ver < min_version[inx]) {
|
||||
break;
|
||||
}
|
||||
inx++;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
LOG(("nsHttpHandler::SetFastOpenOSSupport %s supported.\n",
|
||||
mFastOpenSupported ? "" : "not"));
|
||||
}
|
||||
|
||||
nsHttpHandler::~nsHttpHandler()
|
||||
@ -314,6 +386,8 @@ nsHttpHandler::Init()
|
||||
prefBranch->AddObserver(HTTP_PREF("tcp_keepalive.long_lived_connections"), this, true);
|
||||
prefBranch->AddObserver(SAFE_HINT_HEADER_VALUE, this, true);
|
||||
prefBranch->AddObserver(SECURITY_PREFIX, this, true);
|
||||
prefBranch->AddObserver(TCP_FAST_OPEN_ENABLE, this, true);
|
||||
prefBranch->AddObserver(TCP_FAST_OPEN_FAILURE_LIMIT, this, true);
|
||||
PrefsChanged(prefBranch, nullptr);
|
||||
}
|
||||
|
||||
@ -556,6 +630,22 @@ nsHttpHandler::IsAcceptableEncoding(const char *enc, bool isSecure)
|
||||
return rv;
|
||||
}
|
||||
|
||||
void
|
||||
nsHttpHandler::IncrementFastOpenConsecutiveFailureCounter()
|
||||
{
|
||||
LOG(("nsHttpHandler::IncrementFastOpenConsecutiveFailureCounter - "
|
||||
"failed=%d failure_limit=%d", mFastOpenConsecutiveFailureCounter,
|
||||
mFastOpenConsecutiveFailureLimit));
|
||||
if (mFastOpenConsecutiveFailureCounter < mFastOpenConsecutiveFailureLimit) {
|
||||
mFastOpenConsecutiveFailureCounter++;
|
||||
if (mFastOpenConsecutiveFailureCounter == mFastOpenConsecutiveFailureLimit) {
|
||||
LOG(("nsHttpHandler::IncrementFastOpenConsecutiveFailureCounter - "
|
||||
"Fast open failed too many times"));
|
||||
SetFastOpenNotSupported();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHttpHandler::GetStreamConverterService(nsIStreamConverterService **result)
|
||||
{
|
||||
@ -1649,6 +1739,23 @@ nsHttpHandler::PrefsChanged(nsIPrefBranch *prefs, const char *pref)
|
||||
}
|
||||
}
|
||||
|
||||
if (PREF_CHANGED(TCP_FAST_OPEN_ENABLE)) {
|
||||
rv = prefs->GetBoolPref(TCP_FAST_OPEN_ENABLE, &cVar);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
mUseFastOpen = cVar;
|
||||
}
|
||||
}
|
||||
|
||||
if (PREF_CHANGED(TCP_FAST_OPEN_FAILURE_LIMIT)) {
|
||||
rv = prefs->GetIntPref(TCP_FAST_OPEN_FAILURE_LIMIT, &val);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
if (val < 0) {
|
||||
val = 0;
|
||||
}
|
||||
mFastOpenConsecutiveFailureLimit = val;
|
||||
}
|
||||
}
|
||||
|
||||
if (PREF_CHANGED(HTTP_PREF("keep_empty_response_headers_as_empty_string"))) {
|
||||
rv = prefs->GetBoolPref(HTTP_PREF("keep_empty_response_headers_as_empty_string"),
|
||||
&cVar);
|
||||
|
@ -164,6 +164,23 @@ public:
|
||||
return mTCPKeepaliveLongLivedIdleTimeS;
|
||||
}
|
||||
|
||||
bool UseFastOpen() { return mUseFastOpen && mFastOpenSupported; }
|
||||
// If one of tcp connections return PR_NOT_TCP_SOCKET_ERROR while trying
|
||||
// fast open, it means that Fast Open is turned off so we will not try again
|
||||
// until a restart. This is only on Linux.
|
||||
// For windows 10 we can only check whether a version of windows support
|
||||
// Fast Open at run time, so if we get error PR_NOT_IMPLEMENTED_ERROR it
|
||||
// means that Fast Open is not supported and we will set mFastOpenSupported
|
||||
// to false.
|
||||
void SetFastOpenNotSupported() { mFastOpenSupported = false; }
|
||||
|
||||
void IncrementFastOpenConsecutiveFailureCounter();
|
||||
|
||||
void ResetFastOpenConsecutiveFailureCounter()
|
||||
{
|
||||
mFastOpenConsecutiveFailureCounter = 0;
|
||||
}
|
||||
|
||||
// returns the HTTP framing check level preference, as controlled with
|
||||
// network.http.enforce-framing.http1 and network.http.enforce-framing.soft
|
||||
FrameCheckLevel GetEnforceH1Framing() { return mEnforceH1Framing; }
|
||||
@ -387,6 +404,7 @@ private:
|
||||
|
||||
void NotifyObservers(nsIHttpChannel *chan, const char *event);
|
||||
|
||||
void SetFastOpenOSSupport();
|
||||
private:
|
||||
|
||||
// cached services
|
||||
@ -582,6 +600,11 @@ private:
|
||||
// The ratio for dispatching transactions from the focused window.
|
||||
float mFocusedWindowTransactionRatio;
|
||||
|
||||
Atomic<bool, Relaxed> mUseFastOpen;
|
||||
Atomic<bool, Relaxed> mFastOpenSupported;
|
||||
uint32_t mFastOpenConsecutiveFailureLimit;
|
||||
uint32_t mFastOpenConsecutiveFailureCounter;
|
||||
|
||||
private:
|
||||
// For Rate Pacing Certain Network Events. Only assign this pointer on
|
||||
// socket thread.
|
||||
|
@ -2141,11 +2141,23 @@ nsHttpTransaction::GetNetworkAddresses(NetAddr &self, NetAddr &peer)
|
||||
peer = mPeerAddr;
|
||||
}
|
||||
|
||||
bool
|
||||
nsHttpTransaction::CanDo0RTT()
|
||||
{
|
||||
if (mRequestHead->IsSafeMethod() &&
|
||||
(!mConnection ||
|
||||
!mConnection->IsProxyConnectInProgress())) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
nsHttpTransaction::Do0RTT()
|
||||
{
|
||||
if (mRequestHead->IsSafeMethod() &&
|
||||
!mConnection->IsProxyConnectInProgress()) {
|
||||
(!mConnection ||
|
||||
!mConnection->IsProxyConnectInProgress())) {
|
||||
m0RTTInProgress = true;
|
||||
}
|
||||
return m0RTTInProgress;
|
||||
@ -2179,6 +2191,34 @@ nsHttpTransaction::Finish0RTT(bool aRestart, bool aAlpnChanged /* ignored */)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHttpTransaction::RestartOnFastOpenError()
|
||||
{
|
||||
// This will happen on connection error during Fast Open or if connect
|
||||
// during Fast Open takes too long. So we should not have received any
|
||||
// data!
|
||||
MOZ_ASSERT(!mReceivedData);
|
||||
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
|
||||
|
||||
LOG(("nsHttpTransaction::RestartOnFastOpenError - restarting transaction "
|
||||
"%p\n", this));
|
||||
|
||||
// rewind streams in case we already wrote out the request
|
||||
nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mRequestStream);
|
||||
if (seekable)
|
||||
seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
|
||||
// clear old connection state...
|
||||
mSecurityInfo = nullptr;
|
||||
|
||||
if (!mConnInfo->GetRoutedHost().IsEmpty()) {
|
||||
MutexAutoLock lock(*nsHttp::GetLock());
|
||||
RefPtr<nsHttpConnectionInfo> ci;
|
||||
mConnInfo->CloneAsDirectRoute(getter_AddRefs(ci));
|
||||
mConnInfo = ci;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsHttpTransaction::Refused0RTT()
|
||||
{
|
||||
|
@ -177,6 +177,9 @@ public:
|
||||
// restart - this indicates that state for dev tools
|
||||
void Refused0RTT();
|
||||
|
||||
MOZ_MUST_USE bool CanDo0RTT() override;
|
||||
MOZ_MUST_USE nsresult RestartOnFastOpenError() override;
|
||||
|
||||
uint64_t TopLevelOuterContentWindowId() override
|
||||
{
|
||||
return mTopLevelOuterContentWindowId;
|
||||
|
Loading…
x
Reference in New Issue
Block a user