Files
worker/checks/chttp/http.go
Gleb Tv 2c884c5612
Некоторые проверки не удались
CI / test (push) Successful in 2m5s
Docker / Build and publish worker image (push) Failing after 31s
refactor: adopt worker module path
2026-07-13 17:56:12 +03:00

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
}