You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

192 lines
4.7 KiB

2 years ago
package http
import (
"context"
"crypto/tls"
"git.diulo.com/mogfee/kit/encoding"
"git.diulo.com/mogfee/kit/errors"
"git.diulo.com/mogfee/kit/internal/httputil"
"git.diulo.com/mogfee/kit/middleware"
"git.diulo.com/mogfee/kit/registry"
"git.diulo.com/mogfee/kit/selector"
"io"
"net/http"
"time"
)
type DecodeErrorFunc func(ctx context.Context, res *http.Response) error
type EncodeRequestFunc func(ctx context.Context, contentType string, in any) (body []byte, err error)
type DecodeResponseFunc func(ctx context.Context, res *http.Response, out any) error
type ClientOption func(options *clientOptions)
type clientOptions struct {
ctx context.Context
tlsConf *tls.Config
timeout time.Duration
endpoint string
userAgent string
encoder EncodeRequestFunc
decoder DecodeResponseFunc
errorDecoder DecodeErrorFunc
transport http.RoundTripper
nodeFilters []selector.NodeFilter
discovery registry.Discovery
middleware []middleware.Middleware
block bool
}
// WithTransport with client transport.
func WithTransport(trans http.RoundTripper) ClientOption {
return func(o *clientOptions) {
o.transport = trans
}
}
// WithTimeout with client request timeout.
func WithTimeout(d time.Duration) ClientOption {
return func(o *clientOptions) {
o.timeout = d
}
}
// WithUserAgent with client user agent.
func WithUserAgent(ua string) ClientOption {
return func(o *clientOptions) {
o.userAgent = ua
}
}
// WithMiddleware with client middleware.
func WithMiddleware(m ...middleware.Middleware) ClientOption {
return func(o *clientOptions) {
o.middleware = m
}
}
// WithEndpoint with client addr.
func WithEndpoint(endpoint string) ClientOption {
return func(o *clientOptions) {
o.endpoint = endpoint
}
}
// WithRequestEncoder with client request encoder.
func WithRequestEncoder(encoder EncodeRequestFunc) ClientOption {
return func(o *clientOptions) {
o.encoder = encoder
}
}
// WithResponseDecoder with client response decoder.
func WithResponseDecoder(decoder DecodeResponseFunc) ClientOption {
return func(o *clientOptions) {
o.decoder = decoder
}
}
// WithErrorDecoder with client error decoder.
func WithErrorDecoder(errorDecoder DecodeErrorFunc) ClientOption {
return func(o *clientOptions) {
o.errorDecoder = errorDecoder
}
}
// WithDiscovery with client discovery.
func WithDiscovery(d registry.Discovery) ClientOption {
return func(o *clientOptions) {
o.discovery = d
}
}
// WithNodeFilter with select filters
func WithNodeFilter(filters ...selector.NodeFilter) ClientOption {
return func(o *clientOptions) {
o.nodeFilters = filters
}
}
// WithBlock with client block.
func WithBlock() ClientOption {
return func(o *clientOptions) {
o.block = true
}
}
// WithTLSConfig with tls config.
func WithTLSConfig(c *tls.Config) ClientOption {
return func(o *clientOptions) {
o.tlsConf = c
}
}
type Client struct {
opts clientOptions
targe *Target
r *resolver
cc *http.Client
insecure bool
selector selector.Selector
}
func NewClient(ctx context.Context, opts ...ClientOption) (*Client, error) {
options := clientOptions{
ctx: ctx,
timeout: 2000 * time.Millisecond,
encoder: DefaultrequestEncoder,
decoder: DefaultResponseDecoder,
errorDecoder: DefaultErrorDecoder,
transport: http.DefaultTransport,
}
for _, o := range opts {
o(&options)
}
if options.tlsConf != nil {
if tr, ok := options.transport.(*http.Transport); ok {
tr.TLSClientConfig = options.tlsConf
}
}
insecure := options.tlsConf
target, err := parseTarget(options.encoder, insecure)
if err != nil {
return nil, err
}
selector = selector.GlobalSelector().Build()
var r *resolver
if options.discovery != nil {
}
}
func DefaultrequestEncoder(ctx context.Context, contentType string, v any) ([]byte, error) {
name := httputil.ContentSubtype(contentType)
return encoding.GetCodec(name).Marshal(v)
}
func DefaultResponseDecoder(ctx context.Context, res *http.Response, v any) error {
defer res.Body.Close()
data, err := io.ReadAll(res.Body)
if err != nil {
return err
}
return CodecForResponse(res).Unmarshal(data, v)
}
func DefaultErrorDecoder(ctx context.Context, res *http.Response) error {
if res.StatusCode >= 200 && res.Status <= 299 {
return nil
}
data, err := io.ReadAll(res.Body)
defer res.Body.Close()
if err == nil {
e := new(errors.Error)
if err = CodecForResponse(res).Unmarshal(data, e); err == nil {
e.Code = int32(res.StatusCode)
return e
}
}
return errors.Newf(res.StatusCode, errors.UnknownReason, "").WithCause(err)
}
func CodecForResponse(r *http.Response) encoding.Codec {
codec := encoding.GetCodec(httputil.ContentSubtype(r.Request.Header.Get("Content-Type")))
if codec != nil {
return codec
}
return encoding.GetCodec("json")
}