gitlab.com/flarenetwork/coreth@v0.1.1/rpc/websocket_test.go (about) 1 // (c) 2019-2020, Ava Labs, Inc. 2 // 3 // This file is a derived work, based on the go-ethereum library whose original 4 // notices appear below. 5 // 6 // It is distributed under a license compatible with the licensing terms of the 7 // original code from which it is derived. 8 // 9 // Much love to the original authors for their work. 10 // ********** 11 // Copyright 2018 The go-ethereum Authors 12 // This file is part of the go-ethereum library. 13 // 14 // The go-ethereum library is free software: you can redistribute it and/or modify 15 // it under the terms of the GNU Lesser General Public License as published by 16 // the Free Software Foundation, either version 3 of the License, or 17 // (at your option) any later version. 18 // 19 // The go-ethereum library is distributed in the hope that it will be useful, 20 // but WITHOUT ANY WARRANTY; without even the implied warranty of 21 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 // GNU Lesser General Public License for more details. 23 // 24 // You should have received a copy of the GNU Lesser General Public License 25 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 26 27 package rpc 28 29 import ( 30 "context" 31 "net" 32 "net/http" 33 "net/http/httptest" 34 "reflect" 35 "strings" 36 "testing" 37 "time" 38 39 "github.com/gorilla/websocket" 40 ) 41 42 func TestWebsocketClientHeaders(t *testing.T) { 43 t.Parallel() 44 45 endpoint, header, err := wsClientHeaders("wss://testuser:test-PASS_01@example.com:1234", "https://example.com") 46 if err != nil { 47 t.Fatalf("wsGetConfig failed: %s", err) 48 } 49 if endpoint != "wss://example.com:1234" { 50 t.Fatal("User should have been stripped from the URL") 51 } 52 if header.Get("authorization") != "Basic dGVzdHVzZXI6dGVzdC1QQVNTXzAx" { 53 t.Fatal("Basic auth header is incorrect") 54 } 55 if header.Get("origin") != "https://example.com" { 56 t.Fatal("Origin not set") 57 } 58 } 59 60 // This test checks that the server rejects connections from disallowed origins. 61 func TestWebsocketOriginCheck(t *testing.T) { 62 t.Parallel() 63 64 var ( 65 srv = newTestServer() 66 httpsrv = httptest.NewServer(srv.WebsocketHandler([]string{"http://example.com"})) 67 wsURL = "ws:" + strings.TrimPrefix(httpsrv.URL, "http:") 68 ) 69 defer srv.Stop() 70 defer httpsrv.Close() 71 72 client, err := DialWebsocket(context.Background(), wsURL, "http://ekzample.com") 73 if err == nil { 74 client.Close() 75 t.Fatal("no error for wrong origin") 76 } 77 wantErr := wsHandshakeError{websocket.ErrBadHandshake, "403 Forbidden"} 78 if !reflect.DeepEqual(err, wantErr) { 79 t.Fatalf("wrong error for wrong origin: %q", err) 80 } 81 82 // Connections without origin header should work. 83 client, err = DialWebsocket(context.Background(), wsURL, "") 84 if err != nil { 85 t.Fatal("error for empty origin") 86 } 87 client.Close() 88 } 89 90 // This test checks whether calls exceeding the request size limit are rejected. 91 /* 92 func TestWebsocketLargeCall(t *testing.T) { 93 t.Parallel() 94 95 var ( 96 srv = newTestServer() 97 httpsrv = httptest.NewServer(srv.WebsocketHandler([]string{"*"})) 98 wsURL = "ws:" + strings.TrimPrefix(httpsrv.URL, "http:") 99 ) 100 defer srv.Stop() 101 defer httpsrv.Close() 102 103 client, err := DialWebsocket(context.Background(), wsURL, "") 104 if err != nil { 105 t.Fatalf("can't dial: %v", err) 106 } 107 defer client.Close() 108 109 // This call sends slightly less than the limit and should work. 110 var result echoResult 111 arg := strings.Repeat("x", maxRequestContentLength-200) 112 if err := client.Call(&result, "test_echo", arg, 1); err != nil { 113 t.Fatalf("valid call didn't work: %v", err) 114 } 115 if result.String != arg { 116 t.Fatal("wrong string echoed") 117 } 118 119 // This call sends twice the allowed size and shouldn't work. 120 arg = strings.Repeat("x", maxRequestContentLength*2) 121 err = client.Call(&result, "test_echo", arg) 122 if err == nil { 123 t.Fatal("no error for too large call") 124 } 125 } 126 */ 127 128 // This test checks that client handles WebSocket ping frames correctly. 129 func TestClientWebsocketPing(t *testing.T) { 130 t.Parallel() 131 132 var ( 133 sendPing = make(chan struct{}) 134 server = wsPingTestServer(t, sendPing) 135 ctx, cancel = context.WithTimeout(context.Background(), 1*time.Second) 136 ) 137 defer cancel() 138 defer server.Shutdown(ctx) 139 140 client, err := DialContext(ctx, "ws://"+server.Addr) 141 if err != nil { 142 t.Fatalf("client dial error: %v", err) 143 } 144 defer client.Close() 145 146 resultChan := make(chan int) 147 sub, err := client.EthSubscribe(ctx, resultChan, "foo") 148 if err != nil { 149 t.Fatalf("client subscribe error: %v", err) 150 } 151 // Note: Unsubscribe is not called on this subscription because the mockup 152 // server can't handle the request. 153 154 // Wait for the context's deadline to be reached before proceeding. 155 // This is important for reproducing https://github.com/ethereum/go-ethereum/issues/19798 156 <-ctx.Done() 157 close(sendPing) 158 159 // Wait for the subscription result. 160 timeout := time.NewTimer(5 * time.Second) 161 defer timeout.Stop() 162 for { 163 select { 164 case err := <-sub.Err(): 165 t.Error("client subscription error:", err) 166 case result := <-resultChan: 167 t.Log("client got result:", result) 168 return 169 case <-timeout.C: 170 t.Error("didn't get any result within the test timeout") 171 return 172 } 173 } 174 } 175 176 // This checks that the websocket transport can deal with large messages. 177 func TestClientWebsocketLargeMessage(t *testing.T) { 178 var ( 179 srv = NewServer(0) 180 httpsrv = httptest.NewServer(srv.WebsocketHandler(nil)) 181 wsURL = "ws:" + strings.TrimPrefix(httpsrv.URL, "http:") 182 ) 183 defer srv.Stop() 184 defer httpsrv.Close() 185 186 respLength := wsMessageSizeLimit - 50 187 srv.RegisterName("test", largeRespService{respLength}) 188 189 c, err := DialWebsocket(context.Background(), wsURL, "") 190 if err != nil { 191 t.Fatal(err) 192 } 193 194 var r string 195 if err := c.Call(&r, "test_largeResp"); err != nil { 196 t.Fatal("call failed:", err) 197 } 198 if len(r) != respLength { 199 t.Fatalf("response has wrong length %d, want %d", len(r), respLength) 200 } 201 } 202 203 // wsPingTestServer runs a WebSocket server which accepts a single subscription request. 204 // When a value arrives on sendPing, the server sends a ping frame, waits for a matching 205 // pong and finally delivers a single subscription result. 206 func wsPingTestServer(t *testing.T, sendPing <-chan struct{}) *http.Server { 207 var srv http.Server 208 shutdown := make(chan struct{}) 209 srv.RegisterOnShutdown(func() { 210 close(shutdown) 211 }) 212 srv.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 213 // Upgrade to WebSocket. 214 upgrader := websocket.Upgrader{ 215 CheckOrigin: func(r *http.Request) bool { return true }, 216 } 217 conn, err := upgrader.Upgrade(w, r, nil) 218 if err != nil { 219 t.Errorf("server WS upgrade error: %v", err) 220 return 221 } 222 defer conn.Close() 223 224 // Handle the connection. 225 wsPingTestHandler(t, conn, shutdown, sendPing) 226 }) 227 228 // Start the server. 229 listener, err := net.Listen("tcp", "127.0.0.1:0") 230 if err != nil { 231 t.Fatal("can't listen:", err) 232 } 233 srv.Addr = listener.Addr().String() 234 go srv.Serve(listener) 235 return &srv 236 } 237 238 func wsPingTestHandler(t *testing.T, conn *websocket.Conn, shutdown, sendPing <-chan struct{}) { 239 // Canned responses for the eth_subscribe call in TestClientWebsocketPing. 240 const ( 241 subResp = `{"jsonrpc":"2.0","id":1,"result":"0x00"}` 242 subNotify = `{"jsonrpc":"2.0","method":"eth_subscription","params":{"subscription":"0x00","result":1}}` 243 ) 244 245 // Handle subscribe request. 246 if _, _, err := conn.ReadMessage(); err != nil { 247 t.Errorf("server read error: %v", err) 248 return 249 } 250 if err := conn.WriteMessage(websocket.TextMessage, []byte(subResp)); err != nil { 251 t.Errorf("server write error: %v", err) 252 return 253 } 254 255 // Read from the connection to process control messages. 256 var pongCh = make(chan string) 257 conn.SetPongHandler(func(d string) error { 258 t.Logf("server got pong: %q", d) 259 pongCh <- d 260 return nil 261 }) 262 go func() { 263 for { 264 typ, msg, err := conn.ReadMessage() 265 if err != nil { 266 return 267 } 268 t.Logf("server got message (%d): %q", typ, msg) 269 } 270 }() 271 272 // Write messages. 273 var ( 274 wantPong string 275 timer = time.NewTimer(0) 276 ) 277 defer timer.Stop() 278 <-timer.C 279 for { 280 select { 281 case _, open := <-sendPing: 282 if !open { 283 sendPing = nil 284 } 285 t.Logf("server sending ping") 286 conn.WriteMessage(websocket.PingMessage, []byte("ping")) 287 wantPong = "ping" 288 case data := <-pongCh: 289 if wantPong == "" { 290 t.Errorf("unexpected pong") 291 } else if data != wantPong { 292 t.Errorf("got pong with wrong data %q", data) 293 } 294 wantPong = "" 295 timer.Reset(200 * time.Millisecond) 296 case <-timer.C: 297 t.Logf("server sending response") 298 conn.WriteMessage(websocket.TextMessage, []byte(subNotify)) 299 case <-shutdown: 300 conn.Close() 301 return 302 } 303 } 304 }