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.
 
 
 

88 lines
2.7 KiB

  1. // Copyright 2016 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package gensupport
  5. import (
  6. "context"
  7. "encoding/json"
  8. "errors"
  9. "net/http"
  10. )
  11. // Hook is the type of a function that is called once before each HTTP request
  12. // that is sent by a generated API. It returns a function that is called after
  13. // the request returns.
  14. // Hooks are not called if the context is nil.
  15. type Hook func(ctx context.Context, req *http.Request) func(resp *http.Response)
  16. var hooks []Hook
  17. // RegisterHook registers a Hook to be called before each HTTP request by a
  18. // generated API. Hooks are called in the order they are registered. Each
  19. // hook can return a function; if it is non-nil, it is called after the HTTP
  20. // request returns. These functions are called in the reverse order.
  21. // RegisterHook should not be called concurrently with itself or SendRequest.
  22. func RegisterHook(h Hook) {
  23. hooks = append(hooks, h)
  24. }
  25. // SendRequest sends a single HTTP request using the given client.
  26. // If ctx is non-nil, it calls all hooks, then sends the request with
  27. // req.WithContext, then calls any functions returned by the hooks in
  28. // reverse order.
  29. func SendRequest(ctx context.Context, client *http.Client, req *http.Request) (*http.Response, error) {
  30. // Disallow Accept-Encoding because it interferes with the automatic gzip handling
  31. // done by the default http.Transport. See https://github.com/google/google-api-go-client/issues/219.
  32. if _, ok := req.Header["Accept-Encoding"]; ok {
  33. return nil, errors.New("google api: custom Accept-Encoding headers not allowed")
  34. }
  35. if ctx == nil {
  36. return client.Do(req)
  37. }
  38. // Call hooks in order of registration, store returned funcs.
  39. post := make([]func(resp *http.Response), len(hooks))
  40. for i, h := range hooks {
  41. fn := h(ctx, req)
  42. post[i] = fn
  43. }
  44. // Send request.
  45. resp, err := send(ctx, client, req)
  46. // Call returned funcs in reverse order.
  47. for i := len(post) - 1; i >= 0; i-- {
  48. if fn := post[i]; fn != nil {
  49. fn(resp)
  50. }
  51. }
  52. return resp, err
  53. }
  54. func send(ctx context.Context, client *http.Client, req *http.Request) (*http.Response, error) {
  55. if client == nil {
  56. client = http.DefaultClient
  57. }
  58. resp, err := client.Do(req.WithContext(ctx))
  59. // If we got an error, and the context has been canceled,
  60. // the context's error is probably more useful.
  61. if err != nil {
  62. select {
  63. case <-ctx.Done():
  64. err = ctx.Err()
  65. default:
  66. }
  67. }
  68. return resp, err
  69. }
  70. // DecodeResponse decodes the body of res into target. If there is no body,
  71. // target is unchanged.
  72. func DecodeResponse(target interface{}, res *http.Response) error {
  73. if res.StatusCode == http.StatusNoContent {
  74. return nil
  75. }
  76. return json.NewDecoder(res.Body).Decode(target)
  77. }