github.com/theQRL/go-zond@v0.1.1/graphql/service.go (about) 1 // Copyright 2019 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 graphql 18 19 import ( 20 "context" 21 "encoding/json" 22 "net/http" 23 "strconv" 24 "sync" 25 "time" 26 27 "github.com/graph-gophers/graphql-go" 28 gqlErrors "github.com/graph-gophers/graphql-go/errors" 29 "github.com/theQRL/go-zond/internal/ethapi" 30 "github.com/theQRL/go-zond/node" 31 "github.com/theQRL/go-zond/rpc" 32 "github.com/theQRL/go-zond/zond/filters" 33 ) 34 35 type handler struct { 36 Schema *graphql.Schema 37 } 38 39 func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { 40 var params struct { 41 Query string `json:"query"` 42 OperationName string `json:"operationName"` 43 Variables map[string]interface{} `json:"variables"` 44 } 45 if err := json.NewDecoder(r.Body).Decode(¶ms); err != nil { 46 http.Error(w, err.Error(), http.StatusBadRequest) 47 return 48 } 49 50 var ( 51 ctx = r.Context() 52 responded sync.Once 53 timer *time.Timer 54 cancel context.CancelFunc 55 ) 56 ctx, cancel = context.WithCancel(ctx) 57 defer cancel() 58 59 if timeout, ok := rpc.ContextRequestTimeout(ctx); ok { 60 timer = time.AfterFunc(timeout, func() { 61 responded.Do(func() { 62 // Cancel request handling. 63 cancel() 64 65 // Create the timeout response. 66 response := &graphql.Response{ 67 Errors: []*gqlErrors.QueryError{{Message: "request timed out"}}, 68 } 69 responseJSON, err := json.Marshal(response) 70 if err != nil { 71 http.Error(w, err.Error(), http.StatusInternalServerError) 72 return 73 } 74 75 // Setting this disables gzip compression in package node. 76 w.Header().Set("transfer-encoding", "identity") 77 78 // Flush the response. Since we are writing close to the response timeout, 79 // chunked transfer encoding must be disabled by setting content-length. 80 w.Header().Set("content-type", "application/json") 81 w.Header().Set("content-length", strconv.Itoa(len(responseJSON))) 82 w.Write(responseJSON) 83 if flush, ok := w.(http.Flusher); ok { 84 flush.Flush() 85 } 86 }) 87 }) 88 } 89 90 response := h.Schema.Exec(ctx, params.Query, params.OperationName, params.Variables) 91 if timer != nil { 92 timer.Stop() 93 } 94 responded.Do(func() { 95 responseJSON, err := json.Marshal(response) 96 if err != nil { 97 http.Error(w, err.Error(), http.StatusInternalServerError) 98 return 99 } 100 if len(response.Errors) > 0 { 101 w.WriteHeader(http.StatusBadRequest) 102 } 103 w.Header().Set("Content-Type", "application/json") 104 w.Write(responseJSON) 105 }) 106 } 107 108 // New constructs a new GraphQL service instance. 109 func New(stack *node.Node, backend ethapi.Backend, filterSystem *filters.FilterSystem, cors, vhosts []string) error { 110 _, err := newHandler(stack, backend, filterSystem, cors, vhosts) 111 return err 112 } 113 114 // newHandler returns a new `http.Handler` that will answer GraphQL queries. 115 // It additionally exports an interactive query browser on the / endpoint. 116 func newHandler(stack *node.Node, backend ethapi.Backend, filterSystem *filters.FilterSystem, cors, vhosts []string) (*handler, error) { 117 q := Resolver{backend, filterSystem} 118 119 s, err := graphql.ParseSchema(schema, &q) 120 if err != nil { 121 return nil, err 122 } 123 h := handler{Schema: s} 124 handler := node.NewHTTPHandlerStack(h, cors, vhosts, nil) 125 126 stack.RegisterHandler("GraphQL UI", "/graphql/ui", GraphiQL{}) 127 stack.RegisterHandler("GraphQL UI", "/graphql/ui/", GraphiQL{}) 128 stack.RegisterHandler("GraphQL", "/graphql", handler) 129 stack.RegisterHandler("GraphQL", "/graphql/", handler) 130 131 return &h, nil 132 }