WebSocket client

This commit is contained in:
crusader@loafle.com 2017-08-10 16:39:58 +09:00
parent 53f87c2c80
commit abaf9c217a
10 changed files with 272 additions and 2 deletions

11
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,11 @@
// Place your settings in this file to overwrite default and user settings.
{
// Specifies Lint tool name.
"go.lintTool": "gometalinter",
// Flags to pass to Lint tool (e.g. ["-min_confidence=.8"])
"go.lintFlags": [
"--config=${workspaceRoot}/golint.json"
]
}

View File

@ -21,10 +21,10 @@ import:
- google
- package: google.golang.org/grpc
- package: gopkg.in/yaml.v2
- package: github.com/gorilla/websocket
version: v1.2.0
testImport:
- package: github.com/google/uuid
- package: github.com/stretchr/testify
subpackages:
- assert

39
golint.json Normal file
View File

@ -0,0 +1,39 @@
{
"DisableAll": true,
"Enable": [
"aligncheck",
"deadcode",
"dupl",
"errcheck",
"gas",
"goconst",
"gocyclo",
"gofmt",
"goimports",
"golint",
"gotype",
"ineffassign",
"interfacer",
"lll",
"megacheck",
"misspell",
"structcheck",
"test",
"testify",
"unconvert",
"varcheck",
"vet",
"vetshadow"
],
"Aggregate": true,
"Concurrency": 16,
"Cyclo": 60,
"Deadline": "60s",
"DuplThreshold": 50,
"EnableGC": true,
"LineLength": 120,
"MinConfidence": 0.8,
"MinOccurrences": 3,
"MinConstLength": 3,
"Sort": ["severity"]
}

23
websocket/options.go Normal file
View File

@ -0,0 +1,23 @@
package websocket
import (
"net/http"
"time"
)
type Options interface {
}
type options struct {
OnError func(res http.ResponseWriter, req *http.Request, status int, reason error)
OnCheckOrigin func(req *http.Request) bool
WriteTimeout time.Duration
ReadTimeout time.Duration
PongTimeout time.Duration
PingTimeout time.Duration
PingPeriod time.Duration
MaxMessageSize int64
BinaryMessage bool
ReadBufferSize int
WriteBufferSize int
}

View File

@ -0,0 +1,30 @@
package protocol
import "errors"
// ErrorCode is type when used to communicate with WebSocket RPC
type ErrorCode int
const (
// EParse is error code that is invalid json
EParse ErrorCode = -32700
// EInvalidReq is error code that is invalid request
EInvalidReq ErrorCode = -32600
// ENotFoundMethod is error code that is not exist method
ENotFoundMethod ErrorCode = -32601
// EInvalidParams is error code that is invalid parameters
EInvalidParams ErrorCode = -32602
// EInternal is error code that is internel error
EInternal ErrorCode = -32603
// EServer is error code that is server error
EServer ErrorCode = -32000
)
var ErrNullResult = errors.New("result is null")
// Error is error struct when used to communicate with WebSocket RPC
type Error struct {
Code ErrorCode `json:"code"`
Message string `json:"message"`
Data interface{} `json:"data,omitempty"`
}

View File

@ -0,0 +1,6 @@
package protocol
// Header is struct when used to communicate with WebSocket RPC
type Header struct {
Protocol string `json:"protocol"`
}

View File

@ -0,0 +1,8 @@
package protocol
// Notification is struct when used to communicate with WebSocket RPC
type Notification struct {
Header
Method string `json:"method"`
Params []string `json:"params,omitempty"`
}

View File

@ -0,0 +1,7 @@
package protocol
// Request is struct when used to communicate with WebSocket RPC
type Request struct {
Notification
ID int64 `json:"id"`
}

View File

@ -0,0 +1,9 @@
package protocol
// Response is struct when used to communicate with WebSocket RPC
type Response struct {
Header
Result *string `json:"result,omitempty"`
Error *Error `json:"error,omitempty"`
ID int64 `json:"id"`
}

137
websocket/rpc.go Normal file
View File

@ -0,0 +1,137 @@
package websocket
import (
"encoding/json"
"fmt"
"io"
"log"
"sync"
protocol "git.loafle.net/overflow/overflow_probe/websocket/protocol"
"github.com/gorilla/websocket"
)
const (
protocolName string = "1.0"
)
type CallCallback func(interface{}, *protocol.Error)
type WebSocketRPC interface {
Call(cb CallCallback, method string, params []string)
Send(method string, params []string)
}
type webSocketRPC struct {
conn *websocket.Conn
messageType int
writeMTX sync.Mutex
requestID int64
requestQueue map[int64]CallCallback
}
// New creates a websocket rpc client and returns it
func New(serverURL string) WebSocketRPC {
return newInstance(serverURL)
}
func newInstance(serverURL string) WebSocketRPC {
var dialer *websocket.Dialer
conn, _, err := dialer.Dial(serverURL, nil)
if err != nil {
fmt.Println(err)
}
w := &webSocketRPC{
conn: conn,
requestID: 0,
requestQueue: make(map[int64]CallCallback),
}
return w
}
func (w *webSocketRPC) readHandler() {
for {
// messageType, data, err := c.conn.ReadMessage()
messageType, r, err := w.conn.NextReader()
if err != nil {
if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway) {
}
break
} else {
w.onMessageReceived(messageType, r)
}
}
}
func (w *webSocketRPC) onMessageReceived(messageType int, r io.Reader) {
res := new(protocol.Response)
err := json.NewDecoder(r).Decode(res)
if err != nil {
// Check message is Notification
noti := new(protocol.Notification)
err = json.NewDecoder(r).Decode(noti)
if err != nil {
log.Println(err)
}
w.onNotificationReceived(noti)
}
w.onResponseReceived(res)
}
func (w *webSocketRPC) onResponseReceived(r *protocol.Response) {
cb := w.requestQueue[r.ID]
cb(r.Result, r.Error)
}
func (w *webSocketRPC) onNotificationReceived(n *protocol.Notification) {
}
func (w *webSocketRPC) Call(cb CallCallback, method string, params []string) {
w.writeMTX.Lock()
w.requestID++
rID := w.requestID
req := new(protocol.Request)
req.Protocol = protocolName
req.Method = method
req.Params = params
req.ID = rID
jReq, err := json.Marshal(req)
if nil != err {
log.Println(fmt.Errorf("%v", err))
}
err = w.conn.WriteMessage(w.messageType, jReq)
w.writeMTX.Unlock()
if nil != err {
}
w.requestQueue[rID] = cb
}
func (w *webSocketRPC) Send(method string, params []string) {
w.writeMTX.Lock()
req := new(protocol.Request)
req.Protocol = protocolName
req.Method = method
req.Params = params
jReq, err := json.Marshal(req)
if nil != err {
log.Println(fmt.Errorf("%v", err))
}
err = w.conn.WriteMessage(w.messageType, jReq)
w.writeMTX.Unlock()
if nil != err {
}
}