1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
|
package centrifuge
import (
"encoding/json"
"fmt"
"strings"
"sync"
)
// Disconnect allows to configure how client will be disconnected from server.
// The important note that Disconnect serialized to JSON must be less than 127 bytes
// due to WebSocket protocol limitations (because at moment we send Disconnect inside
// reason field of WebSocket close handshake).
// Note that due to performance reasons we cache Disconnect text representation
// for Close Frame on first send to client so changing field values inside existing
// Disconnect instance won't be reflected in WebSocket/Sockjs Close frames.
type Disconnect struct {
// Code is disconnect code.
Code uint32 `json:"code,omitempty"`
// Reason is a short description of disconnect.
Reason string `json:"reason"`
// Reconnect gives client an advice to reconnect after disconnect or not.
Reconnect bool `json:"reconnect"`
closeTextOnce sync.Once
cachedCloseText string
}
var _ error = (*Disconnect)(nil)
// String representation.
func (d *Disconnect) String() string {
return fmt.Sprintf("code: %d, reason: %s, reconnect: %t", d.Code, d.Reason, d.Reconnect)
}
// Error representation.
func (d *Disconnect) Error() string {
return fmt.Sprintf("disconnected: code: %d, reason: %s, reconnect: %t", d.Code, d.Reason, d.Reconnect)
}
// CloseText allows to build disconnect advice sent inside Close frame.
// At moment we don't encode Code here to not duplicate information
// since it is sent separately as Code of WebSocket/SockJS Close Frame.
func (d *Disconnect) CloseText() string {
d.closeTextOnce.Do(func() {
buf := strings.Builder{}
buf.WriteString(`{"reason":`)
reason, _ := json.Marshal(d.Reason)
buf.Write(reason)
buf.WriteString(`,"reconnect":`)
if d.Reconnect {
buf.WriteString("true")
} else {
buf.WriteString("false")
}
buf.WriteString(`}`)
d.cachedCloseText = buf.String()
})
return d.cachedCloseText
}
// Some predefined disconnect structures used by library internally. Though
// it's always possible to create Disconnect with any field values on the fly.
// Library users supposed to use codes in range 4000-4999 for custom disconnects.
var (
// DisconnectNormal is clean disconnect when client cleanly closed connection.
DisconnectNormal = &Disconnect{
Code: 3000,
Reason: "normal",
Reconnect: true,
}
// DisconnectShutdown sent when node is going to shut down.
DisconnectShutdown = &Disconnect{
Code: 3001,
Reason: "shutdown",
Reconnect: true,
}
// DisconnectInvalidToken sent when client came with invalid token.
DisconnectInvalidToken = &Disconnect{
Code: 3002,
Reason: "invalid token",
Reconnect: false,
}
// DisconnectBadRequest sent when client uses malformed protocol
// frames or wrong order of commands.
DisconnectBadRequest = &Disconnect{
Code: 3003,
Reason: "bad request",
Reconnect: false,
}
// DisconnectServerError sent when internal error occurred on server.
DisconnectServerError = &Disconnect{
Code: 3004,
Reason: "internal server error",
Reconnect: true,
}
// DisconnectExpired sent when client connection expired.
DisconnectExpired = &Disconnect{
Code: 3005,
Reason: "expired",
Reconnect: true,
}
// DisconnectSubExpired sent when client subscription expired.
DisconnectSubExpired = &Disconnect{
Code: 3006,
Reason: "subscription expired",
Reconnect: true,
}
// DisconnectStale sent to close connection that did not become
// authenticated in configured interval after dialing.
DisconnectStale = &Disconnect{
Code: 3007,
Reason: "stale",
Reconnect: false,
}
// DisconnectSlow sent when client can't read messages fast enough.
DisconnectSlow = &Disconnect{
Code: 3008,
Reason: "slow",
Reconnect: true,
}
// DisconnectWriteError sent when an error occurred while writing to
// client connection.
DisconnectWriteError = &Disconnect{
Code: 3009,
Reason: "write error",
Reconnect: true,
}
// DisconnectInsufficientState sent when server detects wrong client
// position in channel Publication stream. Disconnect allows client
// to restore missed publications on reconnect.
DisconnectInsufficientState = &Disconnect{
Code: 3010,
Reason: "insufficient state",
Reconnect: true,
}
// DisconnectForceReconnect sent when server disconnects connection.
DisconnectForceReconnect = &Disconnect{
Code: 3011,
Reason: "force reconnect",
Reconnect: true,
}
// DisconnectForceNoReconnect sent when server disconnects connection
// and asks it to not reconnect again.
DisconnectForceNoReconnect = &Disconnect{
Code: 3012,
Reason: "force disconnect",
Reconnect: false,
}
// DisconnectConnectionLimit can be sent when client connection exceeds a
// configured connection limit (per user ID or due to other rule).
DisconnectConnectionLimit = &Disconnect{
Code: 3013,
Reason: "connection limit",
Reconnect: false,
}
)
|