github.com/99designs/gqlgen@v0.17.45/graphql/handler/transport/http_graphql.go (about) 1 package transport 2 3 import ( 4 "mime" 5 "net/http" 6 "net/url" 7 "strings" 8 9 "github.com/vektah/gqlparser/v2/gqlerror" 10 11 "github.com/99designs/gqlgen/graphql" 12 ) 13 14 // GRAPHQL implements the application/graphql side of the HTTP transport 15 // see: https://graphql.org/learn/serving-over-http/#post-request 16 // If the "application/graphql" Content-Type header is present, treat 17 // the HTTP POST body contents as the GraphQL query string. 18 type GRAPHQL struct { 19 // Map of all headers that are added to graphql response. If not 20 // set, only one header: Content-Type: application/json will be set. 21 ResponseHeaders map[string][]string 22 } 23 24 var _ graphql.Transport = GRAPHQL{} 25 26 func (h GRAPHQL) Supports(r *http.Request) bool { 27 if r.Header.Get("Upgrade") != "" { 28 return false 29 } 30 31 mediaType, _, err := mime.ParseMediaType(r.Header.Get("Content-Type")) 32 if err != nil { 33 return false 34 } 35 36 return r.Method == "POST" && mediaType == "application/graphql" 37 } 38 39 func (h GRAPHQL) Do(w http.ResponseWriter, r *http.Request, exec graphql.GraphExecutor) { 40 ctx := r.Context() 41 writeHeaders(w, h.ResponseHeaders) 42 params := &graphql.RawParams{} 43 start := graphql.Now() 44 params.Headers = r.Header 45 params.ReadTime = graphql.TraceTiming{ 46 Start: start, 47 End: graphql.Now(), 48 } 49 50 bodyString, err := getRequestBody(r) 51 if err != nil { 52 gqlErr := gqlerror.Errorf("could not get request body: %+v", err) 53 resp := exec.DispatchError(ctx, gqlerror.List{gqlErr}) 54 writeJson(w, resp) 55 return 56 } 57 58 params.Query, err = cleanupBody(bodyString) 59 if err != nil { 60 w.WriteHeader(http.StatusUnprocessableEntity) 61 gqlErr := gqlerror.Errorf("could not cleanup body: %+v", err) 62 resp := exec.DispatchError(ctx, gqlerror.List{gqlErr}) 63 writeJson(w, resp) 64 return 65 } 66 67 rc, OpErr := exec.CreateOperationContext(ctx, params) 68 if OpErr != nil { 69 w.WriteHeader(statusFor(OpErr)) 70 resp := exec.DispatchError(graphql.WithOperationContext(ctx, rc), OpErr) 71 writeJson(w, resp) 72 return 73 } 74 75 var responses graphql.ResponseHandler 76 responses, ctx = exec.DispatchOperation(ctx, rc) 77 writeJson(w, responses(ctx)) 78 } 79 80 // Makes sure we strip "query=" keyword from body and 81 // that body is not url escaped 82 func cleanupBody(body string) (out string, err error) { 83 // Some clients send 'query=' at the start of body payload. Let's remove 84 // it to get GQL query only. 85 body = strings.TrimPrefix(body, "query=") 86 87 // Body payload can be url encoded or not. We check if %7B - "{" character 88 // is where query starts. If it is, query is url encoded. 89 if strings.HasPrefix(body, "%7B") { 90 body, err = url.QueryUnescape(body) 91 92 if err != nil { 93 return body, err 94 } 95 } 96 97 return body, err 98 }