123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179 |
- package http
- import (
- "bytes"
- "context"
- "encoding/json"
- "io/ioutil"
- "net/http"
- "net/url"
- "reflect"
- "strconv"
- "strings"
- "github.com/chuangxin1/httprouter"
- "github.com/go-kit/kit/circuitbreaker"
- "github.com/go-kit/kit/endpoint"
- "github.com/go-kit/kit/sd"
- "github.com/go-kit/kit/sd/lb"
- "github.com/sony/gobreaker"
- "git.chuangxin1.com/csacred/toolkit"
- httptransport "github.com/go-kit/kit/transport/http"
- )
- // EncodeResponseFunc encodes the passed response object to the HTTP response
- // writer. It's designed to be used in HTTP servers, for server-side
- // endpoints. One straightforward EncodeResponseFunc could be something that
- // JSON encodes the object directly to the response body.
- type EncodeResponseFunc func(context.Context, http.ResponseWriter, interface{}) error
- // DecodeResponseFunc extracts a user-domain response object from an HTTP
- // response object. It's designed to be used in HTTP clients, for client-side
- // endpoints. One straightforward DecodeResponseFunc could be something that
- // JSON decodes from the response body to the concrete response type.
- type DecodeResponseFunc func(context.Context, *http.Response) (response interface{}, err error)
- // RouteVars returns the route variables for the current request, if any.
- func RouteVars(ctx context.Context) map[string]string {
- return httprouter.ContextVars(ctx)
- }
- // ContextVars returns the
- func ContextVars(ctx context.Context, key interface{}) interface{} {
- return ctx.Value(key)
- }
- // CopyURL copy url
- func CopyURL(base *url.URL, path string) *url.URL {
- next := *base
- next.Path = path
- return &next
- }
- func routePath(ctx context.Context, req *http.Request) {
- path := req.URL.Path
- prefix, _ := ctx.Value(toolkit.ContextKeyGateWayPrefix).(string)
- routePath := httprouter.ContextRoutePath(ctx)
- if prefix != "" && routePath != "" {
- if strings.Contains(routePath, prefix) {
- absolutePath := routePath[len(prefix):]
- if absolutePath != path {
- req.URL.Path = absolutePath
- }
- }
- }
- }
- // URLValuesStruct convert struct to url.Values
- func URLValuesStruct(obj interface{}) url.Values {
- t := reflect.TypeOf(obj)
- v := reflect.ValueOf(obj)
- values := url.Values{}
- for i := 0; i < t.NumField(); i++ {
- key := t.Field(i).Tag.Get("form")
- value := format(v.Field(i), v.Field(i).Interface())
- values.Add(key, value)
- }
- return values
- }
- func format(v reflect.Value, data interface{}) string {
- var s string
- switch v.Kind() {
- case reflect.String:
- s = data.(string)
- case reflect.Int:
- s = strconv.FormatInt(int64(data.(int)), 10)
- case reflect.Uint:
- s = strconv.FormatUint(data.(uint64), 10)
- case reflect.Bool:
- s = strconv.FormatBool(data.(bool))
- case reflect.Float32:
- case reflect.Float64:
- s = strconv.FormatFloat(data.(float64), 'f', -1, 32)
- default:
- s = "" // fmt.Sprintf("unsupported kind %s", v.Type())
- }
- return s
- }
- // ClientEncodeGetRequest client get encode request
- func ClientEncodeGetRequest(ctx context.Context, req *http.Request, request interface{}) error {
- values := URLValuesStruct(request)
- auth, ok := ctx.Value(toolkit.ContextKeyRequestAuthorization).(string)
- if ok {
- req.Header.Set(toolkit.HTTPHeaderAuthorization, auth)
- }
- routePath(ctx, req)
- token, _ := ctx.Value(toolkit.ContextKeyAccessToken).(string)
- if token != "" {
- values.Set(toolkit.VarUserAuthorization, token)
- }
- req.URL.RawQuery = values.Encode()
- return nil
- }
- // ClientEncodeJSONRequest is an EncodeRequestFunc that serializes the request
- // as a JSON object to the Request body. Many JSON-over-HTTP services can use
- // it as a sensible default. If the request implements Headerer, the provided
- // headers will be applied to the request.
- func ClientEncodeJSONRequest(ctx context.Context, req *http.Request, request interface{}) error {
- req.Header.Set("Content-Type", "application/json; charset=utf-8")
- routePath(ctx, req)
- if headerer, ok := request.(httptransport.Headerer); ok {
- for k := range headerer.Headers() {
- req.Header.Set(k, headerer.Headers().Get(k))
- }
- }
- if auth, ok := ctx.Value(toolkit.ContextKeyRequestAuthorization).(string); ok {
- req.Header.Set("Authorization", auth)
- }
- values := url.Values{}
- token, _ := ctx.Value(toolkit.ContextKeyAccessToken).(string)
- if token != "" {
- values.Set(toolkit.VarUserAuthorization, token)
- }
- req.URL.RawQuery = values.Encode()
- var b bytes.Buffer
- req.Body = ioutil.NopCloser(&b)
- return json.NewEncoder(&b).Encode(request)
- }
- // ClientRequestEndpoint client request Endpoint
- func ClientRequestEndpoint(ctx context.Context, u *url.URL, method, router string, dec DecodeResponseFunc) endpoint.Endpoint {
- var e endpoint.Endpoint
- options := []httptransport.ClientOption{}
- var enc httptransport.EncodeRequestFunc
- switch method {
- case "POST":
- enc = ClientEncodeJSONRequest
- default:
- enc = ClientEncodeGetRequest
- }
- e = httptransport.NewClient(
- method,
- CopyURL(u, router),
- enc,
- httptransport.DecodeResponseFunc(dec),
- options...,
- ).Endpoint()
- e = circuitbreaker.Gobreaker(
- gobreaker.NewCircuitBreaker(gobreaker.Settings{}))(e)
- //e = ratelimit.NewErroringLimiter(rate.NewLimiter(rate.Every(time.Second), qps))(e)
- return e
- }
- // ClientLoadBalancer load balance
- func ClientLoadBalancer(endpoints sd.FixedEndpointer) endpoint.Endpoint {
- balancer := lb.NewRoundRobin(endpoints)
- return lb.Retry(retryMax, retryTimeout, balancer)
- }
|