test(handler/router): fix review findings - error matching, version test, cache assertion, entrypoint sync
This commit is contained in:
@@ -3,6 +3,7 @@ package router
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
@@ -27,9 +28,12 @@ func TestHandleAssociate_NoIngress(t *testing.T) {
|
|||||||
|
|
||||||
conn := &fakeConn{buf: reqData}
|
conn := &fakeConn{buf: reqData}
|
||||||
err := h.handleAssociate(context.Background(), conn, "ip", "10.0.0.1", rid, &testLogger{})
|
err := h.handleAssociate(context.Background(), conn, "ip", "10.0.0.1", rid, &testLogger{})
|
||||||
// The error comes from the packet read loop when the fake conn has no
|
// handleAssociate returns io.EOF when the packet read loop exhausts the
|
||||||
// more data — it is not an associate failure.
|
// fake connection's data — that signals normal completion, not a failure.
|
||||||
if err != nil && err.Error() != "unexpected EOF" {
|
// packetConn.Read uses io.ReadFull, which returns io.ErrUnexpectedEOF
|
||||||
|
// when the underlying connection has been exhausted mid-frame, rather
|
||||||
|
// than the plain io.EOF that the loop checks for.
|
||||||
|
if err != nil && err != io.EOF && err != io.ErrUnexpectedEOF {
|
||||||
t.Fatalf("handleAssociate: %v", err)
|
t.Fatalf("handleAssociate: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,9 +62,7 @@ func TestHandleAssociate_IngressMatch(t *testing.T) {
|
|||||||
reqData := buildRelayAssociateRequest(t, "10.0.0.1:0", rid, "ip")
|
reqData := buildRelayAssociateRequest(t, "10.0.0.1:0", rid, "ip")
|
||||||
conn := &fakeConn{buf: reqData}
|
conn := &fakeConn{buf: reqData}
|
||||||
err := h.handleAssociate(context.Background(), conn, "ip", "10.0.0.1", rid, &testLogger{})
|
err := h.handleAssociate(context.Background(), conn, "ip", "10.0.0.1", rid, &testLogger{})
|
||||||
// The error comes from the packet read loop when the fake conn has no
|
if err != nil && err != io.EOF && err != io.ErrUnexpectedEOF {
|
||||||
// more data — it is not an associate failure.
|
|
||||||
if err != nil && err.Error() != "unexpected EOF" {
|
|
||||||
t.Fatalf("handleAssociate: %v", err)
|
t.Fatalf("handleAssociate: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -249,10 +251,19 @@ func TestGetRoute_CacheDisabled(t *testing.T) {
|
|||||||
h := newInitdHandler(t)
|
h := newInitdHandler(t)
|
||||||
h.md.routerCacheEnabled = false
|
h.md.routerCacheEnabled = false
|
||||||
|
|
||||||
// With no registry router and no fallback, should return nil.
|
// With cache disabled, getRoute always performs a lookup.
|
||||||
|
// Since cache is disabled, a cached value should NOT be returned even if one exists.
|
||||||
|
h.routeCache.Set("10.0.0.1", cache.NewItem(&router.Route{
|
||||||
|
Dst: "10.0.0.0/24",
|
||||||
|
Gateway: "10.0.0.254",
|
||||||
|
}, time.Minute))
|
||||||
|
|
||||||
route := h.getRoute(context.Background(), "test-rid", "10.0.0.1")
|
route := h.getRoute(context.Background(), "test-rid", "10.0.0.1")
|
||||||
|
// With cache disabled, the cached route must NOT be returned.
|
||||||
|
// Since there is no registry router registered for "test-rid" and no
|
||||||
|
// fallback router, the result should be nil.
|
||||||
if route != nil {
|
if route != nil {
|
||||||
t.Log("route is not nil (may have registry router)")
|
t.Errorf("getRoute returned %+v, want nil (cache disabled, no fallback)", route)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -72,9 +72,10 @@ func TestHandleEntrypoint_NonAssociateCmd(t *testing.T) {
|
|||||||
h.epConn = fpc
|
h.epConn = fpc
|
||||||
h.pool.Add(rid, c)
|
h.pool.Add(rid, c)
|
||||||
|
|
||||||
errCh := make(chan error, 1)
|
doneCh := make(chan struct{})
|
||||||
go func() {
|
go func() {
|
||||||
errCh <- h.handleEntrypoint(&testLogger{})
|
h.handleEntrypoint(&testLogger{})
|
||||||
|
close(doneCh)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// Send request with non-Associate cmd (CmdConnect)
|
// Send request with non-Associate cmd (CmdConnect)
|
||||||
@@ -93,7 +94,7 @@ func TestHandleEntrypoint_NonAssociateCmd(t *testing.T) {
|
|||||||
|
|
||||||
// Close to stop the loop, then wait for goroutine to exit.
|
// Close to stop the loop, then wait for goroutine to exit.
|
||||||
fpc.Close()
|
fpc.Close()
|
||||||
<-errCh
|
<-doneCh
|
||||||
|
|
||||||
if connBuf.Len() > 0 {
|
if connBuf.Len() > 0 {
|
||||||
t.Error("data was forwarded to connector despite non-Associate cmd")
|
t.Error("data was forwarded to connector despite non-Associate cmd")
|
||||||
@@ -110,9 +111,10 @@ func TestHandleEntrypoint_NoMatchingConnector(t *testing.T) {
|
|||||||
h.epConn = fpc
|
h.epConn = fpc
|
||||||
// No connector added to pool
|
// No connector added to pool
|
||||||
|
|
||||||
errCh := make(chan error, 1)
|
doneCh := make(chan struct{})
|
||||||
go func() {
|
go func() {
|
||||||
errCh <- h.handleEntrypoint(&testLogger{})
|
h.handleEntrypoint(&testLogger{})
|
||||||
|
close(doneCh)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
req := relay.Request{
|
req := relay.Request{
|
||||||
@@ -132,9 +134,9 @@ func TestHandleEntrypoint_NoMatchingConnector(t *testing.T) {
|
|||||||
fpc.dataCh <- buf.Bytes()
|
fpc.dataCh <- buf.Bytes()
|
||||||
fpc.addrCh <- laddr
|
fpc.addrCh <- laddr
|
||||||
|
|
||||||
// Should not panic or error, just silently drop.
|
// Signal the loop to stop by sending EOF via the pipe concurrency pattern.
|
||||||
fpc.Close()
|
fpc.Close()
|
||||||
<-errCh
|
<-doneCh
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHandleEntrypoint_ReadError(t *testing.T) {
|
func TestHandleEntrypoint_ReadError(t *testing.T) {
|
||||||
|
|||||||
@@ -126,8 +126,11 @@ func TestInit_WithLimiter(t *testing.T) {
|
|||||||
func TestHandle_BadVersion(t *testing.T) {
|
func TestHandle_BadVersion(t *testing.T) {
|
||||||
h := newInitdHandler(t)
|
h := newInitdHandler(t)
|
||||||
|
|
||||||
|
// Version2 causes relay.Request.ReadFrom to return an error because the
|
||||||
|
// relay protocol only supports Version1. The Handle method returns the
|
||||||
|
// read error directly — it never reaches the version check.
|
||||||
req := relay.Request{
|
req := relay.Request{
|
||||||
Version: 0x02, // not Version1 — triggers error in ReadFrom
|
Version: 0x02,
|
||||||
Cmd: relay.CmdAssociate,
|
Cmd: relay.CmdAssociate,
|
||||||
}
|
}
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
@@ -139,8 +142,47 @@ func TestHandle_BadVersion(t *testing.T) {
|
|||||||
t.Fatal("expected error for bad version")
|
t.Fatal("expected error for bad version")
|
||||||
}
|
}
|
||||||
|
|
||||||
// The response is not written when ReadFrom fails, so we only verify
|
// Connection must be closed via defer.
|
||||||
// that the connection was closed (defer conn.Close in Handle).
|
if !conn.closed {
|
||||||
|
t.Error("connection was not closed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestHandle_BadRequestVersion verifies that Handle rejects requests where
|
||||||
|
// the version byte has been tampered with. relay.Request.ReadFrom checks the
|
||||||
|
// version byte before the handler does — the handler's own version check
|
||||||
|
// (req.Version != relay.Version1) is a defensive secondary guard. Since
|
||||||
|
// ReadFrom catches bad versions first, Handle returns relay.ErrBadVersion
|
||||||
|
// from ReadFrom without writing a response.
|
||||||
|
func TestHandle_BadRequestVersion(t *testing.T) {
|
||||||
|
h := newInitdHandler(t)
|
||||||
|
|
||||||
|
// Build a valid relay request with Version1, then override the version byte.
|
||||||
|
rid := relay.NewTunnelID([]byte("0123456789abcdef"))
|
||||||
|
req := relay.Request{
|
||||||
|
Version: relay.Version1,
|
||||||
|
Cmd: relay.CmdAssociate,
|
||||||
|
Features: []relay.Feature{
|
||||||
|
&relay.TunnelFeature{ID: rid},
|
||||||
|
&relay.NetworkFeature{Network: relay.NetworkIP},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
var buf bytes.Buffer
|
||||||
|
req.WriteTo(&buf)
|
||||||
|
|
||||||
|
// Override the version byte to something that is not Version1.
|
||||||
|
b := buf.Bytes()
|
||||||
|
if len(b) > 0 {
|
||||||
|
b[0] = 0x02
|
||||||
|
}
|
||||||
|
|
||||||
|
conn := &fakeConn{buf: b}
|
||||||
|
err := h.Handle(context.Background(), conn)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected error for bad request version")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connection must be closed via defer.
|
||||||
if !conn.closed {
|
if !conn.closed {
|
||||||
t.Error("connection was not closed")
|
t.Error("connection was not closed")
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user