Bug 1622861 - Make closing of http3 connection more understandable and remove some unused code. r=mayhemer

Differential Revision: https://phabricator.services.mozilla.com/D67005

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Dragana Damjanovic 2020-03-20 16:08:16 +00:00
Родитель 4b2930e55c
Коммит 2c4e6310d5
3 изменённых файлов: 82 добавлений и 98 удалений

Просмотреть файл

@ -62,6 +62,7 @@ Http3Session::Http3Session()
mShouldClose(false),
mIsClosedByNeqo(false),
mError(NS_OK),
mSocketError(NS_OK),
mBeforeConnectedError(false) {
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
LOG(("Http3Session::Http3Session [this=%p]", this));
@ -181,13 +182,15 @@ Http3Session::~Http3Session() {
Shutdown();
}
PRIntervalTime Http3Session::IdleTime() {
// Seting this value to 0 will never triger PruneDeadConnections for
// this connection. We want to let neqo-transport perform close on idle
// connections.
return 0;
}
// This function may return a socket error.
// It will not return an error if socket error is
// NS_BASE_STREAM_WOULD_BLOCK.
// A caller of this function will close the Http3 connection
// in case of a error.
// The only callers is:
// HttpConnectionUDP::OnInputStreamReady ->
// HttpConnectionUDP::OnSocketReadable ->
// Http3Session::WriteSegmentsAgain
nsresult Http3Session::ProcessInput() {
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
MOZ_ASSERT(mSegmentReaderWriter);
@ -215,6 +218,9 @@ nsresult Http3Session::ProcessInput() {
LOG(("Http3Session::ProcessInput error=%" PRIx32 " [this=%p]",
static_cast<uint32_t>(rv), this));
if (NS_SUCCEEDED(mSocketError)) {
mSocketError = rv;
}
return rv;
}
@ -259,7 +265,6 @@ nsresult Http3Session::ProcessEvents(uint32_t count, uint32_t* countWritten,
? NS_BINDING_RETARGETED
: NS_OK);
*again = false;
Unused << ResumeRecv();
} else if (NS_FAILED(rv) && rv != NS_BASE_STREAM_WOULD_BLOCK) {
return rv;
}
@ -276,7 +281,6 @@ nsresult Http3Session::ProcessEvents(uint32_t count, uint32_t* countWritten,
? NS_BINDING_RETARGETED
: NS_OK);
*again = false;
Unused << ResumeRecv();
} else if (NS_FAILED(rv) && rv != NS_BASE_STREAM_WOULD_BLOCK) {
return rv;
}
@ -361,10 +365,19 @@ nsresult Http3Session::ProcessEvents(uint32_t count, uint32_t* countWritten,
}
*again = false;
Unused << ResumeRecv();
return NS_OK;
}
// This function may return a socket error.
// It will not return an error if socket error is
// NS_BASE_STREAM_WOULD_BLOCK.
// A Caller of this function will close the Http3 connection
// if this function returns an error.
// Callers are:
// 1) HttpConnectionUDP::OnQuicTimeoutExpired
// 2) HttpConnectionUDP::OnOutputStreamReady ->
// HttpConnectionUDP::OnSocketWritable ->
// Http3Session::ReadSegmentsAgain
nsresult Http3Session::ProcessOutput() {
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
MOZ_ASSERT(mSegmentReaderWriter);
@ -372,47 +385,32 @@ nsresult Http3Session::ProcessOutput() {
LOG(("Http3Session::ProcessOutput reader=%p, [this=%p]",
mSegmentReaderWriter.get(), this));
nsresult rv = NS_OK;
// Check if we have a packet that could not have been sent in a previous
// iteration.
if (mPacketToSend.Length()) {
uint32_t written = 0;
rv = mSegmentReaderWriter->OnReadSegment(
(const char*)mPacketToSend.Elements(), mPacketToSend.Length(),
&written);
if (NS_FAILED(rv)) {
if ((rv == NS_BASE_STREAM_WOULD_BLOCK) && mConnection) {
// The socket is still blocked, wait again.
Unused << mConnection->ResumeSend();
}
return rv;
}
MOZ_ASSERT(written == mPacketToSend.Length());
mPacketToSend.TruncateLength(0);
}
// Process neqo.
mHttp3Connection->ProcessHttp3();
uint64_t timeout = mHttp3Connection->ProcessOutput();
// Maybe get new packets to send.
while (NS_SUCCEEDED(mHttp3Connection->GetDataToSend(mPacketToSend))) {
// Check if we have a packet that could not have been sent in a previous
// iteration or maybe get new packets to send.
while (mPacketToSend.Length() ||
NS_SUCCEEDED(mHttp3Connection->GetDataToSend(mPacketToSend))) {
MOZ_ASSERT(mPacketToSend.Length());
LOG(("Http3Session::ProcessOutput sending packet with %u bytes [this=%p].",
(uint32_t)mPacketToSend.Length(), this));
uint32_t written = 0;
rv = mSegmentReaderWriter->OnReadSegment(
nsresult rv = mSegmentReaderWriter->OnReadSegment(
(const char*)mPacketToSend.Elements(), mPacketToSend.Length(),
&written);
if (NS_FAILED(rv)) {
if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
// The socket is blocked, keep the packet and we will send it when the
// socket is ready to send data again.
if (mConnection) {
Unused << mConnection->ResumeSend();
}
SetupTimer(timeout);
if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
// The socket is blocked, keep the packet and we will send it when the
// socket is ready to send data again.
if (mConnection) {
Unused << mConnection->ResumeSend();
}
SetupTimer(timeout);
return NS_OK;
}
if (NS_FAILED(rv)) {
mSocketError = rv;
// Ok the socket is blocked or there is an error, return from here,
// we do not need to set a timer if error is not
// NS_BASE_STREAM_WOULD_BLOCK, i.e. we are closing the connection.
@ -423,7 +421,7 @@ nsresult Http3Session::ProcessOutput() {
}
SetupTimer(timeout);
return rv;
return NS_OK;
}
// This is only called when timer expires.
@ -819,21 +817,21 @@ nsresult Http3Session::WriteSegmentsAgain(nsAHttpSegmentWriter* writer,
void Http3Session::Close(nsresult aReason) {
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
if (NS_FAILED(mError)) {
CloseInternal(false);
} else {
mError = aReason;
// If necko closes connection, this will map to "closing" key and 37 in the
// graph.
Telemetry::Accumulate(Telemetry::HTTP3_CONNECTTION_CLOSE_CODE,
NS_LITERAL_CSTRING("closing"), 37);
CloseInternal(true);
}
// If necko closes connection, this will map to "closing" key and 37 in the
// graph.
Telemetry::Accumulate(Telemetry::HTTP3_CONNECTTION_CLOSE_CODE,
NS_LITERAL_CSTRING("closing"), 37);
if (mCleanShutdown || mIsClosedByNeqo) {
// It is network-tear-down or neqo is state CLOSED(it does not need to send
// any more packets or wait for new packets).
if (mCleanShutdown || mIsClosedByNeqo || NS_FAILED(mSocketError)) {
// It is network-tear-down, a socker error or neqo is state CLOSED
// (it does not need to send any more packets or wait for new packets).
// We need to remove all references, so that
// Http3Session will be destroyed.
if (mTimer) {
@ -845,6 +843,7 @@ void Http3Session::Close(nsresult aReason) {
mState = CLOSED;
}
if (mConnection) {
// resume sending to send CLOSE_CONNECTION frame.
Unused << mConnection->ResumeSend();
}
}

Просмотреть файл

@ -51,21 +51,13 @@ class Http3Session final : public nsAHttpTransaction,
bool IsConnected() const { return mState == CONNECTED; }
bool IsClosing() const { return (mState == CLOSING || mState == CLOSED); }
nsresult GetError() const { return mError; }
nsresult Process();
bool IsClosed() const { return mState == CLOSED; }
bool AddStream(nsAHttpTransaction* aHttpTransaction, int32_t aPriority,
nsIInterfaceRequestor* aCallbacks);
bool CanReuse();
// TODO: use this.
bool RoomForMoreStreams() { return mQueuedStreams.GetSize() == 0; }
// We will let neqo-transport handle connection timeouts.
uint32_t ReadTimeoutTick(PRIntervalTime now) { return UINT32_MAX; }
// overload of nsAHttpTransaction
MOZ_MUST_USE nsresult ReadSegmentsAgain(nsAHttpSegmentReader*, uint32_t,
uint32_t*, bool*) final;
@ -85,16 +77,12 @@ class Http3Session final : public nsAHttpTransaction,
nsresult ReadResponseData(uint64_t aStreamId, char* aBuf, uint32_t aCount,
uint32_t* aCountWritten, bool* aFin);
const static uint32_t kDefaultReadAmount = 2048;
void CloseStream(Http3Stream* aStream, nsresult aResult);
void SetCleanShutdown(bool aCleanShutdown) {
mCleanShutdown = aCleanShutdown;
}
PRIntervalTime IdleTime();
bool TestJoinConnection(const nsACString& hostname, int32_t port);
bool JoinConnection(const nsACString& hostname, int32_t port);
@ -153,7 +141,12 @@ class Http3Session final : public nsAHttpTransaction,
bool mGoawayReceived;
bool mShouldClose;
bool mIsClosedByNeqo;
// mError is neqo error (a protocol error) and that may mean that we will
// send some packets after that.
nsresult mError;
// This is a socket error, there is no poioint in sending anything on that
// socket.
nsresult mSocketError;
bool mBeforeConnectedError;
uint64_t mCurrentForegroundTabOuterContentWindowId;

Просмотреть файл

@ -217,38 +217,16 @@ void HttpConnectionUDP::Close(nsresult reason, bool aIsShutdown) {
}
}
if (NS_FAILED(reason)) {
// The connection and security errors clear out alt-svc mappings
// in case any previously validated ones are now invalid
if (((reason == NS_ERROR_NET_RESET) ||
(NS_ERROR_GET_MODULE(reason) == NS_ERROR_MODULE_SECURITY)) &&
mConnInfo && !(mTransactionCaps & NS_HTTP_ERROR_SOFTLY)) {
gHttpHandler->ClearHostMapping(mConnInfo);
if (mSocketTransport) {
mSocketTransport->SetEventSink(nullptr, nullptr);
mSocketTransport->SetSecurityCallbacks(nullptr);
mSocketTransport->Close(reason);
if (mSocketOut) {
mSocketOut->AsyncWait(nullptr, 0, 0, nullptr);
}
if (mSocketTransport) {
mSocketTransport->SetEventSink(nullptr, nullptr);
// If there are bytes sitting in the input queue then read them
// into a junk buffer to avoid generating a tcp rst by closing a
// socket with data pending. TLS is a classic case of this where
// a Alert record might be superfulous to a clean HTTP3 shutdown.
// Never block to do this and limit it to a small amount of data.
// During shutdown just be fast!
if (mSocketIn && !aIsShutdown) {
char buffer[4000];
uint32_t count, total = 0;
nsresult rv;
do {
rv = mSocketIn->Read(buffer, 4000, &count);
if (NS_SUCCEEDED(rv)) total += count;
} while (NS_SUCCEEDED(rv) && count > 0 && total < 64000);
LOG(("HttpConnectionUDP::Close drained %d bytes\n", total));
}
mSocketTransport->SetSecurityCallbacks(nullptr);
mSocketTransport->Close(reason);
if (mSocketOut) mSocketOut->AsyncWait(nullptr, 0, 0, nullptr);
if (mSocketIn) {
mSocketIn->AsyncWait(nullptr, 0, 0, nullptr);
}
}
}
@ -500,12 +478,28 @@ void HttpConnectionUDP::CloseTransaction(nsAHttpTransaction* trans,
MOZ_ASSERT(trans == mHttp3Session);
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
// mask this error code because its not a real error.
if (reason == NS_BASE_STREAM_CLOSED) reason = NS_OK;
if (NS_SUCCEEDED(reason) || (reason == NS_BASE_STREAM_CLOSED)) {
MOZ_ASSERT(false);
return;
}
DontReuse();
// The connection and security errors clear out alt-svc mappings
// in case any previously validated ones are now invalid
if (((reason == NS_ERROR_NET_RESET) ||
(NS_ERROR_GET_MODULE(reason) == NS_ERROR_MODULE_SECURITY)) &&
mConnInfo && !(mTransactionCaps & NS_HTTP_ERROR_SOFTLY)) {
gHttpHandler->ClearHostMapping(mConnInfo);
}
mDontReuse = true;
mHttp3Session->SetCleanShutdown(aIsShutdown);
mHttp3Session->Close(reason);
if (!mHttp3Session->IsClosed()) {
// During closing phase we still keep mHttp3Session session,
// to resend CLOSE_CONNECTION frames.
return;
}
mHttp3Session = nullptr;
{
@ -513,9 +507,7 @@ void HttpConnectionUDP::CloseTransaction(nsAHttpTransaction* trans,
mCallbacks = nullptr;
}
if (NS_FAILED(reason) && (reason != NS_BINDING_RETARGETED)) {
Close(reason, aIsShutdown);
}
Close(reason, aIsShutdown);
// flag the connection as reused here for convenience sake. certainly
// it might be going away instead ;-)