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