Fix race condition on status in Relay.Publish method and failure to send

A race-condition exists between setting of the (unprotected) status and the callback which sets the status upon receiving an OK.
The message is sent which can receive an OK in separate goroutine (setting status) prior to the status being set to 'sent.'
The OK can be received prior to the status being set.

This fix also sets the status to PublishStatusFailed if the WriteJSON call fails.
This commit is contained in:
Patrick Bennett 2023-04-21 00:10:11 -04:00 committed by fiatjaf_
parent 9b89a49e5e
commit b077a41f83
2 changed files with 23 additions and 2 deletions

View File

@ -305,10 +305,11 @@ func (r *Relay) Publish(ctx context.Context, event Event) (Status, error) {
// publish event
message := []any{"EVENT", event}
debugLog("{%s} sending %v\n", r.URL, message)
status = PublishStatusSent
if err := r.Connection.WriteJSON(message); err != nil {
status = PublishStatusFailed
return status, err
}
status = PublishStatusSent
sub := r.PrepareSubscription(ctx)
sub.SetLabel("publish-check")

View File

@ -87,7 +87,27 @@ func TestPublishBlocked(t *testing.T) {
rl := mustRelayConnect(ws.URL)
status, _ := rl.Publish(context.Background(), textNote)
if status != PublishStatusFailed {
t.Errorf("published status is %d, not %d", status, PublishStatusSucceeded)
t.Errorf("published status is %d, not %d", status, PublishStatusFailed)
}
}
func TestPublishWriteFailed(t *testing.T) {
// test note to be sent over websocket
textNote := Event{Kind: 1, Content: "hello"}
textNote.ID = textNote.GetID()
// fake relay server
ws := newWebsocketServer(func(conn *websocket.Conn) {
// reject receive - force send error
conn.Close()
})
defer ws.Close()
// connect a client and send a text note
rl := mustRelayConnect(ws.URL)
status, _ := rl.Publish(context.Background(), textNote)
if status != PublishStatusFailed {
t.Errorf("published status is %d, not %d", status, PublishStatusFailed)
}
}