Некоторые проверки не удались
CI / test (push) Successful in 2m5s
Docker / Build and publish worker image (push) Failing after 31s
180 строки
4.1 KiB
Go
180 строки
4.1 KiB
Go
// Package chttp provides HTTP check functionality for RSMon.
|
|
package chttp
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"io"
|
|
"mime/multipart"
|
|
"net"
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
"rocketgit.ru/rsmon/worker/app/models"
|
|
)
|
|
|
|
const (
|
|
stateERR = "ERR"
|
|
stateFAIL = "FAIL"
|
|
)
|
|
|
|
// UserAgent is the HTTP User-Agent header sent with all HTTP checks.
|
|
const UserAgent = "Mozilla/5.0 (compatible; RSMon/1.0; +https://rsmon.ru/bot)"
|
|
|
|
func ipv6(ip []string) []string {
|
|
var ips []string
|
|
for _, a := range ip {
|
|
ip := net.ParseIP(a)
|
|
check := ip.To4()
|
|
if check == nil {
|
|
ips = append(ips, a)
|
|
}
|
|
}
|
|
return ips
|
|
}
|
|
|
|
// Perform executes an HTTP check.
|
|
func Perform(c *models.Check) *Result {
|
|
result := &Result{}
|
|
result.State = "START"
|
|
if c.URL == nil {
|
|
result.State = stateFAIL
|
|
result.Error = errors.New("no url or bad url")
|
|
return result
|
|
}
|
|
|
|
var requestBody io.Reader
|
|
|
|
settings := c.GetSettings()
|
|
var err error
|
|
var to time.Duration
|
|
var slow time.Duration
|
|
|
|
if settings.Timeout > 0 && settings.Timeout < 300000 {
|
|
to = time.Millisecond * time.Duration(settings.Timeout)
|
|
} else {
|
|
to = time.Second * 60
|
|
}
|
|
|
|
if settings.SlowTime > 0 {
|
|
slow = time.Millisecond * time.Duration(settings.SlowTime)
|
|
} else {
|
|
slow = time.Second * 5
|
|
}
|
|
|
|
client := &http.Client{
|
|
Timeout: to,
|
|
CheckRedirect: func(_ *http.Request, _ []*http.Request) error {
|
|
return http.ErrUseLastResponse
|
|
},
|
|
}
|
|
if settings.CheckIp {
|
|
addr, err := net.LookupHost(*c.URL)
|
|
if err != nil {
|
|
result.Status = stateFAIL
|
|
result.Error = err
|
|
}
|
|
if settings.CheckIPv6 {
|
|
addr = ipv6(addr)
|
|
}
|
|
for _, a := range addr {
|
|
_, err := net.Dial("tcp", net.JoinHostPort(a, "80"))
|
|
if err != nil {
|
|
result.Error = err
|
|
break
|
|
}
|
|
result.State = "OK"
|
|
}
|
|
}
|
|
switch settings.RequestType {
|
|
case "application/json":
|
|
body, _ := json.Marshal(settings.RequestContent)
|
|
requestBody = bytes.NewReader(body)
|
|
case "application/x-www-form-urlencoded":
|
|
requestBody = strings.NewReader(settings.RequestContent)
|
|
case "multipart/form-data":
|
|
buf := bytes.NewBuffer([]byte{})
|
|
writer := multipart.NewWriter(buf)
|
|
_, _ = writer.CreateFormField(settings.RequestContent)
|
|
_ = writer.Close()
|
|
requestBody = bytes.NewReader(buf.Bytes())
|
|
settings.RequestType = writer.FormDataContentType()
|
|
case "text/plain":
|
|
requestBody = bytes.NewBufferString(settings.RequestContent)
|
|
}
|
|
if settings.RequestMethod == "" {
|
|
settings.RequestMethod = "GET"
|
|
}
|
|
request, err := http.NewRequest(strings.ToUpper(settings.RequestMethod), *c.URL, requestBody)
|
|
if err != nil {
|
|
result.State = stateFAIL
|
|
result.Error = err
|
|
return result
|
|
}
|
|
|
|
if settings.HTTPUsername != "" && settings.HTTPPassword != "" {
|
|
request.SetBasicAuth(settings.HTTPUsername, settings.HTTPPassword)
|
|
}
|
|
|
|
request.Header.Set("User-Agent", UserAgent)
|
|
request.Header.Set("Cache-Control", "max-age=0")
|
|
request.Header.Set("Connection", "close")
|
|
if settings.RequestType != "" {
|
|
request.Header.Set("Content-Type", settings.RequestType)
|
|
}
|
|
if len(settings.RequestHeader) != 0 {
|
|
for _, h := range settings.RequestHeader {
|
|
request.Header.Set(h.Key, h.Value)
|
|
}
|
|
}
|
|
start := time.Now()
|
|
resp, err := client.Do(request)
|
|
if err != nil {
|
|
result.State = stateERR
|
|
result.Error = errors.Wrap(err, "request exec")
|
|
return result
|
|
}
|
|
defer resp.Body.Close() //nolint:errcheck // accepted lint exception
|
|
|
|
result.Status = resp.Status
|
|
result.StatusCode = resp.StatusCode
|
|
result.Headers = resp.Header
|
|
|
|
body, err := io.ReadAll(resp.Body)
|
|
result.Duration = time.Since(start)
|
|
|
|
if err != nil {
|
|
result.State = stateERR
|
|
result.Error = errors.Wrap(err, "read body")
|
|
return result
|
|
}
|
|
result.Length = len(body)
|
|
result.Body = body
|
|
|
|
warns, err := settings.CheckAnswer(resp, body)
|
|
|
|
for _, w := range warns {
|
|
result.State = "WARN"
|
|
result.Warnings = append(result.Warnings, w)
|
|
}
|
|
if err != nil {
|
|
result.State = stateERR
|
|
result.Error = errors.Wrap(err, "response check")
|
|
}
|
|
|
|
if result.Error == nil {
|
|
if result.Duration > slow {
|
|
result.State = "WARN"
|
|
result.Warnings = append(result.Warnings, "slow")
|
|
} else if result.State == "START" {
|
|
result.State = "OK"
|
|
}
|
|
} else {
|
|
result.State = stateERR
|
|
}
|
|
return result
|
|
}
|