mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2025-11-01 07:52:25 -05:00
- github.com/KimMachineGun/automemlimit v0.7.2 => v0.7.3
- github.com/gin-contrib/cors v1.7.5 => v1.7.6
- github.com/minio/minio-go/v7 v7.0.92 => v7.0.94
- github.com/spf13/cast v1.8.0 => v1.9.2
- github.com/uptrace/bun{,/*} v1.2.11 => v1.2.14
- golang.org/x/image v0.27.0 => v0.28.0
- golang.org/x/net v0.40.0 => v0.41.0
- code.superseriousbusiness.org/go-swagger v0.31.0-gts-go1.23-fix => v0.32.3-gts-go1.23-fix
Reviewed-on: https://codeberg.org/superseriousbusiness/gotosocial/pulls/4304
Co-authored-by: kim <grufwub@gmail.com>
Co-committed-by: kim <grufwub@gmail.com>
120 lines
2.5 KiB
Go
120 lines
2.5 KiB
Go
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
|
|
// Use of this source code is governed by a MIT style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package sse
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"reflect"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
// Server-Sent Events
|
|
// W3C Working Draft 29 October 2009
|
|
// http://www.w3.org/TR/2009/WD-eventsource-20091029/
|
|
|
|
const ContentType = "text/event-stream;charset=utf-8"
|
|
|
|
var (
|
|
contentType = []string{ContentType}
|
|
noCache = []string{"no-cache"}
|
|
)
|
|
|
|
var fieldReplacer = strings.NewReplacer(
|
|
"\n", "\\n",
|
|
"\r", "\\r")
|
|
|
|
var dataReplacer = strings.NewReplacer(
|
|
"\n", "\ndata:",
|
|
"\r", "\\r")
|
|
|
|
type Event struct {
|
|
Event string
|
|
Id string
|
|
Retry uint
|
|
Data interface{}
|
|
}
|
|
|
|
func Encode(writer io.Writer, event Event) error {
|
|
w := checkWriter(writer)
|
|
writeId(w, event.Id)
|
|
writeEvent(w, event.Event)
|
|
writeRetry(w, event.Retry)
|
|
return writeData(w, event.Data)
|
|
}
|
|
|
|
func writeId(w stringWriter, id string) {
|
|
if len(id) > 0 {
|
|
_, _ = w.WriteString("id:")
|
|
_, _ = fieldReplacer.WriteString(w, id)
|
|
_, _ = w.WriteString("\n")
|
|
}
|
|
}
|
|
|
|
func writeEvent(w stringWriter, event string) {
|
|
if len(event) > 0 {
|
|
_, _ = w.WriteString("event:")
|
|
_, _ = fieldReplacer.WriteString(w, event)
|
|
_, _ = w.WriteString("\n")
|
|
}
|
|
}
|
|
|
|
func writeRetry(w stringWriter, retry uint) {
|
|
if retry > 0 {
|
|
_, _ = w.WriteString("retry:")
|
|
_, _ = w.WriteString(strconv.FormatUint(uint64(retry), 10))
|
|
_, _ = w.WriteString("\n")
|
|
}
|
|
}
|
|
|
|
func writeData(w stringWriter, data interface{}) error {
|
|
_, _ = w.WriteString("data:")
|
|
|
|
bData, ok := data.([]byte)
|
|
if ok {
|
|
_, _ = dataReplacer.WriteString(w, string(bData))
|
|
_, _ = w.WriteString("\n\n")
|
|
return nil
|
|
}
|
|
|
|
switch kindOfData(data) { //nolint:exhaustive
|
|
case reflect.Struct, reflect.Slice, reflect.Map:
|
|
err := json.NewEncoder(w).Encode(data)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_, _ = w.WriteString("\n")
|
|
default:
|
|
_, _ = dataReplacer.WriteString(w, fmt.Sprint(data))
|
|
_, _ = w.WriteString("\n\n")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (r Event) Render(w http.ResponseWriter) error {
|
|
r.WriteContentType(w)
|
|
return Encode(w, r)
|
|
}
|
|
|
|
func (r Event) WriteContentType(w http.ResponseWriter) {
|
|
header := w.Header()
|
|
header["Content-Type"] = contentType
|
|
|
|
if _, exist := header["Cache-Control"]; !exist {
|
|
header["Cache-Control"] = noCache
|
|
}
|
|
}
|
|
|
|
func kindOfData(data interface{}) reflect.Kind {
|
|
value := reflect.ValueOf(data)
|
|
valueType := value.Kind()
|
|
if valueType == reflect.Ptr {
|
|
valueType = value.Elem().Kind()
|
|
}
|
|
return valueType
|
|
}
|