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:
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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:
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user