1
1
package httpapi_test
2
2
3
3
import (
4
+ "bufio"
4
5
"bytes"
5
6
"context"
6
7
"encoding/json"
7
8
"fmt"
9
+ "io"
10
+ "net"
8
11
"net/http"
9
12
"net/http/httptest"
10
13
"strings"
@@ -157,22 +160,68 @@ func TestWebsocketCloseMsg(t *testing.T) {
157
160
})
158
161
}
159
162
163
+ type mockHijacker struct {
164
+ http.ResponseWriter
165
+ connection net.Conn
166
+ rw * bufio.ReadWriter
167
+ }
168
+
169
+ func (mh mockHijacker ) Hijack () (net.Conn , * bufio.ReadWriter , error ) {
170
+ return mh .connection , mh .rw , nil
171
+ }
172
+
173
+ func (mh mockHijacker ) Flush () {
174
+ if f , ok := mh .ResponseWriter .(http.Flusher ); ok {
175
+ f .Flush ()
176
+ }
177
+ }
178
+
160
179
func TestOneWayWebSocket (t * testing.T ) {
161
180
t .Parallel ()
162
- url := "ws://www.fake-website.com/logs"
181
+
182
+ createBaseRequest := func (t * testing.T ) * http.Request {
183
+ url := "ws://www.fake-website.com/logs"
184
+ ctx := testutil .Context (t , testutil .WaitShort )
185
+ req , err := http .NewRequestWithContext (ctx , http .MethodGet , url , nil )
186
+ require .NoError (t , err )
187
+ req .Header = http.Header {
188
+ "Connection" : {"Upgrade" },
189
+ "Upgrade" : {"websocket" },
190
+ "Sec-WebSocket-Version" : {"13" },
191
+ "Sec-WebSocket-Key" : {"dGhlIHNhbXBsZSBub25jZQ==" },
192
+ }
193
+ // Todo: Figure out why headers are missing without these calls
194
+ req .Header .Add ("Sec-WebSocket-Version" , "13" )
195
+ req .Header .Add ("Sec-WebSocket-Key" , "dGhlIHNhbXBsZSBub25jZQ==" )
196
+
197
+ return req
198
+ }
199
+
200
+ wrapWriter := func (rw http.ResponseWriter , r io.Reader ) http.ResponseWriter {
201
+ server , _ := net .Pipe ()
202
+ reader := bufio .NewReader (r )
203
+ writer := bufio .NewWriter (rw )
204
+ readWriter := bufio .NewReadWriter (reader , writer )
205
+
206
+ hijacker := mockHijacker {
207
+ connection : server ,
208
+ ResponseWriter : rw ,
209
+ rw : readWriter ,
210
+ }
211
+
212
+ return hijacker
213
+ }
163
214
164
215
t .Run ("Produces an error if the socket connection could not be established" , func (t * testing.T ) {
165
216
t .Parallel ()
166
217
167
218
// WebSocket connections cannot be created on HTTP/1.0 and below
168
- ctx := testutil .Context (t , testutil .WaitShort )
169
- r , err := http .NewRequestWithContext (ctx , http .MethodGet , url , nil )
170
- require .NoError (t , err )
171
- r .ProtoMajor = 1
172
- r .ProtoMinor = 0
173
- r .Proto = "HTTP/1.0"
219
+ req := createBaseRequest (t )
220
+ req .ProtoMajor = 1
221
+ req .ProtoMinor = 0
222
+ req .Proto = "HTTP/1.0"
174
223
175
- _ , _ , err = httpapi .OneWayWebSocket [any ](httptest .NewRecorder (), r )
224
+ _ , _ , err : = httpapi .OneWayWebSocket [any ](httptest .NewRecorder (), req )
176
225
require .ErrorContains (
177
226
t ,
178
227
err ,
@@ -182,6 +231,21 @@ func TestOneWayWebSocket(t *testing.T) {
182
231
183
232
t .Run ("Returned callback can publish a new event to the WebSocket connection" , func (t * testing.T ) {
184
233
t .Parallel ()
234
+
235
+ r := strings .NewReader ("" )
236
+ recorder := httptest .NewRecorder ()
237
+ writer := wrapWriter (recorder , r )
238
+ send , _ , err := httpapi .OneWayWebSocket [codersdk.ServerSentEvent ](
239
+ writer ,
240
+ createBaseRequest (t ),
241
+ )
242
+ require .NoError (t , err )
243
+
244
+ err = send (codersdk.ServerSentEvent {
245
+ Type : codersdk .ServerSentEventTypeData ,
246
+ Data : "Blah" ,
247
+ })
248
+ require .NoError (t , err )
185
249
})
186
250
187
251
t .Run ("Signals to an outside consumer when the socket has been closed" , func (t * testing.T ) {
0 commit comments