HW11 is completed

pull/12/head
Andrey Ivanov 2020-08-28 12:40:06 +03:00 committed by Andrey Ivanov
parent 34aba1ad22
commit 9a628307f4
5 changed files with 158 additions and 9 deletions

View File

@ -1,5 +1,8 @@
module github.com/fixme_my_friend/hw11_telnet_client
module github.com/tiburon-777/HW_OTUS/hw11_telnet_client
go 1.14
require github.com/stretchr/testify v1.5.1
require (
github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.5.1
)

View File

@ -2,6 +2,8 @@ github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=

View File

@ -1,6 +1,53 @@
package main
import (
"flag"
"log"
"net"
"os"
"sync"
"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?
var timeout time.Duration
var wg sync.WaitGroup
flag.DurationVar(&timeout, "timeout", time.Second*10, "Set connection timeout. Default = 10s")
flag.Parse()
args := flag.Args()
if len(args) < 2 {
log.Fatal("incorrect host/port")
}
addr := net.JoinHostPort(args[0], args[1])
client := NewTelnetClient(addr, timeout, os.Stdin, os.Stdout)
if err := client.Connect(); err != nil {
log.Fatal("Can't connect: ", err.Error())
}
log.Println("...connected to", addr)
defer client.Close()
wg.Add(2)
go readRoutine(&wg, client)
go writeRoutine(&wg, client)
wg.Wait()
}
func readRoutine(wg *sync.WaitGroup, client TelnetClient) {
defer wg.Done()
for {
if err := client.Receive(); err != nil {
log.Fatal(err)
return
}
}
}
func writeRoutine(wg *sync.WaitGroup, client TelnetClient) {
defer wg.Done()
for {
if err := client.Send(); err != nil {
log.Fatal(err)
return
}
}
}

View File

@ -1,18 +1,58 @@
package main
import (
"bufio"
"errors"
"io"
"net"
"time"
)
type TelnetClient interface {
// Place your code here
Connect() error
Send() error
Receive() error
Close() error
}
type Client struct {
address string
timeout time.Duration
in io.ReadCloser
inScanner *bufio.Scanner
out io.Writer
conn net.Conn
connScanner *bufio.Scanner
}
func NewTelnetClient(address string, timeout time.Duration, in io.ReadCloser, out io.Writer) TelnetClient {
// Place your code here
return nil
return &Client{address: address, timeout: timeout, in: in, out: out}
}
// Place your code here
// P.S. Author's solution takes no more than 50 lines
func (t *Client) Connect() error {
var err error
t.conn, err = net.DialTimeout("tcp", t.address, t.timeout)
t.inScanner = bufio.NewScanner(t.in)
t.connScanner = bufio.NewScanner(t.conn)
return err
}
func (t *Client) Send() error {
if !t.inScanner.Scan() {
return errors.New("...EOF")
}
_, err := t.conn.Write(append(t.inScanner.Bytes(), '\n'))
return err
}
func (t *Client) Receive() error {
if !t.connScanner.Scan() {
return errors.New("...connection closed by peer")
}
_, err := t.out.Write(append(t.connScanner.Bytes(), '\n'))
return err
}
func (t *Client) Close() error {
return t.conn.Close()
}

View File

@ -62,4 +62,61 @@ func TestTelnetClient(t *testing.T) {
wg.Wait()
})
t.Run("bad host with timeout", func(t *testing.T) {
timeout := time.Second * 5
client := NewTelnetClient("mail.ru:25", timeout, nil, nil)
start := time.Now()
err := client.Connect()
require.Error(t, err, "Can't connect: dial tcp 94.100.180.201:25: i/o timeout")
require.GreaterOrEqual(t, time.Since(start).Milliseconds(), timeout.Milliseconds()-time.Second.Milliseconds())
require.LessOrEqual(t, time.Since(start).Milliseconds(), timeout.Milliseconds()+time.Second.Milliseconds())
})
t.Run("close by peer", func(t *testing.T) {
l, err := net.Listen("tcp", "127.0.0.1:")
require.NoError(t, err)
defer func() { require.NoError(t, l.Close()) }()
var wg sync.WaitGroup
wg.Add(2)
go func() {
defer wg.Done()
in := &bytes.Buffer{}
out := &bytes.Buffer{}
timeout, err := time.ParseDuration("10s")
require.NoError(t, err)
client := NewTelnetClient(l.Addr().String(), timeout, ioutil.NopCloser(in), out)
require.NoError(t, client.Connect())
defer func() { require.NoError(t, client.Close()) }()
in.WriteString("hello\n")
err = client.Send()
require.NoError(t, err)
err = client.Receive()
require.Error(t, err, "...connection closed by peer")
}()
go func() {
defer wg.Done()
conn, err := l.Accept()
require.NoError(t, err)
require.NotNil(t, conn)
request := make([]byte, 1024)
n, err := conn.Read(request)
require.NoError(t, err)
require.Equal(t, "hello\n", string(request)[:n])
require.NoError(t, conn.Close())
}()
wg.Wait()
})
}