Reticulum-Go/pkg/interfaces/https_test.go

218 lines
4.8 KiB
Go

// SPDX-License-Identifier: Apache-2.0
// Copyright (c) 2024-2026 Quad4.io
//go:build !js
package interfaces
import (
"bytes"
"net"
"sync"
"sync/atomic"
"testing"
"time"
"quad4/reticulum-go/pkg/common"
)
func freeHTTPSPort(t *testing.T) int {
t.Helper()
ln, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatal(err)
}
defer ln.Close()
return ln.Addr().(*net.TCPAddr).Port
}
func waitHTTPSPeers(t *testing.T, srv *HTTPSServerInterface, n int, timeout time.Duration) {
t.Helper()
deadline := time.Now().Add(timeout)
for time.Now().Before(deadline) {
if srv.PeerCount() >= n {
return
}
time.Sleep(20 * time.Millisecond)
}
t.Fatalf("want %d peers, have %d", n, srv.PeerCount())
}
func listenHTTPSPort(t *testing.T, srv *HTTPSServerInterface) int {
t.Helper()
addr := srv.ListenAddr()
if addr == nil {
t.Fatal("nil listen addr")
}
ta, ok := addr.(*net.TCPAddr)
if !ok {
t.Fatalf("unexpected addr %T", addr)
}
return ta.Port
}
func TestNormalizeHTTPSPath(t *testing.T) {
if got := normalizeHTTPSPath(""); got != "/rns" {
t.Fatalf("empty: %q", got)
}
if got := normalizeHTTPSPath("/"); got != "/rns" {
t.Fatalf("slash: %q", got)
}
if got := normalizeHTTPSPath("rns"); got != "/rns" {
t.Fatalf("bare: %q", got)
}
if got := normalizeHTTPSPath("/rns/"); got != "/rns" {
t.Fatalf("trim: %q", got)
}
}
func TestHTTPSClientServerEcho(t *testing.T) {
port := freeHTTPSPort(t)
srv, err := NewHTTPSServerInterface("https_srv", "127.0.0.1", port, HTTPSServerOptions{
Path: "/rns",
LongPoll: 500 * time.Millisecond,
})
if err != nil {
t.Fatal(err)
}
pin, err := srv.LeafSPKIPinHex()
if err != nil {
t.Fatal(err)
}
var got atomic.Value
var wg sync.WaitGroup
wg.Add(1)
srv.SetPacketCallback(func(data []byte, _ common.NetworkInterface) {
got.Store(append([]byte(nil), data...))
wg.Done()
})
if err := srv.Start(); err != nil {
t.Fatal(err)
}
defer srv.Stop()
port = listenHTTPSPort(t, srv)
cli, err := NewHTTPSClientInterfaceWithRetries("https_cli", "127.0.0.1", port, true, 20, HTTPSClientOptions{
PeerKey: pin,
Path: "/rns",
LongPoll: 500 * time.Millisecond,
})
if err != nil {
t.Fatal(err)
}
defer cli.Stop()
waitOnline(t, cli, 5*time.Second)
waitHTTPSPeers(t, srv, 1, 5*time.Second)
payload := []byte{0x01, 0x02, 0x03, 0xaa, 0xbb}
if err := cli.Send(payload, ""); err != nil {
t.Fatal(err)
}
done := make(chan struct{})
go func() {
wg.Wait()
close(done)
}()
select {
case <-done:
case <-time.After(3 * time.Second):
t.Fatal("timeout waiting for server packet")
}
recv, _ := got.Load().([]byte)
if !bytes.Equal(recv, payload) {
t.Fatalf("got %x want %x", recv, payload)
}
}
func TestHTTPSServerToClient(t *testing.T) {
port := freeHTTPSPort(t)
srv, err := NewHTTPSServerInterface("https_srv2", "127.0.0.1", port, HTTPSServerOptions{
LongPoll: 500 * time.Millisecond,
})
if err != nil {
t.Fatal(err)
}
if err := srv.Start(); err != nil {
t.Fatal(err)
}
defer srv.Stop()
port = listenHTTPSPort(t, srv)
cli, err := NewHTTPSClientInterfaceWithRetries("https_cli2", "127.0.0.1", port, true, 20, HTTPSClientOptions{
LongPoll: 500 * time.Millisecond,
})
if err != nil {
t.Fatal(err)
}
defer cli.Stop()
waitOnline(t, cli, 5*time.Second)
waitHTTPSPeers(t, srv, 1, 5*time.Second)
var got atomic.Value
var wg sync.WaitGroup
wg.Add(1)
cli.SetPacketCallback(func(data []byte, _ common.NetworkInterface) {
got.Store(append([]byte(nil), data...))
wg.Done()
})
payload := []byte{0x10, 0x20, 0x30}
if err := srv.Send(payload, ""); err != nil {
t.Fatal(err)
}
done := make(chan struct{})
go func() {
wg.Wait()
close(done)
}()
select {
case <-done:
case <-time.After(3 * time.Second):
t.Fatal("timeout waiting for client packet")
}
recv, _ := got.Load().([]byte)
if !bytes.Equal(recv, payload) {
t.Fatalf("got %x want %x", recv, payload)
}
}
func TestHTTPSPeerKeyPin(t *testing.T) {
port := freeHTTPSPort(t)
srv, err := NewHTTPSServerInterface("https_pin_srv", "127.0.0.1", port, HTTPSServerOptions{
LongPoll: 500 * time.Millisecond,
})
if err != nil {
t.Fatal(err)
}
pin, err := srv.LeafSPKIPinHex()
if err != nil {
t.Fatal(err)
}
if err := srv.Start(); err != nil {
t.Fatal(err)
}
defer srv.Stop()
port = listenHTTPSPort(t, srv)
cli, err := NewHTTPSClientInterfaceWithRetries("https_pin_cli", "127.0.0.1", port, true, 10, HTTPSClientOptions{
PeerKey: pin,
LongPoll: 500 * time.Millisecond,
})
if err != nil {
t.Fatal(err)
}
defer cli.Stop()
waitOnline(t, cli, 5*time.Second)
}
func TestHTTPSTypeAndMTU(t *testing.T) {
srv, err := NewHTTPSServerInterface("https_meta", "127.0.0.1", 0, HTTPSServerOptions{})
if err != nil {
t.Fatal(err)
}
if srv.GetType() != common.IFTypeHTTPS {
t.Fatalf("type %v want IFTypeHTTPS", srv.GetType())
}
if srv.GetMTU() != DefaultMTU {
t.Fatalf("MTU %d want %d", srv.GetMTU(), DefaultMTU)
}
}