func (rh *ResponseHandler) Start() {
	log.Debug().Msg("ResponseHandler starting")
	epRunSummary := make(map[string]*api.EndpointDetail)
	runSummary := api.RunSummary{RqstStats: api.RqstStats{MaxRqstDurationNanos: time.Duration(-1), MinRqstDurationNanos: time.Duration(math.MaxInt64)}}
	runResults := api.RunResults{RunSummary: runSummary}
	runResults.EndpointSummary = make(map[string]map[string]int)
	start := time.Now()
	var totalRunTime time.Duration
	responses := make([]Response, 0, 10)
	for {
		select {
		case resp, ok := <-rh.ResponseC:
			if !ok {
				defer close(rh.DoneC)
				log.Debug().Msg("ResponseHandler: Summarizing results and exiting")
				for _, r := range responses {
					rh.accumulateResponseStats(r, &totalRunTime, &runResults, epRunSummary)
					runResults.RunSummary.DNSLookupNanos = append(runResults.RunSummary.DNSLookupNanos, r.DNSLookupDuration)
					runResults.RunSummary.TCPConnSetupNanos = append(runResults.RunSummary.TCPConnSetupNanos, r.TCPConnDuration)
					runResults.RunSummary.RqstRoundTripNanos = append(runResults.RunSummary.RqstRoundTripNanos, r.RoundTripDuration)
					runResults.RunSummary.TLSHandshakeNanos = append(runResults.RunSummary.TLSHandshakeNanos, r.TLSHandshakeDuration)
				}
				err := rh.finalizeResponseStats(start, &totalRunTime, &runResults, epRunSummary)
				if err != nil {
					log.Error().Err(err)
					return
				}
				if rh.OutputType == Text {
					fmt.Println("")
					printRunSummary(runResults.RunSummary)
					fmt.Println("")
					printRqstLatency(runResults.RunSummary.RqstStats)
					min, max := rh.generateHistogram(&runResults)
					fmt.Printf("\nRequest Latency Histogram (secs):\n")
					fmt.Println(rh.generateHistogramString(min, max))
					fmt.Println("")
					printEndpointDetails(runResults.EndpointDetails)
					fmt.Println("")
					printNetworkDetails(runResults.RunSummary)
					return
				}
				rsjson, err := json.MarshalIndent(runResults, "    ", "  ")
				if err != nil {
					log.Error().Err(err).Msgf("error marshaling RunSummary into string: %+v.\n", runResults)
					return
				}
				fmt.Printf("%s\n", string(rsjson[2:len(rsjson)-1]))
				return
			}
			responses = append(responses, resp)
			if rh.NumRqsts > 0 {
				rh.ProgressC <- struct{}{}
			}
		}
	}
}
Start begins the process of accepting responses. It expects to be run as a goroutine.
package internal
import (
	"encoding/json"
	"fmt"
	"math"
	"sort"
	"strings"
	"time"
	"github.com/rs/zerolog/log"
	"github.com/youngkin/heyyall/api"
)
type Response struct {
	HTTPStatus           int
	Endpoint             api.Endpoint
	RequestDuration      time.Duration
	DNSLookupDuration    time.Duration
	TCPConnDuration      time.Duration
	RoundTripDuration    time.Duration
	TLSHandshakeDuration time.Duration
}
type ResponseHandler struct {
	OutputType OutputType
	ResponseC  chan Response
	ProgressC  chan interface{}
	DoneC      chan interface{}
	NumRqsts   int
	NormFactor int
	histogram map[float64]int
}