2020-11-20 18:11:25 +08:00
|
|
|
package rule
|
|
|
|
|
|
|
|
import (
|
2021-12-22 21:20:29 +08:00
|
|
|
"crypto/tls"
|
2020-11-26 19:21:27 +08:00
|
|
|
"errors"
|
|
|
|
"fmt"
|
2020-11-20 18:11:25 +08:00
|
|
|
"io"
|
2021-12-22 21:20:29 +08:00
|
|
|
"net"
|
2020-11-21 01:20:40 +08:00
|
|
|
"os"
|
|
|
|
"os/exec"
|
2021-12-23 00:17:28 +08:00
|
|
|
"regexp"
|
2021-08-04 19:13:22 +08:00
|
|
|
"strings"
|
2020-11-20 18:11:25 +08:00
|
|
|
"time"
|
|
|
|
|
2022-01-08 15:05:55 +08:00
|
|
|
"github.com/nadoo/glider/pkg/pool"
|
2020-11-20 18:11:25 +08:00
|
|
|
)
|
|
|
|
|
|
|
|
// Checker is a forwarder health checker.
|
|
|
|
type Checker interface {
|
2022-01-27 12:42:49 +08:00
|
|
|
Check(fwdr *Forwarder) (elap time.Duration, err error)
|
2020-11-20 18:11:25 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
type tcpChecker struct {
|
|
|
|
addr string
|
|
|
|
timeout time.Duration
|
|
|
|
}
|
|
|
|
|
|
|
|
func newTcpChecker(addr string, timeout time.Duration) *tcpChecker {
|
2021-12-22 21:20:29 +08:00
|
|
|
if _, port, _ := net.SplitHostPort(addr); port == "" {
|
|
|
|
addr = net.JoinHostPort(addr, "80")
|
|
|
|
}
|
2020-11-20 18:11:25 +08:00
|
|
|
return &tcpChecker{addr, timeout}
|
|
|
|
}
|
|
|
|
|
2020-11-26 19:21:27 +08:00
|
|
|
// Check implements the Checker interface.
|
2022-01-27 12:42:49 +08:00
|
|
|
func (c *tcpChecker) Check(fwdr *Forwarder) (time.Duration, error) {
|
2020-11-20 18:11:25 +08:00
|
|
|
startTime := time.Now()
|
2022-01-27 12:42:49 +08:00
|
|
|
rc, err := fwdr.Dial("tcp", c.addr)
|
2020-11-20 18:11:25 +08:00
|
|
|
if err != nil {
|
2020-11-26 19:21:27 +08:00
|
|
|
return 0, err
|
2020-11-20 18:11:25 +08:00
|
|
|
}
|
2021-12-22 21:20:29 +08:00
|
|
|
rc.Close()
|
2020-11-26 19:21:27 +08:00
|
|
|
return time.Since(startTime), nil
|
2020-11-20 18:11:25 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
type httpChecker struct {
|
2021-12-23 00:17:28 +08:00
|
|
|
addr string
|
|
|
|
uri string
|
|
|
|
expect string
|
|
|
|
timeout time.Duration
|
|
|
|
|
2021-12-22 21:20:29 +08:00
|
|
|
tlsConfig *tls.Config
|
|
|
|
serverName string
|
2021-12-23 00:17:28 +08:00
|
|
|
|
|
|
|
regex *regexp.Regexp
|
2020-11-20 18:11:25 +08:00
|
|
|
}
|
|
|
|
|
2021-12-22 21:20:29 +08:00
|
|
|
func newHttpChecker(addr, uri, expect string, timeout time.Duration, withTLS bool) *httpChecker {
|
2021-12-23 00:17:28 +08:00
|
|
|
c := &httpChecker{
|
|
|
|
addr: addr,
|
|
|
|
uri: uri,
|
|
|
|
expect: expect,
|
|
|
|
timeout: timeout,
|
|
|
|
regex: regexp.MustCompile(expect),
|
|
|
|
}
|
|
|
|
|
2021-12-22 21:20:29 +08:00
|
|
|
if _, p, _ := net.SplitHostPort(addr); p == "" {
|
|
|
|
if withTLS {
|
|
|
|
c.addr = net.JoinHostPort(addr, "443")
|
|
|
|
} else {
|
|
|
|
c.addr = net.JoinHostPort(addr, "80")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
c.serverName = c.addr[:strings.LastIndex(c.addr, ":")]
|
|
|
|
if withTLS {
|
|
|
|
c.tlsConfig = &tls.Config{ServerName: c.serverName}
|
|
|
|
}
|
|
|
|
return c
|
2020-11-20 18:11:25 +08:00
|
|
|
}
|
|
|
|
|
2020-11-26 19:21:27 +08:00
|
|
|
// Check implements the Checker interface.
|
2022-01-27 12:42:49 +08:00
|
|
|
func (c *httpChecker) Check(fwdr *Forwarder) (time.Duration, error) {
|
2020-11-20 18:11:25 +08:00
|
|
|
startTime := time.Now()
|
2022-01-27 12:42:49 +08:00
|
|
|
rc, err := fwdr.Dial("tcp", c.addr)
|
2020-11-20 18:11:25 +08:00
|
|
|
if err != nil {
|
2020-11-26 19:21:27 +08:00
|
|
|
return 0, err
|
2020-11-20 18:11:25 +08:00
|
|
|
}
|
2021-12-22 21:20:29 +08:00
|
|
|
|
|
|
|
if c.tlsConfig != nil {
|
|
|
|
tlsConn := tls.Client(rc, c.tlsConfig)
|
|
|
|
if err := tlsConn.Handshake(); err != nil {
|
|
|
|
tlsConn.Close()
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
rc = tlsConn
|
|
|
|
}
|
2020-11-20 18:11:25 +08:00
|
|
|
defer rc.Close()
|
|
|
|
|
|
|
|
if c.timeout > 0 {
|
|
|
|
rc.SetDeadline(time.Now().Add(c.timeout))
|
|
|
|
}
|
|
|
|
|
2020-11-26 19:21:27 +08:00
|
|
|
if _, err = io.WriteString(rc,
|
2021-12-22 21:20:29 +08:00
|
|
|
"GET "+c.uri+" HTTP/1.1\r\nHost:"+c.serverName+"\r\n\r\n"); err != nil {
|
2020-11-26 19:21:27 +08:00
|
|
|
return 0, err
|
2020-11-20 18:11:25 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
r := pool.GetBufReader(rc)
|
|
|
|
defer pool.PutBufReader(r)
|
|
|
|
|
2021-08-04 19:13:22 +08:00
|
|
|
line, err := r.ReadString('\n')
|
2020-11-20 18:11:25 +08:00
|
|
|
if err != nil {
|
2020-11-26 19:21:27 +08:00
|
|
|
return 0, err
|
2020-11-20 18:11:25 +08:00
|
|
|
}
|
|
|
|
|
2021-12-23 00:17:28 +08:00
|
|
|
if !c.regex.MatchString(line) {
|
2020-11-26 19:21:27 +08:00
|
|
|
return 0, fmt.Errorf("expect: %s, got: %s", c.expect, line)
|
2020-11-20 18:11:25 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
elapsed := time.Since(startTime)
|
|
|
|
if elapsed > c.timeout {
|
2020-11-26 19:21:27 +08:00
|
|
|
return elapsed, errors.New("timeout")
|
2020-11-20 18:11:25 +08:00
|
|
|
}
|
|
|
|
|
2020-11-26 19:21:27 +08:00
|
|
|
return elapsed, nil
|
2020-11-20 18:11:25 +08:00
|
|
|
}
|
2020-11-21 01:20:40 +08:00
|
|
|
|
2020-11-26 19:21:27 +08:00
|
|
|
type fileChecker struct{ path string }
|
2020-11-21 01:20:40 +08:00
|
|
|
|
2020-11-26 19:21:27 +08:00
|
|
|
func newFileChecker(path string) *fileChecker { return &fileChecker{path} }
|
2020-11-21 01:20:40 +08:00
|
|
|
|
2020-11-26 19:21:27 +08:00
|
|
|
// Check implements the Checker interface.
|
2022-01-27 12:42:49 +08:00
|
|
|
func (c *fileChecker) Check(fwdr *Forwarder) (time.Duration, error) {
|
2020-11-21 01:20:40 +08:00
|
|
|
cmd := exec.Command(c.path)
|
|
|
|
cmd.Stdout = os.Stdout
|
|
|
|
cmd.Env = os.Environ()
|
2022-01-27 12:42:49 +08:00
|
|
|
cmd.Env = append(cmd.Env, "FORWARDER_ADDR="+fwdr.Addr())
|
|
|
|
cmd.Env = append(cmd.Env, "FORWARDER_URL="+fwdr.URL())
|
2020-11-26 19:21:27 +08:00
|
|
|
return 0, cmd.Run()
|
2020-11-21 01:20:40 +08:00
|
|
|
}
|