node: refactor package node (#21105)
This PR significantly changes the APIs for instantiating Ethereum nodes in a Go program. The new APIs are not backwards-compatible, but we feel that this is made up for by the much simpler way of registering services on node.Node. You can find more information and rationale in the design document: https://gist.github.com/renaynay/5bec2de19fde66f4d04c535fd24f0775. There is also a new feature in Node's Go API: it is now possible to register arbitrary handlers on the user-facing HTTP server. In geth, this facility is used to enable GraphQL. There is a single minor change relevant for geth users in this PR: The GraphQL API is no longer available separately from the JSON-RPC HTTP server. If you want GraphQL, you need to enable it using the ./geth --http --graphql flag combination. The --graphql.port and --graphql.addr flags are no longer available.
This commit is contained in:
		| @@ -1,38 +1,110 @@ | ||||
| // Copyright 2020 The go-ethereum Authors | ||||
| // This file is part of the go-ethereum library. | ||||
| // | ||||
| // The go-ethereum library is free software: you can redistribute it and/or modify | ||||
| // it under the terms of the GNU Lesser General Public License as published by | ||||
| // the Free Software Foundation, either version 3 of the License, or | ||||
| // (at your option) any later version. | ||||
| // | ||||
| // The go-ethereum library is distributed in the hope that it will be useful, | ||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
| // GNU Lesser General Public License for more details. | ||||
| // | ||||
| // You should have received a copy of the GNU Lesser General Public License | ||||
| // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. | ||||
|  | ||||
| package node | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"net/http" | ||||
| 	"net/http/httptest" | ||||
| 	"testing" | ||||
|  | ||||
| 	"github.com/ethereum/go-ethereum/internal/testlog" | ||||
| 	"github.com/ethereum/go-ethereum/log" | ||||
| 	"github.com/ethereum/go-ethereum/rpc" | ||||
| 	"github.com/gorilla/websocket" | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| ) | ||||
|  | ||||
| func TestNewWebsocketUpgradeHandler_websocket(t *testing.T) { | ||||
| 	srv := rpc.NewServer() | ||||
| // TestCorsHandler makes sure CORS are properly handled on the http server. | ||||
| func TestCorsHandler(t *testing.T) { | ||||
| 	srv := createAndStartServer(t, httpConfig{CorsAllowedOrigins: []string{"test", "test.com"}}, false, wsConfig{}) | ||||
| 	defer srv.stop() | ||||
|  | ||||
| 	handler := NewWebsocketUpgradeHandler(nil, srv.WebsocketHandler([]string{})) | ||||
| 	ts := httptest.NewServer(handler) | ||||
| 	defer ts.Close() | ||||
| 	resp := testRequest(t, "origin", "test.com", "", srv) | ||||
| 	assert.Equal(t, "test.com", resp.Header.Get("Access-Control-Allow-Origin")) | ||||
|  | ||||
| 	responses := make(chan *http.Response) | ||||
| 	go func(responses chan *http.Response) { | ||||
| 		client := &http.Client{} | ||||
|  | ||||
| 		req, _ := http.NewRequest(http.MethodGet, ts.URL, nil) | ||||
| 		req.Header.Set("Connection", "upgrade") | ||||
| 		req.Header.Set("Upgrade", "websocket") | ||||
| 		req.Header.Set("Sec-WebSocket-Version", "13") | ||||
| 		req.Header.Set("Sec-Websocket-Key", "SGVsbG8sIHdvcmxkIQ==") | ||||
|  | ||||
| 		resp, err := client.Do(req) | ||||
| 		if err != nil { | ||||
| 			t.Error("could not issue a GET request to the test http server", err) | ||||
| 		} | ||||
| 		responses <- resp | ||||
| 	}(responses) | ||||
|  | ||||
| 	response := <-responses | ||||
| 	assert.Equal(t, "websocket", response.Header.Get("Upgrade")) | ||||
| 	resp2 := testRequest(t, "origin", "bad", "", srv) | ||||
| 	assert.Equal(t, "", resp2.Header.Get("Access-Control-Allow-Origin")) | ||||
| } | ||||
|  | ||||
| // TestVhosts makes sure vhosts are properly handled on the http server. | ||||
| func TestVhosts(t *testing.T) { | ||||
| 	srv := createAndStartServer(t, httpConfig{Vhosts: []string{"test"}}, false, wsConfig{}) | ||||
| 	defer srv.stop() | ||||
|  | ||||
| 	resp := testRequest(t, "", "", "test", srv) | ||||
| 	assert.Equal(t, resp.StatusCode, http.StatusOK) | ||||
|  | ||||
| 	resp2 := testRequest(t, "", "", "bad", srv) | ||||
| 	assert.Equal(t, resp2.StatusCode, http.StatusForbidden) | ||||
| } | ||||
|  | ||||
| // TestWebsocketOrigins makes sure the websocket origins are properly handled on the websocket server. | ||||
| func TestWebsocketOrigins(t *testing.T) { | ||||
| 	srv := createAndStartServer(t, httpConfig{}, true, wsConfig{Origins: []string{"test"}}) | ||||
| 	defer srv.stop() | ||||
|  | ||||
| 	dialer := websocket.DefaultDialer | ||||
| 	_, _, err := dialer.Dial("ws://"+srv.listenAddr(), http.Header{ | ||||
| 		"Content-type":          []string{"application/json"}, | ||||
| 		"Sec-WebSocket-Version": []string{"13"}, | ||||
| 		"Origin":                []string{"test"}, | ||||
| 	}) | ||||
| 	assert.NoError(t, err) | ||||
|  | ||||
| 	_, _, err = dialer.Dial("ws://"+srv.listenAddr(), http.Header{ | ||||
| 		"Content-type":          []string{"application/json"}, | ||||
| 		"Sec-WebSocket-Version": []string{"13"}, | ||||
| 		"Origin":                []string{"bad"}, | ||||
| 	}) | ||||
| 	assert.Error(t, err) | ||||
| } | ||||
|  | ||||
| func createAndStartServer(t *testing.T, conf httpConfig, ws bool, wsConf wsConfig) *httpServer { | ||||
| 	t.Helper() | ||||
|  | ||||
| 	srv := newHTTPServer(testlog.Logger(t, log.LvlDebug), rpc.DefaultHTTPTimeouts) | ||||
|  | ||||
| 	assert.NoError(t, srv.enableRPC(nil, conf)) | ||||
| 	if ws { | ||||
| 		assert.NoError(t, srv.enableWS(nil, wsConf)) | ||||
| 	} | ||||
| 	assert.NoError(t, srv.setListenAddr("localhost", 0)) | ||||
| 	assert.NoError(t, srv.start()) | ||||
|  | ||||
| 	return srv | ||||
| } | ||||
|  | ||||
| func testRequest(t *testing.T, key, value, host string, srv *httpServer) *http.Response { | ||||
| 	t.Helper() | ||||
|  | ||||
| 	body := bytes.NewReader([]byte(`{"jsonrpc":"2.0","id":1,method":"rpc_modules"}`)) | ||||
| 	req, _ := http.NewRequest("POST", "http://"+srv.listenAddr(), body) | ||||
| 	req.Header.Set("content-type", "application/json") | ||||
| 	if key != "" && value != "" { | ||||
| 		req.Header.Set(key, value) | ||||
| 	} | ||||
| 	if host != "" { | ||||
| 		req.Host = host | ||||
| 	} | ||||
|  | ||||
| 	client := http.DefaultClient | ||||
| 	resp, err := client.Do(req) | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| 	return resp | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user