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.
193 lines
4.8 KiB
193 lines
4.8 KiB
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 { |
|
//} |
|
|
|
return nil, 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.StatusCode <= 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") |
|
}
|
|
|