Documentation ¶
Index ¶
- Constants
- Variables
- func CodeText(code int) string
- func IsDUHCode(code int) bool
- func NewClientError(msg string, err error, details map[string]string) error
- func NewInfraError(req *http.Request, resp *http.Response, body []byte) error
- func NewLimitReader(r io.ReadCloser, n int64) io.ReadCloser
- func NewReplyError(req *http.Request, resp *http.Response, reply *v1.Reply) error
- func NewServiceError(code int, msg string, err error, details map[string]string) error
- func ReadRequest(r *http.Request, m proto.Message, limit int64) error
- func Reply(w http.ResponseWriter, r *http.Request, code int, resp proto.Message)
- func ReplyError(w http.ResponseWriter, r *http.Request, err error)
- func ReplyWithCode(w http.ResponseWriter, r *http.Request, code int, details map[string]string, ...)
- func SetupTLS(conf *TLSConfig) error
- func TrimSuffix(s, sep string) string
- func WaitForConnect(ctx context.Context, address string, cfg *tls.Config) error
- type Client
- type ClientError
- type ErrDataLimitExceeded
- type Error
- type HttpLogAdaptor
- type LimitReader
- type NoOpLogger
- type StandardLogger
- type TLSConfig
Constants ¶
const ( DetailsHttpCode = "http.code" DetailsHttpUrl = "http.url" DetailsHttpMethod = "http.method" DetailsHttpStatus = "http.status" DetailsHttpBody = "http.body" DetailsCodeText = "duh.code-text" )
const ( CodeOK = 200 CodeBadRequest = 400 CodeForbidden = 403 CodeNotFound = 404 CodeConflict = 409 CodeTooManyRequests = 429 CodeClientError = 452 CodeRequestFailed = 453 CodeRetryRequest = 454 CodeClientContentError = 455 CodeInternalError = 500 CodeNotImplemented = 501 CodeTransportError = 512 )
const ( Bytes = 1 Kibibyte = 1024 Kilobyte = 1000 MegaByte = Kilobyte * 1000 Mebibyte = Kibibyte * 1024 Gibibyte = Mebibyte * 1024 Gigabyte = MegaByte * 1024 )
const ( ContentTypeProtoBuf = "application/protobuf" ContentTypeJSON = "application/json" ContentOctetStream = "application/octet-stream" )
Variables ¶
var ( // DefaultClient is the default HTTP client to use when making RPC calls. // We use the HTTP/1 client as it outperforms both GRPC and HTTP/2 // See: // * https://github.com/duh-rpc/duh-go-benchmarks // * https://github.com/golang/go/issues/47840 // * https://www.emcfarlane.com/blog/2023-05-15-grpc-servehttp // * https://github.com/kgersen/h3ctx DefaultClient = HTTP1Client // HTTP1Client is the default golang http with a limit on Idle connections HTTP1Client = &Client{ Client: &http.Client{ Transport: &http.Transport{ IdleConnTimeout: 90 * time.Second, MaxIdleConns: 100, MaxIdleConnsPerHost: 10, MaxConnsPerHost: 0, }, }, } // HTTP2Client is a client configured for H2C HTTP/2 HTTP2Client = &Client{ Client: &http.Client{ Transport: &http2.Transport{ AllowHTTP: true, DialTLSContext: func(ctx context.Context, network, addr string, cfg *tls.Config) (net.Conn, error) { var d net.Dialer return d.DialContext(ctx, network, addr) }, }, }, } )
var (
SupportedMimeTypes = []string{ContentTypeJSON, ContentTypeProtoBuf}
)
Functions ¶
func NewClientError ¶
NewClientError returns an error that originates with the client code not from the service implementation or from the infrastructure.
func NewInfraError ¶
NewInfraError returns an error that originates from the infrastructure, and does not originate from the client or service implementation.
func NewLimitReader ¶ added in v0.9.0
func NewLimitReader(r io.ReadCloser, n int64) io.ReadCloser
NewLimitReader returns a ReaderCloser that returns ErrDataLimitExceeded after n bytes have been read.
func NewReplyError ¶
NewReplyError returns an error that originates from the service implementation, and does not originate from the client or infrastructure.
This method is intended to be used by client implementations to pass v1.Reply responses back to the caller as an error.
func NewServiceError ¶
NewServiceError returns a new serviceError. Server Implementations should use this to respond to requests with an error. TODO: Ensure you can get the `cause` of the error from serviceError struct
func ReadRequest ¶
ReadRequest reads the given http.Request body []byte into the given proto.Message. The provided message must be mutable (e.g., a non-nil pointer to a message). It also handles content negotiation via the 'Content-Type' header provided in the http.Request headers
func Reply ¶
Reply responds to a request with the specified protobuf message and status code. Reply() provides content negotiation for protobuf if the request has the 'Accept' header set. If no 'Accept' header was provided, Reply() will marshall the proto.Message into JSON.
func ReplyError ¶
func ReplyError(w http.ResponseWriter, r *http.Request, err error)
ReplyError replies to the request with the error provided. If 'err' satisfies the Error interface, then it will return the code and message provided by the Error. If 'err' does not satisfy the Error it will then return a status of CodeInternalError with the err.Reply() as the message.
func ReplyWithCode ¶
func ReplyWithCode(w http.ResponseWriter, r *http.Request, code int, details map[string]string, msg string)
ReplyWithCode replies to the request with the specified message and status code
func SetupTLS ¶ added in v0.2.0
SetupTLS attempts to build a server and client TLS configuration given the TLSConfig provided.
func TrimSuffix ¶
TrimSuffix trims everything after the first separator is found
Types ¶
type Client ¶
func (*Client) Do ¶
Do calls http.Client.Do() and un-marshals the response into the proto struct passed. In the case of unexpected request or response errors, Do will return *duh.ClientError with as much detail as possible.
func (*Client) DoOctetStream ¶
DoOctetStream sends the request and expects a `application/octet-stream` response from the server. If server doesn't respond with `application/octet-stream` then it is assumed to be an error of v1.Reply If the reply isn't a v1.Reply then the body of the response is returned as an error. TODO: Implement this and clean this up
type ClientError ¶
type ClientError struct {
// contains filtered or unexported fields
}
TODO: Decide if this should be public or not, I'm leaning toward not being public
func (*ClientError) Code ¶
func (e *ClientError) Code() int
func (*ClientError) Details ¶
func (e *ClientError) Details() map[string]string
func (*ClientError) Error ¶
func (e *ClientError) Error() string
func (*ClientError) Message ¶
func (e *ClientError) Message() string
func (*ClientError) ProtoMessage ¶
func (e *ClientError) ProtoMessage() proto.Message
type ErrDataLimitExceeded ¶ added in v0.9.0
type ErrDataLimitExceeded struct {
Max int64
}
func (*ErrDataLimitExceeded) Code ¶ added in v0.9.0
func (e *ErrDataLimitExceeded) Code() int
func (*ErrDataLimitExceeded) Details ¶ added in v0.9.0
func (e *ErrDataLimitExceeded) Details() map[string]string
func (*ErrDataLimitExceeded) Error ¶ added in v0.9.0
func (e *ErrDataLimitExceeded) Error() string
func (*ErrDataLimitExceeded) Message ¶ added in v0.9.0
func (e *ErrDataLimitExceeded) Message() string
func (*ErrDataLimitExceeded) ProtoMessage ¶ added in v0.9.0
func (e *ErrDataLimitExceeded) ProtoMessage() proto.Message
type Error ¶
type Error interface { // ProtoMessage Creates v1.Reply protobuf from this Error ProtoMessage() proto.Message // Code is the code retrieved from v1.Reply.Code or the HTTP Status Code Code() int // Error is the error message this error wrapped (Used on the server side) Error() string // Details is the Details of the error retrieved from v1.Reply.details Details() map[string]string // Message is the message retrieved from v1.Reply.Reply Message() string }
type HttpLogAdaptor ¶ added in v0.3.0
type HttpLogAdaptor struct {
// contains filtered or unexported fields
}
func NewHttpLogAdaptor ¶ added in v0.3.0
func NewHttpLogAdaptor(log StandardLogger) *HttpLogAdaptor
NewHttpLogAdaptor creates a new adaptor suitable for forwarding logging from ErrorLog to a standard logger
srv := &http.Server{ ErrorLog: log.New(duh.NewHttpLogAdaptor(slog.Default(), "", 0), Addr: "localhost:8080", ..... }
func (*HttpLogAdaptor) Close ¶ added in v0.3.0
func (l *HttpLogAdaptor) Close() error
type LimitReader ¶ added in v0.9.0
type LimitReader struct {
// contains filtered or unexported fields
}
func (*LimitReader) Close ¶ added in v0.9.0
func (l *LimitReader) Close() error
type NoOpLogger ¶ added in v0.2.0
type NoOpLogger struct{}
func (NoOpLogger) Debug ¶ added in v0.2.0
func (NoOpLogger) Debug(msg string, args ...any)
func (NoOpLogger) Error ¶ added in v0.2.0
func (NoOpLogger) Error(msg string, args ...any)
func (NoOpLogger) Info ¶ added in v0.2.0
func (NoOpLogger) Info(msg string, args ...any)
func (NoOpLogger) Warn ¶ added in v0.2.0
func (NoOpLogger) Warn(msg string, args ...any)
type StandardLogger ¶ added in v0.2.0
type TLSConfig ¶ added in v0.2.0
type TLSConfig struct { // (Optional) The path to the Trusted Certificate Authority. CaFile string // (Optional) The path to the Trusted Certificate Authority private key. CaKeyFile string // (Optional) The path to the un-encrypted key for the server certificate. KeyFile string // (Optional) The path to the server certificate. CertFile string // (Optional) If true will generate self-signed certificates. If CaFile and CaKeyFile // is set but no KeyFile or CertFile is set then will generate a self-signed key using // the CaFile provided. AutoTLS bool // (Optional) Configures the MinVersion for ServerTLS. If not set, defaults to TLS 1.0 MinVersion uint16 // (Optional) Sets the Client Authentication type as defined in the 'tls' package. // Defaults to tls.NoClientCert.See the standard library tls.ClientAuthType for valid values. // If set to anything but tls.NoClientCert then SetupTLS() attempts to load ClientAuthCaFile, // ClientAuthKeyFile and ClientAuthCertFile and sets those certs into the ClientTLS struct. If // none of the ClientXXXFile's are set, uses KeyFile and CertFile for client authentication. ClientAuth tls.ClientAuthType // (Optional) The path to the Trusted Certificate Authority used for client auth. If ClientAuth is // set and this field is empty, then CaFile is used to auth clients. ClientAuthCaFile string // (Optional) The path to the client private key, which is used to create the ClientTLS config. If // ClientAuth is set and this field is empty then KeyFile is used to create the ClientTLS. ClientAuthKeyFile string // (Optional) The path to the client cert key, which is used to create the ClientTLS config. If // ClientAuth is set and this field is empty then KeyFile is used to create the ClientTLS. ClientAuthCertFile string // (Optional) If InsecureSkipVerify is true, TLS clients will accept any certificate // presented by the server and any host name in that certificate. InsecureSkipVerify bool // (Optional) A Logger which implements the declared logger interface (typically *logrus.Entry) Logger StandardLogger // (Optional) The CA Certificate in PEM format. Used if CaFile is unset CaPEM *bytes.Buffer // (Optional) The CA Private Key in PEM format. Used if CaKeyFile is unset CaKeyPEM *bytes.Buffer // (Optional) The Certificate Key in PEM format. Used if KeyFile is unset. KeyPEM *bytes.Buffer // (Optional) The Certificate in PEM format. Used if CertFile is unset. CertPEM *bytes.Buffer // (Optional) The client auth CA Certificate in PEM format. Used if ClientAuthCaFile is unset. ClientAuthCaPEM *bytes.Buffer // (Optional) The client auth private key in PEM format. Used if ClientAuthKeyFile is unset. ClientAuthKeyPEM *bytes.Buffer // (Optional) The client auth Certificate in PEM format. Used if ClientAuthCertFile is unset. ClientAuthCertPEM *bytes.Buffer // (Optional) the server name to check when validating the provided certificate ClientAuthServerName string // ServerOrgName is the organization name used when generating a TLS certificate ServerOrgName string // TODO: Make sure there is a default // (Optional) The config created for use by the server. If set, all other // fields in this struct are ignored and this config is used. If unset, SetupTLS() // will create a config using the above fields. ServerTLS *tls.Config // (Optional) The config created for use by clients and peer communication. If set, all other // fields in this struct are ignored and this config is used. If unset, SetupTLS() // will create a config using the above fields. ClientTLS *tls.Config }