master
go 96 lines 2 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package prometheus
4
5 import (
6 "bufio"
7 "compress/gzip"
8 "context"
9 "fmt"
10 "io"
11 "net/http"
12 "os"
13 "strings"
14
15 "github.com/netdata/netdata/go/plugins/pkg/web"
16 )
17
18 const acceptHeader = `text/plain;version=0.0.4;q=1,*/*;q=0.1`
19
20 // fetcher writes the raw exposition text for one scrape into w. A single
21 // prometheus instance owns one fetcher and reuses it across scrapes.
22 type fetcher interface {
23 fetch(ctx context.Context, w io.Writer) error
24 }
25
26 // fileFetcher reads the exposition text from a local file (file:// URLs).
27 type fileFetcher struct {
28 path string
29 }
30
31 func (f *fileFetcher) fetch(_ context.Context, w io.Writer) error {
32 file, err := os.Open(f.path)
33 if err != nil {
34 return err
35 }
36 defer func() { _ = file.Close() }()
37
38 _, err = io.Copy(w, file)
39
40 return err
41 }
42
43 // httpFetcher scrapes the exposition text over HTTP, transparently decompressing
44 // gzip responses. The gzip reader and its buffered source are reused across scrapes.
45 type httpFetcher struct {
46 client *http.Client
47 request web.RequestConfig
48
49 gzipr *gzip.Reader
50 bodyBuf *bufio.Reader
51 }
52
53 func (f *httpFetcher) fetch(ctx context.Context, w io.Writer) error {
54 req, err := web.NewHTTPRequest(f.request)
55 if err != nil {
56 return err
57 }
58 req = req.WithContext(ctx)
59
60 req.Header.Add("Accept", acceptHeader)
61 req.Header.Add("Accept-Encoding", "gzip")
62
63 resp, err := f.client.Do(req)
64 if err != nil {
65 return err
66 }
67
68 defer web.CloseBody(resp)
69
70 if resp.StatusCode != http.StatusOK {
71 return fmt.Errorf("server '%s' returned HTTP status code %d (%s)", req.URL, resp.StatusCode, resp.Status)
72 }
73
74 if !strings.EqualFold(resp.Header.Get("Content-Encoding"), "gzip") {
75 _, err = io.Copy(w, resp.Body)
76 return err
77 }
78
79 if f.gzipr == nil {
80 f.bodyBuf = bufio.NewReader(resp.Body)
81 f.gzipr, err = gzip.NewReader(f.bodyBuf)
82 if err != nil {
83 return err
84 }
85 } else {
86 f.bodyBuf.Reset(resp.Body)
87 if err := f.gzipr.Reset(f.bodyBuf); err != nil {
88 return err
89 }
90 }
91
92 _, err = io.Copy(w, f.gzipr)
93 _ = f.gzipr.Close()
94
95 return err
96 }