- go-hua/log 已弃用: *log.Logger 参数改为两方法接口 spmux.Logger (*logrus.Logger 天然满足), 传 nil 静默(logErrorf/logDebugf 判空) - 协议识别改用 zogo/httpprobe; 测试与例程同步适配 - 附测试、examples/spmux 例程(含 Logger 适配演示)与包 README
129 lines
2.7 KiB
Go
129 lines
2.7 KiB
Go
package spmux
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"io"
|
|
"net"
|
|
"net/http"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// freePort 找一个空闲端口(有轻微竞态,测试环境可接受)
|
|
func freePort(t *testing.T) int {
|
|
l, err := net.Listen("tcp", "127.0.0.1:0")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
port := l.Addr().(*net.TCPAddr).Port
|
|
l.Close()
|
|
return port
|
|
}
|
|
|
|
func TestPortMuxHttpRouting(t *testing.T) {
|
|
port := freePort(t)
|
|
|
|
pm := NewPortMux(port, nil)
|
|
if err := pm.Start(); err != nil {
|
|
t.Fatalf("Start: %v", err)
|
|
}
|
|
|
|
httpDone := make(chan struct{})
|
|
go func() {
|
|
defer close(httpDone)
|
|
http.Serve(pm.HttpListener(), http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
fmt.Fprint(w, "mux-http-ok")
|
|
}))
|
|
}()
|
|
// 先 Close 解除 Serve 阻塞,再等待协程退出(顺序不能反,否则死锁)
|
|
defer func() {
|
|
pm.Close()
|
|
<-httpDone
|
|
}()
|
|
|
|
time.Sleep(100 * time.Millisecond)
|
|
|
|
resp, err := http.Get(fmt.Sprintf("http://127.0.0.1:%d/", port))
|
|
if err != nil {
|
|
t.Fatalf("HTTP 请求失败: %v", err)
|
|
}
|
|
body, _ := io.ReadAll(resp.Body)
|
|
resp.Body.Close()
|
|
if string(body) != "mux-http-ok" {
|
|
t.Errorf("HTTP 响应 = %q", string(body))
|
|
}
|
|
}
|
|
|
|
func TestPortMuxTcpRouting(t *testing.T) {
|
|
port := freePort(t)
|
|
|
|
pm := NewPortMux(port, nil)
|
|
if err := pm.Start(); err != nil {
|
|
t.Fatalf("Start: %v", err)
|
|
}
|
|
|
|
tcpDone := make(chan struct{})
|
|
go func() {
|
|
defer close(tcpDone)
|
|
for {
|
|
c, err := pm.TcpListener().Accept()
|
|
if err != nil {
|
|
return
|
|
}
|
|
go func(cc net.Conn) {
|
|
defer cc.Close()
|
|
buf := make([]byte, 1024)
|
|
for {
|
|
n, err := cc.Read(buf)
|
|
if n > 0 {
|
|
cc.Write(append(buf[:n:n], '\n'))
|
|
}
|
|
if err != nil {
|
|
return
|
|
}
|
|
}
|
|
}(c)
|
|
}
|
|
}()
|
|
// 先 Close 解除 Accept 阻塞,再等待协程退出(顺序不能反,否则死锁)
|
|
defer func() {
|
|
pm.Close()
|
|
<-tcpDone
|
|
}()
|
|
|
|
time.Sleep(100 * time.Millisecond)
|
|
|
|
// 非 HTTP 数据(超过 8 字节)应路由到 TcpListener
|
|
raw, err := net.Dial("tcp", fmt.Sprintf("127.0.0.1:%d", port))
|
|
if err != nil {
|
|
t.Fatalf("TCP 连接失败: %v", err)
|
|
}
|
|
fmt.Fprint(raw, "ping-ping")
|
|
raw.SetReadDeadline(time.Now().Add(3 * time.Second))
|
|
line, err := bufio.NewReader(raw).ReadString('\n')
|
|
if err != nil {
|
|
t.Fatalf("读取 echo: %v", err)
|
|
}
|
|
if line != "ping-ping\n" {
|
|
t.Errorf("echo = %q", line)
|
|
}
|
|
raw.Close()
|
|
}
|
|
|
|
func TestPortListenerAddr(t *testing.T) {
|
|
port := freePort(t)
|
|
pm := NewPortMux(port, nil)
|
|
if err := pm.Start(); err != nil {
|
|
t.Fatalf("Start: %v", err)
|
|
}
|
|
defer pm.Close()
|
|
|
|
if pm.HttpListener().Addr().String() == "" {
|
|
t.Error("HttpListener Addr 不应为空")
|
|
}
|
|
if pm.TcpListener().Addr().String() == "" {
|
|
t.Error("TcpListener Addr 不应为空")
|
|
}
|
|
}
|