...

Source file src/github.com/99designs/gqlgen/graphql/handler/transport/http_graphql.go

Documentation: github.com/99designs/gqlgen/graphql/handler/transport

     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  }
    99  

View as plain text