<commit_before>package http

import (
	"context"
	"encoding/json"
	"net/http"
	_ "net/http/pprof" // used for debug pprof at the default path.
	"strings"
	"time"

	"github.com/influxdata/influxdb/kit/prom"
	"github.com/influxdata/influxdb/kit/tracing"
	"github.com/opentracing/opentracing-go"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"go.uber.org/zap"
)

const (
	// MetricsPath exposes the prometheus metrics over /metrics.
	MetricsPath = "/metrics"
	// ReadyPath exposes the readiness of the service over /ready.
	ReadyPath = "/ready"
	// HealthPath exposes the health of the service over /health.
	HealthPath = "/health"
	// DebugPath exposes /debug/pprof for go debugging.
	DebugPath = "/debug"
)

// Handler provides basic handling of metrics, health and debug endpoints.
// All other requests are passed down to the sub handler.
type Handler struct {
	name string
	// MetricsHandler handles metrics requests
	MetricsHandler http.Handler
	// ReadyHandler handles readiness checks
	ReadyHandler http.Handler
	// HealthHandler handles health requests
	HealthHandler http.Handler
	// DebugHandler handles debug requests
	DebugHandler http.Handler
	// Handler handles all other requests
	Handler http.Handler

	requests   *prometheus.CounterVec
	requestDur *prometheus.HistogramVec

	// log logs all HTTP requests as they are served
	log *zap.Logger
}

// NewHandler creates a new handler with the given name.
// The name is used to tag the metrics produced by this handler.
//
// The MetricsHandler is set to the default prometheus handler.
// It is the caller's responsibility to call prometheus.MustRegister(h.PrometheusCollectors()...).
// In most cases, you want to use NewHandlerFromRegistry instead.
func NewHandler(name string) *Handler {
	h := &Handler{
		name:           name,
		MetricsHandler: promhttp.Handler(),
		DebugHandler:   http.DefaultServeMux,
	}
	h.initMetrics()
	return h
}

// NewHandlerFromRegistry creates a new handler with the given name,
// and sets the /metrics endpoint to use the metrics from the given registry,
// after self-registering h's metrics.
func NewHandlerFromRegistry(log *zap.Logger, name string, reg *prom.Registry) *Handler {
	h := &Handler{
		name:           name,
		MetricsHandler: reg.HTTPHandler(),
		ReadyHandler:   http.HandlerFunc(ReadyHandler),
		HealthHandler:  http.HandlerFunc(HealthHandler),
		DebugHandler:   http.DefaultServeMux,
		log:            log,
	}
	h.initMetrics()
	reg.MustRegister(h.PrometheusCollectors()...)
	return h
}

// ServeHTTP delegates a request to the appropriate subhandler.
func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	var span opentracing.Span
	span, r = tracing.ExtractFromHTTPRequest(r, h.name)
	ua := userAgent(r)
	span.LogKV("user_agent", ua, "referer", r.Referer(), "remote_addr", r.RemoteAddr)
	for k, v := range r.Header {
		if len(v) == 0 {
			continue
		}
		// If header has multiple values, only the first value will be logged on the trace.
		span.LogKV(k, v[0])
	}

	defer span.Finish()

	// TODO: better way to do this?
	statusW := newStatusResponseWriter(w)
	w = statusW

	// TODO: This could be problematic eventually. But for now it should be fine.
	defer func(start time.Time) {
		duration := time.Since(start)
		statusClass := statusW.statusCodeClass()
		h.requests.With(prometheus.Labels{
			"handler":    h.name,
			"method":     r.Method,
			"path":       r.URL.Path,
			"status":     statusClass,
			"user_agent": ua,
		}).Inc()
		h.requestDur.With(prometheus.Labels{
			"handler":    h.name,
			"method":     r.Method,
			"path":       r.URL.Path,
			"status":     statusClass,
			"user_agent": ua,
		}).Observe(duration.Seconds())
	}(time.Now())

	switch {
	case r.URL.Path == MetricsPath:
		h.MetricsHandler.ServeHTTP(w, r)
	case r.URL.Path == ReadyPath:
		h.ReadyHandler.ServeHTTP(w, r)
	case r.URL.Path == HealthPath:
		h.HealthHandler.ServeHTTP(w, r)
	case strings.HasPrefix(r.URL.Path, DebugPath):
		h.DebugHandler.ServeHTTP(w, r)
	default:
		h.Handler.ServeHTTP(w, r)
	}
}

func encodeResponse(ctx context.Context, w http.ResponseWriter, code int, res interface{}) error {
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	w.WriteHeader(code)

	return json.NewEncoder(w).Encode(res)
}

