mirror of
https://github.com/go-i2p/go-sam-go.git
synced 2025-12-01 09:54:58 -05:00
fix stream listener test
This commit is contained in:
214
stream_test.go
214
stream_test.go
@@ -2,9 +2,10 @@ package sam3
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/go-i2p/i2pkeys"
|
"github.com/go-i2p/i2pkeys"
|
||||||
)
|
)
|
||||||
@@ -156,64 +157,142 @@ func Test_StreamingServerClient(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func ExampleStreamSession() {
|
func ExampleStreamSession() {
|
||||||
// Creates a new StreamingSession, dials to a local test listener and gets a SAMConn
|
// Creates a new StreamingSession with a local server and client.
|
||||||
// which behaves just like a normal net.Conn.
|
// Demonstrates how a SAMConn behaves just like a normal net.Conn.
|
||||||
//
|
//
|
||||||
// Requirements: This example requires a running I2P router with SAM bridge enabled.
|
// Requirements: This example requires a running I2P router with SAM bridge enabled.
|
||||||
|
|
||||||
const samBridge = "127.0.0.1:7656"
|
const samBridge = "127.0.0.1:7656"
|
||||||
|
|
||||||
sam, err := NewSAM(samBridge)
|
// Create server session first
|
||||||
|
server_sam, err := NewSAM(samBridge)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Failed to connect to I2P SAM bridge: %v", err)
|
fmt.Printf("Failed to connect to I2P SAM bridge: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer sam.Close()
|
defer server_sam.Close()
|
||||||
keys, err := sam.NewKeys()
|
|
||||||
|
server_keys, err := server_sam.NewKeys()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Failed to generate I2P keys: %v", err)
|
fmt.Printf("Failed to generate server I2P keys: %v", err)
|
||||||
return
|
|
||||||
}
|
|
||||||
// See the example Option_* variables.
|
|
||||||
ss, err := sam.NewStreamSession("stream_example", keys, Options_Small)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("Failed to create stream session: %v", err)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note: In a real example, you would set up a test listener here
|
server_session, err := server_sam.NewStreamSession("stream_server", server_keys, Options_Small)
|
||||||
// For demonstration purposes, we'll use a placeholder destination
|
|
||||||
someone, err := sam.Lookup("test.i2p")
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Failed to lookup test destination: %v", err)
|
fmt.Printf("Failed to create server stream session: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer server_session.Close()
|
||||||
|
|
||||||
|
// Synchronization channels
|
||||||
|
serverReady := make(chan bool)
|
||||||
|
clientDone := make(chan bool)
|
||||||
|
|
||||||
|
// Server goroutine - listens and responds with HTTP-like content
|
||||||
|
go func() {
|
||||||
|
listener, err := server_session.Listen()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Server failed to listen: %v", err)
|
||||||
|
serverReady <- false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer listener.Close()
|
||||||
|
serverReady <- true
|
||||||
|
|
||||||
|
conn, err := listener.Accept()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
// Read client request
|
||||||
|
buf := make([]byte, 256)
|
||||||
|
_, _ = conn.Read(buf)
|
||||||
|
|
||||||
|
// Send HTTP-like response
|
||||||
|
response := "HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n<html><body>Test</body></html>"
|
||||||
|
conn.Write([]byte(response))
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Wait for server to be ready
|
||||||
|
if !<-serverReady {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
conn, err := ss.DialI2P(someone)
|
// Client operations in separate goroutine
|
||||||
if err != nil {
|
go func() {
|
||||||
fmt.Printf("Failed to dial test destination: %v", err)
|
client_sam, err := NewSAM(samBridge)
|
||||||
return
|
if err != nil {
|
||||||
}
|
fmt.Printf("Client failed to connect to I2P: %v", err)
|
||||||
defer conn.Close()
|
clientDone <- false
|
||||||
fmt.Println("Sending HTTP GET /")
|
return
|
||||||
if _, err := conn.Write([]byte("GET /\n")); err != nil {
|
}
|
||||||
fmt.Printf("Failed to write to connection: %v", err)
|
defer client_sam.Close()
|
||||||
return
|
|
||||||
}
|
client_keys, err := client_sam.NewKeys()
|
||||||
buf := make([]byte, 4096)
|
if err != nil {
|
||||||
n, err := conn.Read(buf)
|
fmt.Printf("Client failed to generate keys: %v", err)
|
||||||
if err != nil {
|
clientDone <- false
|
||||||
fmt.Printf("Failed to read from connection: %v", err)
|
return
|
||||||
return
|
}
|
||||||
}
|
|
||||||
if !strings.Contains(strings.ToLower(string(buf[:n])), "http") && !strings.Contains(strings.ToLower(string(buf[:n])), "html") {
|
client_session, err := client_sam.NewStreamSession("stream_client", client_keys, Options_Small)
|
||||||
fmt.Printf("Failed to get HTTP/HTML response from test destination (got %d bytes)", n)
|
if err != nil {
|
||||||
return
|
fmt.Printf("Client failed to create session: %v", err)
|
||||||
} else {
|
clientDone <- false
|
||||||
fmt.Println("Read HTTP/HTML from test destination")
|
return
|
||||||
log.Println("Read HTTP/HTML from test destination")
|
}
|
||||||
}
|
defer client_session.Close()
|
||||||
return
|
|
||||||
|
// I2P tunnel establishment can take 30-120 seconds, so we retry dialing
|
||||||
|
var conn net.Conn
|
||||||
|
var dialErr error
|
||||||
|
for attempt := 0; attempt < 6; attempt++ {
|
||||||
|
conn, dialErr = client_session.DialI2P(server_keys.Addr())
|
||||||
|
if dialErr == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if attempt < 5 {
|
||||||
|
// Exponential backoff: 15s, 30s, 45s, 60s, 75s
|
||||||
|
sleepTime := time.Duration(15*(attempt+1)) * time.Second
|
||||||
|
fmt.Printf("Dial attempt %d failed: %v. Retrying in %v...\n", attempt+1, dialErr, sleepTime)
|
||||||
|
time.Sleep(sleepTime)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if dialErr != nil {
|
||||||
|
fmt.Printf("Client failed to dial server after retries: %v", dialErr)
|
||||||
|
clientDone <- false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
fmt.Println("Sending HTTP GET /")
|
||||||
|
if _, err := conn.Write([]byte("GET /\n")); err != nil {
|
||||||
|
fmt.Printf("Failed to write to connection: %v", err)
|
||||||
|
clientDone <- false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := make([]byte, 4096)
|
||||||
|
n, err := conn.Read(buf)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Failed to read from connection: %v", err)
|
||||||
|
clientDone <- false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
response := string(buf[:n])
|
||||||
|
if strings.Contains(strings.ToLower(response), "http") || strings.Contains(strings.ToLower(response), "html") {
|
||||||
|
fmt.Println("Read HTTP/HTML from test destination")
|
||||||
|
} else {
|
||||||
|
fmt.Printf("Failed to get HTTP/HTML response (got %d bytes)", n)
|
||||||
|
}
|
||||||
|
clientDone <- true
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Wait for client to complete
|
||||||
|
<-clientDone
|
||||||
|
|
||||||
// Output:
|
// Output:
|
||||||
// Sending HTTP GET /
|
// Sending HTTP GET /
|
||||||
@@ -229,49 +308,64 @@ func ExampleStreamListener() {
|
|||||||
|
|
||||||
const samBridge = "127.0.0.1:7656"
|
const samBridge = "127.0.0.1:7656"
|
||||||
|
|
||||||
sam, err := NewSAM(samBridge)
|
server_sam, err := NewSAM(samBridge)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Failed to connect to I2P SAM bridge: %v", err)
|
fmt.Printf("Failed to connect to I2P SAM bridge: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer sam.Close()
|
defer server_sam.Close()
|
||||||
keys, err := sam.NewKeys()
|
keys, err := server_sam.NewKeys()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Failed to generate I2P keys: %v", err)
|
fmt.Printf("Failed to generate I2P keys: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
quit := make(chan bool)
|
// Create server session BEFORE starting client goroutine
|
||||||
|
server_session, err := server_sam.NewStreamSession("server_example", keys, Options_Small)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Failed to create server session: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Client connecting to the server
|
// Channels for synchronization and results
|
||||||
|
quit := make(chan bool)
|
||||||
|
serverReady := make(chan bool)
|
||||||
|
|
||||||
|
// Client connecting to the server - waits for server readiness signal
|
||||||
go func(server i2pkeys.I2PAddr) {
|
go func(server i2pkeys.I2PAddr) {
|
||||||
csam, err := NewSAM(samBridge)
|
// Wait for server to be fully ready (session + listener created)
|
||||||
|
if !<-serverReady {
|
||||||
|
quit <- false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
client_sam, err := NewSAM(samBridge)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Client failed to connect to I2P: %v", err)
|
fmt.Printf("Client failed to connect to I2P: %v", err)
|
||||||
quit <- false
|
quit <- false
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer csam.Close()
|
defer client_sam.Close()
|
||||||
keys, err := csam.NewKeys()
|
keys, err := client_sam.NewKeys()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Client failed to generate keys: %v", err)
|
fmt.Printf("Client failed to generate keys: %v", err)
|
||||||
quit <- false
|
quit <- false
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
cs, err := csam.NewStreamSession("client_example", keys, Options_Small)
|
client_session, err := client_sam.NewStreamSession("client_example", keys, Options_Small)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Client failed to create session: %v", err)
|
fmt.Printf("Client failed to create session: %v", err)
|
||||||
quit <- false
|
quit <- false
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
conn, err := cs.DialI2P(server)
|
client_conn, err := client_session.DialI2P(server)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Client failed to dial server: %v", err)
|
fmt.Printf("Client failed to dial server: %v", err)
|
||||||
quit <- false
|
quit <- false
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
buf := make([]byte, 256)
|
buf := make([]byte, 256)
|
||||||
n, err := conn.Read(buf)
|
n, err := client_conn.Read(buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Client failed to read: %v", err)
|
fmt.Printf("Client failed to read: %v", err)
|
||||||
quit <- false
|
quit <- false
|
||||||
@@ -279,18 +373,18 @@ func ExampleStreamListener() {
|
|||||||
}
|
}
|
||||||
fmt.Println(string(buf[:n]))
|
fmt.Println(string(buf[:n]))
|
||||||
quit <- true
|
quit <- true
|
||||||
}(keys.Addr()) // end of client
|
}(keys.Addr()) // end of client - pass server address
|
||||||
|
|
||||||
ss, err := sam.NewStreamSession("server_example", keys, Options_Small)
|
// Create listener and signal client can proceed
|
||||||
if err != nil {
|
l, err := server_session.Listen()
|
||||||
fmt.Printf("Failed to create server session: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
l, err := ss.Listen()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Failed to listen: %v", err)
|
fmt.Printf("Failed to listen: %v", err)
|
||||||
|
serverReady <- false // Signal failure to client
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
defer l.Close()
|
||||||
|
serverReady <- true // Signal success to client
|
||||||
|
|
||||||
conn, err := l.Accept()
|
conn, err := l.Accept()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Failed to accept connection: %v", err)
|
fmt.Printf("Failed to accept connection: %v", err)
|
||||||
|
|||||||
Reference in New Issue
Block a user