// Copyright 2023 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. //go:build go1.21 package quic import ( "fmt" ) // A transportError is a transport error code from RFC 9000 Section 20.1. // // The transportError type doesn't implement the error interface to ensure we always // distinguish between errors sent to and received from the peer. // See the localTransportError and peerTransportError types below. type transportError uint64 // https://www.rfc-editor.org/rfc/rfc9000.html#section-20.1 const ( errNo = transportError(0x00) errInternal = transportError(0x01) errConnectionRefused = transportError(0x02) errFlowControl = transportError(0x03) errStreamLimit = transportError(0x04) errStreamState = transportError(0x05) errFinalSize = transportError(0x06) errFrameEncoding = transportError(0x07) errTransportParameter = transportError(0x08) errConnectionIDLimit = transportError(0x09) errProtocolViolation = transportError(0x0a) errInvalidToken = transportError(0x0b) errApplicationError = transportError(0x0c) errCryptoBufferExceeded = transportError(0x0d) errKeyUpdateError = transportError(0x0e) errAEADLimitReached = transportError(0x0f) errNoViablePath = transportError(0x10) errTLSBase = transportError(0x0100) // 0x0100-0x01ff; base + TLS code ) func (e transportError) String() string { switch e { case errNo: return "NO_ERROR" case errInternal: return "INTERNAL_ERROR" case errConnectionRefused: return "CONNECTION_REFUSED" case errFlowControl: return "FLOW_CONTROL_ERROR" case errStreamLimit: return "STREAM_LIMIT_ERROR" case errStreamState: return "STREAM_STATE_ERROR" case errFinalSize: return "FINAL_SIZE_ERROR" case errFrameEncoding: return "FRAME_ENCODING_ERROR" case errTransportParameter: return "TRANSPORT_PARAMETER_ERROR" case errConnectionIDLimit: return "CONNECTION_ID_LIMIT_ERROR" case errProtocolViolation: return "PROTOCOL_VIOLATION" case errInvalidToken: return "INVALID_TOKEN" case errApplicationError: return "APPLICATION_ERROR" case errCryptoBufferExceeded: return "CRYPTO_BUFFER_EXCEEDED" case errKeyUpdateError: return "KEY_UPDATE_ERROR" case errAEADLimitReached: return "AEAD_LIMIT_REACHED" case errNoViablePath: return "NO_VIABLE_PATH" } if e >= 0x0100 && e <= 0x01ff { return fmt.Sprintf("CRYPTO_ERROR(%v)", uint64(e)&0xff) } return fmt.Sprintf("ERROR %d", uint64(e)) } // A localTransportError is an error sent to the peer. type localTransportError struct { code transportError reason string } func (e localTransportError) Error() string { if e.reason == "" { return fmt.Sprintf("closed connection: %v", e.code) } return fmt.Sprintf("closed connection: %v: %q", e.code, e.reason) } // A peerTransportError is an error received from the peer. type peerTransportError struct { code transportError reason string } func (e peerTransportError) Error() string { return fmt.Sprintf("peer closed connection: %v: %q", e.code, e.reason) } // A StreamErrorCode is an application protocol error code (RFC 9000, Section 20.2) // indicating whay a stream is being closed. type StreamErrorCode uint64 func (e StreamErrorCode) Error() string { return fmt.Sprintf("stream error code %v", uint64(e)) } // An ApplicationError is an application protocol error code (RFC 9000, Section 20.2). // Application protocol errors may be sent when terminating a stream or connection. type ApplicationError struct { Code uint64 Reason string } func (e *ApplicationError) Error() string { // TODO: Include the Reason string here, but sanitize it first. return fmt.Sprintf("AppError %v", e.Code) } // Is reports a match if err is an *ApplicationError with a matching Code. func (e *ApplicationError) Is(err error) bool { e2, ok := err.(*ApplicationError) return ok && e2.Code == e.Code }