Code refactoring for bpa operator
[icn.git] / cmd / bpa-operator / vendor / github.com / prometheus / client_golang / prometheus / http.go
1 // Copyright 2014 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 prometheus
15
16 import (
17         "bufio"
18         "compress/gzip"
19         "io"
20         "net"
21         "net/http"
22         "strconv"
23         "strings"
24         "sync"
25         "time"
26
27         "github.com/prometheus/common/expfmt"
28 )
29
30 // TODO(beorn7): Remove this whole file. It is a partial mirror of
31 // promhttp/http.go (to avoid circular import chains) where everything HTTP
32 // related should live. The functions here are just for avoiding
33 // breakage. Everything is deprecated.
34
35 const (
36         contentTypeHeader     = "Content-Type"
37         contentEncodingHeader = "Content-Encoding"
38         acceptEncodingHeader  = "Accept-Encoding"
39 )
40
41 var gzipPool = sync.Pool{
42         New: func() interface{} {
43                 return gzip.NewWriter(nil)
44         },
45 }
46
47 // Handler returns an HTTP handler for the DefaultGatherer. It is
48 // already instrumented with InstrumentHandler (using "prometheus" as handler
49 // name).
50 //
51 // Deprecated: Please note the issues described in the doc comment of
52 // InstrumentHandler. You might want to consider using promhttp.Handler instead.
53 func Handler() http.Handler {
54         return InstrumentHandler("prometheus", UninstrumentedHandler())
55 }
56
57 // UninstrumentedHandler returns an HTTP handler for the DefaultGatherer.
58 //
59 // Deprecated: Use promhttp.HandlerFor(DefaultGatherer, promhttp.HandlerOpts{})
60 // instead. See there for further documentation.
61 func UninstrumentedHandler() http.Handler {
62         return http.HandlerFunc(func(rsp http.ResponseWriter, req *http.Request) {
63                 mfs, err := DefaultGatherer.Gather()
64                 if err != nil {
65                         httpError(rsp, err)
66                         return
67                 }
68
69                 contentType := expfmt.Negotiate(req.Header)
70                 header := rsp.Header()
71                 header.Set(contentTypeHeader, string(contentType))
72
73                 w := io.Writer(rsp)
74                 if gzipAccepted(req.Header) {
75                         header.Set(contentEncodingHeader, "gzip")
76                         gz := gzipPool.Get().(*gzip.Writer)
77                         defer gzipPool.Put(gz)
78
79                         gz.Reset(w)
80                         defer gz.Close()
81
82                         w = gz
83                 }
84
85                 enc := expfmt.NewEncoder(w, contentType)
86
87                 for _, mf := range mfs {
88                         if err := enc.Encode(mf); err != nil {
89                                 httpError(rsp, err)
90                                 return
91                         }
92                 }
93         })
94 }
95
96 var instLabels = []string{"method", "code"}
97
98 type nower interface {
99         Now() time.Time
100 }
101
102 type nowFunc func() time.Time
103
104 func (n nowFunc) Now() time.Time {
105         return n()
106 }
107
108 var now nower = nowFunc(func() time.Time {
109         return time.Now()
110 })
111
112 // InstrumentHandler wraps the given HTTP handler for instrumentation. It
113 // registers four metric collectors (if not already done) and reports HTTP
114 // metrics to the (newly or already) registered collectors: http_requests_total
115 // (CounterVec), http_request_duration_microseconds (Summary),
116 // http_request_size_bytes (Summary), http_response_size_bytes (Summary). Each
117 // has a constant label named "handler" with the provided handlerName as
118 // value. http_requests_total is a metric vector partitioned by HTTP method
119 // (label name "method") and HTTP status code (label name "code").
120 //
121 // Deprecated: InstrumentHandler has several issues. Use the tooling provided in
122 // package promhttp instead. The issues are the following: (1) It uses Summaries
123 // rather than Histograms. Summaries are not useful if aggregation across
124 // multiple instances is required. (2) It uses microseconds as unit, which is
125 // deprecated and should be replaced by seconds. (3) The size of the request is
126 // calculated in a separate goroutine. Since this calculator requires access to
127 // the request header, it creates a race with any writes to the header performed
128 // during request handling.  httputil.ReverseProxy is a prominent example for a
129 // handler performing such writes. (4) It has additional issues with HTTP/2, cf.
130 // https://github.com/prometheus/client_golang/issues/272.
131 func InstrumentHandler(handlerName string, handler http.Handler) http.HandlerFunc {
132         return InstrumentHandlerFunc(handlerName, handler.ServeHTTP)
133 }
134
135 // InstrumentHandlerFunc wraps the given function for instrumentation. It
136 // otherwise works in the same way as InstrumentHandler (and shares the same
137 // issues).
138 //
139 // Deprecated: InstrumentHandlerFunc is deprecated for the same reasons as
140 // InstrumentHandler is. Use the tooling provided in package promhttp instead.
141 func InstrumentHandlerFunc(handlerName string, handlerFunc func(http.ResponseWriter, *http.Request)) http.HandlerFunc {
142         return InstrumentHandlerFuncWithOpts(
143                 SummaryOpts{
144                         Subsystem:   "http",
145                         ConstLabels: Labels{"handler": handlerName},
146                         Objectives:  map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001},
147                 },
148                 handlerFunc,
149         )
150 }
151
152 // InstrumentHandlerWithOpts works like InstrumentHandler (and shares the same
153 // issues) but provides more flexibility (at the cost of a more complex call
154 // syntax). As InstrumentHandler, this function registers four metric
155 // collectors, but it uses the provided SummaryOpts to create them. However, the
156 // fields "Name" and "Help" in the SummaryOpts are ignored. "Name" is replaced
157 // by "requests_total", "request_duration_microseconds", "request_size_bytes",
158 // and "response_size_bytes", respectively. "Help" is replaced by an appropriate
159 // help string. The names of the variable labels of the http_requests_total
160 // CounterVec are "method" (get, post, etc.), and "code" (HTTP status code).
161 //
162 // If InstrumentHandlerWithOpts is called as follows, it mimics exactly the
163 // behavior of InstrumentHandler:
164 //
165 //     prometheus.InstrumentHandlerWithOpts(
166 //         prometheus.SummaryOpts{
167 //              Subsystem:   "http",
168 //              ConstLabels: prometheus.Labels{"handler": handlerName},
169 //         },
170 //         handler,
171 //     )
172 //
173 // Technical detail: "requests_total" is a CounterVec, not a SummaryVec, so it
174 // cannot use SummaryOpts. Instead, a CounterOpts struct is created internally,
175 // and all its fields are set to the equally named fields in the provided
176 // SummaryOpts.
177 //
178 // Deprecated: InstrumentHandlerWithOpts is deprecated for the same reasons as
179 // InstrumentHandler is. Use the tooling provided in package promhttp instead.
180 func InstrumentHandlerWithOpts(opts SummaryOpts, handler http.Handler) http.HandlerFunc {
181         return InstrumentHandlerFuncWithOpts(opts, handler.ServeHTTP)
182 }
183
184 // InstrumentHandlerFuncWithOpts works like InstrumentHandlerFunc (and shares
185 // the same issues) but provides more flexibility (at the cost of a more complex
186 // call syntax). See InstrumentHandlerWithOpts for details how the provided
187 // SummaryOpts are used.
188 //
189 // Deprecated: InstrumentHandlerFuncWithOpts is deprecated for the same reasons
190 // as InstrumentHandler is. Use the tooling provided in package promhttp instead.
191 func InstrumentHandlerFuncWithOpts(opts SummaryOpts, handlerFunc func(http.ResponseWriter, *http.Request)) http.HandlerFunc {
192         reqCnt := NewCounterVec(
193                 CounterOpts{
194                         Namespace:   opts.Namespace,
195                         Subsystem:   opts.Subsystem,
196                         Name:        "requests_total",
197                         Help:        "Total number of HTTP requests made.",
198                         ConstLabels: opts.ConstLabels,
199                 },
200                 instLabels,
201         )
202         if err := Register(reqCnt); err != nil {
203                 if are, ok := err.(AlreadyRegisteredError); ok {
204                         reqCnt = are.ExistingCollector.(*CounterVec)
205                 } else {
206                         panic(err)
207                 }
208         }
209
210         opts.Name = "request_duration_microseconds"
211         opts.Help = "The HTTP request latencies in microseconds."
212         reqDur := NewSummary(opts)
213         if err := Register(reqDur); err != nil {
214                 if are, ok := err.(AlreadyRegisteredError); ok {
215                         reqDur = are.ExistingCollector.(Summary)
216                 } else {
217                         panic(err)
218                 }
219         }
220
221         opts.Name = "request_size_bytes"
222         opts.Help = "The HTTP request sizes in bytes."
223         reqSz := NewSummary(opts)
224         if err := Register(reqSz); err != nil {
225                 if are, ok := err.(AlreadyRegisteredError); ok {
226                         reqSz = are.ExistingCollector.(Summary)
227                 } else {
228                         panic(err)
229                 }
230         }
231
232         opts.Name = "response_size_bytes"
233         opts.Help = "The HTTP response sizes in bytes."
234         resSz := NewSummary(opts)
235         if err := Register(resSz); err != nil {
236                 if are, ok := err.(AlreadyRegisteredError); ok {
237                         resSz = are.ExistingCollector.(Summary)
238                 } else {
239                         panic(err)
240                 }
241         }
242
243         return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
244                 now := time.Now()
245
246                 delegate := &responseWriterDelegator{ResponseWriter: w}
247                 out := computeApproximateRequestSize(r)
248
249                 _, cn := w.(http.CloseNotifier)
250                 _, fl := w.(http.Flusher)
251                 _, hj := w.(http.Hijacker)
252                 _, rf := w.(io.ReaderFrom)
253                 var rw http.ResponseWriter
254                 if cn && fl && hj && rf {
255                         rw = &fancyResponseWriterDelegator{delegate}
256                 } else {
257                         rw = delegate
258                 }
259                 handlerFunc(rw, r)
260
261                 elapsed := float64(time.Since(now)) / float64(time.Microsecond)
262
263                 method := sanitizeMethod(r.Method)
264                 code := sanitizeCode(delegate.status)
265                 reqCnt.WithLabelValues(method, code).Inc()
266                 reqDur.Observe(elapsed)
267                 resSz.Observe(float64(delegate.written))
268                 reqSz.Observe(float64(<-out))
269         })
270 }
271
272 func computeApproximateRequestSize(r *http.Request) <-chan int {
273         // Get URL length in current goroutine for avoiding a race condition.
274         // HandlerFunc that runs in parallel may modify the URL.
275         s := 0
276         if r.URL != nil {
277                 s += len(r.URL.String())
278         }
279
280         out := make(chan int, 1)
281
282         go func() {
283                 s += len(r.Method)
284                 s += len(r.Proto)
285                 for name, values := range r.Header {
286                         s += len(name)
287                         for _, value := range values {
288                                 s += len(value)
289                         }
290                 }
291                 s += len(r.Host)
292
293                 // N.B. r.Form and r.MultipartForm are assumed to be included in r.URL.
294
295                 if r.ContentLength != -1 {
296                         s += int(r.ContentLength)
297                 }
298                 out <- s
299                 close(out)
300         }()
301
302         return out
303 }
304
305 type responseWriterDelegator struct {
306         http.ResponseWriter
307
308         status      int
309         written     int64
310         wroteHeader bool
311 }
312
313 func (r *responseWriterDelegator) WriteHeader(code int) {
314         r.status = code
315         r.wroteHeader = true
316         r.ResponseWriter.WriteHeader(code)
317 }
318
319 func (r *responseWriterDelegator) Write(b []byte) (int, error) {
320         if !r.wroteHeader {
321                 r.WriteHeader(http.StatusOK)
322         }
323         n, err := r.ResponseWriter.Write(b)
324         r.written += int64(n)
325         return n, err
326 }
327
328 type fancyResponseWriterDelegator struct {
329         *responseWriterDelegator
330 }
331
332 func (f *fancyResponseWriterDelegator) CloseNotify() <-chan bool {
333         return f.ResponseWriter.(http.CloseNotifier).CloseNotify()
334 }
335
336 func (f *fancyResponseWriterDelegator) Flush() {
337         f.ResponseWriter.(http.Flusher).Flush()
338 }
339
340 func (f *fancyResponseWriterDelegator) Hijack() (net.Conn, *bufio.ReadWriter, error) {
341         return f.ResponseWriter.(http.Hijacker).Hijack()
342 }
343
344 func (f *fancyResponseWriterDelegator) ReadFrom(r io.Reader) (int64, error) {
345         if !f.wroteHeader {
346                 f.WriteHeader(http.StatusOK)
347         }
348         n, err := f.ResponseWriter.(io.ReaderFrom).ReadFrom(r)
349         f.written += n
350         return n, err
351 }
352
353 func sanitizeMethod(m string) string {
354         switch m {
355         case "GET", "get":
356                 return "get"
357         case "PUT", "put":
358                 return "put"
359         case "HEAD", "head":
360                 return "head"
361         case "POST", "post":
362                 return "post"
363         case "DELETE", "delete":
364                 return "delete"
365         case "CONNECT", "connect":
366                 return "connect"
367         case "OPTIONS", "options":
368                 return "options"
369         case "NOTIFY", "notify":
370                 return "notify"
371         default:
372                 return strings.ToLower(m)
373         }
374 }
375
376 func sanitizeCode(s int) string {
377         switch s {
378         case 100:
379                 return "100"
380         case 101:
381                 return "101"
382
383         case 200:
384                 return "200"
385         case 201:
386                 return "201"
387         case 202:
388                 return "202"
389         case 203:
390                 return "203"
391         case 204:
392                 return "204"
393         case 205:
394                 return "205"
395         case 206:
396                 return "206"
397
398         case 300:
399                 return "300"
400         case 301:
401                 return "301"
402         case 302:
403                 return "302"
404         case 304:
405                 return "304"
406         case 305:
407                 return "305"
408         case 307:
409                 return "307"
410
411         case 400:
412                 return "400"
413         case 401:
414                 return "401"
415         case 402:
416                 return "402"
417         case 403:
418                 return "403"
419         case 404:
420                 return "404"
421         case 405:
422                 return "405"
423         case 406:
424                 return "406"
425         case 407:
426                 return "407"
427         case 408:
428                 return "408"
429         case 409:
430                 return "409"
431         case 410:
432                 return "410"
433         case 411:
434                 return "411"
435         case 412:
436                 return "412"
437         case 413:
438                 return "413"
439         case 414:
440                 return "414"
441         case 415:
442                 return "415"
443         case 416:
444                 return "416"
445         case 417:
446                 return "417"
447         case 418:
448                 return "418"
449
450         case 500:
451                 return "500"
452         case 501:
453                 return "501"
454         case 502:
455                 return "502"
456         case 503:
457                 return "503"
458         case 504:
459                 return "504"
460         case 505:
461                 return "505"
462
463         case 428:
464                 return "428"
465         case 429:
466                 return "429"
467         case 431:
468                 return "431"
469         case 511:
470                 return "511"
471
472         default:
473                 return strconv.Itoa(s)
474         }
475 }
476
477 // gzipAccepted returns whether the client will accept gzip-encoded content.
478 func gzipAccepted(header http.Header) bool {
479         a := header.Get(acceptEncodingHeader)
480         parts := strings.Split(a, ",")
481         for _, part := range parts {
482                 part = strings.TrimSpace(part)
483                 if part == "gzip" || strings.HasPrefix(part, "gzip;") {
484                         return true
485                 }
486         }
487         return false
488 }
489
490 // httpError removes any content-encoding header and then calls http.Error with
491 // the provided error and http.StatusInternalServerErrer. Error contents is
492 // supposed to be uncompressed plain text. However, same as with a plain
493 // http.Error, any header settings will be void if the header has already been
494 // sent. The error message will still be written to the writer, but it will
495 // probably be of limited use.
496 func httpError(rsp http.ResponseWriter, err error) {
497         rsp.Header().Del(contentEncodingHeader)
498         http.Error(
499                 rsp,
500                 "An error has occurred while serving metrics:\n\n"+err.Error(),
501                 http.StatusInternalServerError,
502         )
503 }