cancellable.go 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. // Copyright 2015 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 cancellable provides helper function to cancel http requests.
  5. package cancellable
  6. import (
  7. "io"
  8. "net/http"
  9. "github.com/docker/engine-api/client/transport"
  10. "golang.org/x/net/context"
  11. )
  12. func nop() {}
  13. var (
  14. testHookContextDoneBeforeHeaders = nop
  15. testHookDoReturned = nop
  16. testHookDidBodyClose = nop
  17. )
  18. // Do sends an HTTP request with the provided transport.Sender and returns an HTTP response.
  19. // If the client is nil, http.DefaultClient is used.
  20. // If the context is canceled or times out, ctx.Err() will be returned.
  21. //
  22. // FORK INFORMATION:
  23. //
  24. // This function deviates from the upstream version in golang.org/x/net/context/ctxhttp by
  25. // taking a Sender interface rather than a *http.Client directly. That allow us to use
  26. // this funcion with mocked clients and hijacked connections.
  27. func Do(ctx context.Context, client transport.Sender, req *http.Request) (*http.Response, error) {
  28. if client == nil {
  29. client = http.DefaultClient
  30. }
  31. // Request cancelation changed in Go 1.5, see canceler.go and canceler_go14.go.
  32. cancel := canceler(client, req)
  33. type responseAndError struct {
  34. resp *http.Response
  35. err error
  36. }
  37. result := make(chan responseAndError, 1)
  38. go func() {
  39. resp, err := client.Do(req)
  40. testHookDoReturned()
  41. result <- responseAndError{resp, err}
  42. }()
  43. var resp *http.Response
  44. select {
  45. case <-ctx.Done():
  46. testHookContextDoneBeforeHeaders()
  47. cancel()
  48. // Clean up after the goroutine calling client.Do:
  49. go func() {
  50. if r := <-result; r.resp != nil && r.resp.Body != nil {
  51. testHookDidBodyClose()
  52. r.resp.Body.Close()
  53. }
  54. }()
  55. return nil, ctx.Err()
  56. case r := <-result:
  57. var err error
  58. resp, err = r.resp, r.err
  59. if err != nil {
  60. return resp, err
  61. }
  62. }
  63. c := make(chan struct{})
  64. go func() {
  65. select {
  66. case <-ctx.Done():
  67. cancel()
  68. case <-c:
  69. // The response's Body is closed.
  70. }
  71. }()
  72. resp.Body = &notifyingReader{resp.Body, c}
  73. return resp, nil
  74. }
  75. // notifyingReader is an io.ReadCloser that closes the notify channel after
  76. // Close is called or a Read fails on the underlying ReadCloser.
  77. type notifyingReader struct {
  78. io.ReadCloser
  79. notify chan<- struct{}
  80. }
  81. func (r *notifyingReader) Read(p []byte) (int, error) {
  82. n, err := r.ReadCloser.Read(p)
  83. if err != nil && r.notify != nil {
  84. close(r.notify)
  85. r.notify = nil
  86. }
  87. return n, err
  88. }
  89. func (r *notifyingReader) Close() error {
  90. err := r.ReadCloser.Close()
  91. if r.notify != nil {
  92. close(r.notify)
  93. r.notify = nil
  94. }
  95. return err
  96. }