github.com/yankunsam/loki/v2@v2.6.3-0.20220817130409-389df5235c27/pkg/querier/queryrange/queryrangebase/roundtrip.go (about)

     1  // Copyright 2016 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  // Mostly lifted from prometheus/web/api/v1/api.go.
    15  
    16  package queryrangebase
    17  
    18  import (
    19  	"context"
    20  	"flag"
    21  	"io"
    22  	"io/ioutil"
    23  	"net/http"
    24  	"time"
    25  
    26  	"github.com/grafana/dskit/flagext"
    27  	"github.com/opentracing/opentracing-go"
    28  	"github.com/pkg/errors"
    29  	"github.com/weaveworks/common/httpgrpc"
    30  	"github.com/weaveworks/common/user"
    31  )
    32  
    33  const day = 24 * time.Hour
    34  
    35  // PassthroughMiddleware is a noop middleware
    36  var PassthroughMiddleware = MiddlewareFunc(func(next Handler) Handler {
    37  	return next
    38  })
    39  
    40  // Config for query_range middleware chain.
    41  type Config struct {
    42  	// Deprecated: SplitQueriesByInterval will be removed in the next major release
    43  	SplitQueriesByInterval time.Duration `yaml:"split_queries_by_interval"`
    44  
    45  	AlignQueriesWithStep bool `yaml:"align_queries_with_step"`
    46  	ResultsCacheConfig   `yaml:"results_cache"`
    47  	CacheResults         bool `yaml:"cache_results"`
    48  	MaxRetries           int  `yaml:"max_retries"`
    49  	ShardedQueries       bool `yaml:"parallelise_shardable_queries"`
    50  	// List of headers which query_range middleware chain would forward to downstream querier.
    51  	ForwardHeaders flagext.StringSlice `yaml:"forward_headers_list"`
    52  }
    53  
    54  // RegisterFlags adds the flags required to config this to the given FlagSet.
    55  func (cfg *Config) RegisterFlags(f *flag.FlagSet) {
    56  	f.IntVar(&cfg.MaxRetries, "querier.max-retries-per-request", 5, "Maximum number of retries for a single request; beyond this, the downstream error is returned.")
    57  	f.BoolVar(&cfg.AlignQueriesWithStep, "querier.align-querier-with-step", false, "Mutate incoming queries to align their start and end with their step.")
    58  	f.BoolVar(&cfg.CacheResults, "querier.cache-results", false, "Cache query results.")
    59  	f.BoolVar(&cfg.ShardedQueries, "querier.parallelise-shardable-queries", true, "Perform query parallelisations based on storage sharding configuration and query ASTs. This feature is supported only by the chunks storage engine.")
    60  	f.Var(&cfg.ForwardHeaders, "frontend.forward-headers-list", "List of headers forwarded by the query Frontend to downstream querier.")
    61  	cfg.ResultsCacheConfig.RegisterFlags(f)
    62  }
    63  
    64  // Validate validates the config.
    65  func (cfg *Config) Validate() error {
    66  	if cfg.SplitQueriesByInterval != 0 {
    67  		return errors.New("the yaml flag `split_queries_by_interval` must now be set in the `limits_config` section instead of the `query_range` config section")
    68  	}
    69  	if cfg.CacheResults {
    70  		if err := cfg.ResultsCacheConfig.Validate(); err != nil {
    71  			return errors.Wrap(err, "invalid ResultsCache config")
    72  		}
    73  	}
    74  	return nil
    75  }
    76  
    77  // HandlerFunc is like http.HandlerFunc, but for Handler.
    78  type HandlerFunc func(context.Context, Request) (Response, error)
    79  
    80  // Do implements Handler.
    81  func (q HandlerFunc) Do(ctx context.Context, req Request) (Response, error) {
    82  	return q(ctx, req)
    83  }
    84  
    85  // Handler is like http.Handle, but specifically for Prometheus query_range calls.
    86  type Handler interface {
    87  	Do(context.Context, Request) (Response, error)
    88  }
    89  
    90  // MiddlewareFunc is like http.HandlerFunc, but for Middleware.
    91  type MiddlewareFunc func(Handler) Handler
    92  
    93  // Wrap implements Middleware.
    94  func (q MiddlewareFunc) Wrap(h Handler) Handler {
    95  	return q(h)
    96  }
    97  
    98  // Middleware is a higher order Handler.
    99  type Middleware interface {
   100  	Wrap(Handler) Handler
   101  }
   102  
   103  // MergeMiddlewares produces a middleware that applies multiple middleware in turn;
   104  // ie Merge(f,g,h).Wrap(handler) == f.Wrap(g.Wrap(h.Wrap(handler)))
   105  func MergeMiddlewares(middleware ...Middleware) Middleware {
   106  	return MiddlewareFunc(func(next Handler) Handler {
   107  		for i := len(middleware) - 1; i >= 0; i-- {
   108  			next = middleware[i].Wrap(next)
   109  		}
   110  		return next
   111  	})
   112  }
   113  
   114  // Tripperware is a signature for all http client-side middleware.
   115  type Tripperware func(http.RoundTripper) http.RoundTripper
   116  
   117  // RoundTripFunc is to http.RoundTripper what http.HandlerFunc is to http.Handler.
   118  type RoundTripFunc func(*http.Request) (*http.Response, error)
   119  
   120  // RoundTrip implements http.RoundTripper.
   121  func (f RoundTripFunc) RoundTrip(r *http.Request) (*http.Response, error) {
   122  	return f(r)
   123  }
   124  
   125  type roundTripper struct {
   126  	next    http.RoundTripper
   127  	handler Handler
   128  	codec   Codec
   129  	headers []string
   130  }
   131  
   132  // NewRoundTripper merges a set of middlewares into an handler, then inject it into the `next` roundtripper
   133  // using the codec to translate requests and responses.
   134  func NewRoundTripper(next http.RoundTripper, codec Codec, headers []string, middlewares ...Middleware) http.RoundTripper {
   135  	transport := roundTripper{
   136  		next:    next,
   137  		codec:   codec,
   138  		headers: headers,
   139  	}
   140  	transport.handler = MergeMiddlewares(middlewares...).Wrap(&transport)
   141  	return transport
   142  }
   143  
   144  func (q roundTripper) RoundTrip(r *http.Request) (*http.Response, error) {
   145  	// include the headers specified in the roundTripper during decoding the request.
   146  	request, err := q.codec.DecodeRequest(r.Context(), r, q.headers)
   147  	if err != nil {
   148  		return nil, err
   149  	}
   150  
   151  	if span := opentracing.SpanFromContext(r.Context()); span != nil {
   152  		request.LogToSpan(span)
   153  	}
   154  
   155  	response, err := q.handler.Do(r.Context(), request)
   156  	if err != nil {
   157  		return nil, err
   158  	}
   159  
   160  	return q.codec.EncodeResponse(r.Context(), response)
   161  }
   162  
   163  // Do implements Handler.
   164  func (q roundTripper) Do(ctx context.Context, r Request) (Response, error) {
   165  	request, err := q.codec.EncodeRequest(ctx, r)
   166  	if err != nil {
   167  		return nil, err
   168  	}
   169  
   170  	if err := user.InjectOrgIDIntoHTTPRequest(ctx, request); err != nil {
   171  		return nil, httpgrpc.Errorf(http.StatusBadRequest, err.Error())
   172  	}
   173  
   174  	response, err := q.next.RoundTrip(request)
   175  	if err != nil {
   176  		return nil, err
   177  	}
   178  	defer func() {
   179  		_, _ = io.Copy(ioutil.Discard, io.LimitReader(response.Body, 1024)) //nolint:errcheck
   180  		response.Body.Close()
   181  	}()
   182  
   183  	return q.codec.DecodeResponse(ctx, response, r)
   184  }