github.com/cryptotooltop/go-ethereum@v0.0.0-20231103184714-151d1922f3e5/node/api_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 "io" 22 "net" 23 "net/http" 24 "net/url" 25 "strings" 26 "testing" 27 28 "github.com/stretchr/testify/assert" 29 30 "github.com/scroll-tech/go-ethereum/rpc" 31 ) 32 33 // This test uses the admin_startRPC and admin_startWS APIs, 34 // checking whether the HTTP server is started correctly. 35 func TestStartRPC(t *testing.T) { 36 type test struct { 37 name string 38 cfg Config 39 fn func(*testing.T, *Node, *privateAdminAPI) 40 41 // Checks. These run after the node is configured and all API calls have been made. 42 wantReachable bool // whether the HTTP server should be reachable at all 43 wantHandlers bool // whether RegisterHandler handlers should be accessible 44 wantRPC bool // whether JSON-RPC/HTTP should be accessible 45 wantWS bool // whether JSON-RPC/WS should be accessible 46 } 47 48 tests := []test{ 49 { 50 name: "all off", 51 cfg: Config{}, 52 fn: func(t *testing.T, n *Node, api *privateAdminAPI) { 53 }, 54 wantReachable: false, 55 wantHandlers: false, 56 wantRPC: false, 57 wantWS: false, 58 }, 59 { 60 name: "rpc enabled through config", 61 cfg: Config{HTTPHost: "127.0.0.1"}, 62 fn: func(t *testing.T, n *Node, api *privateAdminAPI) { 63 }, 64 wantReachable: true, 65 wantHandlers: true, 66 wantRPC: true, 67 wantWS: false, 68 }, 69 { 70 name: "rpc enabled through API", 71 cfg: Config{}, 72 fn: func(t *testing.T, n *Node, api *privateAdminAPI) { 73 _, err := api.StartHTTP(sp("127.0.0.1"), ip(0), nil, nil, nil) 74 assert.NoError(t, err) 75 }, 76 wantReachable: true, 77 wantHandlers: true, 78 wantRPC: true, 79 wantWS: false, 80 }, 81 { 82 name: "rpc start again after failure", 83 cfg: Config{}, 84 fn: func(t *testing.T, n *Node, api *privateAdminAPI) { 85 // Listen on a random port. 86 listener, err := net.Listen("tcp", "127.0.0.1:0") 87 if err != nil { 88 t.Fatal("can't listen:", err) 89 } 90 defer listener.Close() 91 port := listener.Addr().(*net.TCPAddr).Port 92 93 // Now try to start RPC on that port. This should fail. 94 _, err = api.StartHTTP(sp("127.0.0.1"), ip(port), nil, nil, nil) 95 if err == nil { 96 t.Fatal("StartHTTP should have failed on port", port) 97 } 98 99 // Try again after unblocking the port. It should work this time. 100 listener.Close() 101 _, err = api.StartHTTP(sp("127.0.0.1"), ip(port), nil, nil, nil) 102 assert.NoError(t, err) 103 }, 104 wantReachable: true, 105 wantHandlers: true, 106 wantRPC: true, 107 wantWS: false, 108 }, 109 { 110 name: "rpc stopped through API", 111 cfg: Config{HTTPHost: "127.0.0.1"}, 112 fn: func(t *testing.T, n *Node, api *privateAdminAPI) { 113 _, err := api.StopHTTP() 114 assert.NoError(t, err) 115 }, 116 wantReachable: false, 117 wantHandlers: false, 118 wantRPC: false, 119 wantWS: false, 120 }, 121 { 122 name: "rpc stopped twice", 123 cfg: Config{HTTPHost: "127.0.0.1"}, 124 fn: func(t *testing.T, n *Node, api *privateAdminAPI) { 125 _, err := api.StopHTTP() 126 assert.NoError(t, err) 127 128 _, err = api.StopHTTP() 129 assert.NoError(t, err) 130 }, 131 wantReachable: false, 132 wantHandlers: false, 133 wantRPC: false, 134 wantWS: false, 135 }, 136 { 137 name: "ws enabled through config", 138 cfg: Config{WSHost: "127.0.0.1"}, 139 wantReachable: true, 140 wantHandlers: false, 141 wantRPC: false, 142 wantWS: true, 143 }, 144 { 145 name: "ws enabled through API", 146 cfg: Config{}, 147 fn: func(t *testing.T, n *Node, api *privateAdminAPI) { 148 _, err := api.StartWS(sp("127.0.0.1"), ip(0), nil, nil) 149 assert.NoError(t, err) 150 }, 151 wantReachable: true, 152 wantHandlers: false, 153 wantRPC: false, 154 wantWS: true, 155 }, 156 { 157 name: "ws stopped through API", 158 cfg: Config{WSHost: "127.0.0.1"}, 159 fn: func(t *testing.T, n *Node, api *privateAdminAPI) { 160 _, err := api.StopWS() 161 assert.NoError(t, err) 162 }, 163 wantReachable: false, 164 wantHandlers: false, 165 wantRPC: false, 166 wantWS: false, 167 }, 168 { 169 name: "ws stopped twice", 170 cfg: Config{WSHost: "127.0.0.1"}, 171 fn: func(t *testing.T, n *Node, api *privateAdminAPI) { 172 _, err := api.StopWS() 173 assert.NoError(t, err) 174 175 _, err = api.StopWS() 176 assert.NoError(t, err) 177 }, 178 wantReachable: false, 179 wantHandlers: false, 180 wantRPC: false, 181 wantWS: false, 182 }, 183 { 184 name: "ws enabled after RPC", 185 cfg: Config{HTTPHost: "127.0.0.1"}, 186 fn: func(t *testing.T, n *Node, api *privateAdminAPI) { 187 wsport := n.http.port 188 _, err := api.StartWS(sp("127.0.0.1"), ip(wsport), nil, nil) 189 assert.NoError(t, err) 190 }, 191 wantReachable: true, 192 wantHandlers: true, 193 wantRPC: true, 194 wantWS: true, 195 }, 196 { 197 name: "ws enabled after RPC then stopped", 198 cfg: Config{HTTPHost: "127.0.0.1"}, 199 fn: func(t *testing.T, n *Node, api *privateAdminAPI) { 200 wsport := n.http.port 201 _, err := api.StartWS(sp("127.0.0.1"), ip(wsport), nil, nil) 202 assert.NoError(t, err) 203 204 _, err = api.StopWS() 205 assert.NoError(t, err) 206 }, 207 wantReachable: true, 208 wantHandlers: true, 209 wantRPC: true, 210 wantWS: false, 211 }, 212 { 213 name: "rpc stopped with ws enabled", 214 fn: func(t *testing.T, n *Node, api *privateAdminAPI) { 215 _, err := api.StartHTTP(sp("127.0.0.1"), ip(0), nil, nil, nil) 216 assert.NoError(t, err) 217 218 wsport := n.http.port 219 _, err = api.StartWS(sp("127.0.0.1"), ip(wsport), nil, nil) 220 assert.NoError(t, err) 221 222 _, err = api.StopHTTP() 223 assert.NoError(t, err) 224 }, 225 wantReachable: false, 226 wantHandlers: false, 227 wantRPC: false, 228 wantWS: false, 229 }, 230 { 231 name: "rpc enabled after ws", 232 fn: func(t *testing.T, n *Node, api *privateAdminAPI) { 233 _, err := api.StartWS(sp("127.0.0.1"), ip(0), nil, nil) 234 assert.NoError(t, err) 235 236 wsport := n.http.port 237 _, err = api.StartHTTP(sp("127.0.0.1"), ip(wsport), nil, nil, nil) 238 assert.NoError(t, err) 239 }, 240 wantReachable: true, 241 wantHandlers: true, 242 wantRPC: true, 243 wantWS: true, 244 }, 245 } 246 247 for _, test := range tests { 248 test := test 249 t.Run(test.name, func(t *testing.T) { 250 t.Parallel() 251 252 // Apply some sane defaults. 253 config := test.cfg 254 // config.Logger = testlog.Logger(t, log.LvlDebug) 255 config.P2P.NoDiscovery = true 256 257 // Create Node. 258 stack, err := New(&config) 259 if err != nil { 260 t.Fatal("can't create node:", err) 261 } 262 defer stack.Close() 263 264 // Register the test handler. 265 stack.RegisterHandler("test", "/test", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 266 w.Write([]byte("OK")) 267 })) 268 269 if err := stack.Start(); err != nil { 270 t.Fatal("can't start node:", err) 271 } 272 273 // Run the API call hook. 274 if test.fn != nil { 275 test.fn(t, stack, &privateAdminAPI{stack}) 276 } 277 278 // Check if the HTTP endpoints are available. 279 baseURL := stack.HTTPEndpoint() 280 reachable := checkReachable(baseURL) 281 handlersAvailable := checkBodyOK(baseURL + "/test") 282 rpcAvailable := checkRPC(baseURL) 283 wsAvailable := checkRPC(strings.Replace(baseURL, "http://", "ws://", 1)) 284 if reachable != test.wantReachable { 285 t.Errorf("HTTP server is %sreachable, want it %sreachable", not(reachable), not(test.wantReachable)) 286 } 287 if handlersAvailable != test.wantHandlers { 288 t.Errorf("RegisterHandler handlers %savailable, want them %savailable", not(handlersAvailable), not(test.wantHandlers)) 289 } 290 if rpcAvailable != test.wantRPC { 291 t.Errorf("HTTP RPC %savailable, want it %savailable", not(rpcAvailable), not(test.wantRPC)) 292 } 293 if wsAvailable != test.wantWS { 294 t.Errorf("WS RPC %savailable, want it %savailable", not(wsAvailable), not(test.wantWS)) 295 } 296 }) 297 } 298 } 299 300 // checkReachable checks if the TCP endpoint in rawurl is open. 301 func checkReachable(rawurl string) bool { 302 u, err := url.Parse(rawurl) 303 if err != nil { 304 panic(err) 305 } 306 conn, err := net.Dial("tcp", u.Host) 307 if err != nil { 308 return false 309 } 310 conn.Close() 311 return true 312 } 313 314 // checkBodyOK checks whether the given HTTP URL responds with 200 OK and body "OK". 315 func checkBodyOK(url string) bool { 316 resp, err := http.Get(url) 317 if err != nil { 318 return false 319 } 320 defer resp.Body.Close() 321 322 if resp.StatusCode != 200 { 323 return false 324 } 325 buf := make([]byte, 2) 326 if _, err = io.ReadFull(resp.Body, buf); err != nil { 327 return false 328 } 329 return bytes.Equal(buf, []byte("OK")) 330 } 331 332 // checkRPC checks whether JSON-RPC works against the given URL. 333 func checkRPC(url string) bool { 334 c, err := rpc.Dial(url) 335 if err != nil { 336 return false 337 } 338 defer c.Close() 339 340 _, err = c.SupportedModules() 341 return err == nil 342 } 343 344 // string/int pointer helpers. 345 func sp(s string) *string { return &s } 346 func ip(i int) *int { return &i } 347 348 func not(ok bool) string { 349 if ok { 350 return "" 351 } 352 return "not " 353 }