func (c Checker) checkDown(resp *http.Response) error {
	var validStatus map[int]bool
	if c.UpStatus > 0 {
		validStatus = map[int]bool{
			c.UpStatus: true,
		}
	} else {
		validStatus = map[int]bool{
			http.StatusOK:                   true,
			http.StatusCreated:              true,
			http.StatusAccepted:             true,
			http.StatusNonAuthoritativeInfo: true,
			http.StatusNoContent:            true,
		}
	}
	if !validStatus[resp.StatusCode] {
		return fmt.Errorf("response status %s", resp.Status)
	}
	if c.MustContain == "" && c.MustNotContain == "" {
		return nil
	}
	bodyBytes, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return fmt.Errorf("reading response body: %w", err)
	}
	body := string(bodyBytes)
	if c.MustContain != "" && !strings.Contains(body, c.MustContain) {
		return fmt.Errorf("response does not contain '%s'", c.MustContain)
	}
	if c.MustNotContain != "" && strings.Contains(body, c.MustNotContain) {
		return fmt.Errorf("response contains '%s'", c.MustNotContain)
	}
	return nil
}
checkDown checks whether the endpoint is down based on resp and
the configuration of c. It returns a non-nil error if down.
Note that it does not check for degraded response.
package http
import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"strings"
	"time"
	"github.com/sourcegraph/checkup/types"
)
const Type = "http"
type Checker struct {
	Name string `json:"endpoint_name"`
	URL string `json:"endpoint_url"`
	UpStatus int `json:"up_status,omitempty"`
	ThresholdRTT time.Duration `json:"threshold_rtt,omitempty"`
	MustContain string `json:"must_contain,omitempty"`
	MustNotContain string `json:"must_not_contain,omitempty"`
	Attempts int `json:"attempts,omitempty"`
	AttemptSpacing time.Duration `json:"attempt_spacing,omitempty"`
	Client *http.Client `json:"-"`
	Headers http.Header `json:"headers,omitempty"`
}
func New(config json.RawMessage) (Checker, error) {
	var checker Checker
	err := json.Unmarshal(config, &checker)
	return checker, err
}
func (Checker) Type() string {
	return Type
}
func (c Checker) Check() (types.Result, error) {
	if c.Attempts < 1 {
		c.Attempts = 1
	}
	if c.Client == nil {
		c.Client = DefaultHTTPClient
	}
	result := types.NewResult()
	result.Title = c.Name
	result.Endpoint = c.URL
	req, err := http.NewRequest("GET", c.URL, nil)
	if err != nil {
		return result, err
	}
	if c.Headers != nil {
		for key, header := range c.Headers {
			req.Header.Add(key, strings.Join(header, ", "))
			if strings.ToLower(key) == "host" {
				req.Host = header[0]
			}
		}
	}
	result.Times = c.doChecks(req)
	return c.conclude(result), nil
}
func (c Checker) doChecks(req *http.Request) types.Attempts {
	checks := make(types.Attempts, c.Attempts)
	for i := 0; i < c.Attempts; i++ {
		start := time.Now()
		resp, err := c.Client.Do(req)
		checks[i].RTT = time.Since(start)
		if err != nil {
			checks[i].Error = err.Error()
			continue
		}
		err = c.checkDown(resp)
		if err != nil {
			checks[i].Error = err.Error()
		}
		resp.Body.Close()
		if c.AttemptSpacing > 0 {
			time.Sleep(c.AttemptSpacing)
		}
	}
	return checks
}
func (c Checker) conclude(result types.Result) types.Result {
	result.ThresholdRTT = c.ThresholdRTT
	for i := range result.Times {
		if result.Times[i].Error != "" {
			result.Down = true
			return result
		}
	}
	if c.ThresholdRTT > 0 {
		stats := result.ComputeStats()
		if stats.Median > c.ThresholdRTT {
			result.Notice = fmt.Sprintf("median round trip time exceeded threshold (%s)", c.ThresholdRTT)
			result.Degraded = true
			return result
		}
	}
	result.Healthy = true
	return result
}