refactor http client code
License: MIT Signed-off-by: Jeromy <jeromyj@gmail.com>
Jeromy committed
Jul 25, 2015 at 19:56 UTC
fd75b646307354977534d1c559b8e913333d472b
1 file changed
+58
-61
commands/http/client.go
+58
-61
@@ -184,55 +184,16 @@ func getResponse(httpRes *http.Response, req cmds.Request) (cmds.Response, error
184
185
res.SetCloser(httpRes.Body)
186
187
- if len(httpRes.Header.Get(streamHeader)) > 0 && contentType != "application/json" {
188
- // if output is a stream, we can just use the body reader
187
+ if contentType != "application/json" {
188
+ // for all non json output types, just stream back the output
189
res.SetOutput(&httpResponseReader{httpRes})
190
return res, nil
191
192
} else if len(httpRes.Header.Get(channelHeader)) > 0 {
193
// if output is coming from a channel, decode each chunk
194
outChan := make(chan interface{})
195
- go func() {
196
- dec := json.NewDecoder(&httpResponseReader{httpRes})
197
- outputType := reflect.TypeOf(req.Command().Type)
198
-
199
- ctx := req.Context()
200
-
201
- for {
202
- var v interface{}
203
- var err error
204
- if outputType != nil {
205
- v = reflect.New(outputType).Interface()
206
- err = dec.Decode(v)
207
- } else {
208
- err = dec.Decode(&v)
209
- }
210
-
211
- // since we are just looping reading on the response, the only way to
212
- // know we are 'done' is for the consumer to close the response body.
213
- // doing so doesnt throw an io.EOF, but we want to treat it like one.
214
- if err != nil && strings.Contains(err.Error(), "read on closed response body") {
215
- err = io.EOF
216
- }
217
- if err != nil && err != io.EOF {
218
- log.Error(err)
219
- return
220
- }
221
-
222
- select {
223
- case <-ctx.Done():
224
- close(outChan)
225
- return
226
- default:
227
- }
228
-
229
- if err == io.EOF {
230
- close(outChan)
231
- return
232
- }
233
- outChan <- v
234
- }
235
- }()
195
+
196
+ go readStreamedJson(req, httpRes, outChan)
197
198
res.SetOutput((<-chan interface{})(outChan))
199
return res, nil
@@ -240,22 +201,24 @@ func getResponse(httpRes *http.Response, req cmds.Request) (cmds.Response, error
201
202
dec := json.NewDecoder(&httpResponseReader{httpRes})
203
204
+ // If we ran into an error
205
if httpRes.StatusCode >= http.StatusBadRequest {
206
e := cmds.Error{}
207
246
- if httpRes.StatusCode == http.StatusNotFound {
208
+ switch {
209
+ case httpRes.StatusCode == http.StatusNotFound:
210
// handle 404s
211
e.Message = "Command not found."
212
e.Code = cmds.ErrClient
213
251
- } else if contentType == "text/plain" {
214
+ case contentType == "text/plain":
215
// handle non-marshalled errors
216
buf := bytes.NewBuffer(nil)
217
io.Copy(buf, httpRes.Body)
218
e.Message = string(buf.Bytes())
219
e.Code = cmds.ErrNormal
220
258
- } else {
221
+ default:
222
// handle marshalled errors
223
err = dec.Decode(&e)
224
if err != nil {
@@ -265,25 +228,59 @@ func getResponse(httpRes *http.Response, req cmds.Request) (cmds.Response, error
228
229
res.SetError(e, e.Code)
230
268
- } else {
269
- outputType := reflect.TypeOf(req.Command().Type)
270
- var v interface{}
271
-
272
- if outputType != nil {
273
- v = reflect.New(outputType).Interface()
274
- err = dec.Decode(v)
275
- } else {
276
- err = dec.Decode(&v)
277
- }
278
- if err != nil && err != io.EOF {
279
- return nil, err
231
+ return res, nil
232
+ }
233
+
234
+ outputType := reflect.TypeOf(req.Command().Type)
235
+ v, err := decodeTypedVal(outputType, dec)
236
+ if err != nil && err != io.EOF {
237
+ return nil, err
238
+ }
239
+
240
+ res.SetOutput(v)
241
+
242
+ return res, nil
243
+}
244
+
245
+func readStreamedJson(req cmds.Request, httpRes *http.Response, out chan<- interface{}) {
246
+ defer close(out)
247
+ dec := json.NewDecoder(&httpResponseReader{httpRes})
248
+ outputType := reflect.TypeOf(req.Command().Type)
249
+
250
+ ctx := req.Context()
251
+
252
+ for {
253
+ v, err := decodeTypedVal(outputType, dec)
254
+ if err != nil {
255
+ // since we are just looping reading on the response, the only way to
256
+ // know we are 'done' is for the consumer to close the response body.
257
+ // doing so doesnt throw an io.EOF, but we want to treat it like one.
258
+ if !(strings.Contains(err.Error(), "read on closed response body") || err == io.EOF) {
259
+ log.Error(err)
260
+ }
261
+ return
262
}
281
- if v != nil {
282
- res.SetOutput(v)
263
+
264
+ select {
265
+ case <-ctx.Done():
266
+ return
267
+ case out <- v:
268
}
269
+
270
}
271
+}
272
286
- return res, nil
273
+func decodeTypedVal(t reflect.Type, dec *json.Decoder) (interface{}, error) {
274
+ var v interface{}
275
+ var err error
276
+ if t != nil {
277
+ v = reflect.New(t).Interface()
278
+ err = dec.Decode(v)
279
+ } else {
280
+ err = dec.Decode(&v)
281
+ }
282
+
283
+ return v, err
284
}
285
286
type httpResponseReader struct {