Example #1
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())
		}
	}
}
	c := make(chan string)

	thing1Handler, ok := m["thing1"]
	if !ok {
		http.Error(w, "No backend named thing1 in context", http.StatusInternalServerError)
		return
	}

	thing2Handler, ok := m["thing2"]
	if !ok {
		http.Error(w, "No backend named thing2 in context", http.StatusInternalServerError)
		return
	}

	end2endTimer := timing.TimerFromContext(ctx)
	cont := end2endTimer.StartContributor("backend stuff")
	go func() { c <- callThingBackend("thing one", thing1Handler, r) }()
	go func() { c <- callThingBackend("thing two", thing2Handler, r) }()

	var results []string
	timeout := time.After(150 * time.Millisecond)
	for i := 0; i < 2; i++ {
		select {
		case result := <-c:
			results = append(results, result)
			cont.End(nil)
		case <-timeout:
			cont.End(errors.New("timeout error"))
			http.Error(w, "Timeout", http.StatusInternalServerError)
			return