Skip to content

Commit efc5d86

Browse files
authored
feat: Add ability to provide dynamic query parameters (#44)
1 parent c7ba525 commit efc5d86

File tree

3 files changed

+105
-5
lines changed

3 files changed

+105
-5
lines changed

stream.go

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"io"
77
"io/ioutil"
88
"net/http"
9+
"net/url"
910
"sync"
1011
"time"
1112
)
@@ -14,11 +15,12 @@ import (
1415
// It will try and reconnect if the connection is lost, respecting both
1516
// received retry delays and event id's.
1617
type Stream struct {
17-
c *http.Client
18-
req *http.Request
19-
lastEventID string
20-
readTimeout time.Duration
21-
retryDelay *retryDelayStrategy
18+
c *http.Client
19+
req *http.Request
20+
queryParamsFunc *func(existing url.Values) url.Values
21+
lastEventID string
22+
readTimeout time.Duration
23+
retryDelay *retryDelayStrategy
2224
// Events emits the events received by the stream
2325
Events chan Event
2426
// Errors emits any errors encountered while reading events from the stream.
@@ -187,6 +189,10 @@ func newStream(request *http.Request, configuredOptions streamOptions) *Stream {
187189
closer: make(chan struct{}),
188190
}
189191

192+
if configuredOptions.queryParamsFunc != nil {
193+
stream.queryParamsFunc = configuredOptions.queryParamsFunc
194+
}
195+
190196
if configuredOptions.errorHandler == nil {
191197
// The Errors channel is only used if there is no error handler.
192198
stream.Errors = make(chan error)
@@ -231,6 +237,9 @@ func (stream *Stream) connect() (io.ReadCloser, error) {
231237
stream.req.Header.Set("Last-Event-ID", stream.lastEventID)
232238
}
233239
req := *stream.req
240+
if stream.queryParamsFunc != nil {
241+
req.URL.RawQuery = (*stream.queryParamsFunc)(req.URL.Query()).Encode()
242+
}
234243

235244
// All but the initial connection will need to regenerate the body
236245
if stream.connections > 0 && req.GetBody != nil {

stream_options.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package eventsource
22

33
import (
44
"net/http"
5+
"net/url"
56
"time"
67
)
78

@@ -16,6 +17,7 @@ type streamOptions struct {
1617
retryResetInterval time.Duration
1718
initialRetryTimeout time.Duration
1819
errorHandler StreamErrorHandler
20+
queryParamsFunc *func(existing url.Values) url.Values
1921
}
2022

2123
// StreamOption is a common interface for optional configuration parameters that can be
@@ -24,6 +26,22 @@ type StreamOption interface {
2426
apply(s *streamOptions) error
2527
}
2628

29+
type dynamicQueryParamsOption struct {
30+
queryParamsFunc func(existing url.Values) url.Values
31+
}
32+
33+
func (o dynamicQueryParamsOption) apply(s *streamOptions) error {
34+
s.queryParamsFunc = &o.queryParamsFunc
35+
return nil
36+
}
37+
38+
// StreamOptionDynamicQueryParams returns an option that sets a function to
39+
// generate query parameters each time the stream needs to make a fresh
40+
// connection.
41+
func StreamOptionDynamicQueryParams(f func(existing url.Values) url.Values) StreamOption {
42+
return dynamicQueryParamsOption{queryParamsFunc: f}
43+
}
44+
2745
type readTimeoutOption struct {
2846
timeout time.Duration
2947
}

stream_requests_test.go

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import (
44
"bytes"
55
"net/http"
66
"net/http/httptest"
7+
"net/url"
8+
"strconv"
79
"testing"
810
"time"
911

@@ -45,6 +47,77 @@ func TestStreamSendsLastEventID(t *testing.T) {
4547
assert.Equal(t, lastID, r0.Request.Header.Get("Last-Event-ID"))
4648
}
4749

50+
func TestCanReplaceStreamQueryParameters(t *testing.T) {
51+
streamHandler, streamControl := httphelpers.SSEHandler(nil)
52+
defer streamControl.Close()
53+
handler, requestsCh := httphelpers.RecordingHandler(streamHandler)
54+
55+
httpServer := httptest.NewServer(handler)
56+
defer httpServer.Close()
57+
58+
option := StreamOptionDynamicQueryParams(func(existing url.Values) url.Values {
59+
return url.Values{
60+
"filter": []string{"my-custom-filter"},
61+
"basis": []string{"last-known-basis"},
62+
}
63+
})
64+
65+
stream := mustSubscribe(t, httpServer.URL, option)
66+
defer stream.Close()
67+
68+
r0 := <-requestsCh
69+
assert.Equal(t, "my-custom-filter", r0.Request.URL.Query().Get("filter"))
70+
assert.Equal(t, "last-known-basis", r0.Request.URL.Query().Get("basis"))
71+
}
72+
73+
func TestCanUpdateStreamQueryParameters(t *testing.T) {
74+
streamHandler, streamControl := httphelpers.SSEHandler(nil)
75+
defer streamControl.Close()
76+
handler, requestsCh := httphelpers.RecordingHandler(streamHandler)
77+
78+
httpServer := httptest.NewServer(handler)
79+
defer httpServer.Close()
80+
81+
option := StreamOptionDynamicQueryParams(func(existing url.Values) url.Values {
82+
if existing.Has("count") {
83+
count, _ := strconv.Atoi(existing.Get("count"))
84+
85+
if count == 1 {
86+
existing.Set("count", strconv.Itoa(count+1))
87+
return existing
88+
}
89+
90+
return url.Values{}
91+
}
92+
93+
return url.Values{
94+
"initial": []string{"payload is set"},
95+
"count": []string{"1"},
96+
}
97+
})
98+
99+
stream := mustSubscribe(t, httpServer.URL, option, StreamOptionInitialRetry(time.Millisecond))
100+
defer stream.Close()
101+
102+
r0 := <-requestsCh
103+
assert.Equal(t, "payload is set", r0.Request.URL.Query().Get("initial"))
104+
assert.Equal(t, "1", r0.Request.URL.Query().Get("count"))
105+
106+
streamControl.EndAll()
107+
<-stream.Errors // Accept the error to unblock the retry handler
108+
109+
r1 := <-requestsCh
110+
assert.Equal(t, "payload is set", r1.Request.URL.Query().Get("initial"))
111+
assert.Equal(t, "2", r1.Request.URL.Query().Get("count"))
112+
113+
streamControl.EndAll()
114+
<-stream.Errors // Accept the error to unblock the retry handler
115+
116+
r2 := <-requestsCh
117+
assert.False(t, r2.Request.URL.Query().Has("initial"))
118+
assert.False(t, r2.Request.URL.Query().Has("count"))
119+
}
120+
48121
func TestStreamReconnectWithRequestBodySendsBodyTwice(t *testing.T) {
49122
body := []byte("my-body")
50123

0 commit comments

Comments
 (0)