Update datagram and stream session tests to use inbound and outbound lengths of 1 for improved testing accuracy

This commit is contained in:
eyedeekay
2025-10-06 13:22:41 -04:00
parent 2eebe2eb2f
commit 0f230dc017
11 changed files with 82 additions and 54 deletions

View File

@@ -17,7 +17,7 @@ func TestDatagramSession_Dial(t *testing.T) {
// Create listener session // Create listener session
listenerSession, err := NewDatagramSession(sam1, "test_dial_listener", keys1, []string{ listenerSession, err := NewDatagramSession(sam1, "test_dial_listener", keys1, []string{
"inbound.length=0", "outbound.length=0", "inbound.length=1", "outbound.length=1",
}) })
if err != nil { if err != nil {
t.Fatalf("Failed to create listener session: %v", err) t.Fatalf("Failed to create listener session: %v", err)
@@ -32,7 +32,7 @@ func TestDatagramSession_Dial(t *testing.T) {
// Create dialer session // Create dialer session
dialerSession, err := NewDatagramSession(sam2, "test_dial_dialer", keys2, []string{ dialerSession, err := NewDatagramSession(sam2, "test_dial_dialer", keys2, []string{
"inbound.length=0", "outbound.length=0", "inbound.length=1", "outbound.length=1",
}) })
if err != nil { if err != nil {
t.Fatalf("Failed to create dialer session: %v", err) t.Fatalf("Failed to create dialer session: %v", err)

View File

@@ -9,7 +9,7 @@ func TestDatagramSession_Listen(t *testing.T) {
defer sam.Close() defer sam.Close()
session, err := NewDatagramSession(sam, "test_listen", keys, []string{ session, err := NewDatagramSession(sam, "test_listen", keys, []string{
"inbound.length=0", "outbound.length=0", "inbound.length=1", "outbound.length=1",
}) })
if err != nil { if err != nil {
t.Fatalf("Failed to create session: %v", err) t.Fatalf("Failed to create session: %v", err)

View File

@@ -65,8 +65,8 @@ func TestNewDatagramSession(t *testing.T) {
name: "session with small tunnel config", name: "session with small tunnel config",
idBase: "test_datagram_small", idBase: "test_datagram_small",
options: []string{ options: []string{
"inbound.length=0", "inbound.length=1",
"outbound.length=0", "outbound.length=1",
"inbound.lengthVariance=0", "inbound.lengthVariance=0",
"outbound.lengthVariance=0", "outbound.lengthVariance=0",
"inbound.quantity=1", "inbound.quantity=1",

View File

@@ -25,8 +25,8 @@ func Test_DatagramServerClient(t *testing.T) {
} }
// fmt.Println("\tServer: My address: " + keys.Addr().Base32()) // fmt.Println("\tServer: My address: " + keys.Addr().Base32())
fmt.Println("\tServer: Creating tunnel") fmt.Println("\tServer: Creating tunnel")
// ds, err := sam.NewDatagramSession("DGserverTun", keys, []string{"inbound.length=0", "outbound.length=0", "inbound.lengthVariance=0", "outbound.lengthVariance=0", "inbound.quantity=1", "outbound.quantity=1"}, 0) // ds, err := sam.NewDatagramSession("DGserverTun", keys, []string{"inbound.length=1", "outbound.length=1", "inbound.lengthVariance=0", "outbound.lengthVariance=0", "inbound.quantity=1", "outbound.quantity=1"}, 0)
ds, err := sam.NewDatagramSession("DGserverTun", keys, []string{"inbound.length=0", "outbound.length=0", "inbound.lengthVariance=0", "outbound.lengthVariance=0", "inbound.quantity=1", "outbound.quantity=1"}, 0) ds, err := sam.NewDatagramSession("DGserverTun", keys, []string{"inbound.length=1", "outbound.length=1", "inbound.lengthVariance=0", "outbound.lengthVariance=0", "inbound.quantity=1", "outbound.quantity=1"}, 0)
if err != nil { if err != nil {
fmt.Println("Server: Failed to create tunnel: " + err.Error()) fmt.Println("Server: Failed to create tunnel: " + err.Error())
t.Fail() t.Fail()
@@ -46,8 +46,8 @@ func Test_DatagramServerClient(t *testing.T) {
return return
} }
fmt.Println("\tClient: Creating tunnel") fmt.Println("\tClient: Creating tunnel")
// ds2, err := sam2.NewDatagramSession("DGclientTun", keys, []string{"inbound.length=0", "outbound.length=0", "inbound.lengthVariance=0", "outbound.lengthVariance=0", "inbound.quantity=1", "outbound.quantity=1"}, 0) // ds2, err := sam2.NewDatagramSession("DGclientTun", keys, []string{"inbound.length=1", "outbound.length=1", "inbound.lengthVariance=0", "outbound.lengthVariance=0", "inbound.quantity=1", "outbound.quantity=1"}, 0)
ds2, err := sam2.NewDatagramSession("DGclientTun", keys, []string{"inbound.length=0", "outbound.length=0", "inbound.lengthVariance=0", "outbound.lengthVariance=0", "inbound.quantity=1", "outbound.quantity=1"}, 0) ds2, err := sam2.NewDatagramSession("DGclientTun", keys, []string{"inbound.length=1", "outbound.length=1", "inbound.lengthVariance=0", "outbound.lengthVariance=0", "inbound.quantity=1", "outbound.quantity=1"}, 0)
if err != nil { if err != nil {
c <- false c <- false
return return

View File

@@ -9,11 +9,13 @@ import (
// Example demonstrates basic usage of the sam3 library for I2P connectivity. // Example demonstrates basic usage of the sam3 library for I2P connectivity.
// This example shows how to establish a SAM connection, generate keys, and create sessions. // This example shows how to establish a SAM connection, generate keys, and create sessions.
//
// Requirements: This example requires a running I2P router with SAM bridge enabled.
func Example() { func Example() {
// Connect to the local I2P SAM bridge // Connect to the local I2P SAM bridge
sam, err := sam3.NewSAM("127.0.0.1:7656") sam, err := sam3.NewSAM("127.0.0.1:7656")
if err != nil { if err != nil {
log.Printf("Cannot connect to I2P: %v", err) fmt.Printf("Cannot connect to I2P: %v", err)
return return
} }
defer sam.Close() defer sam.Close()
@@ -21,14 +23,14 @@ func Example() {
// Generate I2P keys for this session // Generate I2P keys for this session
keys, err := sam.NewKeys() keys, err := sam.NewKeys()
if err != nil { if err != nil {
log.Printf("Failed to generate keys: %v", err) fmt.Printf("Failed to generate keys: %v", err)
return return
} }
// Create a stream session for TCP-like connections // Create a stream session for TCP-like connections
session, err := sam.NewStreamSession("example-session", keys, sam3.Options_Default) session, err := sam.NewStreamSession("example-session", keys, sam3.Options_Default)
if err != nil { if err != nil {
log.Printf("Failed to create session: %v", err) fmt.Printf("Failed to create session: %v", err)
return return
} }
defer session.Close() defer session.Close()
@@ -38,11 +40,13 @@ func Example() {
} }
// ExampleNewSAM demonstrates how to establish a connection to the I2P SAM bridge. // ExampleNewSAM demonstrates how to establish a connection to the I2P SAM bridge.
//
// Requirements: This example requires a running I2P router with SAM bridge enabled.
func ExampleNewSAM() { func ExampleNewSAM() {
// Connect to the default I2P SAM bridge address // Connect to the default I2P SAM bridge address
sam, err := sam3.NewSAM(sam3.SAMDefaultAddr("")) sam, err := sam3.NewSAM(sam3.SAMDefaultAddr(""))
if err != nil { if err != nil {
log.Printf("Cannot connect to I2P: %v", err) fmt.Printf("Cannot connect to I2P: %v", err)
return return
} }
defer sam.Close() defer sam.Close()
@@ -51,24 +55,26 @@ func ExampleNewSAM() {
} }
// ExampleSAM_NewStreamSession demonstrates creating a stream session for reliable connections. // ExampleSAM_NewStreamSession demonstrates creating a stream session for reliable connections.
//
// Requirements: This example requires a running I2P router with SAM bridge enabled.
func ExampleSAM_NewStreamSession() { func ExampleSAM_NewStreamSession() {
sam, err := sam3.NewSAM("127.0.0.1:7656") sam, err := sam3.NewSAM("127.0.0.1:7656")
if err != nil { if err != nil {
log.Printf("Cannot connect to I2P: %v", err) fmt.Printf("Cannot connect to I2P: %v", err)
return return
} }
defer sam.Close() defer sam.Close()
keys, err := sam.NewKeys() keys, err := sam.NewKeys()
if err != nil { if err != nil {
log.Printf("Failed to generate keys: %v", err) fmt.Printf("Failed to generate keys: %v", err)
return return
} }
// Create a stream session with default tunnel configuration // Create a stream session with default tunnel configuration
session, err := sam.NewStreamSession("my-app", keys, sam3.Options_Default) session, err := sam.NewStreamSession("my-app", keys, sam3.Options_Default)
if err != nil { if err != nil {
log.Printf("Failed to create stream session: %v", err) fmt.Printf("Failed to create stream session: %v", err)
return return
} }
defer session.Close() defer session.Close()
@@ -77,24 +83,26 @@ func ExampleSAM_NewStreamSession() {
} }
// ExampleSAM_NewPrimarySession demonstrates creating a primary session for managing sub-sessions. // ExampleSAM_NewPrimarySession demonstrates creating a primary session for managing sub-sessions.
//
// Requirements: This example requires a running I2P router with SAM bridge enabled.
func ExampleSAM_NewPrimarySession() { func ExampleSAM_NewPrimarySession() {
sam, err := sam3.NewSAM("127.0.0.1:7656") sam, err := sam3.NewSAM("127.0.0.1:7656")
if err != nil { if err != nil {
log.Printf("Cannot connect to I2P: %v", err) fmt.Printf("Cannot connect to I2P: %v", err)
return return
} }
defer sam.Close() defer sam.Close()
keys, err := sam.NewKeys() keys, err := sam.NewKeys()
if err != nil { if err != nil {
log.Printf("Failed to generate keys: %v", err) fmt.Printf("Failed to generate keys: %v", err)
return return
} }
// Create a primary session that can manage multiple sub-sessions // Create a primary session that can manage multiple sub-sessions
primary, err := sam.NewPrimarySession("master-session", keys, sam3.Options_Medium) primary, err := sam.NewPrimarySession("master-session", keys, sam3.Options_Medium)
if err != nil { if err != nil {
log.Printf("Failed to create primary session: %v", err) fmt.Printf("Failed to create primary session: %v", err)
return return
} }
defer primary.Close() defer primary.Close()
@@ -104,24 +112,26 @@ func ExampleSAM_NewPrimarySession() {
} }
// ExampleSAM_NewDatagramSession demonstrates creating a datagram session for UDP-like messaging. // ExampleSAM_NewDatagramSession demonstrates creating a datagram session for UDP-like messaging.
//
// Requirements: This example requires a running I2P router with SAM bridge enabled.
func ExampleSAM_NewDatagramSession() { func ExampleSAM_NewDatagramSession() {
sam, err := sam3.NewSAM("127.0.0.1:7656") sam, err := sam3.NewSAM("127.0.0.1:7656")
if err != nil { if err != nil {
log.Printf("Cannot connect to I2P: %v", err) fmt.Printf("Cannot connect to I2P: %v", err)
return return
} }
defer sam.Close() defer sam.Close()
keys, err := sam.NewKeys() keys, err := sam.NewKeys()
if err != nil { if err != nil {
log.Printf("Failed to generate keys: %v", err) fmt.Printf("Failed to generate keys: %v", err)
return return
} }
// Create a datagram session for authenticated messaging // Create a datagram session for authenticated messaging
session, err := sam.NewDatagramSession("udp-app", keys, sam3.Options_Small, 0) session, err := sam.NewDatagramSession("udp-app", keys, sam3.Options_Small, 0)
if err != nil { if err != nil {
log.Printf("Failed to create datagram session: %v", err) fmt.Printf("Failed to create datagram session: %v", err)
return return
} }
defer session.Close() defer session.Close()

View File

@@ -67,8 +67,8 @@ func TestNewPrimarySession(t *testing.T) {
name: "primary session with small tunnel config", name: "primary session with small tunnel config",
idBase: "test_primary_small", idBase: "test_primary_small",
options: []string{ options: []string{
"inbound.length=0", "inbound.length=1",
"outbound.length=0", "outbound.length=1",
"inbound.quantity=1", "inbound.quantity=1",
"outbound.quantity=1", "outbound.quantity=1",
}, },

View File

@@ -24,7 +24,7 @@ func Test_PrimaryDatagramServerClient(t *testing.T) {
return return
} }
sam, err := earlysam.NewPrimarySession("PrimaryTunnel", keys, []string{"inbound.length=0", "outbound.length=0", "inbound.lengthVariance=0", "outbound.lengthVariance=0", "inbound.quantity=1", "outbound.quantity=1"}) sam, err := earlysam.NewPrimarySession("PrimaryTunnel", keys, []string{"inbound.length=1", "outbound.length=1", "inbound.lengthVariance=0", "outbound.lengthVariance=0", "inbound.quantity=1", "outbound.quantity=1"})
if err != nil { if err != nil {
t.Fail() t.Fail()
return return
@@ -54,8 +54,8 @@ func Test_PrimaryDatagramServerClient(t *testing.T) {
return return
} }
fmt.Println("\tClient: Creating tunnel") fmt.Println("\tClient: Creating tunnel")
// ds2, err := sam2.NewDatagramSession("PRIMARYClientTunnel", keys, []string{"inbound.length=0", "outbound.length=0", "inbound.lengthVariance=0", "outbound.lengthVariance=0", "inbound.quantity=1", "outbound.quantity=1"}, 0) // ds2, err := sam2.NewDatagramSession("PRIMARYClientTunnel", keys, []string{"inbound.length=1", "outbound.length=1", "inbound.lengthVariance=0", "outbound.lengthVariance=0", "inbound.quantity=1", "outbound.quantity=1"}, 0)
ds2, err := sam2.NewDatagramSession("PRIMARYClientTunnel", keys, []string{"inbound.length=0", "outbound.length=0", "inbound.lengthVariance=0", "outbound.lengthVariance=0", "inbound.quantity=1", "outbound.quantity=1"}, 0) ds2, err := sam2.NewDatagramSession("PRIMARYClientTunnel", keys, []string{"inbound.length=1", "outbound.length=1", "inbound.lengthVariance=0", "outbound.lengthVariance=0", "inbound.quantity=1", "outbound.quantity=1"}, 0)
if err != nil { if err != nil {
c <- false c <- false
return return

View File

@@ -35,7 +35,7 @@ func Test_PrimaryStreamingDial(t *testing.T) {
return return
} }
sam, err := earlysam.NewPrimarySession("PrimaryTunnel", keys, []string{"inbound.length=0", "outbound.length=0", "inbound.lengthVariance=0", "outbound.lengthVariance=0", "inbound.quantity=1", "outbound.quantity=1"}) sam, err := earlysam.NewPrimarySession("PrimaryTunnel", keys, []string{"inbound.length=1", "outbound.length=1", "inbound.lengthVariance=0", "outbound.lengthVariance=0", "inbound.quantity=1", "outbound.quantity=1"})
if err != nil { if err != nil {
t.Fail() t.Fail()
return return
@@ -98,7 +98,7 @@ func Test_PrimaryStreamingServerClient(t *testing.T) {
return return
} }
sam, err := earlysam.NewPrimarySession("PrimaryServerClientTunnel", keys, []string{"inbound.length=0", "outbound.length=0", "inbound.lengthVariance=0", "outbound.lengthVariance=0", "inbound.quantity=1", "outbound.quantity=1"}) sam, err := earlysam.NewPrimarySession("PrimaryServerClientTunnel", keys, []string{"inbound.length=1", "outbound.length=1", "inbound.lengthVariance=0", "outbound.lengthVariance=0", "inbound.quantity=1", "outbound.quantity=1"})
if err != nil { if err != nil {
t.Fail() t.Fail()
return return

View File

@@ -14,7 +14,7 @@ func TestStreamSession_Listen(t *testing.T) {
defer sam.Close() defer sam.Close()
session, err := NewStreamSession(sam, "test_listen", keys, []string{ session, err := NewStreamSession(sam, "test_listen", keys, []string{
"inbound.length=0", "outbound.length=0", "inbound.length=1", "outbound.length=1",
}) })
if err != nil { if err != nil {
t.Fatalf("Failed to create session: %v", err) t.Fatalf("Failed to create session: %v", err)

View File

@@ -49,8 +49,8 @@ func TestNewStreamSession(t *testing.T) {
name: "session with small tunnel config", name: "session with small tunnel config",
id: "test_stream_small", id: "test_stream_small",
options: []string{ options: []string{
"inbound.length=0", "inbound.length=1",
"outbound.length=0", "outbound.length=1",
"inbound.lengthVariance=0", "inbound.lengthVariance=0",
"outbound.lengthVariance=0", "outbound.lengthVariance=0",
"inbound.quantity=1", "inbound.quantity=1",

View File

@@ -93,7 +93,7 @@ func Test_StreamingServerClient(t *testing.T) {
return return
} }
fmt.Println("\tServer: Creating tunnel") fmt.Println("\tServer: Creating tunnel")
ss, err := sam.NewStreamSession("serverTun", keys, []string{"inbound.length=0", "outbound.length=0", "inbound.lengthVariance=0", "outbound.lengthVariance=0", "inbound.quantity=1", "outbound.quantity=1"}) ss, err := sam.NewStreamSession("serverTun", keys, []string{"inbound.length=1", "outbound.length=1", "inbound.lengthVariance=0", "outbound.lengthVariance=0", "inbound.quantity=1", "outbound.quantity=1"})
if err != nil { if err != nil {
return return
} }
@@ -114,7 +114,7 @@ func Test_StreamingServerClient(t *testing.T) {
return return
} }
fmt.Println("\tClient: Creating tunnel") fmt.Println("\tClient: Creating tunnel")
ss2, err := sam2.NewStreamSession("clientTun", keys, []string{"inbound.length=0", "outbound.length=0", "inbound.lengthVariance=0", "outbound.lengthVariance=0", "inbound.quantity=1", "outbound.quantity=1"}) ss2, err := sam2.NewStreamSession("clientTun", keys, []string{"inbound.length=1", "outbound.length=1", "inbound.lengthVariance=0", "outbound.lengthVariance=0", "inbound.quantity=1", "outbound.quantity=1"})
if err != nil { if err != nil {
c <- false c <- false
return return
@@ -160,48 +160,54 @@ func Test_StreamingServerClient(t *testing.T) {
func ExampleStreamSession() { func ExampleStreamSession() {
// Creates a new StreamingSession, dials to idk.i2p and gets a SAMConn // Creates a new StreamingSession, dials to idk.i2p and gets a SAMConn
// which behaves just like a normal net.Conn. // which behaves just like a normal net.Conn.
//
// 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) sam, err := NewSAM(samBridge)
if err != nil { if err != nil {
fmt.Println(err.Error()) fmt.Printf("Failed to connect to I2P SAM bridge: %v", err)
return return
} }
defer sam.Close() defer sam.Close()
keys, err := sam.NewKeys() keys, err := sam.NewKeys()
if err != nil { if err != nil {
fmt.Println(err.Error()) fmt.Printf("Failed to generate I2P keys: %v", err)
return return
} }
// See the example Option_* variables. // See the example Option_* variables.
ss, err := sam.NewStreamSession("stream_example", keys, Options_Small) ss, err := sam.NewStreamSession("stream_example", keys, Options_Small)
if err != nil { if err != nil {
fmt.Println(err.Error()) fmt.Printf("Failed to create stream session: %v", err)
return return
} }
someone, err := sam.Lookup("idk.i2p") someone, err := sam.Lookup("idk.i2p")
if err != nil { if err != nil {
fmt.Println(err.Error()) fmt.Printf("Failed to lookup idk.i2p: %v", err)
return return
} }
conn, err := ss.DialI2P(someone) conn, err := ss.DialI2P(someone)
if err != nil { if err != nil {
fmt.Println(err.Error()) fmt.Printf("Failed to dial idk.i2p: %v", err)
return return
} }
defer conn.Close() defer conn.Close()
fmt.Println("Sending HTTP GET /") fmt.Println("Sending HTTP GET /")
if _, err := conn.Write([]byte("GET /\n")); err != nil { if _, err := conn.Write([]byte("GET /\n")); err != nil {
fmt.Println(err.Error()) fmt.Printf("Failed to write to connection: %v", err)
return return
} }
buf := make([]byte, 4096) buf := make([]byte, 4096)
n, err := conn.Read(buf) n, err := conn.Read(buf)
if err != nil {
fmt.Printf("Failed to read from connection: %v", err)
return
}
if !strings.Contains(strings.ToLower(string(buf[:n])), "http") && !strings.Contains(strings.ToLower(string(buf[:n])), "html") { if !strings.Contains(strings.ToLower(string(buf[:n])), "http") && !strings.Contains(strings.ToLower(string(buf[:n])), "html") {
fmt.Printf("Probably failed to StreamSession.DialI2P(idk.i2p)? It replied %d bytes, but nothing that looked like http/html", n) fmt.Printf("Failed to get HTTP/HTML response from idk.i2p (got %d bytes)", n)
log.Printf("Probably failed to StreamSession.DialI2P(idk.i2p)? It replied %d bytes, but nothing that looked like http/html", n) return
} else { } else {
fmt.Println("Read HTTP/HTML from idk.i2p") fmt.Println("Read HTTP/HTML from idk.i2p")
log.Println("Read HTTP/HTML from idk.i2p") log.Println("Read HTTP/HTML from idk.i2p")
@@ -217,18 +223,20 @@ func ExampleStreamListener() {
// One server Accept()ing on a StreamListener, and one client that Dials // One server Accept()ing on a StreamListener, and one client that Dials
// through I2P to the server. Server writes "Hello world!" through a SAMConn // through I2P to the server. Server writes "Hello world!" through a SAMConn
// (which implements net.Conn) and the client prints the message. // (which implements net.Conn) and the client prints the message.
//
// 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) sam, err := NewSAM(samBridge)
if err != nil { if err != nil {
fmt.Println(err.Error()) fmt.Printf("Failed to connect to I2P SAM bridge: %v", err)
return return
} }
defer sam.Close() defer sam.Close()
keys, err := sam.NewKeys() keys, err := sam.NewKeys()
if err != nil { if err != nil {
fmt.Println(err.Error()) fmt.Printf("Failed to generate I2P keys: %v", err)
return return
} }
@@ -238,31 +246,33 @@ func ExampleStreamListener() {
go func(server i2pkeys.I2PAddr) { go func(server i2pkeys.I2PAddr) {
csam, err := NewSAM(samBridge) csam, err := NewSAM(samBridge)
if err != nil { if err != nil {
fmt.Println(err.Error()) fmt.Printf("Client failed to connect to I2P: %v", err)
quit <- false
return return
} }
defer csam.Close() defer csam.Close()
keys, err := csam.NewKeys() keys, err := csam.NewKeys()
if err != nil { if err != nil {
fmt.Println(err.Error()) fmt.Printf("Client failed to generate keys: %v", err)
quit <- false
return return
} }
cs, err := csam.NewStreamSession("client_example", keys, Options_Small) cs, err := csam.NewStreamSession("client_example", keys, Options_Small)
if err != nil { if err != nil {
fmt.Println(err.Error()) fmt.Printf("Client failed to create session: %v", err)
quit <- false quit <- false
return return
} }
conn, err := cs.DialI2P(server) conn, err := cs.DialI2P(server)
if err != nil { if err != nil {
fmt.Println(err.Error()) 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 := conn.Read(buf)
if err != nil { if err != nil {
fmt.Println(err.Error()) fmt.Printf("Client failed to read: %v", err)
quit <- false quit <- false
return return
} }
@@ -272,22 +282,30 @@ func ExampleStreamListener() {
ss, err := sam.NewStreamSession("server_example", keys, Options_Small) ss, err := sam.NewStreamSession("server_example", keys, Options_Small)
if err != nil { if err != nil {
fmt.Println(err.Error()) fmt.Printf("Failed to create server session: %v", err)
return return
} }
l, err := ss.Listen() l, err := ss.Listen()
if err != nil { if err != nil {
fmt.Println(err.Error()) fmt.Printf("Failed to listen: %v", err)
return return
} }
conn, err := l.Accept() conn, err := l.Accept()
if err != nil { if err != nil {
fmt.Println(err.Error()) fmt.Printf("Failed to accept connection: %v", err)
return
}
_, err = conn.Write([]byte("Hello world!"))
if err != nil {
fmt.Printf("Failed to write to client: %v", err)
return return
} }
conn.Write([]byte("Hello world!"))
<-quit // waits for client to die, for example only success := <-quit // waits for client to complete
if !success {
fmt.Printf("Client operation failed")
return
}
// Output: // Output:
// Hello world! // Hello world!