-
Notifications
You must be signed in to change notification settings - Fork 1.1k
/
http_form_urlencoded.go
119 lines (97 loc) · 2.92 KB
/
http_form_urlencoded.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
package transport
import (
"io"
"mime"
"net/http"
"net/url"
"strings"
"github.com/vektah/gqlparser/v2/gqlerror"
"github.com/99designs/gqlgen/graphql"
)
// FORM implements the application/x-www-form-urlencoded side of the default HTTP transport
type UrlEncodedForm struct {
// Map of all headers that are added to graphql response. If not
// set, only one header: Content-Type: application/json will be set.
ResponseHeaders map[string][]string
}
var _ graphql.Transport = UrlEncodedForm{}
func (h UrlEncodedForm) Supports(r *http.Request) bool {
if r.Header.Get("Upgrade") != "" {
return false
}
mediaType, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
if err != nil {
return false
}
return r.Method == "POST" && mediaType == "application/x-www-form-urlencoded"
}
func (h UrlEncodedForm) Do(w http.ResponseWriter, r *http.Request, exec graphql.GraphExecutor) {
ctx := r.Context()
writeHeaders(w, h.ResponseHeaders)
params := &graphql.RawParams{}
start := graphql.Now()
params.Headers = r.Header
params.ReadTime = graphql.TraceTiming{
Start: start,
End: graphql.Now(),
}
bodyString, err := getRequestBody(r)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
gqlErr := gqlerror.Errorf("could not get form body: %+v", err)
resp := exec.DispatchError(ctx, gqlerror.List{gqlErr})
writeJson(w, resp)
return
}
params, err = h.parseBody(bodyString)
if err != nil {
w.WriteHeader(http.StatusUnprocessableEntity)
gqlErr := gqlerror.Errorf("could not cleanup body: %+v", err)
resp := exec.DispatchError(ctx, gqlerror.List{gqlErr})
writeJson(w, resp)
return
}
rc, OpErr := exec.CreateOperationContext(ctx, params)
if OpErr != nil {
w.WriteHeader(statusFor(OpErr))
resp := exec.DispatchError(graphql.WithOperationContext(ctx, rc), OpErr)
writeJson(w, resp)
return
}
var responses graphql.ResponseHandler
responses, ctx = exec.DispatchOperation(ctx, rc)
writeJson(w, responses(ctx))
}
func (h UrlEncodedForm) parseBody(bodyString string) (*graphql.RawParams, error) {
switch {
case strings.Contains(bodyString, "\"query\":"):
// body is json
return h.parseJson(bodyString)
case strings.HasPrefix(bodyString, "query=%7B"):
// body is urlencoded
return h.parseEncoded(bodyString)
default:
// body is plain text
params := &graphql.RawParams{}
params.Query = strings.TrimPrefix(bodyString, "query=")
return params, nil
}
}
func (h UrlEncodedForm) parseEncoded(bodyString string) (*graphql.RawParams, error) {
params := &graphql.RawParams{}
query, err := url.QueryUnescape(bodyString)
if err != nil {
return nil, err
}
params.Query = strings.TrimPrefix(query, "query=")
return params, nil
}
func (h UrlEncodedForm) parseJson(bodyString string) (*graphql.RawParams, error) {
params := &graphql.RawParams{}
bodyReader := io.NopCloser(strings.NewReader(bodyString))
err := jsonDecode(bodyReader, ¶ms)
if err != nil {
return nil, err
}
return params, nil
}