1
0
mirror of https://github.com/kataras/iris.git synced 2026-01-09 13:05:56 +00:00

add websocket client stress test, passed and update the vendors (this commit fixes the https://github.com/kataras/iris/issues/1178 and https://github.com/kataras/iris/issues/1173)

Former-commit-id: 74ccd8f4bf60a71f1eb0e34149a6f19de95a9148
This commit is contained in:
Gerasimos (Makis) Maropoulos
2019-02-14 03:28:41 +02:00
parent 946c100f7d
commit 07994adabb
10 changed files with 247 additions and 70 deletions

View File

@@ -15,16 +15,15 @@ const (
DefaultWebsocketWriteTimeout = 0
// DefaultWebsocketReadTimeout 0, no timeout
DefaultWebsocketReadTimeout = 0
// DefaultWebsocketPongTimeout 60 * time.Second
DefaultWebsocketPongTimeout = 60 * time.Second
// DefaultWebsocketPingPeriod (DefaultPongTimeout * 9) / 10
DefaultWebsocketPingPeriod = (DefaultWebsocketPongTimeout * 9) / 10
// DefaultWebsocketMaxMessageSize 1024
DefaultWebsocketMaxMessageSize = 1024
// DefaultWebsocketReadBufferSize 4096
DefaultWebsocketReadBufferSize = 4096
// DefaultWebsocketWriterBufferSize 4096
DefaultWebsocketWriterBufferSize = 4096
// DefaultWebsocketPingPeriod is 0 but
// could be 10 * time.Second.
DefaultWebsocketPingPeriod = 0
// DefaultWebsocketMaxMessageSize 0
DefaultWebsocketMaxMessageSize = 0
// DefaultWebsocketReadBufferSize 0
DefaultWebsocketReadBufferSize = 0
// DefaultWebsocketWriterBufferSize 0
DefaultWebsocketWriterBufferSize = 0
// DefaultEvtMessageKey is the default prefix of the underline websocket events
// that are being established under the hoods.
//
@@ -76,11 +75,9 @@ type Config struct {
// 0 means no timeout.
// Default value is 0
ReadTimeout time.Duration
// PongTimeout allowed to read the next pong message from the connection.
// Default value is 60 * time.Second
PongTimeout time.Duration
// PingPeriod send ping messages to the connection within this period. Must be less than PongTimeout.
// Default value is 60 *time.Second
// PingPeriod send ping messages to the connection repeatedly after this period.
// The value should be close to the ReadTimeout to avoid issues.
// Default value is 0.
PingPeriod time.Duration
// MaxMessageSize max message size allowed from connection.
// Default value is 1024
@@ -89,12 +86,13 @@ type Config struct {
// compatible if you wanna use the Connection's EmitMessage to send a custom binary data to the client, like a native server-client communication.
// Default value is false
BinaryMessages bool
// ReadBufferSize is the buffer size for the connection reader.
// Default value is 4096
ReadBufferSize int
// WriteBufferSize is the buffer size for the connection writer.
// Default value is 4096
WriteBufferSize int
// ReadBufferSize and WriteBufferSize specify I/O buffer sizes. If a buffer
// size is zero, then buffers allocated by the HTTP server are used. The
// I/O buffer sizes do not limit the size of the messages that can be sent
// or received.
//
// Default value is 0.
ReadBufferSize, WriteBufferSize int
// EnableCompression specify if the server should attempt to negotiate per
// message compression (RFC 7692). Setting this value to true does not
// guarantee that compression will be supported. Currently only "no context
@@ -121,10 +119,6 @@ func (c Config) Validate() Config {
c.ReadTimeout = DefaultWebsocketReadTimeout
}
if c.PongTimeout < 0 {
c.PongTimeout = DefaultWebsocketPongTimeout
}
if c.PingPeriod <= 0 {
c.PingPeriod = DefaultWebsocketPingPeriod
}

View File

@@ -2,6 +2,7 @@ package websocket
import (
"bytes"
stdContext "context"
"errors"
"io"
"net"
@@ -278,6 +279,12 @@ type (
var _ Connection = &connection{}
// WrapConnection wraps the underline websocket connection into a new iris websocket connection.
// The caller should call the `connection#Wait` (which blocks) to enable its read and write functionality.
func WrapConnection(underlineConn UnderlineConnection, cfg ConnectionConfig) Connection {
return newConnection(underlineConn, cfg)
}
func newConnection(underlineConn UnderlineConnection, cfg ConnectionConfig) *connection {
cfg = cfg.Validate()
c := &connection{
@@ -306,7 +313,6 @@ func newServerConnection(ctx context.Context, s *Server, underlineConn Underline
EvtMessagePrefix: s.config.EvtMessagePrefix,
WriteTimeout: s.config.WriteTimeout,
ReadTimeout: s.config.ReadTimeout,
PongTimeout: s.config.PongTimeout,
PingPeriod: s.config.PingPeriod,
MaxMessageSize: s.config.MaxMessageSize,
BinaryMessages: s.config.BinaryMessages,
@@ -383,24 +389,25 @@ func (c *connection) startPinger() {
c.underline.SetPingHandler(pingHandler)
go func() {
for {
// using sleep avoids the ticker error that causes a memory leak
time.Sleep(c.config.PingPeriod)
if atomic.LoadUint32(&c.disconnected) > 0 {
// verifies if already disconected
break
if c.config.PingPeriod > 0 {
go func() {
for {
time.Sleep(c.config.PingPeriod)
if c == nil || atomic.LoadUint32(&c.disconnected) > 0 {
// verifies if already disconected.
return
}
//fire all OnPing methods
c.fireOnPing()
// try to ping the client, if failed then it disconnects.
err := c.Write(websocket.PingMessage, []byte{})
if err != nil {
// must stop to exit the loop and exit from the routine.
return
}
}
//fire all OnPing methods
c.fireOnPing()
// try to ping the client, if failed then it disconnects
err := c.Write(websocket.PingMessage, []byte{})
if err != nil {
// must stop to exit the loop and finish the go routine
break
}
}
}()
}()
}
}
func (c *connection) fireOnPing() {
@@ -444,14 +451,13 @@ func (c *connection) startReader() {
_, data, err := conn.ReadMessage()
if err != nil {
if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseAbnormalClosure) {
if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseAbnormalClosure, websocket.CloseNormalClosure) {
c.FireOnError(err)
}
break
} else {
c.messageReceived(data)
return
}
c.messageReceived(data)
}
}
@@ -722,14 +728,12 @@ type ConnectionConfig struct {
// 0 means no timeout.
// Default value is 0
ReadTimeout time.Duration
// PongTimeout allowed to read the next pong message from the connection.
// Default value is 60 * time.Second
PongTimeout time.Duration
// PingPeriod send ping messages to the connection within this period. Must be less than PongTimeout.
// Default value is 60 *time.Second
// PingPeriod send ping messages to the connection repeatedly after this period.
// The value should be close to the ReadTimeout to avoid issues.
// Default value is 0
PingPeriod time.Duration
// MaxMessageSize max message size allowed from connection.
// Default value is 1024
// Default value is 0. Unlimited but it is recommended to be 1024 for medium to large messages.
MaxMessageSize int64
// BinaryMessages set it to true in order to denotes binary data messages instead of utf-8 text
// compatible if you wanna use the Connection's EmitMessage to send a custom binary data to the client, like a native server-client communication.
@@ -765,10 +769,6 @@ func (c ConnectionConfig) Validate() ConnectionConfig {
c.ReadTimeout = DefaultWebsocketReadTimeout
}
if c.PongTimeout < 0 {
c.PongTimeout = DefaultWebsocketPongTimeout
}
if c.PingPeriod <= 0 {
c.PingPeriod = DefaultWebsocketPingPeriod
}
@@ -788,22 +788,42 @@ func (c ConnectionConfig) Validate() ConnectionConfig {
return c
}
// Dial opens a new client connection to a WebSocket.
// ErrBadHandshake is returned when the server response to opening handshake is
// invalid.
var ErrBadHandshake = websocket.ErrBadHandshake
// DialContext creates a new client connection.
//
// The context will be used in the request and in the Dialer.
//
// If the WebSocket handshake fails, `ErrBadHandshake` is returned.
//
// The "url" input parameter is the url to connect to the server, it should be
// the ws:// (or wss:// if secure) + the host + the endpoint of the
// open socket of the server, i.e ws://localhost:8080/my_websocket_endpoint.
func Dial(url string, cfg ConnectionConfig) (ClientConnection, error) {
//
// Custom dialers can be used by wrapping the iris websocket connection via `websocket.WrapConnection`.
func DialContext(ctx stdContext.Context, url string, cfg ConnectionConfig) (ClientConnection, error) {
if ctx == nil {
ctx = stdContext.Background()
}
if !strings.HasPrefix(url, "ws://") {
url = "ws://" + url
}
conn, _, err := websocket.DefaultDialer.Dial(url, nil)
conn, _, err := websocket.DefaultDialer.DialContext(ctx, url, nil)
if err != nil {
return nil, err
}
clientConn := newConnection(conn, cfg)
clientConn := WrapConnection(conn, cfg)
go clientConn.Wait()
return clientConn, nil
}
// Dial creates a new client connection by calling `DialContext` with a background context.
func Dial(url string, cfg ConnectionConfig) (ClientConnection, error) {
return DialContext(stdContext.Background(), url, cfg)
}

View File

@@ -34,7 +34,7 @@ func (e *emitter) EmitMessage(nativeMessage []byte) error {
}
func (e *emitter) Emit(event string, data interface{}) error {
message, err := e.conn.server.messageSerializer.serialize(event, data)
message, err := e.conn.serializer.serialize(event, data)
if err != nil {
return err
}

View File

@@ -81,7 +81,7 @@ var (
)
// websocketMessageSerialize serializes a custom websocket message from websocketServer to be delivered to the client
// returns the string form of the message
// returns the string form of the message
// Supported data types are: string, int, bool, bytes and JSON.
func (ms *messageSerializer) serialize(event string, data interface{}) ([]byte, error) {
b := ms.buf.Get()
@@ -114,6 +114,7 @@ func (ms *messageSerializer) serialize(event string, data interface{}) ([]byte,
//we suppose is json
res, err := json.Marshal(data)
if err != nil {
ms.buf.Put(b)
return nil, err
}
b.WriteString(messageTypeJSON.String())

View File

@@ -44,7 +44,6 @@ type (
// Use a route to serve this file on a specific path, i.e
// app.Any("/iris-ws.js", func(ctx iris.Context) { ctx.Write(mywebsocketServer.ClientSource) })
ClientSource []byte
messageSerializer *messageSerializer
connections map[string]*connection // key = the Connection ID.
rooms map[string][]string // by default a connection is joined to a room which has the connection id as its name
mu sync.RWMutex // for rooms and connections.
@@ -64,7 +63,6 @@ func New(cfg Config) *Server {
return &Server{
config: cfg,
ClientSource: bytes.Replace(ClientSource, []byte(DefaultEvtMessageKey), cfg.EvtMessagePrefix, -1),
messageSerializer: newMessageSerializer(cfg.EvtMessagePrefix),
connections: make(map[string]*connection),
rooms: make(map[string][]string),
onConnectionListeners: make([]ConnectionFunc, 0),

View File

@@ -4,11 +4,6 @@ Source code and other details for the project are available at GitHub:
https://github.com/kataras/iris/tree/master/websocket
Installation
$ go get -u github.com/kataras/iris/websocket
Example code: