Bug 1818756 - Propagate stream error code from client to server, r=jesup,necko-reviewers

Differential Revision: https://phabricator.services.mozilla.com/D171267
This commit is contained in:
Kershaw Chang 2023-05-04 13:40:59 +00:00
Родитель be7e0b20ea
Коммит 7f20106a49
9 изменённых файлов: 111 добавлений и 30 удалений

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

@ -7,6 +7,8 @@
#include "mozilla/dom/UnderlyingSinkCallbackHelpers.h"
#include "StreamUtils.h"
#include "mozilla/dom/UnionTypes.h"
#include "mozilla/dom/WebTransportError.h"
#include "nsHttp.h"
using namespace mozilla::dom;
@ -253,9 +255,22 @@ already_AddRefed<Promise> WritableStreamToOutput::AbortCallbackImpl(
// https://streams.spec.whatwg.org/#writablestream-set-up
// Step 3. Let abortAlgorithmWrapper be an algorithm that runs these steps:
if (aReason.WasPassed() && aReason.Value().isObject()) {
JS::Rooted<JSObject*> obj(aCx, &aReason.Value().toObject());
RefPtr<WebTransportError> error;
UnwrapObject<prototypes::id::WebTransportError, WebTransportError>(
obj, error, nullptr);
if (error) {
mOutput->CloseWithStatus(net::GetNSResultFromWebTransportError(
error->GetStreamErrorCode().Value()));
return nullptr;
}
}
// XXX The close or rather a dedicated abort should be async. For now we have
// to always fall back to the Step 3.3 below.
mOutput->CloseWithStatus(NS_ERROR_ABORT);
// XXX how do we know this stream is used by webtransport?
mOutput->CloseWithStatus(NS_ERROR_WEBTRANSPORT_CODE_BASE);
// Step 3.3. Return a promise resolved with undefined.
// Wrapper handles this

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

@ -1758,17 +1758,16 @@ void Http3Session::CloseWebTransportStream(Http3WebTransportStream* aStream,
HTTP3_APP_ERROR_REQUEST_CANCELLED);
}
aStream->Close(aResult);
CloseStreamInternal(aStream, aResult);
}
void Http3Session::ResetWebTransportStream(Http3WebTransportStream* aStream,
uint8_t aErrorCode) {
uint64_t aErrorCode) {
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
LOG3(("Http3Session::ResetWebTransportStream %p %p 0x%" PRIx32, this, aStream,
static_cast<uint32_t>(aErrorCode)));
LOG3(("Http3Session::ResetWebTransportStream %p %p 0x%" PRIx64, this, aStream,
aErrorCode));
mHttp3Connection->ResetStream(aStream->StreamId(), aErrorCode);
CloseStreamInternal(aStream, NS_ERROR_ABORT);
}
void Http3Session::StreamStopSending(Http3WebTransportStream* aStream,

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

@ -207,7 +207,7 @@ class Http3Session final : public nsAHttpTransaction, public nsAHttpConnection {
nsresult aResult);
void StreamHasDataToWrite(Http3StreamBase* aStream);
void ResetWebTransportStream(Http3WebTransportStream* aStream,
uint8_t aErrorCode);
uint64_t aErrorCode);
void StreamStopSending(Http3WebTransportStream* aStream, uint8_t aErrorCode);
void SendDatagram(Http3WebTransportSession* aSession,

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

@ -400,6 +400,15 @@ nsresult Http3WebTransportStream::ReadSegments() {
rv = mSendStreamPipeIn->AsyncWait(this, 0, 0, gSocketTransportService);
}
again = false;
// Got a WebTransport specific error
if (rv >= NS_ERROR_WEBTRANSPORT_CODE_BASE &&
rv <= NS_ERROR_WEBTRANSPORT_CODE_END) {
uint8_t errorCode = GetWebTransportErrorFromNSResult(rv);
mSendState = SEND_DONE;
Reset(WebTransportErrorToHttp3Error(errorCode));
rv = NS_OK;
}
} else if (NS_FAILED(mSocketOutCondition)) {
if (mSocketOutCondition != NS_BASE_STREAM_WOULD_BLOCK) {
rv = mSocketOutCondition;
@ -491,6 +500,10 @@ nsresult Http3WebTransportStream::WritePipeSegment(nsIOutputStream* stream,
nsresult Http3WebTransportStream::WriteSegments() {
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
if (!mReceiveStreamPipeOut) {
return NS_OK;
}
LOG(("Http3WebTransportStream::WriteSegments [this=%p]", this));
nsresult rv = NS_OK;
@ -529,8 +542,7 @@ nsresult Http3WebTransportStream::WriteSegments() {
}
bool Http3WebTransportStream::Done() const {
// To be implemented in bug 1790403.
return false;
return mSendState == SEND_DONE && mRecvState == RECV_DONE;
}
void Http3WebTransportStream::Close(nsresult aResult) {
@ -555,7 +567,7 @@ void Http3WebTransportStream::SendFin() {
LOG(("Http3WebTransportStream::SendFin [this=%p mSendState=%d]", this,
mSendState));
if (mSendFin || !mSession) {
if (mSendFin || !mSession || mResetError) {
// Already closed.
return;
}
@ -582,12 +594,12 @@ void Http3WebTransportStream::SendFin() {
}
}
void Http3WebTransportStream::Reset(uint8_t aErrorCode) {
void Http3WebTransportStream::Reset(uint64_t aErrorCode) {
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
LOG(("Http3WebTransportStream::Reset [this=%p, mSendState=%d]", this,
mSendState));
if (mResetError || !mSession) {
if (mResetError || !mSession || mSendFin) {
// The stream is already reset.
return;
}
@ -606,6 +618,7 @@ void Http3WebTransportStream::Reset(uint8_t aErrorCode) {
NS_NewRunnableFunction("Http3WebTransportStream::Reset", [self]() {
self->mSession->ResetWebTransportStream(self, *self->mResetError);
self->mSession->StreamHasDataToWrite(self);
self->mSession->ConnectSlowConsumer(self);
}));
});
} break;
@ -616,6 +629,7 @@ void Http3WebTransportStream::Reset(uint8_t aErrorCode) {
mSession->ResetWebTransportStream(this, *mResetError);
// StreamHasDataToWrite needs to be called to trigger ProcessOutput.
mSession->StreamHasDataToWrite(this);
mSession->ConnectSlowConsumer(this);
break;
default:
MOZ_ASSERT_UNREACHABLE("invalid mSendState!");

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

@ -61,7 +61,7 @@ class Http3WebTransportStream final : public Http3StreamBase,
WebTransportStreamType StreamType() const { return mStreamType; }
void SendFin();
void Reset(uint8_t aErrorCode);
void Reset(uint64_t aErrorCode);
void SendStopSending(uint8_t aErrorCode);
already_AddRefed<nsIWebTransportSendStreamStats> GetSendStreamStats();
@ -121,7 +121,7 @@ class Http3WebTransportStream final : public Http3StreamBase,
uint64_t mTotalAcknowledged = 0;
bool mSendFin{false};
// The error code used to reset the stream. Should be only set once.
Maybe<uint8_t> mResetError;
Maybe<uint64_t> mResetError;
// The error code used for STOP_SENDING. Should be only set once.
Maybe<uint8_t> mStopSendingError;

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

@ -38,6 +38,10 @@ const uint32_t kHttp3VersionCount = 5;
const nsCString kHttp3Versions[] = {"h3-29"_ns, "h3-30"_ns, "h3-31"_ns,
"h3-32"_ns, "h3"_ns};
// https://datatracker.ietf.org/doc/html/draft-ietf-webtrans-http3/#section-4.3
constexpr uint64_t kWebTransportErrorCodeStart = 0x52e4a40fa8db;
constexpr uint64_t kWebTransportErrorCodeEnd = 0x52e4a40fa9e2;
// define storage for all atoms
namespace nsHttp {
#define HTTP_ATOM(_name, _value) nsHttpAtom _name(nsLiteralCString{_value});
@ -1081,5 +1085,41 @@ void CreatePushHashKey(const nsCString& scheme, const nsCString& hostHeader,
outKey.Append(pathInfo);
}
nsresult GetNSResultFromWebTransportError(uint8_t aErrorCode) {
return static_cast<nsresult>((uint32_t)NS_ERROR_WEBTRANSPORT_CODE_BASE +
(uint32_t)aErrorCode);
}
uint8_t GetWebTransportErrorFromNSResult(nsresult aResult) {
if (aResult < NS_ERROR_WEBTRANSPORT_CODE_BASE ||
aResult > NS_ERROR_WEBTRANSPORT_CODE_END) {
return 0;
}
return static_cast<uint8_t>((uint32_t)aResult -
(uint32_t)NS_ERROR_WEBTRANSPORT_CODE_BASE);
}
uint64_t WebTransportErrorToHttp3Error(uint8_t aErrorCode) {
return kWebTransportErrorCodeStart + aErrorCode + aErrorCode / 0x1e;
}
uint8_t Http3ErrorToWebTransportError(uint64_t aErrorCode) {
// Ensure the code is within the valid range.
if (aErrorCode < kWebTransportErrorCodeStart ||
aErrorCode > kWebTransportErrorCodeEnd) {
return 0;
}
uint64_t shifted = aErrorCode - kWebTransportErrorCodeStart;
uint64_t result = shifted - shifted / 0x1f;
if (result <= std::numeric_limits<uint8_t>::max()) {
return (uint8_t)result;
}
return 0;
}
} // namespace net
} // namespace mozilla

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

@ -434,6 +434,14 @@ void CreatePushHashKey(const nsCString& scheme, const nsCString& hostHeader,
uint64_t serial, const nsACString& pathInfo,
nsCString& outOrigin, nsCString& outKey);
nsresult GetNSResultFromWebTransportError(uint8_t aErrorCode);
uint8_t GetWebTransportErrorFromNSResult(nsresult aResult);
uint64_t WebTransportErrorToHttp3Error(uint8_t aErrorCode);
uint8_t Http3ErrorToWebTransportError(uint64_t aErrorCode);
} // namespace net
} // namespace mozilla

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

@ -4,19 +4,19 @@
if (os == "mac") and debug: [TIMEOUT, OK]
[TIMEOUT, OK, CRASH]
[Abort client-created bidirectional stream]
expected: FAIL
expected: PASS
[Abort server-initiated bidirectional stream]
expected: FAIL
expected: PASS
[Abort unidirectional stream with WebTransportError]
expected: FAIL
expected: PASS
[Close and abort unidirectional stream]
expected: FAIL
[Abort unidirectional stream with default error code]
expected: FAIL
expected: PASS
[STOP_SENDING coming from server]
expected: TIMEOUT
@ -28,19 +28,19 @@
[streams-close.https.any.html]
expected: [OK, TIMEOUT]
[Abort client-created bidirectional stream]
expected: FAIL
expected: PASS
[Abort server-initiated bidirectional stream]
expected: FAIL
expected: PASS
[Abort unidirectional stream with WebTransportError]
expected: FAIL
expected: PASS
[Close and abort unidirectional stream]
expected: FAIL
[Abort unidirectional stream with default error code]
expected: FAIL
expected: PASS
[STOP_SENDING coming from server]
expected: TIMEOUT
@ -55,19 +55,19 @@
if (os == "mac") and debug: [TIMEOUT, OK]
[TIMEOUT, OK, CRASH]
[Abort client-created bidirectional stream]
expected: FAIL
expected: PASS
[Abort server-initiated bidirectional stream]
expected: FAIL
expected: PASS
[Abort unidirectional stream with WebTransportError]
expected: FAIL
expected: PASS
[Close and abort unidirectional stream]
expected: FAIL
[Abort unidirectional stream with default error code]
expected: FAIL
expected: PASS
[STOP_SENDING coming from server]
expected: TIMEOUT
@ -83,19 +83,19 @@
if not fission: [TIMEOUT, OK, ERROR, CRASH]
[TIMEOUT, OK, ERROR]
[Abort client-created bidirectional stream]
expected: FAIL
expected: PASS
[Abort server-initiated bidirectional stream]
expected: FAIL
expected: PASS
[Abort unidirectional stream with WebTransportError]
expected: FAIL
expected: PASS
[Close and abort unidirectional stream]
expected: FAIL
[Abort unidirectional stream with default error code]
expected: FAIL
expected: PASS
[STOP_SENDING coming from server]
expected: TIMEOUT

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

@ -447,6 +447,11 @@ with modules["NETWORK"]:
# Generic error for non-specific failures during service worker interception
errors["NS_ERROR_INTERCEPTION_FAILED"] = FAILURE(100)
errors["NS_ERROR_WEBTRANSPORT_CODE_BASE"] = FAILURE(200)
errors["NS_ERROR_WEBTRANSPORT_CODE_END"] = (
errors["NS_ERROR_WEBTRANSPORT_CODE_BASE"] + 255
)
# All Http proxy CONNECT response codes
errors["NS_ERROR_PROXY_CODE_BASE"] = FAILURE(1000)
# Redirection 3xx