graphql: always return 400 if errors are present in the response (#21882)

* Make sure to return 400 when errors are present in the response

* graphql: use less memory in chainconfig for tests

Co-authored-by: Martin Holst Swende <martin@swende.se>
# Conflicts:
#	graphql/graphql_test.go
#	graphql/service.go
This commit is contained in:
Antoine Toulme 2020-11-25 01:19:36 -08:00 committed by Igor Mandrigin
parent 86dd1f91dd
commit f981ed79b3
2 changed files with 79 additions and 2 deletions

View File

@ -22,8 +22,12 @@ import (
"net/http" "net/http"
"strings" "strings"
"testing" "testing"
"time"
"github.com/ledgerwatch/turbo-geth/eth" "github.com/ledgerwatch/turbo-geth/eth"
"github.com/ethereum/go-ethereum/consensus/ethash"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/eth"
"github.com/ledgerwatch/turbo-geth/node" "github.com/ledgerwatch/turbo-geth/node"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@ -61,6 +65,7 @@ func TestGraphQLHTTPOnSamePort_GQLRequest_Successful(t *testing.T) {
t.Fatalf("could not read from response body: %v", err) t.Fatalf("could not read from response body: %v", err)
} }
expected := "{\"data\":{\"block\":{\"number\":\"0x0\"}}}" expected := "{\"data\":{\"block\":{\"number\":\"0x0\"}}}"
assert.Equal(t, 200, resp.StatusCode)
assert.Equal(t, expected, string(bodyBytes)) assert.Equal(t, expected, string(bodyBytes))
} }
@ -90,6 +95,32 @@ func TestGraphQLHTTPOnSamePort_GQLRequest_Unsuccessful(t *testing.T) {
assert.Equal(t, "404 page not found\n", string(bodyBytes)) assert.Equal(t, "404 page not found\n", string(bodyBytes))
} }
// Tests that 400 is returned when an invalid RPC request is made.
func TestGraphQL_BadRequest(t *testing.T) {
stack := createNode(t, true)
defer stack.Close()
// start node
if err := stack.Start(); err != nil {
t.Fatalf("could not start node: %v", err)
}
// create http request
body := strings.NewReader("{\"query\": \"{bleh{number}}\",\"variables\": null}")
gqlReq, err := http.NewRequest(http.MethodGet, fmt.Sprintf("http://%s/graphql", "127.0.0.1:9393"), body)
if err != nil {
t.Error("could not issue new http request ", err)
}
gqlReq.Header.Set("Content-Type", "application/json")
// read from response
resp := doHTTPRequest(t, gqlReq)
bodyBytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
t.Fatalf("could not read from response body: %v", err)
}
expected := "{\"errors\":[{\"message\":\"Cannot query field \\\"bleh\\\" on type \\\"Query\\\".\",\"locations\":[{\"line\":1,\"column\":2}]}]}"
assert.Equal(t, expected, string(bodyBytes))
assert.Equal(t, 400, resp.StatusCode)
}
func createNode(t *testing.T, gqlEnabled bool) *node.Node { func createNode(t *testing.T, gqlEnabled bool) *node.Node {
stack, err := node.New(&node.Config{ stack, err := node.New(&node.Config{
HTTPHost: "127.0.0.1", HTTPHost: "127.0.0.1",
@ -111,7 +142,23 @@ func createNode(t *testing.T, gqlEnabled bool) *node.Node {
func createGQLService(t *testing.T, stack *node.Node, endpoint string) { //nolint:unparam func createGQLService(t *testing.T, stack *node.Node, endpoint string) { //nolint:unparam
// create backend // create backend
ethBackend, err := eth.New(stack, &eth.DefaultConfig) ethConf := &eth.Config{
Genesis: core.DeveloperGenesisBlock(15, common.Address{}),
Miner: miner.Config{
Etherbase: common.HexToAddress("0xaabb"),
},
Ethash: ethash.Config{
PowMode: ethash.ModeTest,
},
NetworkId: 1337,
TrieCleanCache: 5,
TrieCleanCacheJournal: "triecache",
TrieCleanCacheRejournal: 60 * time.Minute,
TrieDirtyCache: 5,
TrieTimeout: 60 * time.Minute,
SnapshotCache: 5,
}
ethBackend, err := eth.New(stack, ethConf)
if err != nil { if err != nil {
t.Fatalf("could not create eth backend: %v", err) t.Fatalf("could not create eth backend: %v", err)
} }

View File

@ -23,6 +23,36 @@ import (
"github.com/ledgerwatch/turbo-geth/node" "github.com/ledgerwatch/turbo-geth/node"
) )
type handler struct {
Schema *graphql.Schema
}
func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
var params struct {
Query string `json:"query"`
OperationName string `json:"operationName"`
Variables map[string]interface{} `json:"variables"`
}
if err := json.NewDecoder(r.Body).Decode(&params); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
response := h.Schema.Exec(r.Context(), params.Query, params.OperationName, params.Variables)
responseJSON, err := json.Marshal(response)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
if len(response.Errors) > 0 {
w.WriteHeader(http.StatusBadRequest)
}
w.Header().Set("Content-Type", "application/json")
w.Write(responseJSON)
}
// New constructs a new GraphQL service instance. // New constructs a new GraphQL service instance.
func New(stack *node.Node, backend ethapi.Backend, cors, vhosts []string) error { func New(stack *node.Node, backend ethapi.Backend, cors, vhosts []string) error {
if backend == nil { if backend == nil {
@ -41,7 +71,7 @@ func newHandler(stack *node.Node, backend ethapi.Backend, cors, vhosts []string)
if err != nil { if err != nil {
return err return err
} }
h := &relay.Handler{Schema: s} h := handler{Schema: s}
handler := node.NewHTTPHandlerStack(h, cors, vhosts) handler := node.NewHTTPHandlerStack(h, cors, vhosts)
stack.RegisterHandler("GraphQL UI", "/graphql/ui", GraphiQL{}) stack.RegisterHandler("GraphQL UI", "/graphql/ui", GraphiQL{})