...

Source file src/github.com/prometheus/client_golang/api/client.go

Documentation: github.com/prometheus/client_golang/api

     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  
    14  // Package api provides clients for the HTTP APIs.
    15  package api
    16  
    17  import (
    18  	"bytes"
    19  	"context"
    20  	"errors"
    21  	"net"
    22  	"net/http"
    23  	"net/url"
    24  	"path"
    25  	"strings"
    26  	"time"
    27  )
    28  
    29  // DefaultRoundTripper is used if no RoundTripper is set in Config.
    30  var DefaultRoundTripper http.RoundTripper = &http.Transport{
    31  	Proxy: http.ProxyFromEnvironment,
    32  	DialContext: (&net.Dialer{
    33  		Timeout:   30 * time.Second,
    34  		KeepAlive: 30 * time.Second,
    35  	}).DialContext,
    36  	TLSHandshakeTimeout: 10 * time.Second,
    37  }
    38  
    39  // Config defines configuration parameters for a new client.
    40  type Config struct {
    41  	// The address of the Prometheus to connect to.
    42  	Address string
    43  
    44  	// Client is used by the Client to drive HTTP requests. If not provided,
    45  	// a new one based on the provided RoundTripper (or DefaultRoundTripper) will be used.
    46  	Client *http.Client
    47  
    48  	// RoundTripper is used by the Client to drive HTTP requests. If not
    49  	// provided, DefaultRoundTripper will be used.
    50  	RoundTripper http.RoundTripper
    51  }
    52  
    53  func (cfg *Config) roundTripper() http.RoundTripper {
    54  	if cfg.RoundTripper == nil {
    55  		return DefaultRoundTripper
    56  	}
    57  	return cfg.RoundTripper
    58  }
    59  
    60  func (cfg *Config) client() http.Client {
    61  	if cfg.Client == nil {
    62  		return http.Client{
    63  			Transport: cfg.roundTripper(),
    64  		}
    65  	}
    66  	return *cfg.Client
    67  }
    68  
    69  func (cfg *Config) validate() error {
    70  	if cfg.Client != nil && cfg.RoundTripper != nil {
    71  		return errors.New("api.Config.RoundTripper and api.Config.Client are mutually exclusive")
    72  	}
    73  	return nil
    74  }
    75  
    76  // Client is the interface for an API client.
    77  type Client interface {
    78  	URL(ep string, args map[string]string) *url.URL
    79  	Do(context.Context, *http.Request) (*http.Response, []byte, error)
    80  }
    81  
    82  // NewClient returns a new Client.
    83  //
    84  // It is safe to use the returned Client from multiple goroutines.
    85  func NewClient(cfg Config) (Client, error) {
    86  	u, err := url.Parse(cfg.Address)
    87  	if err != nil {
    88  		return nil, err
    89  	}
    90  	u.Path = strings.TrimRight(u.Path, "/")
    91  
    92  	if err := cfg.validate(); err != nil {
    93  		return nil, err
    94  	}
    95  
    96  	return &httpClient{
    97  		endpoint: u,
    98  		client:   cfg.client(),
    99  	}, nil
   100  }
   101  
   102  type httpClient struct {
   103  	endpoint *url.URL
   104  	client   http.Client
   105  }
   106  
   107  func (c *httpClient) URL(ep string, args map[string]string) *url.URL {
   108  	p := path.Join(c.endpoint.Path, ep)
   109  
   110  	for arg, val := range args {
   111  		arg = ":" + arg
   112  		p = strings.ReplaceAll(p, arg, val)
   113  	}
   114  
   115  	u := *c.endpoint
   116  	u.Path = p
   117  
   118  	return &u
   119  }
   120  
   121  func (c *httpClient) Do(ctx context.Context, req *http.Request) (*http.Response, []byte, error) {
   122  	if ctx != nil {
   123  		req = req.WithContext(ctx)
   124  	}
   125  	resp, err := c.client.Do(req)
   126  	defer func() {
   127  		if resp != nil {
   128  			resp.Body.Close()
   129  		}
   130  	}()
   131  
   132  	if err != nil {
   133  		return nil, nil, err
   134  	}
   135  
   136  	var body []byte
   137  	done := make(chan struct{})
   138  	go func() {
   139  		var buf bytes.Buffer
   140  		_, err = buf.ReadFrom(resp.Body)
   141  		body = buf.Bytes()
   142  		close(done)
   143  	}()
   144  
   145  	select {
   146  	case <-ctx.Done():
   147  		<-done
   148  		err = resp.Body.Close()
   149  		if err == nil {
   150  			err = ctx.Err()
   151  		}
   152  	case <-done:
   153  	}
   154  
   155  	return resp, body, err
   156  }
   157  

View as plain text