1
1
package http
2
2
3
3
import (
4
- "context"
5
- "encoding/json"
6
4
"fmt"
7
5
"net/http"
8
6
"reflect"
@@ -11,59 +9,23 @@ import (
11
9
"github.com/go-chi/render"
12
10
)
13
11
14
- // Respond handles streaming JSON and XML responses, automatically setting the
15
- // Content-Type based on request headers. It will default to a JSON response.
16
-
17
- type ServerSentEvent struct {
18
- Event string
19
- Data []byte
20
- }
21
-
22
- type SSEMarshaler interface {
23
- Marshal (ctx context.Context , payload any ) (ServerSentEvent , error )
24
- }
25
-
26
- type JSONSSEMarshaler struct {}
27
-
28
- func (j JSONSSEMarshaler ) Marshal (ctx context.Context , payload any ) (ServerSentEvent , error ) {
29
- data , err := json .Marshal (payload )
30
- if err != nil {
31
- return ServerSentEvent {}, err
32
- }
33
-
34
- return ServerSentEvent {
35
- Event : "data" ,
36
- Data : data ,
37
- }, nil
38
- }
39
-
40
- type BytesSSEMarshaler struct {}
41
-
42
- func (b BytesSSEMarshaler ) Marshal (ctx context.Context , payload any ) (ServerSentEvent , error ) {
43
- payloadStr := fmt .Sprint (payload )
44
-
45
- data := strings .Join (strings .Split (payloadStr , "\n " ), "\n data: " )
46
-
47
- return ServerSentEvent {
48
- Event : "data" ,
49
- Data : []byte (data ),
50
- }, nil
51
- }
52
-
53
- type DefaultSSEResponder struct {
12
+ type sseResponder struct {
54
13
marshaler SSEMarshaler
55
14
}
56
15
57
- func (d DefaultSSEResponder ) Respond (w http.ResponseWriter , r * http.Request , v interface {}) {
16
+ // Respond handles streaming JSON and XML responses, automatically setting the
17
+ // Content-Type based on request headers.
18
+ // Based on go-chi/render.
19
+ func (s sseResponder ) Respond (w http.ResponseWriter , r * http.Request , v interface {}) {
58
20
if v != nil {
59
21
switch reflect .TypeOf (v ).Kind () {
60
22
case reflect .Chan :
61
23
switch render .GetAcceptedContentType (r ) {
62
24
case render .ContentTypeEventStream :
63
- d .channelEventStream (w , r , v )
25
+ s .channelEventStream (w , r , v )
64
26
return
65
27
default :
66
- v = d .channelIntoSlice (w , r , v )
28
+ v = s .channelIntoSlice (w , r , v )
67
29
}
68
30
}
69
31
}
@@ -79,14 +41,17 @@ func (d DefaultSSEResponder) Respond(w http.ResponseWriter, r *http.Request, v i
79
41
}
80
42
}
81
43
82
- func (d DefaultSSEResponder ) channelEventStream (w http.ResponseWriter , r * http.Request , v interface {}) {
44
+ func (s sseResponder ) channelEventStream (w http.ResponseWriter , r * http.Request , v interface {}) {
83
45
if reflect .TypeOf (v ).Kind () != reflect .Chan {
84
46
panic (fmt .Sprintf ("render: event stream expects a channel, not %v" , reflect .TypeOf (v ).Kind ()))
85
47
}
86
48
87
49
w .Header ().Set ("Content-Type" , "text/event-stream; charset=utf-8" )
88
50
w .Header ().Set ("Cache-Control" , "no-cache" )
89
51
52
+ // Disable proxy buffering for stream responses
53
+ w .Header ().Set ("X-Accel-Buffering" , "no" )
54
+
90
55
if r .ProtoMajor == 1 {
91
56
// An endpoint MUST NOT generate an HTTP/2 message containing connection-specific header fields.
92
57
// Source: RFC7540
@@ -125,7 +90,7 @@ func (d DefaultSSEResponder) channelEventStream(w http.ResponseWriter, r *http.R
125
90
event , ok := v .(ServerSentEvent )
126
91
if ! ok {
127
92
var err error
128
- event , err = d .marshaler .Marshal (ctx , v )
93
+ event , err = s .marshaler .Marshal (ctx , v )
129
94
if err != nil {
130
95
_ , _ = w .Write ([]byte (fmt .Sprintf ("event: error\n data: {\" error\" :\" %v\" }\n \n " , err )))
131
96
if f , ok := w .(http.Flusher ); ok {
@@ -135,7 +100,9 @@ func (d DefaultSSEResponder) channelEventStream(w http.ResponseWriter, r *http.R
135
100
}
136
101
}
137
102
138
- _ , _ = w .Write ([]byte (fmt .Sprintf ("event: %s\n data: %s\n \n " , event .Event , event .Data )))
103
+ data := strings .Join (strings .Split (string (event .Data ), "\n " ), "\n data: " )
104
+
105
+ _ , _ = w .Write ([]byte (fmt .Sprintf ("event: %s\n data: %s\n \n " , event .Event , data )))
139
106
if f , ok := w .(http.Flusher ); ok {
140
107
f .Flush ()
141
108
}
@@ -144,7 +111,7 @@ func (d DefaultSSEResponder) channelEventStream(w http.ResponseWriter, r *http.R
144
111
}
145
112
146
113
// channelIntoSlice buffers channel data into a slice.
147
- func (d DefaultSSEResponder ) channelIntoSlice (w http.ResponseWriter , r * http.Request , from interface {}) interface {} {
114
+ func (s sseResponder ) channelIntoSlice (w http.ResponseWriter , r * http.Request , from interface {}) interface {} {
148
115
ctx := r .Context ()
149
116
150
117
var to []interface {}
0 commit comments