request.go 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. package http
  2. import (
  3. "bytes"
  4. "context"
  5. "encoding/json"
  6. "io/ioutil"
  7. "net/http"
  8. "net/url"
  9. "reflect"
  10. "strconv"
  11. "strings"
  12. "github.com/chuangxin1/httprouter"
  13. "github.com/go-kit/kit/circuitbreaker"
  14. "github.com/go-kit/kit/endpoint"
  15. "github.com/go-kit/kit/sd"
  16. "github.com/go-kit/kit/sd/lb"
  17. "github.com/sony/gobreaker"
  18. "git.chuangxin1.com/csacred/toolkit"
  19. httptransport "github.com/go-kit/kit/transport/http"
  20. )
  21. // EncodeResponseFunc encodes the passed response object to the HTTP response
  22. // writer. It's designed to be used in HTTP servers, for server-side
  23. // endpoints. One straightforward EncodeResponseFunc could be something that
  24. // JSON encodes the object directly to the response body.
  25. type EncodeResponseFunc func(context.Context, http.ResponseWriter, interface{}) error
  26. // DecodeResponseFunc extracts a user-domain response object from an HTTP
  27. // response object. It's designed to be used in HTTP clients, for client-side
  28. // endpoints. One straightforward DecodeResponseFunc could be something that
  29. // JSON decodes from the response body to the concrete response type.
  30. type DecodeResponseFunc func(context.Context, *http.Response) (response interface{}, err error)
  31. // RouteVars returns the route variables for the current request, if any.
  32. func RouteVars(ctx context.Context) map[string]string {
  33. return httprouter.ContextVars(ctx)
  34. }
  35. // ContextVars returns the
  36. func ContextVars(ctx context.Context, key interface{}) interface{} {
  37. return ctx.Value(key)
  38. }
  39. // CopyURL copy url
  40. func CopyURL(base *url.URL, path string) *url.URL {
  41. next := *base
  42. next.Path = path
  43. return &next
  44. }
  45. func routePath(ctx context.Context, req *http.Request) {
  46. path := req.URL.Path
  47. prefix, _ := ctx.Value(toolkit.ContextKeyGateWayPrefix).(string)
  48. routePath := httprouter.ContextRoutePath(ctx)
  49. if prefix != "" && routePath != "" {
  50. if strings.Contains(routePath, prefix) {
  51. absolutePath := routePath[len(prefix):]
  52. if absolutePath != path {
  53. req.URL.Path = absolutePath
  54. }
  55. }
  56. }
  57. }
  58. // URLValuesStruct convert struct to url.Values
  59. func URLValuesStruct(obj interface{}) url.Values {
  60. t := reflect.TypeOf(obj)
  61. v := reflect.ValueOf(obj)
  62. values := url.Values{}
  63. for i := 0; i < t.NumField(); i++ {
  64. key := t.Field(i).Tag.Get("form")
  65. value := format(v.Field(i), v.Field(i).Interface())
  66. values.Add(key, value)
  67. }
  68. return values
  69. }
  70. func format(v reflect.Value, data interface{}) string {
  71. var s string
  72. switch v.Kind() {
  73. case reflect.String:
  74. s = data.(string)
  75. case reflect.Int:
  76. s = strconv.FormatInt(int64(data.(int)), 10)
  77. case reflect.Uint:
  78. s = strconv.FormatUint(data.(uint64), 10)
  79. case reflect.Bool:
  80. s = strconv.FormatBool(data.(bool))
  81. case reflect.Float32:
  82. case reflect.Float64:
  83. s = strconv.FormatFloat(data.(float64), 'f', -1, 32)
  84. default:
  85. s = "" // fmt.Sprintf("unsupported kind %s", v.Type())
  86. }
  87. return s
  88. }
  89. // ClientEncodeGetRequest client get encode request
  90. func ClientEncodeGetRequest(ctx context.Context, req *http.Request, request interface{}) error {
  91. values := URLValuesStruct(request)
  92. auth, ok := ctx.Value(toolkit.ContextKeyRequestAuthorization).(string)
  93. if ok {
  94. req.Header.Set(toolkit.HTTPHeaderAuthorization, auth)
  95. }
  96. routePath(ctx, req)
  97. token, _ := ctx.Value(toolkit.ContextKeyAccessToken).(string)
  98. if token != "" {
  99. values.Set(toolkit.VarUserAuthorization, token)
  100. }
  101. req.URL.RawQuery = values.Encode()
  102. return nil
  103. }
  104. // ClientEncodeJSONRequest is an EncodeRequestFunc that serializes the request
  105. // as a JSON object to the Request body. Many JSON-over-HTTP services can use
  106. // it as a sensible default. If the request implements Headerer, the provided
  107. // headers will be applied to the request.
  108. func ClientEncodeJSONRequest(ctx context.Context, req *http.Request, request interface{}) error {
  109. req.Header.Set("Content-Type", "application/json; charset=utf-8")
  110. routePath(ctx, req)
  111. if headerer, ok := request.(httptransport.Headerer); ok {
  112. for k := range headerer.Headers() {
  113. req.Header.Set(k, headerer.Headers().Get(k))
  114. }
  115. }
  116. if auth, ok := ctx.Value(toolkit.ContextKeyRequestAuthorization).(string); ok {
  117. req.Header.Set("Authorization", auth)
  118. }
  119. values := url.Values{}
  120. token, _ := ctx.Value(toolkit.ContextKeyAccessToken).(string)
  121. if token != "" {
  122. values.Set(toolkit.VarUserAuthorization, token)
  123. }
  124. req.URL.RawQuery = values.Encode()
  125. var b bytes.Buffer
  126. req.Body = ioutil.NopCloser(&b)
  127. return json.NewEncoder(&b).Encode(request)
  128. }
  129. // ClientRequestEndpoint client request Endpoint
  130. func ClientRequestEndpoint(ctx context.Context, u *url.URL, method, router string, dec DecodeResponseFunc) endpoint.Endpoint {
  131. var e endpoint.Endpoint
  132. options := []httptransport.ClientOption{}
  133. var enc httptransport.EncodeRequestFunc
  134. switch method {
  135. case "POST":
  136. enc = ClientEncodeJSONRequest
  137. default:
  138. enc = ClientEncodeGetRequest
  139. }
  140. e = httptransport.NewClient(
  141. method,
  142. CopyURL(u, router),
  143. enc,
  144. httptransport.DecodeResponseFunc(dec),
  145. options...,
  146. ).Endpoint()
  147. e = circuitbreaker.Gobreaker(
  148. gobreaker.NewCircuitBreaker(gobreaker.Settings{}))(e)
  149. //e = ratelimit.NewErroringLimiter(rate.NewLimiter(rate.Every(time.Second), qps))(e)
  150. return e
  151. }
  152. // ClientLoadBalancer load balance
  153. func ClientLoadBalancer(endpoints sd.FixedEndpointer) endpoint.Endpoint {
  154. balancer := lb.NewRoundRobin(endpoints)
  155. return lb.Retry(retryMax, retryTimeout, balancer)
  156. }