diff --git a/stream.go b/stream.go index 3de5ed4..7c70b50 100644 --- a/stream.go +++ b/stream.go @@ -2,11 +2,9 @@ package http import ( "bufio" - "bytes" "context" "fmt" "io" - "io/ioutil" "net" "net/http" "sync" @@ -121,59 +119,55 @@ func (h *httpStream) Close() error { func (h *httpStream) parseRsp(ctx context.Context, log logger.Logger, hrsp *http.Response, cf codec.Codec, rsp interface{}, opts client.CallOptions) error { var err error + var buf []byte + + // fast path return + if hrsp.StatusCode == http.StatusNoContent { + return nil + } select { case <-ctx.Done(): err = ctx.Err() default: - // fast path return - if hrsp.StatusCode == http.StatusNoContent { - return nil + if hrsp.Body != nil { + buf, err = io.ReadAll(hrsp.Body) + if err != nil { + if log.V(logger.ErrorLevel) { + log.Errorf(ctx, "failed to read body: %v", err) + } + return errors.InternalServerError("go.micro.client", string(buf)) + } + } + + if log.V(logger.DebugLevel) { + log.Debugf(ctx, "response %s with %v", buf, hrsp.Header) } if hrsp.StatusCode < 400 { - if log.V(logger.DebugLevel) { - buf, rerr := io.ReadAll(hrsp.Body) - log.Debugf(ctx, "response %s with %v", buf, hrsp.Header) - if err != nil { - return errors.InternalServerError("go.micro.client", rerr.Error()) - } - hrsp.Body = ioutil.NopCloser(bytes.NewBuffer(buf)) - } - if err = cf.ReadBody(hrsp.Body, rsp); err != nil { + if err = cf.Unmarshal(buf, rsp); err != nil { return errors.InternalServerError("go.micro.client", err.Error()) } return nil } + var rerr interface{} errmap, ok := opts.Context.Value(errorMapKey{}).(map[string]interface{}) if ok && errmap != nil { - if err, ok = errmap[fmt.Sprintf("%d", hrsp.StatusCode)].(error); !ok { - err, ok = errmap["default"].(error) + if rerr, ok = errmap[fmt.Sprintf("%d", hrsp.StatusCode)].(error); !ok { + rerr, ok = errmap["default"].(error) } } - if !ok || err == nil { - buf, cerr := io.ReadAll(hrsp.Body) - if log.V(logger.DebugLevel) { - log.Debugf(ctx, "response %s with %v", buf, hrsp.Header) - } - if cerr != nil { - return errors.InternalServerError("go.micro.client", cerr.Error()) - } + if !ok || rerr == nil { return errors.New("go.micro.client", string(buf), int32(hrsp.StatusCode)) } - if log.V(logger.DebugLevel) { - buf, rerr := io.ReadAll(hrsp.Body) - log.Debugf(ctx, "response %s with %v", buf, hrsp.Header) - if err != nil { - return errors.InternalServerError("go.micro.client", rerr.Error()) - } - hrsp.Body = ioutil.NopCloser(bytes.NewBuffer(buf)) + if cerr := cf.Unmarshal(buf, rerr); cerr != nil { + return errors.InternalServerError("go.micro.client", cerr.Error()) } - if cerr := cf.ReadBody(hrsp.Body, err); cerr != nil { - err = errors.InternalServerError("go.micro.client", cerr.Error()) + if err, ok = rerr.(error); !ok { + err = &Error{rerr} } } diff --git a/util.go b/util.go index 3d17b34..f5fcf36 100644 --- a/util.go +++ b/util.go @@ -1,11 +1,9 @@ package http import ( - "bytes" "context" "fmt" "io" - "io/ioutil" "net/http" "net/url" "reflect" @@ -247,45 +245,48 @@ func newTemplate(path string) ([]string, error) { func (h *httpClient) parseRsp(ctx context.Context, hrsp *http.Response, rsp interface{}, opts client.CallOptions) error { var err error + var buf []byte + + // fast path return + if hrsp.StatusCode == http.StatusNoContent { + return nil + } select { case <-ctx.Done(): err = ctx.Err() default: - // fast path return - if hrsp.StatusCode == http.StatusNoContent { - return nil - } ct := DefaultContentType if htype := hrsp.Header.Get("Content-Type"); htype != "" { ct = htype } - cf, cerr := h.newCodec(ct) - if hrsp.StatusCode >= 400 && cerr != nil { - var buf []byte - if hrsp.Body != nil { - buf, err = io.ReadAll(hrsp.Body) - if err != nil && h.opts.Logger.V(logger.ErrorLevel) { + if hrsp.Body != nil { + buf, err = io.ReadAll(hrsp.Body) + if err != nil { + if h.opts.Logger.V(logger.ErrorLevel) { h.opts.Logger.Errorf(ctx, "failed to read body: %v", err) } + return errors.InternalServerError("go.micro.client", string(buf)) } + } + + cf, cerr := h.newCodec(ct) + if cerr != nil { if h.opts.Logger.V(logger.DebugLevel) { - h.opts.Logger.Debugf(ctx, "response %s with %v", buf, hrsp.Header) - } - // response like text/plain or something else, return original error - return errors.New("go.micro.client", string(buf), int32(hrsp.StatusCode)) - } else if cerr != nil { - if h.opts.Logger.V(logger.DebugLevel) { - h.opts.Logger.Debugf(ctx, "response with %v unknown content-type", hrsp.Header, ct) + h.opts.Logger.Debugf(ctx, "response with %v unknown content-type %s", hrsp.Header, ct, buf) } return errors.InternalServerError("go.micro.client", cerr.Error()) } + if h.opts.Logger.V(logger.DebugLevel) { + h.opts.Logger.Debugf(ctx, "response %s with %v", buf, hrsp.Header) + } + // succeseful response if hrsp.StatusCode < 400 { - if err = cf.ReadBody(hrsp.Body, rsp); err != nil { + if err = cf.Unmarshal(buf, rsp); err != nil { return errors.InternalServerError("go.micro.client", err.Error()) } return nil @@ -302,26 +303,10 @@ func (h *httpClient) parseRsp(ctx context.Context, hrsp *http.Response, rsp inte } if !ok || rerr == nil { - buf, rerr := io.ReadAll(hrsp.Body) - if h.opts.Logger.V(logger.DebugLevel) { - h.opts.Logger.Debugf(ctx, "response %s with %v", buf, hrsp.Header) - } - if rerr != nil { - return errors.InternalServerError("go.micro.client", rerr.Error()) - } return errors.New("go.micro.client", string(buf), int32(hrsp.StatusCode)) } - if h.opts.Logger.V(logger.DebugLevel) { - buf, rerr := io.ReadAll(hrsp.Body) - h.opts.Logger.Debugf(ctx, "response %s with %v", buf, hrsp.Header) - if err != nil { - return errors.InternalServerError("go.micro.client", rerr.Error()) - } - hrsp.Body = ioutil.NopCloser(bytes.NewBuffer(buf)) - } - - if cerr := cf.ReadBody(hrsp.Body, rerr); cerr != nil { + if cerr := cf.Unmarshal(buf, rerr); cerr != nil { return errors.InternalServerError("go.micro.client", cerr.Error()) }