156 строки
4.3 KiB
Go
156 строки
4.3 KiB
Go
package models
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"net/http"
|
|
"strings"
|
|
)
|
|
|
|
// CheckHeader provides functionality.
|
|
type CheckHeader struct {
|
|
Key string `json:"key"`
|
|
Value string `json:"value"`
|
|
}
|
|
|
|
// CheckSettings provides functionality.
|
|
type CheckSettings struct {
|
|
// HTTP Basic Auth Username
|
|
HTTPUsername string `json:"http_username,omitempty"`
|
|
// HTTP Basic Auth Password
|
|
HTTPPassword string `json:"http_password,omitempty"`
|
|
|
|
// ExpectedAnswer (default, redirect, custom)
|
|
ExpectedAnswer string `json:"expected_answer,omitempty"`
|
|
// Expected redirect location
|
|
ExpectedLocation string `json:"expected_location,omitempty"`
|
|
// Expected HTTP status codes for custom
|
|
ExpectedHTTPCode int `json:"expected_http_code,omitempty"`
|
|
|
|
// Keyword search type (off, present or absent)
|
|
KeywordType string `json:"keyword_type,omitempty"`
|
|
// Keyword to search for
|
|
KeywordValue string `json:"keyword_value,omitempty"`
|
|
|
|
SlowTime int `json:"slow_time"`
|
|
Timeout int `json:"timeout"`
|
|
// Request Settings
|
|
CheckIp bool `json:"checkip"` //nolint:revive // accepted lint exception
|
|
CheckIPv6 bool `json:"checkipv6"`
|
|
RequestHeader []CheckHeader `json:"request_headers"`
|
|
RequestMethod string `json:"request_method"`
|
|
RequestType string `json:"request_type"`
|
|
RequestContent string `json:"request_content"`
|
|
// SSH Settings
|
|
Port string `json:"port"`
|
|
|
|
// Host optionally overrides the monitor host for checks that do
|
|
// not naturally target the Monitor.Host (e.g. an ICMP/TCP/UDP
|
|
// probe to a separate machine, or a different IP family). Used by
|
|
// cping/ctcp/cudp.
|
|
Host string `json:"host,omitempty"`
|
|
|
|
// Count is the per-check packet count for ping. Defaults to 1 in
|
|
// cping when zero or negative.
|
|
Count int `json:"count,omitempty"`
|
|
|
|
// PacketSize is the ICMP payload size for ping (bytes). Defaults
|
|
// to 56 in cping when zero or negative.
|
|
PacketSize int `json:"packet_size,omitempty"`
|
|
|
|
// Distributed marks this check as eligible for execution on the
|
|
// distributed worker pool (multi-region, multi-worker) instead of
|
|
// only the in-process scheduler. Reserved for paid plans; the
|
|
// controller layer enforces the plan check.
|
|
Distributed bool `json:"distributed"`
|
|
}
|
|
|
|
// redirectCodes contains all HTTP redirect status codes.
|
|
var redirectCodes = []int{300, 301, 302, 303, 307, 308}
|
|
|
|
// CheckKeyword provides functionality.
|
|
func (s *CheckSettings) CheckKeyword(body []byte, warns []string) ([]string, error) {
|
|
var err error
|
|
|
|
switch s.KeywordType {
|
|
case "", "off":
|
|
return warns, nil
|
|
case "present":
|
|
if !strings.Contains(string(body), s.KeywordValue) {
|
|
err = errors.New("expected keyword " + s.KeywordValue + " not found")
|
|
}
|
|
case "absent":
|
|
if strings.Contains(string(body), s.KeywordValue) {
|
|
err = errors.New("unexpected keyword " + s.KeywordValue + " found")
|
|
}
|
|
}
|
|
|
|
return warns, err
|
|
}
|
|
|
|
// CheckAnswer provides functionality.
|
|
func (s *CheckSettings) CheckAnswer(resp *http.Response, body []byte) ([]string, error) {
|
|
// log.Println("check answer, settings:")
|
|
// spew.Dump(s)
|
|
|
|
isRedirect := false
|
|
|
|
for _, rc := range redirectCodes {
|
|
if resp.StatusCode == rc {
|
|
isRedirect = true
|
|
}
|
|
}
|
|
|
|
location := resp.Header.Get("location")
|
|
|
|
// log.Println("redirect?", isRedirect, location)
|
|
|
|
switch s.ExpectedAnswer {
|
|
case "", "default":
|
|
if isRedirect {
|
|
return s.CheckKeyword(body, []string{"redirect: " + location})
|
|
} else if resp.StatusCode != 200 {
|
|
return []string{}, fmt.Errorf("bad status code: %d", resp.StatusCode)
|
|
}
|
|
|
|
case "redirect":
|
|
if !isRedirect {
|
|
return []string{}, fmt.Errorf("bad status code: %d (expected redirect)", resp.StatusCode)
|
|
}
|
|
|
|
if s.ExpectedLocation != "" {
|
|
if location != s.ExpectedLocation {
|
|
return []string{}, fmt.Errorf(
|
|
"bad location: %s (expected %s)",
|
|
location,
|
|
s.ExpectedLocation,
|
|
)
|
|
}
|
|
}
|
|
|
|
case "custom":
|
|
if s.ExpectedHTTPCode == 0 {
|
|
s.ExpectedHTTPCode = 200
|
|
}
|
|
if resp.StatusCode != s.ExpectedHTTPCode {
|
|
return []string{}, fmt.Errorf("bad status code: %d (expected %d)", resp.StatusCode, s.ExpectedHTTPCode)
|
|
}
|
|
|
|
if s.ExpectedLocation != "" {
|
|
if location != s.ExpectedLocation {
|
|
return []string{}, fmt.Errorf(
|
|
"bad location: %s (expected %s)",
|
|
location,
|
|
s.ExpectedLocation,
|
|
)
|
|
}
|
|
}
|
|
|
|
default:
|
|
panic("bad expectedAnswer")
|
|
}
|
|
|
|
// return []string{}, nil
|
|
return s.CheckKeyword(body, []string{})
|
|
}
|