Add wscat task.
This commit is contained in:
parent
50b174f4b0
commit
75cb0a68fc
5 changed files with 189 additions and 0 deletions
2
go.mod
2
go.mod
|
@ -9,11 +9,13 @@ require (
|
|||
github.com/google/go-cmp v0.4.0
|
||||
github.com/gorilla/handlers v1.4.2
|
||||
github.com/gorilla/mux v1.7.4
|
||||
github.com/gorilla/websocket v1.4.2
|
||||
github.com/spf13/cobra v0.0.5
|
||||
github.com/stretchr/testify v1.4.0
|
||||
go.uber.org/goleak v1.0.0
|
||||
golang.org/x/net v0.0.0-20190628185345-da137c7871d7
|
||||
golang.org/x/perf v0.0.0-20191209155426-36b577b0eb03
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58
|
||||
golang.org/x/tools v0.0.0-20200125223703-d33eef8e6825
|
||||
gopkg.in/yaml.v2 v2.2.8
|
||||
)
|
||||
|
|
2
go.sum
2
go.sum
|
@ -32,6 +32,8 @@ github.com/gorilla/handlers v1.4.2 h1:0QniY0USkHQ1RGCLfKxeNHK9bkDHGRYGNDFBCS+YAR
|
|||
github.com/gorilla/handlers v1.4.2/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ=
|
||||
github.com/gorilla/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc=
|
||||
github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
|
|
34
wscat/README.md
Normal file
34
wscat/README.md
Normal file
|
@ -0,0 +1,34 @@
|
|||
## wscat
|
||||
|
||||
wscat - примитивный аналог npm пакета [wscat](https://www.npmjs.com/package/wscat).
|
||||
|
||||
Websocket - это двусторониий канал поверх tcp. wscat - это websocket клиент.
|
||||
|
||||
wscat принимает на вход единственный аргумент `-addr` - адрес websocket сервера.
|
||||
После подключения программа начинает читать с stdin'а и отправлять пользовательские строки на сервер,
|
||||
печатая все сообщения от сервера в stdout.
|
||||
|
||||
Клиент должен обрабатывать SIGINT и SIGTERM и плавно завершаться с кодом 0 дожидаясь горутин.
|
||||
Для этого может пригодиться [context](https://golang.org/pkg/context/).
|
||||
|
||||
Обратите внимание на то, что exit code `go run` - это не exit code исполняемого файла.
|
||||
|
||||
## ПримерПрограмма
|
||||
|
||||
Публичный echo сервер:
|
||||
```
|
||||
✗ $GOPATH/bin/wscat -addr ws://echo.websocket.org
|
||||
abc
|
||||
abcdef
|
||||
def^C2020/04/04 05:01:32 received signal interrupt
|
||||
```
|
||||
```
|
||||
✗ echo $?
|
||||
0
|
||||
```
|
||||
|
||||
|
||||
## Ссылки
|
||||
|
||||
1. websocket: https://en.wikipedia.org/wiki/WebSocket
|
||||
2. signal shutdown: https://p.go.manytask.org/06-http/lecture.slide#20
|
7
wscat/main.go
Normal file
7
wscat/main.go
Normal file
|
@ -0,0 +1,7 @@
|
|||
// +build !solution
|
||||
|
||||
package main
|
||||
|
||||
func main() {
|
||||
|
||||
}
|
144
wscat/main_test.go
Normal file
144
wscat/main_test.go
Normal file
|
@ -0,0 +1,144 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"syscall"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"gitlab.com/slon/shad-go/tools/testtool"
|
||||
)
|
||||
|
||||
const importPath = "gitlab.com/slon/shad-go/wscat"
|
||||
|
||||
var binCache testtool.BinCache
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
os.Exit(func() int {
|
||||
var teardown testtool.CloseFunc
|
||||
binCache, teardown = testtool.NewBinCache()
|
||||
defer teardown()
|
||||
|
||||
return m.Run()
|
||||
}())
|
||||
}
|
||||
|
||||
type Conn struct {
|
||||
in io.WriteCloser
|
||||
out *bytes.Buffer
|
||||
}
|
||||
|
||||
func startCommand(t *testing.T, addr string) (conn *Conn, stop func()) {
|
||||
t.Helper()
|
||||
|
||||
binary, err := binCache.GetBinary(importPath)
|
||||
require.NoError(t, err)
|
||||
|
||||
cmd := exec.Command(binary, "-addr", addr)
|
||||
cmd.Stderr = os.Stderr
|
||||
|
||||
out := &bytes.Buffer{}
|
||||
cmd.Stdout = out
|
||||
|
||||
stdin, err := cmd.StdinPipe()
|
||||
require.NoError(t, err)
|
||||
|
||||
require.NoError(t, cmd.Start())
|
||||
|
||||
conn = &Conn{
|
||||
in: stdin,
|
||||
out: out,
|
||||
}
|
||||
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
_ = cmd.Wait()
|
||||
close(done)
|
||||
}()
|
||||
|
||||
stop = func() {
|
||||
defer func() {
|
||||
_ = cmd.Process.Kill()
|
||||
<-done
|
||||
}()
|
||||
|
||||
// try killing softly
|
||||
_ = cmd.Process.Signal(syscall.SIGTERM)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*100)
|
||||
defer cancel()
|
||||
|
||||
select {
|
||||
case <-done:
|
||||
case <-ctx.Done():
|
||||
t.Fatalf("client shutdown timed out")
|
||||
}
|
||||
}
|
||||
|
||||
require.NoError(t, err)
|
||||
return
|
||||
}
|
||||
|
||||
func TestWScat(t *testing.T) {
|
||||
upgrader := websocket.Upgrader{}
|
||||
|
||||
var received, sent [][]byte
|
||||
h := func(w http.ResponseWriter, r *http.Request) {
|
||||
c, err := upgrader.Upgrade(w, r, nil)
|
||||
if err != nil {
|
||||
log.Print("upgrade:", err)
|
||||
return
|
||||
}
|
||||
defer func() { _ = c.Close() }()
|
||||
|
||||
for {
|
||||
_, message, err := c.ReadMessage()
|
||||
if err != nil {
|
||||
t.Logf("error reading message: %s", err)
|
||||
break
|
||||
}
|
||||
received = append(received, message)
|
||||
|
||||
resp := []byte(testtool.RandomName())
|
||||
err = c.WriteMessage(websocket.TextMessage, resp)
|
||||
require.NoError(t, err)
|
||||
sent = append(sent, resp)
|
||||
}
|
||||
}
|
||||
|
||||
s := httptest.NewServer(http.HandlerFunc(h))
|
||||
defer s.Close()
|
||||
|
||||
wsURL := strings.Replace(s.URL, "http", "ws", 1)
|
||||
t.Logf("starting ws server %s", wsURL)
|
||||
|
||||
conn, stop := startCommand(t, wsURL)
|
||||
defer stop()
|
||||
|
||||
var in [][]byte
|
||||
for i := 0; i < 100; i++ {
|
||||
msg := []byte(testtool.RandomName())
|
||||
in = append(in, msg)
|
||||
|
||||
_, err := conn.in.Write(append(msg, '\n'))
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
// give client time to make a request
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
stop()
|
||||
|
||||
require.Equal(t, bytes.Join(in, nil), bytes.Join(received, nil))
|
||||
require.Equal(t, bytes.Join(sent, nil), conn.out.Bytes())
|
||||
}
|
Loading…
Reference in a new issue