| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111 |
- package network
- import (
- "errors"
- "go/ast"
- "go/parser"
- "go/token"
- "net"
- "net/http"
- "os"
- "path/filepath"
- "runtime"
- "strings"
- "testing"
- "github.com/mhsanaei/3x-ui/v3/internal/logger"
- )
- type failingListener struct{ err error }
- func (l failingListener) Accept() (net.Conn, error) { return nil, l.err }
- func (failingListener) Close() error { return nil }
- func (failingListener) Addr() net.Addr { return testAddr("failing") }
- type testAddr string
- func (a testAddr) Network() string { return string(a) }
- func (a testAddr) String() string { return string(a) }
- func TestServeHTTPLogsUnexpectedListenerFailure(t *testing.T) {
- errInjected := errors.New("injected listener failure")
- ServeHTTP(&http.Server{}, failingListener{err: errInjected}, "Test server")
- for _, line := range logger.GetLogs(100, "error") {
- if strings.Contains(line, errInjected.Error()) {
- return
- }
- }
- t.Fatal("unexpected listener failure was not recorded in the panel log")
- }
- func TestServeHTTPSuppressesNormalServerClose(t *testing.T) {
- const marker = "normal-close-must-stay-silent"
- ServeHTTP(&http.Server{}, failingListener{err: http.ErrServerClosed}, marker)
- for _, line := range logger.GetLogs(100, "error") {
- if strings.Contains(line, marker) {
- t.Fatalf("normal http.ErrServerClosed was recorded as an error: %s", line)
- }
- }
- }
- func TestProductionHTTPServersUseServeHTTPWrapper(t *testing.T) {
- _, currentFile, _, ok := runtime.Caller(0)
- if !ok {
- t.Fatal("locate test source")
- }
- repoRoot := filepath.Clean(filepath.Join(filepath.Dir(currentFile), "../../.."))
- fset := token.NewFileSet()
- err := filepath.WalkDir(repoRoot, func(path string, entry os.DirEntry, walkErr error) error {
- if walkErr != nil {
- return walkErr
- }
- if entry.IsDir() {
- if entry.Name() == ".git" || entry.Name() == "vendor" || entry.Name() == "node_modules" {
- return filepath.SkipDir
- }
- return nil
- }
- if !strings.HasSuffix(path, ".go") || strings.HasSuffix(path, "_test.go") || path == currentFile || path == filepath.Join(filepath.Dir(currentFile), "serve.go") {
- return nil
- }
- parsed, err := parser.ParseFile(fset, path, nil, parser.ImportsOnly)
- if err != nil {
- return err
- }
- usesHTTP := false
- for _, imp := range parsed.Imports {
- if imp.Path.Value == `"net/http"` {
- usesHTTP = true
- break
- }
- }
- if !usesHTTP {
- return nil
- }
- parsed, err = parser.ParseFile(fset, path, nil, 0)
- if err != nil {
- return err
- }
- ast.Inspect(parsed, func(node ast.Node) bool {
- call, ok := node.(*ast.CallExpr)
- if !ok {
- return true
- }
- selector, ok := call.Fun.(*ast.SelectorExpr)
- if ok && selector.Sel.Name == "Serve" {
- position := fset.Position(call.Pos())
- t.Errorf("direct Serve call at %s; production HTTP servers must use network.ServeHTTP", position)
- }
- return true
- })
- return nil
- })
- if err != nil {
- t.Fatalf("scan production Go files: %v", err)
- }
- }
|