HW11 is completed
parent
34aba1ad22
commit
9a628307f4
|
@ -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
|
||||
)
|
||||
|
|
|
@ -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=
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
})
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue