@cryptotaxi247 / kubo / commits / fd75b6463

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 {