Merge branch 'master' into hw12_13_14_15_calendar
commit
acc97b1ac9
|
@ -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
|
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/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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
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/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 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
|
||||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
|
|
|
@ -1,6 +1,53 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
// Place your code here
|
var timeout time.Duration
|
||||||
// P.S. Do not rush to throw context down, think think if it is useful with blocking operation?
|
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
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
|
"net"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type TelnetClient interface {
|
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 {
|
func NewTelnetClient(address string, timeout time.Duration, in io.ReadCloser, out io.Writer) TelnetClient {
|
||||||
// Place your code here
|
return &Client{address: address, timeout: timeout, in: in, out: out}
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Place your code here
|
func (t *Client) Connect() error {
|
||||||
// P.S. Author's solution takes no more than 50 lines
|
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()
|
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