Skip to content

Commit

Permalink
cleanup: deprecate closepool and errclass (#51)
Browse files Browse the repository at this point in the history
These packages have been moved to rbmk-project/common. We keep the old
symbols around and the packages themselves around to preserve history,
but the implementation is now gone.
  • Loading branch information
bassosimone authored Nov 30, 2024
1 parent 2267de7 commit 30adea1
Show file tree
Hide file tree
Showing 13 changed files with 39 additions and 421 deletions.
50 changes: 5 additions & 45 deletions closepool/closepool.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,51 +2,11 @@

// Package closepool allows pooling [io.Closer] instances
// and closing them in a single operation.
package closepool

import (
"errors"
"io"
"slices"
"sync"
)

// Pool allows pooling a set of [io.Closer].
//
// The zero value is ready to use.
type Pool struct {
// handles contains the [io.Closer] to close.
handles []io.Closer

// mu provides mutual exclusion.
mu sync.Mutex
}

// Add adds a given [io.Closer] to the pool.
func (p *Pool) Add(conn io.Closer) {
p.mu.Lock()
p.handles = append(p.handles, conn)
p.mu.Unlock()
}
// Deprecated: use `github.com/rbmk-project/common/closepool` instead.
package closepool

// Close closes all the [io.Closer] inside the pool iterating
// in backward order. Therefore, if one registers a TCP connection
// and then the corresponding TLS connection, the TLS connection
// is closed first. The returned error is the join of all the
// errors that occurred when closing connections.
func (p *Pool) Close() error {
// Lock and copy the [io.Closer] to close.
p.mu.Lock()
handles := p.handles
p.handles = nil
p.mu.Unlock()
import "github.com/rbmk-project/common/closepool"

// Close all the [io.Closer].
var errv []error
for _, handle := range slices.Backward(handles) {
if err := handle.Close(); err != nil {
errv = append(errv, err)
}
}
return errors.Join(errv...)
}
// Pool is an alias for [closepool.Pool].
type Pool = closepool.Pool
120 changes: 0 additions & 120 deletions closepool/closepool_test.go

This file was deleted.

145 changes: 26 additions & 119 deletions errclass/errclass.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ Package errclass implements error classification.
The general idea is to classify golang errors to an enum of strings
with names resembling standard Unix error names.
Deprecated: use `github.com/rbmk-project/common/errclass` instead.
# Design Principles
1. Preserve original error in `err` in the structured logs.
Expand Down Expand Up @@ -64,13 +66,7 @@ platform-specific error constants.
package errclass

import (
"context"
"crypto/x509"
"errors"
"io"
"net"
"os"
"strings"
"github.com/rbmk-project/common/errclass"
)

const (
Expand All @@ -79,169 +75,80 @@ const (
//

// EADDRNOTAVAIL is the address not available error.
EADDRNOTAVAIL = "EADDRNOTAVAIL"
EADDRNOTAVAIL = errclass.EADDRNOTAVAIL

// EADDRINUSE is the address in use error.
EADDRINUSE = "EADDRINUSE"
EADDRINUSE = errclass.EADDRINUSE

// ECONNABORTED is the connection aborted error.
ECONNABORTED = "ECONNABORTED"
ECONNABORTED = errclass.ECONNABORTED

// ECONNREFUSED is the connection refused error.
ECONNREFUSED = "ECONNREFUSED"
ECONNREFUSED = errclass.ECONNREFUSED

// ECONNRESET is the connection reset by peer error.
ECONNRESET = "ECONNRESET"
ECONNRESET = errclass.ECONNRESET

// EHOSTUNREACH is the host unreachable error.
EHOSTUNREACH = "EHOSTUNREACH"
EHOSTUNREACH = errclass.EHOSTUNREACH

// EEOF indicates an unexpected EOF.
EEOF = "EEOF"
EEOF = errclass.EEOF

// EINVAL is the invalid argument error.
EINVAL = "EINVAL"
EINVAL = errclass.EINVAL

// EINTR is the interrupted system call error.
EINTR = "EINTR"
EINTR = errclass.EINTR

// ENETDOWN is the network is down error.
ENETDOWN = "ENETDOWN"
ENETDOWN = errclass.ENETDOWN

// ENETUNREACH is the network unreachable error.
ENETUNREACH = "ENETUNREACH"
ENETUNREACH = errclass.ENETUNREACH

// ENOBUFS is the no buffer space available error.
ENOBUFS = "ENOBUFS"
ENOBUFS = errclass.ENOBUFS

// ENOTCONN is the not connected error.
ENOTCONN = "ENOTCONN"
ENOTCONN = errclass.ENOTCONN

// EPROTONOSUPPORT is the protocol not supported error.
EPROTONOSUPPORT = "EPROTONOSUPPORT"
EPROTONOSUPPORT = errclass.EPROTONOSUPPORT

// ETIMEDOUT is the operation timed out error.
ETIMEDOUT = "ETIMEDOUT"
ETIMEDOUT = errclass.ETIMEDOUT

//
// Errors that we can map using the error message suffix:
//

// EDNS_NONAME is the DNS error for "no such host".
EDNS_NONAME = "EDNS_NONAME"
EDNS_NONAME = errclass.EDNS_NONAME

// EDNS_NODATA is the DNS error for "no answer".
EDNS_NODATA = "EDNS_NODATA"
EDNS_NODATA = errclass.EDNS_NODATA

//
// Errors that we can map using [errors.As]:
//

// ETLS_HOSTNAME_MISMATCH is the TLS error for hostname verification failure.
ETLS_HOSTNAME_MISMATCH = "ETLS_HOSTNAME_MISMATCH"
ETLS_HOSTNAME_MISMATCH = errclass.ETLS_HOSTNAME_MISMATCH

// ETLS_CA_UNKNOWN is the TLS error for unknown certificate authority.
ETLS_CA_UNKNOWN = "ETLS_CA_UNKNOWN"
ETLS_CA_UNKNOWN = errclass.ETLS_CA_UNKNOWN

// ETLS_CERT_INVALID is the TLS error for invalid certificate.
ETLS_CERT_INVALID = "ETLS_CERT_INVALID"
ETLS_CERT_INVALID = errclass.ETLS_CERT_INVALID

//
// Fallback errors:
//

// EGENERIC is the generic, unclassified error.
EGENERIC = "EGENERIC"
EGENERIC = errclass.EGENERIC
)

// errorsIsMap contains the errors that we can map with [errors.Is].
var errorsIsMap = map[error]string{
context.DeadlineExceeded: ETIMEDOUT,
context.Canceled: EINTR,
errEADDRNOTAVAIL: EADDRNOTAVAIL,
errEADDRINUSE: EADDRINUSE,
errECONNABORTED: ECONNABORTED,
errECONNREFUSED: ECONNREFUSED,
errECONNRESET: ECONNRESET,
errEHOSTUNREACH: EHOSTUNREACH,
io.EOF: EEOF,
io.ErrUnexpectedEOF: EEOF,
errEINVAL: EINVAL,
errEINTR: EINTR,
errENETDOWN: ENETDOWN,
errENETUNREACH: ENETUNREACH,
errENOBUFS: ENOBUFS,
errENOTCONN: ENOTCONN,
errEPROTONOSUPPORT: EPROTONOSUPPORT,
errETIMEDOUT: ETIMEDOUT,
net.ErrClosed: EINTR,
os.ErrDeadlineExceeded: ETIMEDOUT,
}

// stringSuffixMap contains the errors that we can map using the error message suffix.
var stringSuffixMap = map[string]string{
"no answer from DNS server": EDNS_NODATA,
"no such host": EDNS_NONAME,
}

// errorsAsList contains the errors that we can map with [errors.As].
var errorsAsList = []struct {
as func(err error) bool
class string
}{
{
as: func(err error) bool {
var candidate x509.HostnameError
return errors.As(err, &candidate)
},
class: ETLS_HOSTNAME_MISMATCH,
},

{
as: func(err error) bool {
var candidate x509.UnknownAuthorityError
return errors.As(err, &candidate)
},
class: ETLS_CA_UNKNOWN,
},

{
as: func(err error) bool {
var candidate x509.CertificateInvalidError
return errors.As(err, &candidate)
},
class: ETLS_CERT_INVALID,
},
}

// New creates a new error class from the given error.
func New(err error) string {
// exclude the nil error case first
if err == nil {
return ""
}

// attemp direct mapping using the [errors.Is] func
for candidate, class := range errorsIsMap {
if errors.Is(err, candidate) {
return class
}
}

// attempt indirect mapping using the [errors.As] func
for _, entry := range errorsAsList {
if entry.as(err) {
return entry.class
}
}

// fallback to attempt matching with the string suffix
for suffix, class := range stringSuffixMap {
if strings.HasSuffix(err.Error(), suffix) {
return class
}
}

// we don't known this error
return EGENERIC
}
// New is an alias for [errclass.New].
var New = errclass.New
Loading

0 comments on commit 30adea1

Please sign in to comment.