github.com/phillinzzz/newBsc@v1.1.6/node/rpcstack_test.go (about) 1 // Copyright 2020 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package node 18 19 import ( 20 "bytes" 21 "fmt" 22 "net/http" 23 "net/url" 24 "strconv" 25 "strings" 26 "testing" 27 28 "github.com/phillinzzz/newBsc/internal/testlog" 29 "github.com/phillinzzz/newBsc/log" 30 "github.com/phillinzzz/newBsc/rpc" 31 "github.com/gorilla/websocket" 32 "github.com/stretchr/testify/assert" 33 ) 34 35 // TestCorsHandler makes sure CORS are properly handled on the http server. 36 func TestCorsHandler(t *testing.T) { 37 srv := createAndStartServer(t, &httpConfig{CorsAllowedOrigins: []string{"test", "test.com"}}, false, &wsConfig{}) 38 defer srv.stop() 39 url := "http://" + srv.listenAddr() 40 41 resp := rpcRequest(t, url, "origin", "test.com") 42 assert.Equal(t, "test.com", resp.Header.Get("Access-Control-Allow-Origin")) 43 44 resp2 := rpcRequest(t, url, "origin", "bad") 45 assert.Equal(t, "", resp2.Header.Get("Access-Control-Allow-Origin")) 46 } 47 48 // TestVhosts makes sure vhosts are properly handled on the http server. 49 func TestVhosts(t *testing.T) { 50 srv := createAndStartServer(t, &httpConfig{Vhosts: []string{"test"}}, false, &wsConfig{}) 51 defer srv.stop() 52 url := "http://" + srv.listenAddr() 53 54 resp := rpcRequest(t, url, "host", "test") 55 assert.Equal(t, resp.StatusCode, http.StatusOK) 56 57 resp2 := rpcRequest(t, url, "host", "bad") 58 assert.Equal(t, resp2.StatusCode, http.StatusForbidden) 59 } 60 61 type originTest struct { 62 spec string 63 expOk []string 64 expFail []string 65 } 66 67 // splitAndTrim splits input separated by a comma 68 // and trims excessive white space from the substrings. 69 // Copied over from flags.go 70 func splitAndTrim(input string) (ret []string) { 71 l := strings.Split(input, ",") 72 for _, r := range l { 73 r = strings.TrimSpace(r) 74 if len(r) > 0 { 75 ret = append(ret, r) 76 } 77 } 78 return ret 79 } 80 81 // TestWebsocketOrigins makes sure the websocket origins are properly handled on the websocket server. 82 func TestWebsocketOrigins(t *testing.T) { 83 tests := []originTest{ 84 { 85 spec: "*", // allow all 86 expOk: []string{"", "http://test", "https://test", "http://test:8540", "https://test:8540", 87 "http://test.com", "https://foo.test", "http://testa", "http://atestb:8540", "https://atestb:8540"}, 88 }, 89 { 90 spec: "test", 91 expOk: []string{"http://test", "https://test", "http://test:8540", "https://test:8540"}, 92 expFail: []string{"http://test.com", "https://foo.test", "http://testa", "http://atestb:8540", "https://atestb:8540"}, 93 }, 94 // scheme tests 95 { 96 spec: "https://test", 97 expOk: []string{"https://test", "https://test:9999"}, 98 expFail: []string{ 99 "test", // no scheme, required by spec 100 "http://test", // wrong scheme 101 "http://test.foo", "https://a.test.x", // subdomain variatoins 102 "http://testx:8540", "https://xtest:8540"}, 103 }, 104 // ip tests 105 { 106 spec: "https://12.34.56.78", 107 expOk: []string{"https://12.34.56.78", "https://12.34.56.78:8540"}, 108 expFail: []string{ 109 "http://12.34.56.78", // wrong scheme 110 "http://12.34.56.78:443", // wrong scheme 111 "http://1.12.34.56.78", // wrong 'domain name' 112 "http://12.34.56.78.a", // wrong 'domain name' 113 "https://87.65.43.21", "http://87.65.43.21:8540", "https://87.65.43.21:8540"}, 114 }, 115 // port tests 116 { 117 spec: "test:8540", 118 expOk: []string{"http://test:8540", "https://test:8540"}, 119 expFail: []string{ 120 "http://test", "https://test", // spec says port required 121 "http://test:8541", "https://test:8541", // wrong port 122 "http://bad", "https://bad", "http://bad:8540", "https://bad:8540"}, 123 }, 124 // scheme and port 125 { 126 spec: "https://test:8540", 127 expOk: []string{"https://test:8540"}, 128 expFail: []string{ 129 "https://test", // missing port 130 "http://test", // missing port, + wrong scheme 131 "http://test:8540", // wrong scheme 132 "http://test:8541", "https://test:8541", // wrong port 133 "http://bad", "https://bad", "http://bad:8540", "https://bad:8540"}, 134 }, 135 // several allowed origins 136 { 137 spec: "localhost,http://127.0.0.1", 138 expOk: []string{"localhost", "http://localhost", "https://localhost:8443", 139 "http://127.0.0.1", "http://127.0.0.1:8080"}, 140 expFail: []string{ 141 "https://127.0.0.1", // wrong scheme 142 "http://bad", "https://bad", "http://bad:8540", "https://bad:8540"}, 143 }, 144 } 145 for _, tc := range tests { 146 srv := createAndStartServer(t, &httpConfig{}, true, &wsConfig{Origins: splitAndTrim(tc.spec)}) 147 url := fmt.Sprintf("ws://%v", srv.listenAddr()) 148 for _, origin := range tc.expOk { 149 if err := wsRequest(t, url, origin); err != nil { 150 t.Errorf("spec '%v', origin '%v': expected ok, got %v", tc.spec, origin, err) 151 } 152 } 153 for _, origin := range tc.expFail { 154 if err := wsRequest(t, url, origin); err == nil { 155 t.Errorf("spec '%v', origin '%v': expected not to allow, got ok", tc.spec, origin) 156 } 157 } 158 srv.stop() 159 } 160 } 161 162 // TestIsWebsocket tests if an incoming websocket upgrade request is handled properly. 163 func TestIsWebsocket(t *testing.T) { 164 r, _ := http.NewRequest("GET", "/", nil) 165 166 assert.False(t, isWebsocket(r)) 167 r.Header.Set("upgrade", "websocket") 168 assert.False(t, isWebsocket(r)) 169 r.Header.Set("connection", "upgrade") 170 assert.True(t, isWebsocket(r)) 171 r.Header.Set("connection", "upgrade,keep-alive") 172 assert.True(t, isWebsocket(r)) 173 r.Header.Set("connection", " UPGRADE,keep-alive") 174 assert.True(t, isWebsocket(r)) 175 } 176 177 func Test_checkPath(t *testing.T) { 178 tests := []struct { 179 req *http.Request 180 prefix string 181 expected bool 182 }{ 183 { 184 req: &http.Request{URL: &url.URL{Path: "/test"}}, 185 prefix: "/test", 186 expected: true, 187 }, 188 { 189 req: &http.Request{URL: &url.URL{Path: "/testing"}}, 190 prefix: "/test", 191 expected: true, 192 }, 193 { 194 req: &http.Request{URL: &url.URL{Path: "/"}}, 195 prefix: "/test", 196 expected: false, 197 }, 198 { 199 req: &http.Request{URL: &url.URL{Path: "/fail"}}, 200 prefix: "/test", 201 expected: false, 202 }, 203 { 204 req: &http.Request{URL: &url.URL{Path: "/"}}, 205 prefix: "", 206 expected: true, 207 }, 208 { 209 req: &http.Request{URL: &url.URL{Path: "/fail"}}, 210 prefix: "", 211 expected: false, 212 }, 213 { 214 req: &http.Request{URL: &url.URL{Path: "/"}}, 215 prefix: "/", 216 expected: true, 217 }, 218 { 219 req: &http.Request{URL: &url.URL{Path: "/testing"}}, 220 prefix: "/", 221 expected: true, 222 }, 223 } 224 225 for i, tt := range tests { 226 t.Run(strconv.Itoa(i), func(t *testing.T) { 227 assert.Equal(t, tt.expected, checkPath(tt.req, tt.prefix)) 228 }) 229 } 230 } 231 232 func createAndStartServer(t *testing.T, conf *httpConfig, ws bool, wsConf *wsConfig) *httpServer { 233 t.Helper() 234 235 srv := newHTTPServer(testlog.Logger(t, log.LvlDebug), rpc.DefaultHTTPTimeouts) 236 assert.NoError(t, srv.enableRPC(nil, *conf)) 237 if ws { 238 assert.NoError(t, srv.enableWS(nil, *wsConf)) 239 } 240 assert.NoError(t, srv.setListenAddr("localhost", 0)) 241 assert.NoError(t, srv.start()) 242 return srv 243 } 244 245 // wsRequest attempts to open a WebSocket connection to the given URL. 246 func wsRequest(t *testing.T, url, browserOrigin string) error { 247 t.Helper() 248 t.Logf("checking WebSocket on %s (origin %q)", url, browserOrigin) 249 250 headers := make(http.Header) 251 if browserOrigin != "" { 252 headers.Set("Origin", browserOrigin) 253 } 254 conn, _, err := websocket.DefaultDialer.Dial(url, headers) 255 if conn != nil { 256 conn.Close() 257 } 258 return err 259 } 260 261 // rpcRequest performs a JSON-RPC request to the given URL. 262 func rpcRequest(t *testing.T, url string, extraHeaders ...string) *http.Response { 263 t.Helper() 264 265 // Create the request. 266 body := bytes.NewReader([]byte(`{"jsonrpc":"2.0","id":1,"method":"rpc_modules","params":[]}`)) 267 req, err := http.NewRequest("POST", url, body) 268 if err != nil { 269 t.Fatal("could not create http request:", err) 270 } 271 req.Header.Set("content-type", "application/json") 272 273 // Apply extra headers. 274 if len(extraHeaders)%2 != 0 { 275 panic("odd extraHeaders length") 276 } 277 for i := 0; i < len(extraHeaders); i += 2 { 278 key, value := extraHeaders[i], extraHeaders[i+1] 279 if strings.ToLower(key) == "host" { 280 req.Host = value 281 } else { 282 req.Header.Set(key, value) 283 } 284 } 285 286 // Perform the request. 287 t.Logf("checking RPC/HTTP on %s %v", url, extraHeaders) 288 resp, err := http.DefaultClient.Do(req) 289 if err != nil { 290 t.Fatal(err) 291 } 292 return resp 293 }