152 lines
3.9 KiB
Go
152 lines
3.9 KiB
Go
package testweb
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"crypto/tls"
|
|
"encoding/json"
|
|
"io"
|
|
"net"
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
const (
|
|
connectDialTimeout = 5 * time.Second
|
|
connectReadTimeout = 15 * time.Second
|
|
replayTimeout = 120 * time.Second
|
|
)
|
|
|
|
func govHTTPClient(timeout time.Duration) *http.Client {
|
|
return &http.Client{
|
|
Timeout: timeout,
|
|
Transport: &http.Transport{
|
|
TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, //nolint:gosec // 政务网自签证书
|
|
DialContext: (&net.Dialer{Timeout: connectDialTimeout}).DialContext,
|
|
ResponseHeaderTimeout: connectReadTimeout,
|
|
},
|
|
}
|
|
}
|
|
|
|
// ConnectResult 政务云连通性探测结果。
|
|
type ConnectResult struct {
|
|
URL string `json:"url"`
|
|
Channel string `json:"channel"`
|
|
ElapsedMs int64 `json:"elapsedMs"`
|
|
HTTPStatus int `json:"httpStatus,omitempty"`
|
|
OK bool `json:"ok"`
|
|
Error string `json:"error,omitempty"`
|
|
Phase string `json:"phase,omitempty"`
|
|
}
|
|
|
|
// TestConnectivity 对 target URL 发起 POST 探测。
|
|
func TestConnectivity(channel, target string) ConnectResult {
|
|
res := ConnectResult{URL: target, Channel: channel}
|
|
start := time.Now()
|
|
defer func() { res.ElapsedMs = time.Since(start).Milliseconds() }()
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), connectReadTimeout+connectDialTimeout)
|
|
defer cancel()
|
|
|
|
req, err := http.NewRequestWithContext(ctx, http.MethodPost, target, strings.NewReader("{}"))
|
|
if err != nil {
|
|
res.Error = err.Error()
|
|
res.Phase = "build_request"
|
|
return res
|
|
}
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
resp, err := govHTTPClient(connectReadTimeout + connectDialTimeout).Do(req)
|
|
if err != nil {
|
|
res.Error = err.Error()
|
|
if strings.Contains(err.Error(), "timeout") || strings.Contains(err.Error(), "deadline") {
|
|
res.Phase = "timeout"
|
|
} else if strings.Contains(err.Error(), "connection refused") {
|
|
res.Phase = "tcp"
|
|
} else if strings.Contains(err.Error(), "tls") || strings.Contains(err.Error(), "certificate") {
|
|
res.Phase = "tls"
|
|
} else {
|
|
res.Phase = "network"
|
|
}
|
|
return res
|
|
}
|
|
defer resp.Body.Close()
|
|
_, _ = io.Copy(io.Discard, resp.Body)
|
|
res.HTTPStatus = resp.StatusCode
|
|
res.OK = true
|
|
res.Phase = "http"
|
|
return res
|
|
}
|
|
|
|
// ReplayResult 回放请求结果。
|
|
type ReplayResult struct {
|
|
URL string `json:"url"`
|
|
ElapsedMs int64 `json:"elapsedMs"`
|
|
HTTPStatus int `json:"httpStatus"`
|
|
Headers map[string]string `json:"headers"`
|
|
Body string `json:"body"`
|
|
BodyJSON string `json:"bodyJSON,omitempty"`
|
|
Error string `json:"error,omitempty"`
|
|
}
|
|
|
|
// ReplayRequest 向政务云回放 POST。
|
|
func ReplayRequest(url string, headers map[string]string, body []byte) ReplayResult {
|
|
res := ReplayResult{URL: url, Headers: make(map[string]string)}
|
|
start := time.Now()
|
|
defer func() { res.ElapsedMs = time.Since(start).Milliseconds() }()
|
|
|
|
var bodyReader io.Reader
|
|
if len(body) > 0 {
|
|
bodyReader = bytes.NewReader(body)
|
|
} else {
|
|
bodyReader = strings.NewReader("")
|
|
}
|
|
req, err := http.NewRequest(http.MethodPost, url, bodyReader)
|
|
if err != nil {
|
|
res.Error = err.Error()
|
|
return res
|
|
}
|
|
for k, v := range headers {
|
|
if strings.EqualFold(k, "X-Forward-Token") {
|
|
continue
|
|
}
|
|
req.Header.Set(k, v)
|
|
}
|
|
|
|
resp, err := govHTTPClient(replayTimeout).Do(req)
|
|
if err != nil {
|
|
res.Error = err.Error()
|
|
return res
|
|
}
|
|
defer resp.Body.Close()
|
|
res.HTTPStatus = resp.StatusCode
|
|
for k, vs := range resp.Header {
|
|
if len(vs) > 0 {
|
|
res.Headers[k] = vs[0]
|
|
}
|
|
}
|
|
raw, _ := io.ReadAll(resp.Body)
|
|
res.Body = string(raw)
|
|
if strings.Contains(strings.ToLower(resp.Header.Get("Content-Type")), "json") {
|
|
res.BodyJSON = formatJSON(raw)
|
|
}
|
|
return res
|
|
}
|
|
|
|
func formatJSON(raw []byte) string {
|
|
s := strings.TrimSpace(string(raw))
|
|
if s == "" {
|
|
return ""
|
|
}
|
|
var v any
|
|
if json.Unmarshal(raw, &v) != nil {
|
|
return s
|
|
}
|
|
b, err := json.MarshalIndent(v, "", " ")
|
|
if err != nil {
|
|
return s
|
|
}
|
|
return string(b)
|
|
}
|