Skip to content

Commit e59b4cf

Browse files
authored
Add RST_STREAM info to errors (#123)
Motivation: If a stream is reset by a remote peer the synthesized status doesn't include the http/2 error code from the RST_STREAM frame which makes debugging harder. Modifications: - Add the http/2 error code to the status message Result: Better error messages
1 parent 24cded1 commit e59b4cf

File tree

6 files changed

+62
-15
lines changed

6 files changed

+62
-15
lines changed

Sources/GRPCNIOTransportCore/Client/GRPCClientStreamHandler.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,8 +132,8 @@ extension GRPCClientStreamHandler {
132132
context.fireErrorCaught(error)
133133
}
134134

135-
case .rstStream:
136-
self.handleUnexpectedInboundClose(context: context, reason: .streamReset)
135+
case .rstStream(let errorCode):
136+
self.handleUnexpectedInboundClose(context: context, reason: .streamReset(errorCode))
137137

138138
case .ping, .goAway, .priority, .settings, .pushPromise, .windowUpdate,
139139
.alternativeService, .origin:

Sources/GRPCNIOTransportCore/GRPCStreamStateMachine.swift

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ internal import GRPCCore
1818
internal import NIOCore
1919
internal import NIOHPACK
2020
internal import NIOHTTP1
21+
internal import NIOHTTP2
2122

2223
package enum Scheme: String {
2324
case http
@@ -613,7 +614,7 @@ struct GRPCStreamStateMachine {
613614
}
614615

615616
enum UnexpectedInboundCloseReason {
616-
case streamReset
617+
case streamReset(HTTP2ErrorCode)
617618
case channelInactive
618619
case errorThrown(any Error)
619620
}
@@ -1924,10 +1925,11 @@ extension MethodDescriptor {
19241925
extension RPCError {
19251926
fileprivate init(_ reason: GRPCStreamStateMachine.UnexpectedInboundCloseReason) {
19261927
switch reason {
1927-
case .streamReset:
1928+
case .streamReset(let errorCode):
19281929
self = RPCError(
19291930
code: .unavailable,
1930-
message: "Stream unexpectedly closed: a RST_STREAM frame was received."
1931+
message:
1932+
"Stream unexpectedly closed: received RST_STREAM frame (\(errorCode.shortDescription))."
19311933
)
19321934
case .channelInactive:
19331935
self = RPCError(code: .unavailable, message: "Stream unexpectedly closed.")
@@ -1950,3 +1952,44 @@ extension RPCError {
19501952
self = RPCError(code: .internalError, message: "Invalid state", cause: invalidState)
19511953
}
19521954
}
1955+
1956+
extension HTTP2ErrorCode {
1957+
var shortDescription: String {
1958+
let prefix = "0x" + String(self.networkCode, radix: 16) + ": "
1959+
let suffix: String
1960+
1961+
switch self {
1962+
case .noError:
1963+
suffix = "no error"
1964+
case .protocolError:
1965+
suffix = "protocol error"
1966+
case .internalError:
1967+
suffix = "internal error"
1968+
case .flowControlError:
1969+
suffix = "flow control error"
1970+
case .settingsTimeout:
1971+
suffix = "settings Timeout"
1972+
case .streamClosed:
1973+
suffix = "stream closed"
1974+
case .frameSizeError:
1975+
suffix = "frame size error"
1976+
case .refusedStream:
1977+
suffix = "fefused stream"
1978+
case .cancel:
1979+
suffix = "cancel"
1980+
case .compressionError:
1981+
suffix = "compression error"
1982+
case .connectError:
1983+
suffix = "connect error"
1984+
case .enhanceYourCalm:
1985+
suffix = "enhance your calm"
1986+
case .inadequateSecurity:
1987+
suffix = "inadequate security"
1988+
case .http11Required:
1989+
suffix = "HTTP/1.1 required"
1990+
default:
1991+
suffix = "unknown error"
1992+
}
1993+
return prefix + suffix
1994+
}
1995+
}

Sources/GRPCNIOTransportCore/Server/GRPCServerStreamHandler.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -202,8 +202,8 @@ extension GRPCServerStreamHandler {
202202
context.fireErrorCaught(error)
203203
}
204204

205-
case .rstStream:
206-
self.handleUnexpectedInboundClose(context: context, reason: .streamReset)
205+
case .rstStream(let errorCode):
206+
self.handleUnexpectedInboundClose(context: context, reason: .streamReset(errorCode))
207207

208208
case .ping, .goAway, .priority, .settings, .pushPromise, .windowUpdate,
209209
.alternativeService, .origin:

Tests/GRPCNIOTransportCoreTests/Client/GRPCClientStreamHandlerTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -954,7 +954,7 @@ final class GRPCClientStreamHandlerTests: XCTestCase {
954954
.status(
955955
.init(
956956
code: .unavailable,
957-
message: "Stream unexpectedly closed: a RST_STREAM frame was received."
957+
message: "Stream unexpectedly closed: received RST_STREAM frame (0x2: internal error)."
958958
),
959959
[:]
960960
)

Tests/GRPCNIOTransportCoreTests/GRPCStreamStateMachineTests.swift

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import GRPCCore
1818
import NIOCore
1919
import NIOEmbedded
2020
import NIOHPACK
21+
import NIOHTTP2
2122
import XCTest
2223

2324
@testable import GRPCNIOTransportCore
@@ -975,10 +976,10 @@ final class GRPCStreamClientStateMachineTests: XCTestCase {
975976
Status(code: .unavailable, message: "Stream unexpectedly closed.")
976977
),
977978
(
978-
GRPCStreamStateMachine.UnexpectedInboundCloseReason.streamReset,
979+
GRPCStreamStateMachine.UnexpectedInboundCloseReason.streamReset(.noError),
979980
Status(
980981
code: .unavailable,
981-
message: "Stream unexpectedly closed: a RST_STREAM frame was received."
982+
message: "Stream unexpectedly closed: received RST_STREAM frame (0x0: no error)."
982983
)
983984
),
984985
(
@@ -1022,7 +1023,7 @@ final class GRPCStreamClientStateMachineTests: XCTestCase {
10221023
func testUnexpectedCloseWhenServerClosed() throws {
10231024
let closeReasons = [
10241025
GRPCStreamStateMachine.UnexpectedInboundCloseReason.channelInactive,
1025-
.streamReset,
1026+
.streamReset(.noError),
10261027
.errorThrown(RPCError(code: .deadlineExceeded, message: "Test error")),
10271028
]
10281029
let states = [
@@ -2470,10 +2471,10 @@ final class GRPCStreamServerStateMachineTests: XCTestCase {
24702471
RPCError(code: .unavailable, message: "Stream unexpectedly closed.")
24712472
),
24722473
(
2473-
GRPCStreamStateMachine.UnexpectedInboundCloseReason.streamReset,
2474+
GRPCStreamStateMachine.UnexpectedInboundCloseReason.streamReset(.noError),
24742475
RPCError(
24752476
code: .unavailable,
2476-
message: "Stream unexpectedly closed: a RST_STREAM frame was received."
2477+
message: "Stream unexpectedly closed: received RST_STREAM frame (0x0: no error)."
24772478
)
24782479
),
24792480
(
@@ -2514,7 +2515,7 @@ final class GRPCStreamServerStateMachineTests: XCTestCase {
25142515
func testUnexpectedCloseWhenClientClosed() throws {
25152516
let closeReasons = [
25162517
GRPCStreamStateMachine.UnexpectedInboundCloseReason.channelInactive,
2517-
.streamReset,
2518+
.streamReset(.noError),
25182519
.errorThrown(RPCError(code: .deadlineExceeded, message: "Test error")),
25192520
]
25202521
let states = [

Tests/GRPCNIOTransportCoreTests/Server/GRPCServerStreamHandlerTests.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -981,7 +981,10 @@ final class GRPCServerStreamHandlerTests: XCTestCase {
981981
)
982982
) { error in
983983
XCTAssertEqual(error.code, .unavailable)
984-
XCTAssertEqual(error.message, "Stream unexpectedly closed: a RST_STREAM frame was received.")
984+
XCTAssertEqual(
985+
error.message,
986+
"Stream unexpectedly closed: received RST_STREAM frame (0x2: internal error)."
987+
)
985988
}
986989

987990
// We should now be closed: check we can't write anymore.

0 commit comments

Comments
 (0)