Example #1
0
//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)
}
Example #2
0
//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())
		}
	}
}
Example #3
0
//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)
}