integrate request builder into HTTP client for googleapis support (#159)
This commit is contained in:
		
							
								
								
									
										122
									
								
								client.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										122
									
								
								client.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,122 @@ | ||||
| package http | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"net/http" | ||||
| 	"strconv" | ||||
| 	"sync" | ||||
| 	"time" | ||||
|  | ||||
| 	"go.unistack.org/micro/v3/client" | ||||
| 	"go.unistack.org/micro/v3/errors" | ||||
| 	"go.unistack.org/micro/v3/options" | ||||
| 	"go.unistack.org/micro/v3/semconv" | ||||
| 	"go.unistack.org/micro/v3/tracer" | ||||
| ) | ||||
|  | ||||
| var DefaultContentType = "application/json" | ||||
|  | ||||
| type Client struct { | ||||
| 	funcCall   client.FuncCall | ||||
| 	funcStream client.FuncStream | ||||
| 	httpClient *http.Client | ||||
| 	opts       client.Options | ||||
| 	mu         sync.RWMutex | ||||
| } | ||||
|  | ||||
| func NewClient(opts ...client.Option) *Client { | ||||
| 	clientOpts := client.NewOptions(opts...) | ||||
|  | ||||
| 	if len(clientOpts.ContentType) == 0 { | ||||
| 		clientOpts.ContentType = DefaultContentType | ||||
| 	} | ||||
|  | ||||
| 	c := &Client{opts: clientOpts} | ||||
|  | ||||
| 	dialer, ok := httpDialerFromOpts(clientOpts) | ||||
| 	if !ok { | ||||
| 		dialer = defaultHTTPDialer() | ||||
| 	} | ||||
|  | ||||
| 	c.httpClient, ok = httpClientFromOpts(clientOpts) | ||||
| 	if !ok { | ||||
| 		c.httpClient = defaultHTTPClient(dialer, clientOpts.TLSConfig) | ||||
| 	} | ||||
|  | ||||
| 	c.funcCall = c.fnCall | ||||
| 	c.funcStream = c.fnStream | ||||
|  | ||||
| 	return c | ||||
| } | ||||
|  | ||||
| func (c *Client) Name() string { | ||||
| 	return c.opts.Name | ||||
| } | ||||
|  | ||||
| func (c *Client) Init(opts ...client.Option) error { | ||||
| 	for _, o := range opts { | ||||
| 		o(&c.opts) | ||||
| 	} | ||||
|  | ||||
| 	c.opts.Hooks.EachPrev(func(hook options.Hook) { | ||||
| 		switch h := hook.(type) { | ||||
| 		case client.HookCall: | ||||
| 			c.funcCall = h(c.funcCall) | ||||
| 		case client.HookStream: | ||||
| 			c.funcStream = h(c.funcStream) | ||||
| 		} | ||||
| 	}) | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (c *Client) Options() client.Options { | ||||
| 	return c.opts | ||||
| } | ||||
|  | ||||
| func (c *Client) NewRequest(service, method string, req any, opts ...client.RequestOption) client.Request { | ||||
| 	reqOpts := client.NewRequestOptions(opts...) | ||||
| 	if reqOpts.ContentType == "" { | ||||
| 		reqOpts.ContentType = c.opts.ContentType | ||||
| 	} | ||||
|  | ||||
| 	return &httpRequest{ | ||||
| 		service: service, | ||||
| 		method:  method, | ||||
| 		request: req, | ||||
| 		opts:    reqOpts, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (c *Client) Call(ctx context.Context, req client.Request, rsp any, opts ...client.CallOption) error { | ||||
| 	ts := time.Now() | ||||
| 	c.opts.Meter.Counter(semconv.ClientRequestInflight, "endpoint", req.Endpoint()).Inc() | ||||
| 	var sp tracer.Span | ||||
| 	ctx, sp = c.opts.Tracer.Start(ctx, req.Endpoint()+" rpc-client", | ||||
| 		tracer.WithSpanKind(tracer.SpanKindClient), | ||||
| 		tracer.WithSpanLabels("endpoint", req.Endpoint()), | ||||
| 	) | ||||
| 	err := c.funcCall(ctx, req, rsp, opts...) | ||||
| 	c.opts.Meter.Counter(semconv.ClientRequestInflight, "endpoint", req.Endpoint()).Dec() | ||||
| 	te := time.Since(ts) | ||||
| 	c.opts.Meter.Summary(semconv.ClientRequestLatencyMicroseconds, "endpoint", req.Endpoint()).Update(te.Seconds()) | ||||
| 	c.opts.Meter.Histogram(semconv.ClientRequestDurationSeconds, "endpoint", req.Endpoint()).Update(te.Seconds()) | ||||
|  | ||||
| 	if me := errors.FromError(err); me == nil { | ||||
| 		sp.Finish() | ||||
| 		c.opts.Meter.Counter(semconv.ClientRequestTotal, "endpoint", req.Endpoint(), "status", "success", "code", strconv.Itoa(int(200))).Inc() | ||||
| 	} else { | ||||
| 		sp.SetStatus(tracer.SpanStatusError, err.Error()) | ||||
| 		c.opts.Meter.Counter(semconv.ClientRequestTotal, "endpoint", req.Endpoint(), "status", "failure", "code", strconv.Itoa(int(me.Code))).Inc() | ||||
| 	} | ||||
|  | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| func (c *Client) Stream(ctx context.Context, req client.Request, opts ...client.CallOption) (client.Stream, error) { | ||||
| 	return c.funcStream(ctx, req, opts...) | ||||
| } | ||||
|  | ||||
| func (c *Client) String() string { | ||||
| 	return "http" | ||||
| } | ||||
		Reference in New Issue
	
	Block a user