client.go 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. // Copyright 2015 The Prometheus Authors
  2. // Licensed under the Apache License, Version 2.0 (the "License");
  3. // you may not use this file except in compliance with the License.
  4. // You may obtain a copy of the License at
  5. //
  6. // http://www.apache.org/licenses/LICENSE-2.0
  7. //
  8. // Unless required by applicable law or agreed to in writing, software
  9. // distributed under the License is distributed on an "AS IS" BASIS,
  10. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11. // See the License for the specific language governing permissions and
  12. // limitations under the License.
  13. // +build go1.7
  14. // Package api provides clients for the HTTP APIs.
  15. package api
  16. import (
  17. "context"
  18. "io/ioutil"
  19. "net"
  20. "net/http"
  21. "net/url"
  22. "path"
  23. "strings"
  24. "time"
  25. )
  26. // DefaultRoundTripper is used if no RoundTripper is set in Config.
  27. var DefaultRoundTripper http.RoundTripper = &http.Transport{
  28. Proxy: http.ProxyFromEnvironment,
  29. Dial: (&net.Dialer{
  30. Timeout: 30 * time.Second,
  31. KeepAlive: 30 * time.Second,
  32. }).Dial,
  33. TLSHandshakeTimeout: 10 * time.Second,
  34. }
  35. // Config defines configuration parameters for a new client.
  36. type Config struct {
  37. // The address of the Prometheus to connect to.
  38. Address string
  39. // RoundTripper is used by the Client to drive HTTP requests. If not
  40. // provided, DefaultRoundTripper will be used.
  41. RoundTripper http.RoundTripper
  42. }
  43. func (cfg *Config) roundTripper() http.RoundTripper {
  44. if cfg.RoundTripper == nil {
  45. return DefaultRoundTripper
  46. }
  47. return cfg.RoundTripper
  48. }
  49. // Client is the interface for an API client.
  50. type Client interface {
  51. URL(ep string, args map[string]string) *url.URL
  52. Do(context.Context, *http.Request) (*http.Response, []byte, error)
  53. }
  54. // NewClient returns a new Client.
  55. //
  56. // It is safe to use the returned Client from multiple goroutines.
  57. func NewClient(cfg Config) (Client, error) {
  58. u, err := url.Parse(cfg.Address)
  59. if err != nil {
  60. return nil, err
  61. }
  62. u.Path = strings.TrimRight(u.Path, "/")
  63. return &httpClient{
  64. endpoint: u,
  65. client: http.Client{Transport: cfg.roundTripper()},
  66. }, nil
  67. }
  68. type httpClient struct {
  69. endpoint *url.URL
  70. client http.Client
  71. }
  72. func (c *httpClient) URL(ep string, args map[string]string) *url.URL {
  73. p := path.Join(c.endpoint.Path, ep)
  74. for arg, val := range args {
  75. arg = ":" + arg
  76. p = strings.Replace(p, arg, val, -1)
  77. }
  78. u := *c.endpoint
  79. u.Path = p
  80. return &u
  81. }
  82. func (c *httpClient) Do(ctx context.Context, req *http.Request) (*http.Response, []byte, error) {
  83. if ctx != nil {
  84. req = req.WithContext(ctx)
  85. }
  86. resp, err := c.client.Do(req)
  87. defer func() {
  88. if resp != nil {
  89. resp.Body.Close()
  90. }
  91. }()
  92. if err != nil {
  93. return nil, nil, err
  94. }
  95. var body []byte
  96. done := make(chan struct{})
  97. go func() {
  98. body, err = ioutil.ReadAll(resp.Body)
  99. close(done)
  100. }()
  101. select {
  102. case <-ctx.Done():
  103. err = resp.Body.Close()
  104. <-done
  105. if err == nil {
  106. err = ctx.Err()
  107. }
  108. case <-done:
  109. }
  110. return resp, body, err
  111. }