check / check (push) Waiting to run
acceptLoop called newConn, which did the reverse DNS lookup, with its three-second timeout, before the loop could accept the next connection, so one client whose address had no answering reverse DNS held up every other IRC connect. serve now does the lookup at its start, on the connection's own goroutine, and the accept loop only accepts. The server keeps the lookup function in a field, as it does the ping times, so that a test can give it one that blocks. The new test checks that a second client registers while the first one's lookup has not returned. Model: opus-5-5
114 lines
2.8 KiB
Go
114 lines
2.8 KiB
Go
package ircserver
|
|
|
|
import (
|
|
"context"
|
|
"log/slog"
|
|
"net"
|
|
"time"
|
|
|
|
"sneak.berlin/go/neoirc/internal/broker"
|
|
"sneak.berlin/go/neoirc/internal/config"
|
|
"sneak.berlin/go/neoirc/internal/db"
|
|
"sneak.berlin/go/neoirc/internal/globals"
|
|
"sneak.berlin/go/neoirc/internal/service"
|
|
)
|
|
|
|
// NewTestServer creates a Server suitable for testing, whose
|
|
// connections use interval and deadline in place of
|
|
// pingInterval and pongDeadline, and resolve in place of
|
|
// resolveHost.
|
|
// The caller must call Stop() when finished.
|
|
func NewTestServer(
|
|
log *slog.Logger,
|
|
cfg *config.Config,
|
|
database *db.Database,
|
|
brk *broker.Broker,
|
|
interval, deadline time.Duration,
|
|
resolve func(ctx context.Context, addr string) string,
|
|
) *Server {
|
|
globs := &globals.Globals{
|
|
Appname: "neoirc",
|
|
Version: "test",
|
|
StartTime: time.Now(),
|
|
}
|
|
|
|
svc := service.NewTestService(
|
|
database, brk, cfg, globs, log,
|
|
)
|
|
|
|
return &Server{ //nolint:exhaustruct
|
|
log: log,
|
|
cfg: cfg,
|
|
database: database,
|
|
brk: brk,
|
|
svc: svc,
|
|
conns: make(map[*Conn]struct{}),
|
|
|
|
pingInterval: interval,
|
|
pongDeadline: deadline,
|
|
resolveHost: resolve,
|
|
}
|
|
}
|
|
|
|
// ResolveHost is the reverse DNS lookup that a server made by
|
|
// New does for each client.
|
|
func ResolveHost(ctx context.Context, addr string) string {
|
|
return resolveHost(ctx, addr)
|
|
}
|
|
|
|
// Start exposes the unexported start method for tests.
|
|
func (s *Server) Start(addr string) error {
|
|
return s.start(context.Background(), addr)
|
|
}
|
|
|
|
// Stop exposes the unexported stop method for tests.
|
|
func (s *Server) Stop() {
|
|
s.stop()
|
|
}
|
|
|
|
// Listener returns the server's net.Listener for tests.
|
|
func (s *Server) Listener() net.Listener {
|
|
return s.listener
|
|
}
|
|
|
|
// PingTimes returns how long the server's connections wait
|
|
// with nothing read before they are sent a PING, and after it
|
|
// before they are closed.
|
|
func (s *Server) PingTimes() (time.Duration, time.Duration) {
|
|
return s.pingInterval, s.pongDeadline
|
|
}
|
|
|
|
// KillWriteWindow is how long Disconnect allows each write
|
|
// to a killed victim.
|
|
const KillWriteWindow = killWriteWindow
|
|
|
|
// PollInterval is how often the relay checks the client
|
|
// output queue when no broker notification arrives.
|
|
const PollInterval = pollInterval
|
|
|
|
// PingInterval is how long the server waits with nothing read
|
|
// from a connection before it sends a PING.
|
|
const PingInterval = pingInterval
|
|
|
|
// PongDeadline is how long the server waits after the PING
|
|
// for a line before it closes the connection.
|
|
const PongDeadline = pongDeadline
|
|
|
|
// StopTimeout is the longest Stop waits for the connections'
|
|
// cleanup.
|
|
const StopTimeout = stopTimeout
|
|
|
|
// NewTestConn returns a Conn for nick on tcpConn, without a
|
|
// server, database or service behind it.
|
|
func NewTestConn(
|
|
log *slog.Logger,
|
|
cfg *config.Config,
|
|
tcpConn net.Conn,
|
|
nick string,
|
|
) *Conn {
|
|
conn := newConn(tcpConn, log, nil, nil, cfg, nil)
|
|
conn.nick = nick
|
|
|
|
return conn
|
|
}
|