github.com/grafana/pyroscope@v1.18.0/pkg/util/delayhandler/http.go (about)

     1  package delayhandler
     2  
     3  import (
     4  	"context"
     5  	"net/http"
     6  	"time"
     7  
     8  	"github.com/opentracing/opentracing-go"
     9  )
    10  
    11  func wrapResponseWriter(w http.ResponseWriter, end time.Time) (http.ResponseWriter, *delayedResponseWriter) {
    12  	wrapped := &delayedResponseWriter{wrapped: w, end: end}
    13  
    14  	// check if the writer implements the Flusher interface
    15  	flusher, ok := w.(http.Flusher)
    16  	if ok {
    17  		return &delayedResponseWriterWithFlush{
    18  			delayedResponseWriter: wrapped,
    19  			flusher:               flusher,
    20  		}, wrapped
    21  	}
    22  
    23  	return wrapped, wrapped
    24  }
    25  
    26  type delayedResponseWriterWithFlush struct {
    27  	*delayedResponseWriter
    28  	flusher http.Flusher
    29  }
    30  
    31  func (w *delayedResponseWriterWithFlush) Flush() {
    32  	w.flusher.Flush()
    33  }
    34  
    35  type delayedResponseWriter struct {
    36  	wrapped       http.ResponseWriter
    37  	end           time.Time
    38  	statusWritten bool
    39  	requestError  bool
    40  }
    41  
    42  func (w *delayedResponseWriter) WriteHeader(statusCode int) {
    43  	// do not forget to write the status code to the wrapped writer
    44  	defer w.wrapped.WriteHeader(statusCode)
    45  	w.statusWritten = true
    46  
    47  	// errors shouldn't be delayed
    48  	if statusCode/100 != 2 {
    49  		w.requestError = true
    50  		return
    51  	}
    52  
    53  	delayLeft := w.end.Sub(timeNow())
    54  	if delayLeft > 0 {
    55  		addDelayHeader(w.wrapped.Header(), delayLeft)
    56  	}
    57  }
    58  
    59  func (w *delayedResponseWriter) Header() http.Header {
    60  	return w.wrapped.Header()
    61  }
    62  
    63  func (w *delayedResponseWriter) Write(p []byte) (int, error) {
    64  	return w.wrapped.Write(p)
    65  }
    66  
    67  func NewHTTP(limits Limits) func(h http.Handler) http.Handler {
    68  	return func(h http.Handler) http.Handler {
    69  		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    70  			start := timeNow()
    71  			ctx := r.Context()
    72  
    73  			delay := getDelay(ctx, limits)
    74  			var delayRw *delayedResponseWriter
    75  			delayCtx := context.Background()
    76  			if delay > 0 {
    77  				var cancel context.CancelFunc
    78  				delayCtx, cancel = context.WithCancel(delayCtx)
    79  				defer cancel()
    80  				ctx = WithDelayCancel(ctx, cancel)
    81  				w, delayRw = wrapResponseWriter(w, start.Add(delay))
    82  
    83  				// only add a span when delay is active
    84  				var sp opentracing.Span
    85  				sp, ctx = opentracing.StartSpanFromContext(ctx, "delayhandler.Handler")
    86  				defer sp.Finish()
    87  			}
    88  
    89  			// now run the chain after me
    90  			h.ServeHTTP(w, r.WithContext(ctx))
    91  
    92  			// if we didn't delay, return immediately
    93  			if delayRw == nil {
    94  				return
    95  			}
    96  
    97  			// if request errored we skip the delay
    98  			if delayRw.requestError {
    99  				return
   100  			}
   101  
   102  			// The delay has been canceled down the chain.
   103  			if delayCtx.Err() != nil {
   104  				return
   105  			}
   106  
   107  			delayLeft := delayRw.end.Sub(timeNow())
   108  			// nothing to do if we're past the end time
   109  			if delayLeft <= 0 {
   110  				return
   111  			}
   112  
   113  			// when headers are not written, we add the delay header
   114  			if !delayRw.statusWritten {
   115  				addDelayHeader(w.Header(), delayLeft)
   116  			}
   117  
   118  			// create a separate span to make the artificial delay clear
   119  			sp, _ := opentracing.StartSpanFromContext(ctx, "delayhandler.Delay")
   120  			sp.SetTag("delayed_by", delayLeft.String())
   121  			defer sp.Finish()
   122  
   123  			// wait for the delay to elapse
   124  			<-timeAfter(delayLeft)
   125  		})
   126  	}
   127  }