//NewContextWithNamedTimer adds a new timer to the request context func NewContextWithNamedTimer(ctx context.Context, name string) context.Context { timer := timer.NewEndToEndTimer(name) return context.WithValue(ctx, timerKey, timer) }
//Create a handler function from a requestHandler func (rh *requestHandler) toHandlerFunc() func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) { ctx := r.Context() //Record call time contribution var timerFromContext = true rt := timing.TimerFromContext(ctx) if rt == nil { timerFromContext = false log.Warn("No EndToEndTimer found in call context - fake it") rt = timer.NewEndToEndTimer("context-missing-timer") } timingContributor := rt.StartContributor(backendName(rh.Backend.Name)) connectString, err := rh.Backend.getConnectAddress() if err != nil { http.Error(w, err.Error(), http.StatusServiceUnavailable) timingContributor.End(err) if timerFromContext == false { fmt.Fprintln(os.Stderr, rt.ToJSONString()) } return } log.Debug("connect string for ", rh.Backend.Name, "is ", connectString) r.URL.Host = connectString r.Host = connectString log.Debug("invoke backend service") serviceName := timing.GetServiceNameFromContext(ctx) if serviceName == "" { serviceName = "backend-call" } r.URL.Scheme = "http" log.Debug("Determine transport") var transport = rh.getTransportForBackend(ctx) if transport == rh.TLSTransport { log.Debug("https transport") r.URL.Scheme = "https" } log.Debug(r.URL.Scheme, " transport for backend ", rh.Backend.Name) beTimer := timingContributor.StartServiceCall(serviceName, connectString) log.Debug("call service ", serviceName, " for backend ", rh.Backend.Name) //resp, err := transport.RoundTrip(r) client := &http.Client{ Transport: transport, } r.RequestURI = "" //Must clear when using http.Client resp, err := ctxhttp.Do(ctx, client, r) beTimer.End(err) if err != nil { go incrementErrorCounts(err) log.Info(err.Error()) switch err { case context.Canceled: w.WriteHeader(http.StatusInternalServerError) case context.DeadlineExceeded: w.WriteHeader(http.StatusGatewayTimeout) default: w.WriteHeader(http.StatusServiceUnavailable) } fmt.Fprintf(w, "Error: %v", err) timingContributor.End(err) if timerFromContext == false { fmt.Fprintln(os.Stderr, rt.ToJSONString()) } return } log.Debug("backend service complete, copy backend response headers to response") for k, v := range resp.Header { for _, vv := range v { w.Header().Add(k, vv) } } log.Debug("write status code to response") w.WriteHeader(resp.StatusCode) log.Debug("Copy body to response") io.Copy(w, resp.Body) resp.Body.Close() timingContributor.End(nil) if timerFromContext == false { fmt.Fprintln(os.Stderr, rt.ToJSONString()) } } }
//NewContextWithTimer adds a new timer to the request context func NewContextWithTimer(ctx context.Context) context.Context { timer := timer.NewEndToEndTimer("unspecified timer") return context.WithValue(ctx, timerKey, timer) }