Skip to content

Commit

Permalink
Merge pull request #11 from DimVlas/hw11_telnet_client
Browse files Browse the repository at this point in the history
Hw11 telnet client
  • Loading branch information
DimVlas authored Jan 8, 2025
2 parents 4a823b8 + 92d712c commit 8100cd0
Show file tree
Hide file tree
Showing 9 changed files with 344 additions and 24 deletions.
6 changes: 1 addition & 5 deletions hw09_struct_validator/rules/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,6 @@ var (

var (
// программные ошибки функций валидации.
// // правило применимо только к строкам
// ErrOnlyStringRule = errors.New("rule applies only to the string")
// // правило применимо только к целым
// ErrOnlyIntRule = errors.New("rule applies only to the int")
// недопустимое условие для правила.
ErrInvalidCond = errors.New("invalid condition for the rule")
// ошибка компиляции регулярного выражения.
Expand All @@ -40,7 +36,7 @@ var (
var (
// целое не может быть меньше условия.
ErrIntCantBeLess = errors.New("cannot be less")
// целое не может быть меньше больше.
// целое не может быть больше условия.
ErrIntCantBeGreater = errors.New("cannot be greater")
// целое на входит в список.
ErrIntNotInList = errors.New("int is not in the list")
Expand Down
12 changes: 0 additions & 12 deletions hw09_struct_validator/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,6 @@ import (
r "github.com/DimVlas/otus_hw/hw09_struct_validator/rules"
)

// implemented in errors.go file
// type ValidationError struct {
// Field string
// Err error
// }

// type ValidationErrors []ValidationError

// func (v ValidationErrors) Error() string {
// panic("implement me")
// }

func Validate(v interface{}) error {
// nothing to validate
if v == nil {
Expand Down
1 change: 0 additions & 1 deletion hw09_struct_validator/validator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import (
"github.com/stretchr/testify/require"
)

// Test the function on different structures and other types.
type (
UserRole string

Expand Down
Empty file removed hw11_telnet_client/.sync
Empty file.
2 changes: 1 addition & 1 deletion hw11_telnet_client/go.mod
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
module github.com/fixme_my_friend/hw11_telnet_client
module github.com/DimVlas/otus_hw/hw11_telnet_client

go 1.22

Expand Down
90 changes: 88 additions & 2 deletions hw11_telnet_client/main.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,92 @@
package main

import (
"errors"
"fmt"
"log"
"os"
"os/signal"
"path"
"syscall"
"time"
)

var (
ErrParseArgs = errors.New("parse args error")
// соединение не установлено.
ErrConnNotEstablish = errors.New("connection is not established")
// завершение клиента.
ErrClientExit = errors.New("connection was closed by client")
// завершение сервера.
ErrServerExit = errors.New("connection was closed by peer")
)

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
host string
port int
)

if err := parseArgs(os.Args[1:], &timeout, &host, &port); err != nil {
log.Println(err)
log.Println("Usage: go-telnet [--timeout=<timeout>] <host> <port>")
os.Exit(1)
}

addr := fmt.Sprintf("%s:%d", host, port)
client := NewTelnetClient(addr, timeout, os.Stdin, os.Stdout)

if err := client.Connect(); err != nil {
log.Fatalln(err)
}

log.Printf("...Connected to %s", addr)

c := make(chan os.Signal, 1)
clientErr := make(chan error)

defer func() {
client.Close()
close(c)
close(clientErr)
}()

signal.Notify(c, syscall.SIGINT)

go func() {
err := client.Send()
if err != nil {
clientErr <- err
return
}

clientErr <- ErrClientExit
}()

go func() {
err := client.Receive()
if err != nil {
clientErr <- err
return
}
clientErr <- ErrServerExit
}()

select {
case <-c:
log.Println("...Connection was closed by", path.Base(os.Args[0]))
return
case err := <-clientErr:
if errors.Is(err, ErrClientExit) {
log.Println("...EOF")
return
}
if errors.Is(err, ErrServerExit) {
log.Println("...Connection was closed by peer")
return
}
log.Println(err)
return
}
}
40 changes: 40 additions & 0 deletions hw11_telnet_client/params.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package main

import (
"bytes"
"errors"
"flag"
"fmt"
"regexp"
"strconv"
"time"
)

func parseArgs(args []string, timeout *time.Duration, host *string, port *int) error {
fs := flag.NewFlagSet("telnet", flag.ContinueOnError)
fs.SetOutput(bytes.NewBuffer(nil))

fs.DurationVar(timeout, "timeout", 10*time.Second, "Connection timeout")
if err := fs.Parse(args); err != nil {
return fmt.Errorf("%w: %w", ErrParseArgs, err)
}

if fs.NArg() != 2 {
return fmt.Errorf("%w: %w", ErrParseArgs, errors.New("not enough arguments"))
}

h := fs.Arg(0)
b, _ := regexp.MatchString(`^[A-Za-z0-9-.]+$`, h)
if !b {
return fmt.Errorf("%w %w", ErrParseArgs, errors.New("host: string \""+h+"\" cannot be a host name or address"))
}
*host = h

p, err := strconv.Atoi(fs.Arg(1))
if err != nil {
return fmt.Errorf("%w %w", ErrParseArgs, fmt.Errorf("port: %w", err))
}
*port = p

return nil
}
70 changes: 67 additions & 3 deletions hw11_telnet_client/telnet.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package main

import (
"bufio"
"errors"
"io"
"net"
"time"
)

Expand All @@ -13,9 +16,70 @@ type TelnetClient interface {
}

func NewTelnetClient(address string, timeout time.Duration, in io.ReadCloser, out io.Writer) TelnetClient {
// Place your code here.
return &telnetClient{
addr: address,
timeout: timeout,
in: in,
out: out,
}
}

type telnetClient struct {
timeout time.Duration
addr string
conn net.Conn
in io.ReadCloser
out io.Writer
}

func (tc *telnetClient) Connect() (err error) {
tc.conn, err = net.DialTimeout("tcp", tc.addr, tc.timeout)
return
}

func (tc *telnetClient) Close() error {
if tc.conn != nil {
return tc.conn.Close()
}
return nil
}

// Place your code here.
// P.S. Author's solution takes no more than 50 lines.
func (tc *telnetClient) Send() error {
if tc.conn == nil {
return ErrConnNotEstablish
}

scanner := bufio.NewScanner(tc.in)
for scanner.Scan() {
mess := append(scanner.Bytes(), '\n')
if _, err := tc.conn.Write(mess); err != nil {
return err
}
}
if err := scanner.Err(); err != nil {
return err
}

return nil
}

func (tc *telnetClient) Receive() error {
if tc.conn == nil {
return ErrConnNotEstablish
}

reader := bufio.NewReader(tc.conn)
for {
message, err := reader.ReadBytes('\n')
if err != nil {
if errors.Is(err, io.EOF) {
return nil
}
return err
}
_, err = tc.out.Write(message)
if err != nil {
return err
}
}
}
Loading

0 comments on commit 8100cd0

Please sign in to comment.