fix: sync MTProto startup and egress fixes

This commit is contained in:
A 2026-07-13 19:05:41 +08:00
parent 50803a604c
commit 305e8a0008
24 changed files with 2880 additions and 317 deletions

View file

@ -34,6 +34,7 @@ type samePortMux struct {
base net.Listener
addr net.Addr
sniffTimeout time.Duration
observe func(connectionIntakeEvent)
tcp *samePortMuxListener
http *samePortMuxListener
@ -49,7 +50,7 @@ type samePortMux struct {
sniffing map[net.Conn]struct{}
}
func newSamePortMux(base net.Listener, sniffTimeout time.Duration) *samePortMux {
func newSamePortMux(base net.Listener, sniffTimeout time.Duration, observers ...func(connectionIntakeEvent)) *samePortMux {
if sniffTimeout <= 0 {
sniffTimeout = 5 * time.Second
}
@ -60,6 +61,9 @@ func newSamePortMux(base net.Listener, sniffTimeout time.Duration) *samePortMux
closed: make(chan struct{}),
sniffing: make(map[net.Conn]struct{}),
}
if len(observers) > 0 {
m.observe = observers[0]
}
m.tcp = newSamePortMuxListener(m.addr, m.closed)
m.http = newSamePortMuxListener(m.addr, m.closed)
return m
@ -144,10 +148,13 @@ func (m *samePortMux) Close() error {
// dispatch 窥探单条连接的前 4 字节并把它交给 tcp 或 http 子 listener。窥探带 sniffTimeout
// 读上界,慢/半开连接最多占用本 goroutine sniffTimeout 后即被回收。
func (m *samePortMux) dispatch(ctx context.Context, conn net.Conn) {
started := time.Now()
remote, local := connRemote(conn), connLocal(conn)
// SetReadDeadline bounds an otherwise healthy slow-loris connection, but Close only owns
// the base listener, not sockets Accept has already returned. Register temporary ownership so
// mux shutdown can close this read immediately. finishSniff removes the socket before hand-off.
if !m.beginSniff(conn) {
m.observeEvent(connectionIntakeEvent{stage: "mux_sniff", outcome: "closed", remote: remote, local: local, duration: time.Since(started)})
_ = conn.Close()
return
}
@ -160,10 +167,16 @@ func (m *samePortMux) dispatch(ctx context.Context, conn net.Conn) {
var header [4]byte
if err := conn.SetReadDeadline(time.Now().Add(m.sniffTimeout)); err != nil {
m.observeEvent(connectionIntakeEvent{stage: "mux_sniff", outcome: "error", remote: remote, local: local, duration: time.Since(started), err: err})
_ = conn.Close()
return
}
if _, err := io.ReadFull(conn, header[:]); err != nil {
outcome := "error"
if isClientDisconnect(err) {
outcome = "client_disconnect"
}
m.observeEvent(connectionIntakeEvent{stage: "mux_sniff", outcome: outcome, remote: remote, local: local, duration: time.Since(started), err: err})
_ = conn.Close()
return
}
@ -171,11 +184,13 @@ func (m *samePortMux) dispatch(ctx context.Context, conn net.Conn) {
// registry entry under sniffMu is the hand-off barrier: Close either captured and closed this
// socket, or it can no longer find it. A concurrently closed mux refuses delivery.
if !m.finishSniff(conn) {
m.observeEvent(connectionIntakeEvent{stage: "mux_sniff", outcome: "closed", remote: remote, local: local, duration: time.Since(started)})
_ = conn.Close()
return
}
finishedSniff = true
if err := conn.SetReadDeadline(time.Time{}); err != nil {
m.observeEvent(connectionIntakeEvent{stage: "mux_sniff", outcome: "error", remote: remote, local: local, duration: time.Since(started), err: err})
_ = conn.Close()
return
}
@ -186,14 +201,24 @@ func (m *samePortMux) dispatch(ctx context.Context, conn net.Conn) {
}
target := m.tcp
transport := "tcp"
if isHTTPHeaderPrefix(header) {
target = m.http
transport = "websocket"
}
m.observeEvent(connectionIntakeEvent{stage: "mux_sniff", outcome: "ready", transport: transport, remote: remote, local: local, duration: time.Since(started), bytes: len(header)})
if !target.deliver(ctx, wrapped) {
m.observeEvent(connectionIntakeEvent{stage: "mux_delivery", outcome: "closed", transport: transport, remote: remote, local: local, duration: time.Since(started)})
_ = conn.Close()
}
}
func (m *samePortMux) observeEvent(event connectionIntakeEvent) {
if m.observe != nil {
m.observe(event)
}
}
func (m *samePortMux) beginSniff(conn net.Conn) bool {
m.sniffMu.Lock()
defer m.sniffMu.Unlock()