summaryrefslogtreecommitdiffstats
path: root/modules/eventsource/event.go
blob: ebcca5090344cc946ffa9dfb2ba5415f0b0da823 (plain)
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
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

package eventsource

import (
	"bytes"
	"fmt"
	"io"
	"strings"
	"time"

	"code.gitea.io/gitea/modules/json"
)

func wrapNewlines(w io.Writer, prefix, value []byte) (sum int64, err error) {
	if len(value) == 0 {
		return 0, nil
	}
	var n int
	last := 0
	for j := bytes.IndexByte(value, '\n'); j > -1; j = bytes.IndexByte(value[last:], '\n') {
		n, err = w.Write(prefix)
		sum += int64(n)
		if err != nil {
			return sum, err
		}
		n, err = w.Write(value[last : last+j+1])
		sum += int64(n)
		if err != nil {
			return sum, err
		}
		last += j + 1
	}
	n, err = w.Write(prefix)
	sum += int64(n)
	if err != nil {
		return sum, err
	}
	n, err = w.Write(value[last:])
	sum += int64(n)
	if err != nil {
		return sum, err
	}
	n, err = w.Write([]byte("\n"))
	sum += int64(n)
	return sum, err
}

// Event is an eventsource event, not all fields need to be set
type Event struct {
	// Name represents the value of the event: tag in the stream
	Name string
	// Data is either JSONified []byte or any that can be JSONd
	Data any
	// ID represents the ID of an event
	ID string
	// Retry tells the receiver only to attempt to reconnect to the source after this time
	Retry time.Duration
}

// WriteTo writes data to w until there's no more data to write or when an error occurs.
// The return value n is the number of bytes written. Any error encountered during the write is also returned.
func (e *Event) WriteTo(w io.Writer) (int64, error) {
	sum := int64(0)
	var nint int
	n, err := wrapNewlines(w, []byte("event: "), []byte(e.Name))
	sum += n
	if err != nil {
		return sum, err
	}

	if e.Data != nil {
		var data []byte
		switch v := e.Data.(type) {
		case []byte:
			data = v
		case string:
			data = []byte(v)
		default:
			var err error
			data, err = json.Marshal(e.Data)
			if err != nil {
				return sum, err
			}
		}
		n, err := wrapNewlines(w, []byte("data: "), data)
		sum += n
		if err != nil {
			return sum, err
		}
	}

	n, err = wrapNewlines(w, []byte("id: "), []byte(e.ID))
	sum += n
	if err != nil {
		return sum, err
	}

	if e.Retry != 0 {
		nint, err = fmt.Fprintf(w, "retry: %d\n", int64(e.Retry/time.Millisecond))
		sum += int64(nint)
		if err != nil {
			return sum, err
		}
	}

	nint, err = w.Write([]byte("\n"))
	sum += int64(nint)

	return sum, err
}

func (e *Event) String() string {
	buf := new(strings.Builder)
	_, _ = e.WriteTo(buf)
	return buf.String()
}