transport/http: encoders and decoders take context
This is necessary to pass context through to the error encoder.
Also, update (fix) all examples.
Peter Bourgon
7 years ago
169 | 169 | log.Fatal(http.ListenAndServe(":8080", nil)) |
170 | 170 | } |
171 | 171 | |
172 | func decodeUppercaseRequest(r *http.Request) (interface{}, error) { | |
172 | func decodeUppercaseRequest(_ context.Context, r *http.Request) (interface{}, error) { | |
173 | 173 | var request uppercaseRequest |
174 | 174 | if err := json.NewDecoder(r.Body).Decode(&request); err != nil { |
175 | 175 | return nil, err |
177 | 177 | return request, nil |
178 | 178 | } |
179 | 179 | |
180 | func decodeCountRequest(r *http.Request) (interface{}, error) { | |
180 | func decodeCountRequest(_ context.Context, r *http.Request) (interface{}, error) { | |
181 | 181 | var request countRequest |
182 | 182 | if err := json.NewDecoder(r.Body).Decode(&request); err != nil { |
183 | 183 | return nil, err |
185 | 185 | return request, nil |
186 | 186 | } |
187 | 187 | |
188 | func encodeResponse(w http.ResponseWriter, response interface{}) error { | |
188 | func encodeResponse(_ context.Context, w http.ResponseWriter, response interface{}) error { | |
189 | 189 | return json.NewEncoder(w).Encode(response) |
190 | 190 | } |
191 | 191 | ``` |
4 | 4 | "encoding/json" |
5 | 5 | "io/ioutil" |
6 | 6 | "net/http" |
7 | ||
8 | "golang.org/x/net/context" | |
7 | 9 | ) |
8 | 10 | |
9 | 11 | // DecodeSumRequest decodes the request from the provided HTTP request, simply |
10 | 12 | // by JSON decoding from the request body. It's designed to be used in |
11 | 13 | // transport/http.Server. |
12 | func DecodeSumRequest(r *http.Request) (interface{}, error) { | |
14 | func DecodeSumRequest(_ context.Context, r *http.Request) (interface{}, error) { | |
13 | 15 | var request SumRequest |
14 | 16 | err := json.NewDecoder(r.Body).Decode(&request) |
15 | 17 | return &request, err |
18 | 20 | // EncodeSumResponse encodes the response to the provided HTTP response |
19 | 21 | // writer, simply by JSON encoding to the writer. It's designed to be used in |
20 | 22 | // transport/http.Server. |
21 | func EncodeSumResponse(w http.ResponseWriter, response interface{}) error { | |
23 | func EncodeSumResponse(_ context.Context, w http.ResponseWriter, response interface{}) error { | |
22 | 24 | return json.NewEncoder(w).Encode(response) |
23 | 25 | } |
24 | 26 | |
25 | 27 | // DecodeConcatRequest decodes the request from the provided HTTP request, |
26 | 28 | // simply by JSON decoding from the request body. It's designed to be used in |
27 | 29 | // transport/http.Server. |
28 | func DecodeConcatRequest(r *http.Request) (interface{}, error) { | |
30 | func DecodeConcatRequest(_ context.Context, r *http.Request) (interface{}, error) { | |
29 | 31 | var request ConcatRequest |
30 | 32 | err := json.NewDecoder(r.Body).Decode(&request) |
31 | 33 | return &request, err |
34 | 36 | // EncodeConcatResponse encodes the response to the provided HTTP response |
35 | 37 | // writer, simply by JSON encoding to the writer. It's designed to be used in |
36 | 38 | // transport/http.Server. |
37 | func EncodeConcatResponse(w http.ResponseWriter, response interface{}) error { | |
39 | func EncodeConcatResponse(_ context.Context, w http.ResponseWriter, response interface{}) error { | |
38 | 40 | return json.NewEncoder(w).Encode(response) |
39 | 41 | } |
40 | 42 | |
41 | 43 | // EncodeSumRequest encodes the request to the provided HTTP request, simply |
42 | 44 | // by JSON encoding to the request body. It's designed to be used in |
43 | 45 | // transport/http.Client. |
44 | func EncodeSumRequest(r *http.Request, request interface{}) error { | |
46 | func EncodeSumRequest(_ context.Context, r *http.Request, request interface{}) error { | |
45 | 47 | var buf bytes.Buffer |
46 | 48 | if err := json.NewEncoder(&buf).Encode(request); err != nil { |
47 | 49 | return err |
53 | 55 | // DecodeSumResponse decodes the response from the provided HTTP response, |
54 | 56 | // simply by JSON decoding from the response body. It's designed to be used in |
55 | 57 | // transport/http.Client. |
56 | func DecodeSumResponse(resp *http.Response) (interface{}, error) { | |
58 | func DecodeSumResponse(_ context.Context, resp *http.Response) (interface{}, error) { | |
57 | 59 | var response SumResponse |
58 | 60 | err := json.NewDecoder(resp.Body).Decode(&response) |
59 | 61 | return response, err |
62 | 64 | // EncodeConcatRequest encodes the request to the provided HTTP request, |
63 | 65 | // simply by JSON encoding to the request body. It's designed to be used in |
64 | 66 | // transport/http.Client. |
65 | func EncodeConcatRequest(r *http.Request, request interface{}) error { | |
67 | func EncodeConcatRequest(_ context.Context, r *http.Request, request interface{}) error { | |
66 | 68 | var buf bytes.Buffer |
67 | 69 | if err := json.NewEncoder(&buf).Encode(request); err != nil { |
68 | 70 | return err |
74 | 76 | // DecodeConcatResponse decodes the response from the provided HTTP response, |
75 | 77 | // simply by JSON decoding from the response body. It's designed to be used in |
76 | 78 | // transport/http.Client. |
77 | func DecodeConcatResponse(resp *http.Response) (interface{}, error) { | |
79 | func DecodeConcatResponse(_ context.Context, resp *http.Response) (interface{}, error) { | |
78 | 80 | var response ConcatResponse |
79 | 81 | err := json.NewDecoder(resp.Body).Decode(&response) |
80 | 82 | return response, err |
172 | 172 | } |
173 | 173 | } |
174 | 174 | |
175 | func passEncode(r *http.Request, request interface{}) error { | |
175 | func passEncode(_ context.Context, r *http.Request, request interface{}) error { | |
176 | 176 | r.Body = request.(io.ReadCloser) |
177 | 177 | return nil |
178 | 178 | } |
179 | 179 | |
180 | func passDecode(r *http.Response) (interface{}, error) { | |
180 | func passDecode(_ context.Context, r *http.Response) (interface{}, error) { | |
181 | 181 | return ioutil.ReadAll(r.Body) |
182 | 182 | } |
183 | 183 |
100 | 100 | return r |
101 | 101 | } |
102 | 102 | |
103 | func decodePostProfileRequest(r *stdhttp.Request) (request interface{}, err error) { | |
103 | func decodePostProfileRequest(_ context.Context, r *stdhttp.Request) (request interface{}, err error) { | |
104 | 104 | var req postProfileRequest |
105 | 105 | if e := json.NewDecoder(r.Body).Decode(&req.Profile); e != nil { |
106 | 106 | return nil, e |
108 | 108 | return req, nil |
109 | 109 | } |
110 | 110 | |
111 | func decodeGetProfileRequest(r *stdhttp.Request) (request interface{}, err error) { | |
111 | func decodeGetProfileRequest(_ context.Context, r *stdhttp.Request) (request interface{}, err error) { | |
112 | 112 | vars := mux.Vars(r) |
113 | 113 | id, ok := vars["id"] |
114 | 114 | if !ok { |
117 | 117 | return getProfileRequest{ID: id}, nil |
118 | 118 | } |
119 | 119 | |
120 | func decodePutProfileRequest(r *stdhttp.Request) (request interface{}, err error) { | |
120 | func decodePutProfileRequest(_ context.Context, r *stdhttp.Request) (request interface{}, err error) { | |
121 | 121 | vars := mux.Vars(r) |
122 | 122 | id, ok := vars["id"] |
123 | 123 | if !ok { |
133 | 133 | }, nil |
134 | 134 | } |
135 | 135 | |
136 | func decodePatchProfileRequest(r *stdhttp.Request) (request interface{}, err error) { | |
136 | func decodePatchProfileRequest(_ context.Context, r *stdhttp.Request) (request interface{}, err error) { | |
137 | 137 | vars := mux.Vars(r) |
138 | 138 | id, ok := vars["id"] |
139 | 139 | if !ok { |
149 | 149 | }, nil |
150 | 150 | } |
151 | 151 | |
152 | func decodeDeleteProfileRequest(r *stdhttp.Request) (request interface{}, err error) { | |
152 | func decodeDeleteProfileRequest(_ context.Context, r *stdhttp.Request) (request interface{}, err error) { | |
153 | 153 | vars := mux.Vars(r) |
154 | 154 | id, ok := vars["id"] |
155 | 155 | if !ok { |
158 | 158 | return deleteProfileRequest{ID: id}, nil |
159 | 159 | } |
160 | 160 | |
161 | func decodeGetAddressesRequest(r *stdhttp.Request) (request interface{}, err error) { | |
161 | func decodeGetAddressesRequest(_ context.Context, r *stdhttp.Request) (request interface{}, err error) { | |
162 | 162 | vars := mux.Vars(r) |
163 | 163 | id, ok := vars["id"] |
164 | 164 | if !ok { |
167 | 167 | return getAddressesRequest{ProfileID: id}, nil |
168 | 168 | } |
169 | 169 | |
170 | func decodeGetAddressRequest(r *stdhttp.Request) (request interface{}, err error) { | |
170 | func decodeGetAddressRequest(_ context.Context, r *stdhttp.Request) (request interface{}, err error) { | |
171 | 171 | vars := mux.Vars(r) |
172 | 172 | id, ok := vars["id"] |
173 | 173 | if !ok { |
183 | 183 | }, nil |
184 | 184 | } |
185 | 185 | |
186 | func decodePostAddressRequest(r *stdhttp.Request) (request interface{}, err error) { | |
186 | func decodePostAddressRequest(_ context.Context, r *stdhttp.Request) (request interface{}, err error) { | |
187 | 187 | vars := mux.Vars(r) |
188 | 188 | id, ok := vars["id"] |
189 | 189 | if !ok { |
199 | 199 | }, nil |
200 | 200 | } |
201 | 201 | |
202 | func decodeDeleteAddressRequest(r *stdhttp.Request) (request interface{}, err error) { | |
202 | func decodeDeleteAddressRequest(_ context.Context, r *stdhttp.Request) (request interface{}, err error) { | |
203 | 203 | vars := mux.Vars(r) |
204 | 204 | id, ok := vars["id"] |
205 | 205 | if !ok { |
227 | 227 | // client. I chose to do it this way because I didn't know if something more |
228 | 228 | // specific was necessary. It's certainly possible to specialize on a |
229 | 229 | // per-response (per-method) basis. |
230 | func encodeResponse(w stdhttp.ResponseWriter, response interface{}) error { | |
230 | func encodeResponse(ctx context.Context, w stdhttp.ResponseWriter, response interface{}) error { | |
231 | 231 | if e, ok := response.(errorer); ok && e.error() != nil { |
232 | 232 | // Not a Go kit transport error, but a business-logic error. |
233 | 233 | // Provide those as HTTP errors. |
234 | encodeError(w, e.error()) | |
234 | encodeError(ctx, e.error(), w) | |
235 | 235 | return nil |
236 | 236 | } |
237 | 237 | return json.NewEncoder(w).Encode(response) |
238 | 238 | } |
239 | 239 | |
240 | func encodeError(w stdhttp.ResponseWriter, err error) { | |
240 | func encodeError(_ context.Context, err error, w stdhttp.ResponseWriter) { | |
241 | 241 | if err == nil { |
242 | 242 | panic("encodeError with nil error") |
243 | 243 | } |
254 | 254 | case errAlreadyExists, errInconsistentIDs: |
255 | 255 | return stdhttp.StatusBadRequest |
256 | 256 | default: |
257 | if _, ok := err.(kithttp.BadRequestError); ok { | |
258 | return stdhttp.StatusBadRequest | |
257 | if e, ok := err.(kithttp.TransportError); ok { | |
258 | switch e.Domain { | |
259 | case kithttp.DomainDecode: | |
260 | return stdhttp.StatusBadRequest | |
261 | case kithttp.DomainDo: | |
262 | return stdhttp.StatusServiceUnavailable | |
263 | default: | |
264 | return stdhttp.StatusInternalServerError | |
265 | } | |
259 | 266 | } |
260 | 267 | return stdhttp.StatusInternalServerError |
261 | 268 | } |
87 | 87 | |
88 | 88 | var errBadRoute = errors.New("bad route") |
89 | 89 | |
90 | func decodeBookCargoRequest(r *http.Request) (interface{}, error) { | |
90 | func decodeBookCargoRequest(_ context.Context, r *http.Request) (interface{}, error) { | |
91 | 91 | var body struct { |
92 | 92 | Origin string `json:"origin"` |
93 | 93 | Destination string `json:"destination"` |
105 | 105 | }, nil |
106 | 106 | } |
107 | 107 | |
108 | func decodeLoadCargoRequest(r *http.Request) (interface{}, error) { | |
108 | func decodeLoadCargoRequest(_ context.Context, r *http.Request) (interface{}, error) { | |
109 | 109 | vars := mux.Vars(r) |
110 | 110 | id, ok := vars["id"] |
111 | 111 | if !ok { |
114 | 114 | return loadCargoRequest{ID: cargo.TrackingID(id)}, nil |
115 | 115 | } |
116 | 116 | |
117 | func decodeRequestRoutesRequest(r *http.Request) (interface{}, error) { | |
117 | func decodeRequestRoutesRequest(_ context.Context, r *http.Request) (interface{}, error) { | |
118 | 118 | vars := mux.Vars(r) |
119 | 119 | id, ok := vars["id"] |
120 | 120 | if !ok { |
123 | 123 | return requestRoutesRequest{ID: cargo.TrackingID(id)}, nil |
124 | 124 | } |
125 | 125 | |
126 | func decodeAssignToRouteRequest(r *http.Request) (interface{}, error) { | |
126 | func decodeAssignToRouteRequest(_ context.Context, r *http.Request) (interface{}, error) { | |
127 | 127 | vars := mux.Vars(r) |
128 | 128 | id, ok := vars["id"] |
129 | 129 | if !ok { |
141 | 141 | }, nil |
142 | 142 | } |
143 | 143 | |
144 | func decodeChangeDestinationRequest(r *http.Request) (interface{}, error) { | |
144 | func decodeChangeDestinationRequest(_ context.Context, r *http.Request) (interface{}, error) { | |
145 | 145 | vars := mux.Vars(r) |
146 | 146 | id, ok := vars["id"] |
147 | 147 | if !ok { |
162 | 162 | }, nil |
163 | 163 | } |
164 | 164 | |
165 | func decodeListCargosRequest(r *http.Request) (interface{}, error) { | |
165 | func decodeListCargosRequest(_ context.Context, r *http.Request) (interface{}, error) { | |
166 | 166 | return listCargosRequest{}, nil |
167 | 167 | } |
168 | 168 | |
169 | func decodeListLocationsRequest(r *http.Request) (interface{}, error) { | |
169 | func decodeListLocationsRequest(_ context.Context, r *http.Request) (interface{}, error) { | |
170 | 170 | return listLocationsRequest{}, nil |
171 | 171 | } |
172 | 172 | |
173 | func encodeResponse(w http.ResponseWriter, response interface{}) error { | |
173 | func encodeResponse(ctx context.Context, w http.ResponseWriter, response interface{}) error { | |
174 | 174 | if e, ok := response.(errorer); ok && e.error() != nil { |
175 | encodeError(w, e.error()) | |
175 | encodeError(ctx, e.error(), w) | |
176 | 176 | return nil |
177 | 177 | } |
178 | 178 | w.Header().Set("Content-Type", "application/json; charset=utf-8") |
184 | 184 | } |
185 | 185 | |
186 | 186 | // encode errors from business-logic |
187 | func encodeError(w http.ResponseWriter, err error) { | |
187 | func encodeError(_ context.Context, err error, w http.ResponseWriter) { | |
188 | 188 | switch err { |
189 | 189 | case cargo.ErrUnknown: |
190 | 190 | w.WriteHeader(http.StatusNotFound) |
36 | 36 | return r |
37 | 37 | } |
38 | 38 | |
39 | func decodeRegisterIncidentRequest(r *http.Request) (interface{}, error) { | |
39 | func decodeRegisterIncidentRequest(_ context.Context, r *http.Request) (interface{}, error) { | |
40 | 40 | var body struct { |
41 | 41 | CompletionTime time.Time `json:"completion_time"` |
42 | 42 | TrackingID string `json:"tracking_id"` |
69 | 69 | return types[s] |
70 | 70 | } |
71 | 71 | |
72 | func encodeResponse(w http.ResponseWriter, response interface{}) error { | |
72 | func encodeResponse(ctx context.Context, w http.ResponseWriter, response interface{}) error { | |
73 | 73 | if e, ok := response.(errorer); ok && e.error() != nil { |
74 | encodeError(w, e.error()) | |
74 | encodeError(ctx, e.error(), w) | |
75 | 75 | return nil |
76 | 76 | } |
77 | 77 | w.Header().Set("Content-Type", "application/json; charset=utf-8") |
83 | 83 | } |
84 | 84 | |
85 | 85 | // encode errors from business-logic |
86 | func encodeError(w http.ResponseWriter, err error) { | |
86 | func encodeError(_ context.Context, err error, w http.ResponseWriter) { | |
87 | 87 | switch err { |
88 | 88 | case cargo.ErrUnknown: |
89 | 89 | w.WriteHeader(http.StatusNotFound) |
96 | 96 | ).Endpoint() |
97 | 97 | } |
98 | 98 | |
99 | func decodeFetchRoutesResponse(resp *http.Response) (interface{}, error) { | |
99 | func decodeFetchRoutesResponse(_ context.Context, resp *http.Response) (interface{}, error) { | |
100 | 100 | var response fetchRoutesResponse |
101 | 101 | if err := json.NewDecoder(resp.Body).Decode(&response); err != nil { |
102 | 102 | return nil, err |
104 | 104 | return response, nil |
105 | 105 | } |
106 | 106 | |
107 | func encodeFetchRoutesRequest(r *http.Request, request interface{}) error { | |
107 | func encodeFetchRoutesRequest(_ context.Context, r *http.Request, request interface{}) error { | |
108 | 108 | req := request.(fetchRoutesRequest) |
109 | 109 | |
110 | 110 | vals := r.URL.Query() |
34 | 34 | return r |
35 | 35 | } |
36 | 36 | |
37 | func decodeTrackCargoRequest(r *http.Request) (interface{}, error) { | |
37 | func decodeTrackCargoRequest(_ context.Context, r *http.Request) (interface{}, error) { | |
38 | 38 | vars := mux.Vars(r) |
39 | 39 | id, ok := vars["id"] |
40 | 40 | if !ok { |
43 | 43 | return trackCargoRequest{ID: id}, nil |
44 | 44 | } |
45 | 45 | |
46 | func encodeResponse(w http.ResponseWriter, response interface{}) error { | |
46 | func encodeResponse(ctx context.Context, w http.ResponseWriter, response interface{}) error { | |
47 | 47 | if e, ok := response.(errorer); ok && e.error() != nil { |
48 | encodeError(w, e.error()) | |
48 | encodeError(ctx, e.error(), w) | |
49 | 49 | return nil |
50 | 50 | } |
51 | 51 | w.Header().Set("Content-Type", "application/json; charset=utf-8") |
57 | 57 | } |
58 | 58 | |
59 | 59 | // encode errors from business-logic |
60 | func encodeError(w http.ResponseWriter, err error) { | |
60 | func encodeError(_ context.Context, err error, w http.ResponseWriter) { | |
61 | 61 | switch err { |
62 | 62 | case cargo.ErrUnknown: |
63 | 63 | w.WriteHeader(http.StatusNotFound) |
73 | 73 | } |
74 | 74 | } |
75 | 75 | |
76 | func decodeUppercaseRequest(r *http.Request) (interface{}, error) { | |
76 | func decodeUppercaseRequest(_ context.Context, r *http.Request) (interface{}, error) { | |
77 | 77 | var request uppercaseRequest |
78 | 78 | if err := json.NewDecoder(r.Body).Decode(&request); err != nil { |
79 | 79 | return nil, err |
81 | 81 | return request, nil |
82 | 82 | } |
83 | 83 | |
84 | func decodeCountRequest(r *http.Request) (interface{}, error) { | |
84 | func decodeCountRequest(_ context.Context, r *http.Request) (interface{}, error) { | |
85 | 85 | var request countRequest |
86 | 86 | if err := json.NewDecoder(r.Body).Decode(&request); err != nil { |
87 | 87 | return nil, err |
89 | 89 | return request, nil |
90 | 90 | } |
91 | 91 | |
92 | func encodeResponse(w http.ResponseWriter, response interface{}) error { | |
92 | func encodeResponse(_ context.Context, w http.ResponseWriter, response interface{}) error { | |
93 | 93 | return json.NewEncoder(w).Encode(response) |
94 | 94 | } |
95 | 95 |
27 | 27 | } |
28 | 28 | } |
29 | 29 | |
30 | func decodeUppercaseRequest(r *http.Request) (interface{}, error) { | |
30 | func decodeUppercaseRequest(_ context.Context, r *http.Request) (interface{}, error) { | |
31 | 31 | var request uppercaseRequest |
32 | 32 | if err := json.NewDecoder(r.Body).Decode(&request); err != nil { |
33 | 33 | return nil, err |
35 | 35 | return request, nil |
36 | 36 | } |
37 | 37 | |
38 | func decodeCountRequest(r *http.Request) (interface{}, error) { | |
38 | func decodeCountRequest(_ context.Context, r *http.Request) (interface{}, error) { | |
39 | 39 | var request countRequest |
40 | 40 | if err := json.NewDecoder(r.Body).Decode(&request); err != nil { |
41 | 41 | return nil, err |
43 | 43 | return request, nil |
44 | 44 | } |
45 | 45 | |
46 | func encodeResponse(w http.ResponseWriter, response interface{}) error { | |
46 | func encodeResponse(_ context.Context, w http.ResponseWriter, response interface{}) error { | |
47 | 47 | return json.NewEncoder(w).Encode(response) |
48 | 48 | } |
49 | 49 |
29 | 29 | } |
30 | 30 | } |
31 | 31 | |
32 | func decodeUppercaseRequest(r *http.Request) (interface{}, error) { | |
32 | func decodeUppercaseRequest(_ context.Context, r *http.Request) (interface{}, error) { | |
33 | 33 | var request uppercaseRequest |
34 | 34 | if err := json.NewDecoder(r.Body).Decode(&request); err != nil { |
35 | 35 | return nil, err |
37 | 37 | return request, nil |
38 | 38 | } |
39 | 39 | |
40 | func decodeCountRequest(r *http.Request) (interface{}, error) { | |
40 | func decodeCountRequest(_ context.Context, r *http.Request) (interface{}, error) { | |
41 | 41 | var request countRequest |
42 | 42 | if err := json.NewDecoder(r.Body).Decode(&request); err != nil { |
43 | 43 | return nil, err |
45 | 45 | return request, nil |
46 | 46 | } |
47 | 47 | |
48 | func decodeUppercaseResponse(r *http.Response) (interface{}, error) { | |
48 | func decodeUppercaseResponse(_ context.Context, r *http.Response) (interface{}, error) { | |
49 | 49 | var response uppercaseResponse |
50 | 50 | if err := json.NewDecoder(r.Body).Decode(&response); err != nil { |
51 | 51 | return nil, err |
53 | 53 | return response, nil |
54 | 54 | } |
55 | 55 | |
56 | func encodeResponse(w http.ResponseWriter, response interface{}) error { | |
56 | func encodeResponse(_ context.Context, w http.ResponseWriter, response interface{}) error { | |
57 | 57 | return json.NewEncoder(w).Encode(response) |
58 | 58 | } |
59 | 59 | |
60 | func encodeRequest(r *http.Request, request interface{}) error { | |
60 | func encodeRequest(_ context.Context, r *http.Request, request interface{}) error { | |
61 | 61 | var buf bytes.Buffer |
62 | 62 | if err := json.NewEncoder(&buf).Encode(request); err != nil { |
63 | 63 | return err |
73 | 73 | |
74 | 74 | req, err := http.NewRequest(c.method, c.tgt.String(), nil) |
75 | 75 | if err != nil { |
76 | return nil, TransportError{DomainNewRequest, err} | |
76 | return nil, TransportError{Domain: DomainNewRequest, Err: err} | |
77 | 77 | } |
78 | 78 | |
79 | if err = c.enc(req, request); err != nil { | |
80 | return nil, TransportError{DomainEncode, err} | |
79 | if err = c.enc(ctx, req, request); err != nil { | |
80 | return nil, TransportError{Domain: DomainEncode, Err: err} | |
81 | 81 | } |
82 | 82 | |
83 | 83 | for _, f := range c.before { |
86 | 86 | |
87 | 87 | resp, err := ctxhttp.Do(ctx, c.client, req) |
88 | 88 | if err != nil { |
89 | return nil, TransportError{DomainDo, err} | |
89 | return nil, TransportError{Domain: DomainDo, Err: err} | |
90 | 90 | } |
91 | 91 | if !c.bufferedStream { |
92 | 92 | defer resp.Body.Close() |
93 | 93 | } |
94 | 94 | |
95 | response, err := c.dec(resp) | |
95 | response, err := c.dec(ctx, resp) | |
96 | 96 | if err != nil { |
97 | return nil, TransportError{DomainDecode, err} | |
97 | return nil, TransportError{Domain: DomainDecode, Err: err} | |
98 | 98 | } |
99 | 99 | |
100 | 100 | return response, nil |
20 | 20 | func TestHTTPClient(t *testing.T) { |
21 | 21 | var ( |
22 | 22 | testbody = "testbody" |
23 | encode = func(*http.Request, interface{}) error { return nil } | |
24 | decode = func(r *http.Response) (interface{}, error) { | |
23 | encode = func(context.Context, *http.Request, interface{}) error { return nil } | |
24 | decode = func(_ context.Context, r *http.Response) (interface{}, error) { | |
25 | 25 | buffer := make([]byte, len(testbody)) |
26 | 26 | r.Body.Read(buffer) |
27 | 27 | return TestResponse{r.Body, string(buffer)}, nil |
84 | 84 | func TestHTTPClientBufferedStream(t *testing.T) { |
85 | 85 | var ( |
86 | 86 | testbody = "testbody" |
87 | encode = func(*http.Request, interface{}) error { return nil } | |
88 | decode = func(r *http.Response) (interface{}, error) { | |
87 | encode = func(context.Context, *http.Request, interface{}) error { return nil } | |
88 | decode = func(_ context.Context, r *http.Response) (interface{}, error) { | |
89 | 89 | return TestResponse{r.Body, ""}, nil |
90 | 90 | } |
91 | 91 | ) |
0 | 0 | package http |
1 | 1 | |
2 | import "net/http" | |
2 | import ( | |
3 | "net/http" | |
4 | ||
5 | "golang.org/x/net/context" | |
6 | ) | |
3 | 7 | |
4 | 8 | // DecodeRequestFunc extracts a user-domain request object from an HTTP |
5 | 9 | // request object. It's designed to be used in HTTP servers, for server-side |
6 | 10 | // endpoints. One straightforward DecodeRequestFunc could be something that |
7 | 11 | // JSON decodes from the request body to the concrete response type. |
8 | type DecodeRequestFunc func(*http.Request) (request interface{}, err error) | |
12 | type DecodeRequestFunc func(context.Context, *http.Request) (request interface{}, err error) | |
9 | 13 | |
10 | 14 | // EncodeRequestFunc encodes the passed request object into the HTTP request |
11 | 15 | // object. It's designed to be used in HTTP clients, for client-side |
12 | 16 | // endpoints. One straightforward EncodeRequestFunc could something that JSON |
13 | 17 | // encodes the object directly to the request body. |
14 | type EncodeRequestFunc func(*http.Request, interface{}) error | |
18 | type EncodeRequestFunc func(context.Context, *http.Request, interface{}) error | |
15 | 19 | |
16 | 20 | // EncodeResponseFunc encodes the passed response object to the HTTP response |
17 | 21 | // writer. It's designed to be used in HTTP servers, for server-side |
18 | 22 | // endpoints. One straightforward EncodeResponseFunc could be something that |
19 | 23 | // JSON encodes the object directly to the response body. |
20 | type EncodeResponseFunc func(http.ResponseWriter, interface{}) error | |
24 | type EncodeResponseFunc func(context.Context, http.ResponseWriter, interface{}) error | |
21 | 25 | |
22 | 26 | // DecodeResponseFunc extracts a user-domain response object from an HTTP |
23 | 27 | // response object. It's designed to be used in HTTP clients, for client-side |
24 | 28 | // endpoints. One straightforward DecodeResponseFunc could be something that |
25 | 29 | // JSON decodes from the response body to the concrete response type. |
26 | type DecodeResponseFunc func(*http.Response) (response interface{}, err error) | |
30 | type DecodeResponseFunc func(context.Context, *http.Response) (response interface{}, err error) |
14 | 14 | func TestClientEndpointEncodeError(t *testing.T) { |
15 | 15 | var ( |
16 | 16 | sampleErr = errors.New("Oh no, an error") |
17 | enc = func(r *http.Request, request interface{}) error { return sampleErr } | |
18 | dec = func(r *http.Response) (response interface{}, err error) { return nil, nil } | |
17 | enc = func(context.Context, *http.Request, interface{}) error { return sampleErr } | |
18 | dec = func(context.Context, *http.Response) (interface{}, error) { return nil, nil } | |
19 | 19 | ) |
20 | 20 | |
21 | 21 | u := &url.URL{ |
82 | 82 | ctx = f(ctx, r) |
83 | 83 | } |
84 | 84 | |
85 | request, err := s.dec(r) | |
85 | request, err := s.dec(ctx, r) | |
86 | 86 | if err != nil { |
87 | 87 | s.logger.Log("err", err) |
88 | 88 | s.errorEncoder(ctx, TransportError{Domain: DomainDecode, Err: err}, w) |
100 | 100 | f(ctx, w) |
101 | 101 | } |
102 | 102 | |
103 | if err := s.enc(w, response); err != nil { | |
103 | if err := s.enc(ctx, w, response); err != nil { | |
104 | 104 | s.logger.Log("err", err) |
105 | 105 | s.errorEncoder(ctx, TransportError{Domain: DomainEncode, Err: err}, w) |
106 | 106 | return |
15 | 15 | handler := httptransport.NewServer( |
16 | 16 | context.Background(), |
17 | 17 | func(context.Context, interface{}) (interface{}, error) { return struct{}{}, nil }, |
18 | func(*http.Request) (interface{}, error) { return struct{}{}, errors.New("dang") }, | |
19 | func(http.ResponseWriter, interface{}) error { return nil }, | |
18 | func(context.Context, *http.Request) (interface{}, error) { return struct{}{}, errors.New("dang") }, | |
19 | func(context.Context, http.ResponseWriter, interface{}) error { return nil }, | |
20 | 20 | ) |
21 | 21 | server := httptest.NewServer(handler) |
22 | 22 | defer server.Close() |
30 | 30 | handler := httptransport.NewServer( |
31 | 31 | context.Background(), |
32 | 32 | func(context.Context, interface{}) (interface{}, error) { return struct{}{}, errors.New("dang") }, |
33 | func(*http.Request) (interface{}, error) { return struct{}{}, nil }, | |
34 | func(http.ResponseWriter, interface{}) error { return nil }, | |
33 | func(context.Context, *http.Request) (interface{}, error) { return struct{}{}, nil }, | |
34 | func(context.Context, http.ResponseWriter, interface{}) error { return nil }, | |
35 | 35 | ) |
36 | 36 | server := httptest.NewServer(handler) |
37 | 37 | defer server.Close() |
45 | 45 | handler := httptransport.NewServer( |
46 | 46 | context.Background(), |
47 | 47 | func(context.Context, interface{}) (interface{}, error) { return struct{}{}, nil }, |
48 | func(*http.Request) (interface{}, error) { return struct{}{}, nil }, | |
49 | func(http.ResponseWriter, interface{}) error { return errors.New("dang") }, | |
48 | func(context.Context, *http.Request) (interface{}, error) { return struct{}{}, nil }, | |
49 | func(context.Context, http.ResponseWriter, interface{}) error { return errors.New("dang") }, | |
50 | 50 | ) |
51 | 51 | server := httptest.NewServer(handler) |
52 | 52 | defer server.Close() |
67 | 67 | handler := httptransport.NewServer( |
68 | 68 | context.Background(), |
69 | 69 | func(context.Context, interface{}) (interface{}, error) { return struct{}{}, errTeapot }, |
70 | func(*http.Request) (interface{}, error) { return struct{}{}, nil }, | |
71 | func(http.ResponseWriter, interface{}) error { return nil }, | |
70 | func(context.Context, *http.Request) (interface{}, error) { return struct{}{}, nil }, | |
71 | func(context.Context, http.ResponseWriter, interface{}) error { return nil }, | |
72 | 72 | httptransport.ServerErrorEncoder(func(_ context.Context, err error, w http.ResponseWriter) { w.WriteHeader(code(err)) }), |
73 | 73 | ) |
74 | 74 | server := httptest.NewServer(handler) |
99 | 99 | handler = httptransport.NewServer( |
100 | 100 | ctx, |
101 | 101 | endpoint, |
102 | func(*http.Request) (interface{}, error) { return struct{}{}, nil }, | |
103 | func(http.ResponseWriter, interface{}) error { return nil }, | |
102 | func(context.Context, *http.Request) (interface{}, error) { return struct{}{}, nil }, | |
103 | func(context.Context, http.ResponseWriter, interface{}) error { return nil }, | |
104 | 104 | httptransport.ServerBefore(func(ctx context.Context, r *http.Request) context.Context { return ctx }), |
105 | 105 | httptransport.ServerAfter(func(ctx context.Context, w http.ResponseWriter) { return }), |
106 | 106 | ) |