[MM-19914] Fix data races in WebSocketEvent (#13039)

* Make WebSocketEvent type immutable

* Update code to use updated immutable WebSocketEvent type

* Export WebSocketEvent fields and mark them as deprecated
Этот коммит содержится в:
Claudio Costa
2019-12-24 09:32:11 +01:00
коммит произвёл GitHub
родитель 9ab7bee0a6
Коммит 80dd2915db
16 изменённых файлов: 216 добавлений и 94 удалений

Просмотреть файл

@@ -76,30 +76,41 @@ type precomputedWebSocketEventJSON struct {
Broadcast json.RawMessage
}
type WebSocketEvent struct {
// webSocketEventJSON mirrors WebSocketEvent to make some of its unexported fields serializable
type webSocketEventJSON struct {
Event string `json:"event"`
Data map[string]interface{} `json:"data"`
Broadcast *WebsocketBroadcast `json:"broadcast"`
Sequence int64 `json:"seq"`
}
// **NOTE**: Direct access to WebSocketEvent fields is deprecated. They will be
// made unexported in next major version release. Provided getter functions should be used instead.
type WebSocketEvent struct {
Event string // Deprecated: use EventType()
Data map[string]interface{} // Deprecated: use GetData()
Broadcast *WebsocketBroadcast // Deprecated: use GetBroadcast()
Sequence int64 // Deprecated: use GetSequence()
precomputedJSON *precomputedWebSocketEventJSON
}
// PrecomputeJSON precomputes and stores the serialized JSON for all fields other than Sequence.
// This makes ToJson much more efficient when sending the same event to multiple connections.
func (m *WebSocketEvent) PrecomputeJSON() {
event, _ := json.Marshal(m.Event)
data, _ := json.Marshal(m.Data)
broadcast, _ := json.Marshal(m.Broadcast)
m.precomputedJSON = &precomputedWebSocketEventJSON{
func (ev *WebSocketEvent) PrecomputeJSON() *WebSocketEvent {
copy := ev.Copy()
event, _ := json.Marshal(copy.Event)
data, _ := json.Marshal(copy.Data)
broadcast, _ := json.Marshal(copy.Broadcast)
copy.precomputedJSON = &precomputedWebSocketEventJSON{
Event: json.RawMessage(event),
Data: json.RawMessage(data),
Broadcast: json.RawMessage(broadcast),
}
return copy
}
func (m *WebSocketEvent) Add(key string, value interface{}) {
m.Data[key] = value
func (ev *WebSocketEvent) Add(key string, value interface{}) {
ev.Data[key] = value
}
func NewWebSocketEvent(event, teamId, channelId, userId string, omitUsers map[string]bool) *WebSocketEvent {
@@ -107,26 +118,85 @@ func NewWebSocketEvent(event, teamId, channelId, userId string, omitUsers map[st
Broadcast: &WebsocketBroadcast{TeamId: teamId, ChannelId: channelId, UserId: userId, OmitUsers: omitUsers}}
}
func (o *WebSocketEvent) IsValid() bool {
return o.Event != ""
}
func (o *WebSocketEvent) EventType() string {
return o.Event
}
func (o *WebSocketEvent) ToJson() string {
if o.precomputedJSON != nil {
return fmt.Sprintf(`{"event": %s, "data": %s, "broadcast": %s, "seq": %d}`, o.precomputedJSON.Event, o.precomputedJSON.Data, o.precomputedJSON.Broadcast, o.Sequence)
func (ev *WebSocketEvent) Copy() *WebSocketEvent {
copy := &WebSocketEvent{
Event: ev.Event,
Data: ev.Data,
Broadcast: ev.Broadcast,
Sequence: ev.Sequence,
precomputedJSON: ev.precomputedJSON,
}
b, _ := json.Marshal(o)
return copy
}
func (ev *WebSocketEvent) GetData() map[string]interface{} {
return ev.Data
}
func (ev *WebSocketEvent) GetBroadcast() *WebsocketBroadcast {
return ev.Broadcast
}
func (ev *WebSocketEvent) GetSequence() int64 {
return ev.Sequence
}
func (ev *WebSocketEvent) SetEvent(event string) *WebSocketEvent {
copy := ev.Copy()
copy.Event = event
return copy
}
func (ev *WebSocketEvent) SetData(data map[string]interface{}) *WebSocketEvent {
copy := ev.Copy()
copy.Data = data
return copy
}
func (ev *WebSocketEvent) SetBroadcast(broadcast *WebsocketBroadcast) *WebSocketEvent {
copy := ev.Copy()
copy.Broadcast = broadcast
return copy
}
func (ev *WebSocketEvent) SetSequence(seq int64) *WebSocketEvent {
copy := ev.Copy()
copy.Sequence = seq
return copy
}
func (ev *WebSocketEvent) IsValid() bool {
return ev.Event != ""
}
func (ev *WebSocketEvent) EventType() string {
return ev.Event
}
func (ev *WebSocketEvent) ToJson() string {
if ev.precomputedJSON != nil {
return fmt.Sprintf(`{"event": %s, "data": %s, "broadcast": %s, "seq": %d}`, ev.precomputedJSON.Event, ev.precomputedJSON.Data, ev.precomputedJSON.Broadcast, ev.Sequence)
}
b, _ := json.Marshal(webSocketEventJSON{
ev.Event,
ev.Data,
ev.Broadcast,
ev.Sequence,
})
return string(b)
}
func WebSocketEventFromJson(data io.Reader) *WebSocketEvent {
var o *WebSocketEvent
json.NewDecoder(data).Decode(&o)
return o
var ev WebSocketEvent
var o webSocketEventJSON
if err := json.NewDecoder(data).Decode(&o); err != nil {
return nil
}
ev.Event = o.Event
ev.Data = o.Data
ev.Broadcast = o.Broadcast
ev.Sequence = o.Sequence
return &ev
}
type WebSocketResponse struct {