From 878312fefd6ae1f8f7a5a23b24edcb088c2b12f2 Mon Sep 17 00:00:00 2001
From: Pavel Pogodaev
Date: Tue, 19 Nov 2024 15:29:54 +0300
Subject: [PATCH] HW11 is completed
Signed-off-by: Pavel Pogodaev
---
hw11_telnet_client/.sync | 0
hw11_telnet_client/main.go | 94 ++++++++++++++++++++++++++++++-
hw11_telnet_client/telnet.go | 63 ++++++++++++++++++++-
hw11_telnet_client/telnet_test.go | 2 +-
4 files changed, 153 insertions(+), 6 deletions(-)
delete mode 100644 hw11_telnet_client/.sync
diff --git a/hw11_telnet_client/.sync b/hw11_telnet_client/.sync
deleted file mode 100644
index e69de29..0000000
diff --git a/hw11_telnet_client/main.go b/hw11_telnet_client/main.go
index 307acaf..939fe94 100644
--- a/hw11_telnet_client/main.go
+++ b/hw11_telnet_client/main.go
@@ -1,6 +1,96 @@
package main
+import (
+ "bufio"
+ "bytes"
+ "context"
+ "flag"
+ "fmt"
+ "io"
+ "os"
+ "os/signal"
+ "sync"
+ "syscall"
+ "time"
+)
+
func main() {
- // Place your code here,
- // P.S. Do not rush to throw context down, think think if it is useful with blocking operation?
+ flag.Parse()
+ port := os.Args[len(os.Args)-1]
+ host := os.Args[len(os.Args)-2]
+ duration := os.Args[len(os.Args)-3]
+
+ durVal, err := time.ParseDuration(duration)
+ if err != nil {
+ panic("wrong duration value " + duration)
+ }
+
+ in := &bytes.Buffer{}
+
+ ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGTERM, syscall.SIGINT, os.Interrupt)
+ defer stop()
+
+ client := NewTelnetClient(host+":"+port, durVal, io.NopCloser(in), os.Stdout)
+ defer client.Close()
+
+ err = client.Connect()
+ if err != nil {
+ fmt.Printf("connection failed: %s\n", err)
+ return
+ }
+
+ var wg sync.WaitGroup
+
+ wg.Add(2)
+ go sendByTcp(ctx, stop, &wg, in, client)
+ go connectionHandler(ctx, &wg, client)
+
+ wg.Wait()
+}
+
+func sendByTcp(ctx context.Context, stop context.CancelFunc, wg *sync.WaitGroup, in *bytes.Buffer, t TelnetClient) {
+ go func() {
+ <-ctx.Done()
+ err := t.Close()
+ if err != nil {
+ fmt.Printf("failed to colse connection %s", err)
+ }
+ wg.Done()
+ }()
+
+ reader := bufio.NewReader(os.Stdin)
+ for {
+ resp, err := reader.ReadString('\n')
+ if err != nil {
+ stop()
+ return
+ }
+
+ in.WriteString(resp)
+ err = t.Send()
+ if err != nil {
+ fmt.Println(err)
+ return
+ }
+ }
+}
+
+func connectionHandler(ctx context.Context, wg *sync.WaitGroup, t TelnetClient) {
+ errCh := make(chan error)
+ defer func() {
+ wg.Done()
+ }()
+
+ for {
+ select {
+ case <-ctx.Done():
+ return
+ case errCh <- t.Receive():
+ err := <-errCh
+ if err != nil {
+ fmt.Println(err)
+ return
+ }
+ }
+ }
}
diff --git a/hw11_telnet_client/telnet.go b/hw11_telnet_client/telnet.go
index 369caa1..f89c634 100644
--- a/hw11_telnet_client/telnet.go
+++ b/hw11_telnet_client/telnet.go
@@ -1,7 +1,10 @@
package main
import (
+ "errors"
+ "fmt"
"io"
+ "net"
"time"
)
@@ -12,10 +15,64 @@ type TelnetClient interface {
Receive() error
}
+type ConnectionParams struct {
+ Address string
+ Timeout time.Duration
+ In io.ReadCloser
+ Out io.Writer
+ Conn net.Conn
+}
+
func NewTelnetClient(address string, timeout time.Duration, in io.ReadCloser, out io.Writer) TelnetClient {
- // Place your code here.
+ return &ConnectionParams{
+ Address: address,
+ Timeout: timeout,
+ In: in,
+ Out: out,
+ }
+}
+
+func (c *ConnectionParams) Connect() error {
+ conn, err := net.DialTimeout("tcp", c.Address, c.Timeout)
+ if err != nil {
+ return fmt.Errorf("connection error: %w", err)
+ }
+
+ c.Conn = conn
+ fmt.Println("connected to ", c.Address)
+
return nil
}
-// Place your code here.
-// P.S. Author's solution takes no more than 50 lines.
+func (c *ConnectionParams) Close() error {
+ if c.Conn != nil {
+ if err := c.Conn.Close(); err != nil {
+ return fmt.Errorf("close error: %w", err)
+ }
+ }
+
+ return nil
+}
+
+func (c *ConnectionParams) Send() error {
+ size, err := io.Copy(c.Conn, c.In)
+ if err != nil {
+ return fmt.Errorf("send error: %w", err)
+ }
+ fmt.Printf("sent %d bytes", size)
+ fmt.Println()
+
+ return nil
+}
+
+func (c *ConnectionParams) Receive() error {
+ _, err := io.Copy(c.Out, c.Conn)
+ if err != nil {
+ if errors.Is(err, io.EOF) {
+ return nil
+ }
+ return fmt.Errorf("receive error: %w", err)
+ }
+
+ return nil
+}
diff --git a/hw11_telnet_client/telnet_test.go b/hw11_telnet_client/telnet_test.go
index fb89f6d..eee771b 100644
--- a/hw11_telnet_client/telnet_test.go
+++ b/hw11_telnet_client/telnet_test.go
@@ -8,7 +8,7 @@ import (
"testing"
"time"
- "github.com/stretchr/testify/require"
+ "github.com/stretchr/testify/require" //nolint:depguard
)
func TestTelnetClient(t *testing.T) {