M1: single-node OpenTunnel-compatible server

HTTP API per the recorded contract, bridge WebSocket, SNI relay with
pass-through tunnel TLS, local CA issuance, JSON store. Verified
end-to-end with the official opentunnel CLI.
This commit is contained in:
2026-10-25 11:52:45 +08:00
commit 682a341010
15 changed files with 2511 additions and 0 deletions
+97
View File
@@ -0,0 +1,97 @@
package server
import (
"bytes"
"crypto/tls"
"net"
"testing"
"time"
)
// TestReadClientHello captures a real ClientHello produced by crypto/tls and
// checks the parser and the byte-exact replay capture.
func TestReadClientHello(t *testing.T) {
client, serverConn := net.Pipe()
defer client.Close()
var written bytes.Buffer
go func() {
// The client writes its ClientHello and then blocks waiting for the
// server; we only need the write side.
tlsClient := tls.Client(captureConn{client, &written}, &tls.Config{
ServerName: "api.demo.opentunnel.xyz",
NextProtos: []string{"h2", "http/1.1"},
InsecureSkipVerify: true,
})
tlsClient.Handshake() // fails: nobody answers
}()
serverConn.SetReadDeadline(time.Now().Add(5 * time.Second))
raw, sni, alpn, err := readClientHello(serverConn)
if err != nil {
t.Fatalf("readClientHello: %v", err)
}
if sni != "api.demo.opentunnel.xyz" {
t.Errorf("sni = %q", sni)
}
if len(alpn) != 2 || alpn[0] != "h2" || alpn[1] != "http/1.1" {
t.Errorf("alpn = %v", alpn)
}
if !bytes.HasPrefix(written.Bytes(), raw) {
t.Errorf("captured %d bytes are not a prefix of the %d written", len(raw), written.Len())
}
}
// captureConn records everything written to it.
type captureConn struct {
net.Conn
w *bytes.Buffer
}
func (c captureConn) Write(p []byte) (int, error) {
c.w.Write(p)
return c.Conn.Write(p)
}
func TestReadClientHelloGarbage(t *testing.T) {
client, serverConn := net.Pipe()
go func() {
client.Write([]byte("not tls at all"))
client.Close()
}()
serverConn.SetReadDeadline(time.Now().Add(2 * time.Second))
if _, _, _, err := readClientHello(serverConn); err == nil {
t.Error("expected an error for non-TLS bytes")
}
}
func TestReadClientHelloTrickle(t *testing.T) {
// A ClientHello trickling in byte by byte still parses.
client, serverConn := net.Pipe()
var written bytes.Buffer
go func() {
tlsClient := tls.Client(captureConn{client, &written}, &tls.Config{
ServerName: "demo.opentunnel.xyz",
InsecureSkipVerify: true,
})
tlsClient.Handshake()
}()
serverConn.SetReadDeadline(time.Now().Add(5 * time.Second))
slow := &slowConn{Conn: serverConn} // one-byte reads, a trickling sender
raw, sni, _, err := readClientHello(slow)
if err != nil {
t.Fatalf("readClientHello: %v", err)
}
if sni != "demo.opentunnel.xyz" {
t.Errorf("sni = %q", sni)
}
if !bytes.HasPrefix(written.Bytes(), raw) {
t.Error("capture mismatch")
}
}
type slowConn struct{ net.Conn }
func (c *slowConn) Read(p []byte) (int, error) {
return c.Conn.Read(p[:1])
}