| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131 |
- // Copyright 2015 The Prometheus Authors
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- // +build go1.7
- // Package api provides clients for the HTTP APIs.
- package api
- import (
- "context"
- "io/ioutil"
- "net"
- "net/http"
- "net/url"
- "path"
- "strings"
- "time"
- )
- // DefaultRoundTripper is used if no RoundTripper is set in Config.
- var DefaultRoundTripper http.RoundTripper = &http.Transport{
- Proxy: http.ProxyFromEnvironment,
- Dial: (&net.Dialer{
- Timeout: 30 * time.Second,
- KeepAlive: 30 * time.Second,
- }).Dial,
- TLSHandshakeTimeout: 10 * time.Second,
- }
- // Config defines configuration parameters for a new client.
- type Config struct {
- // The address of the Prometheus to connect to.
- Address string
- // RoundTripper is used by the Client to drive HTTP requests. If not
- // provided, DefaultRoundTripper will be used.
- RoundTripper http.RoundTripper
- }
- func (cfg *Config) roundTripper() http.RoundTripper {
- if cfg.RoundTripper == nil {
- return DefaultRoundTripper
- }
- return cfg.RoundTripper
- }
- // Client is the interface for an API client.
- type Client interface {
- URL(ep string, args map[string]string) *url.URL
- Do(context.Context, *http.Request) (*http.Response, []byte, error)
- }
- // NewClient returns a new Client.
- //
- // It is safe to use the returned Client from multiple goroutines.
- func NewClient(cfg Config) (Client, error) {
- u, err := url.Parse(cfg.Address)
- if err != nil {
- return nil, err
- }
- u.Path = strings.TrimRight(u.Path, "/")
- return &httpClient{
- endpoint: u,
- client: http.Client{Transport: cfg.roundTripper()},
- }, nil
- }
- type httpClient struct {
- endpoint *url.URL
- client http.Client
- }
- func (c *httpClient) URL(ep string, args map[string]string) *url.URL {
- p := path.Join(c.endpoint.Path, ep)
- for arg, val := range args {
- arg = ":" + arg
- p = strings.Replace(p, arg, val, -1)
- }
- u := *c.endpoint
- u.Path = p
- return &u
- }
- func (c *httpClient) Do(ctx context.Context, req *http.Request) (*http.Response, []byte, error) {
- if ctx != nil {
- req = req.WithContext(ctx)
- }
- resp, err := c.client.Do(req)
- defer func() {
- if resp != nil {
- resp.Body.Close()
- }
- }()
- if err != nil {
- return nil, nil, err
- }
- var body []byte
- done := make(chan struct{})
- go func() {
- body, err = ioutil.ReadAll(resp.Body)
- close(done)
- }()
- select {
- case <-ctx.Done():
- err = resp.Body.Close()
- <-done
- if err == nil {
- err = ctx.Err()
- }
- case <-done:
- }
- return resp, body, err
- }
|