blob: 7cf53ae0493b28f6b0f95b5d4c6acbdbcc0cbdcb [file] [log] [blame]
package ident
import (
"bufio"
"context"
"fmt"
"io"
"net"
"strings"
"testing"
)
// loopback sets up a net.Listener on any available TCP port and returns it and
// a dialer function that returns open connections to that listener.
func loopback(t *testing.T) (net.Listener, func() net.Conn) {
t.Helper()
lis, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatalf("Listen: %v", err)
}
return lis, func() net.Conn {
t.Helper()
conn, err := net.Dial("tcp", lis.Addr().String())
if err != nil {
t.Fatalf("Dial: %v", err)
}
return conn
}
}
// dumbHandler is a handler that returns USERID:UNIX:q3k for every request.
func dumbHandler(ctx context.Context, w ResponseWriter, r *Request) {
w.SendIdent(&IdentResponse{
UserID: "q3k",
})
}
// reqRessps send an ident query to the conn and expects a response with
// USERID:UNIX:q3k on the scanner.
func reqResp(t *testing.T, conn net.Conn, scanner *bufio.Scanner, client, server uint16) {
t.Helper()
if _, err := fmt.Fprintf(conn, "%d,%d\r\n", client, server); err != nil {
t.Fatalf("Write: %v", err)
}
if !scanner.Scan() {
t.Fatalf("Scan: %v", scanner.Err())
}
if want, got := fmt.Sprintf("%d,%d:USERID:UNIX:q3k", client, server), scanner.Text(); want != got {
t.Fatalf("Wanted %q, got %q", want, got)
}
}
// TestServeSimple exercises the basic Server functionality: responding to
// ident requests.
func TestServeSimple(t *testing.T) {
lis, dial := loopback(t)
defer lis.Close()
isrv := NewServer()
isrv.HandleFunc(dumbHandler)
go isrv.Serve(lis)
conn := dial()
defer conn.Close()
scanner := bufio.NewScanner(conn)
// Send a request, expect response.
reqResp(t, conn, scanner, 123, 234)
// Send another request on the same conn, expect response.
reqResp(t, conn, scanner, 234, 345)
// Send another request in parallel, expect response.
conn2 := dial()
defer conn2.Close()
scanner2 := bufio.NewScanner(conn2)
reqResp(t, conn2, scanner2, 345, 456)
}
// TestServeError exercises situations where the server has to deal with
// nasty/broken clients.
func TestServeErrors(t *testing.T) {
lis, dial := loopback(t)
defer lis.Close()
isrv := NewServer()
isrv.HandleFunc(dumbHandler)
go isrv.Serve(lis)
conn := dial()
defer conn.Close()
// Send something that's not ident.
fmt.Fprintf(conn, "GET / HTTP/1.1\r\n\r\n")
// Expect EOF on read.
data := make([]byte, 100)
_, err := conn.Read(data)
if want, got := io.EOF, err; want != got {
t.Fatalf("Expected %v, got %v", want, got)
}
conn = dial()
defer conn.Close()
// Send a very long request line, expect to not be served.
fmt.Fprintf(conn, "123,%s123\r\n", strings.Repeat(" ", 4096))
data = make([]byte, 100)
_, err = conn.Read(data)
// In a large write, the connection will be closed by the server before
// we're finished writing. That will cause the connection to be reset, not
// just EOF'd as above.
if err == nil {
t.Fatalf("Read did not fail")
}
}
// TestServerRestart ensures that the server's serve/stop logic works as expected.
func TestServerRestart(t *testing.T) {
lis, dial := loopback(t)
defer lis.Close()
isrv := NewServer()
isrv.HandleFunc(dumbHandler)
// Stop the server before it's even started.
isrv.Stop()
// The server should now exit immediately.
if err := isrv.Serve(lis); err != nil {
t.Fatalf("Serve: %v", err)
}
// On a subsequent run it should, however, start and serve.
go isrv.Serve(lis)
conn := dial()
defer conn.Close()
scanner := bufio.NewScanner(conn)
// Send a request, expect response.
reqResp(t, conn, scanner, 123, 234)
// Attempting another simultaneous Serve() shoud fail.
if err := isrv.Serve(lis); err == nil {
t.Fatal("Serve() returned nil, wanted error")
}
// Send a request, expect response.
reqResp(t, conn, scanner, 234, 345)
// Stop server, restart server.
isrv.Stop()
go isrv.Serve(lis)
// Send a request, expect response.
reqResp(t, conn, scanner, 345, 456)
}