// PrometheusCollectors satisifies prom.PrometheusCollector.
func (h *Handler) PrometheusCollectors() []prometheus.Collector {
	return []prometheus.Collector{<commit_msg>fix(http): remove authorization and user-agent headers from tr… (#16139)<commit_after>package http

import (
	"context"
	"encoding/json"
	"net/http"
	_ "net/http/pprof" // used for debug pprof at the default path.
	"strings"
	"time"

	"github.com/influxdata/influxdb/kit/prom"
	"github.com/influxdata/influxdb/kit/tracing"
	"github.com/opentracing/opentracing-go"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"go.uber.org/zap"
)

const (
	// MetricsPath exposes the prometheus metrics over /metrics.
	MetricsPath = "/metrics"
	// ReadyPath exposes the readiness of the service over /ready.
	ReadyPath = "/ready"
	// HealthPath exposes the health of the service over /health.
	HealthPath = "/health"
	// DebugPath exposes /debug/pprof for go debugging.
	DebugPath = "/debug"
)

// Handler provides basic handling of metrics, health and debug endpoints.
// All other requests are passed down to the sub handler.
type Handler struct {
	name string
	// MetricsHandler handles metrics requests
	MetricsHandler http.Handler
	// ReadyHandler handles readiness checks
	ReadyHandler http.Handler
	// HealthHandler handles health requests
	HealthHandler http.Handler
	// DebugHandler handles debug requests
	DebugHandler http.Handler
	// Handler handles all other requests
	Handler http.Handler

	requests   *prometheus.CounterVec
	requestDur *prometheus.HistogramVec

	// log logs all HTTP requests as they are served
	log *zap.Logger
}

// NewHandler creates a new handler with the given name.
// The name is used to tag the metrics produced by this handler.
//
// The MetricsHandler is set to the default prometheus handler.
// It is the caller's responsibility to call prometheus.MustRegister(h.PrometheusCollectors()...).
// In most cases, you want to use NewHandlerFromRegistry instead.
func NewHandler(name string) *Handler {
	h := &Handler{
		name:           name,
		MetricsHandler: promhttp.Handler(),
		DebugHandler:   http.DefaultServeMux,
	}
	h.initMetrics()
	return h
}

// NewHandlerFromRegistry creates a new handler with the given name,
// and sets the /metrics endpoint to use the metrics from the given registry,
// after self-registering h's metrics.
func NewHandlerFromRegistry(log *zap.Logger, name string, reg *prom.Registry) *Handler {
	h := &Handler{
		name:           name,
		MetricsHandler: reg.HTTPHandler(),
		ReadyHandler:   http.HandlerFunc(ReadyHandler),
		HealthHandler:  http.HandlerFunc(HealthHandler),
		DebugHandler:   http.DefaultServeMux,
		log:            log,
	}
	h.initMetrics()
	reg.MustRegister(h.PrometheusCollectors()...)
	return h
}

// ServeHTTP delegates a request to the appropriate subhandler.
func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	var span opentracing.Span
	span, r = tracing.ExtractFromHTTPRequest(r, h.name)

	statusW := newStatusResponseWriter(w)
	w = statusW

	// record prometheus metrics and finish traces
	defer func(start time.Time) {
		duration := time.Since(start)
		statusClass := statusW.statusCodeClass()
		ua := userAgent(r)

		h.requests.With(prometheus.Labels{
			"handler":    h.name,
			"method":     r.Method,
			"path":       r.URL.Path,
			"status":     statusClass,
			"user_agent": ua,
		}).Inc()
		h.requestDur.With(prometheus.Labels{
			"handler":    h.name,
			"method":     r.Method,
			"path":       r.URL.Path,
			"status":     statusClass,
			"user_agent": ua,
		}).Observe(duration.Seconds())

		span.LogKV("user_agent", ua)
		for k, v := range r.Header {
			if len(v) == 0 {
				continue
			}

			// yeah, we don't need these
			if k == "Authorization" || k == "User-Agent" {
				continue
			}

			// If header has multiple values, only the first value will be logged on the trace.
			span.LogKV(k, v[0])
		}
		span.Finish()
	}(time.Now())

	switch {
	case r.URL.Path == MetricsPath:
		h.MetricsHandler.ServeHTTP(w, r)
	case r.URL.Path == ReadyPath:
		h.ReadyHandler.ServeHTTP(w, r)
	case r.URL.Path == HealthPath:
		h.HealthHandler.ServeHTTP(w, r)
	case strings.HasPrefix(r.URL.Path, DebugPath):
		h.DebugHandler.ServeHTTP(w, r)
	default:
		h.Handler.ServeHTTP(w, r)
	}
}

func encodeResponse(ctx context.Context, w http.ResponseWriter, code int, res interface{}) error {
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	w.WriteHeader(code)

	return json.NewEncoder(w).Encode(res)
}

// PrometheusCollectors satisifies prom.PrometheusCollector.
func (h *Handler) PrometheusCollectors() []prometheus.Collector {
	return []prometheus.Collector{
{'fraction_non_alphanumeric': 0.11578130198513918, 'fraction_numerical': 0.0012199179327936121, 'mean_word_length': 3.807036247334755, 'pattern_counts': {'":': 20, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3986638', 'n_tokens_mistral': 3094, 'n_tokens_neox': 2872, 'n_words': 822}