Compare commits

...

2 Commits

Author SHA1 Message Date
8de7912a91 detect response content-type
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-04-18 15:45:42 +03:00
6ccb40bab0 minor content-type fixes
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-04-16 17:09:29 +03:00
3 changed files with 85 additions and 59 deletions

48
http.go
View File

@@ -12,6 +12,7 @@ import (
"net/url"
"os"
"strings"
"sync"
"time"
"github.com/unistack-org/micro/v3/broker"
@@ -20,7 +21,6 @@ import (
"github.com/unistack-org/micro/v3/errors"
"github.com/unistack-org/micro/v3/metadata"
"github.com/unistack-org/micro/v3/router"
rutil "github.com/unistack-org/micro/v3/util/reflect"
)
var (
@@ -37,6 +37,7 @@ type httpClient struct {
dialer *net.Dialer
httpcli *http.Client
init bool
sync.RWMutex
}
func newRequest(addr string, req client.Request, ct string, cf codec.Codec, msg interface{}, opts client.CallOptions) (*http.Request, error) {
@@ -100,22 +101,10 @@ func newRequest(addr string, req client.Request, ct string, cf codec.Codec, msg
return nil, errors.BadRequest("go.micro.client", err.Error())
}
var b []byte
if nmsg != nil {
if ct == "application/x-www-form-urlencoded" {
data, err := rutil.StructURLValues(nmsg, "", tags)
b, err := cf.Marshal(nmsg)
if err != nil {
return nil, errors.BadRequest("go.micro.client", err.Error())
}
b = []byte(data.Encode())
} else {
b, err = cf.Marshal(nmsg)
if err != nil {
return nil, errors.BadRequest("go.micro.client", err.Error())
}
}
}
if len(b) > 0 {
hreq.Body = ioutil.NopCloser(bytes.NewBuffer(b))
@@ -143,20 +132,10 @@ func (h *httpClient) call(ctx context.Context, addr string, req client.Request,
// set the content type for the request
header.Set("Content-Type", ct)
var cf codec.Codec
var err error
// get codec
switch ct {
case "application/x-www-form-urlencoded":
cf, err = h.newCodec(strings.Split(DefaultContentType, ";")[0])
default:
cf, err = h.newCodec(strings.Split(ct, ";")[0])
}
cf, err := h.newCodec(ct)
if err != nil {
return errors.InternalServerError("go.micro.client", err.Error())
}
hreq, err := newRequest(addr, req, ct, cf, req.Body(), opts)
if err != nil {
return err
@@ -182,7 +161,13 @@ func (h *httpClient) call(ctx context.Context, addr string, req client.Request,
defer hrsp.Body.Close()
return parseRsp(ctx, hrsp, cf, rsp, opts)
if ct == "application/x-www-form-urlencoded" {
cf, err = h.newCodec(DefaultContentType)
if err != nil {
return errors.InternalServerError("go.micro.client", err.Error())
}
}
return h.parseRsp(ctx, hrsp, rsp, opts)
}
func (h *httpClient) stream(ctx context.Context, addr string, req client.Request, opts client.CallOptions) (client.Stream, error) {
@@ -208,7 +193,7 @@ func (h *httpClient) stream(ctx context.Context, addr string, req client.Request
header.Set("Content-Type", ct)
// get codec
cf, err := h.newCodec(strings.Split(req.ContentType(), ";")[0])
cf, err := h.newCodec(ct)
if err != nil {
return nil, errors.InternalServerError("go.micro.client", err.Error())
}
@@ -238,6 +223,13 @@ func (h *httpClient) stream(ctx context.Context, addr string, req client.Request
}
func (h *httpClient) newCodec(ct string) (codec.Codec, error) {
h.RLock()
defer h.RUnlock()
if idx := strings.IndexRune(ct, ';'); idx >= 0 {
ct = ct[:idx]
}
if c, ok := h.opts.Codecs[ct]; ok {
return c, nil
}
@@ -560,7 +552,7 @@ func (h *httpClient) Publish(ctx context.Context, p client.Message, opts ...clie
md["Content-Type"] = p.ContentType()
md["Micro-Topic"] = p.Topic()
cf, err := h.newCodec(strings.Split(p.ContentType(), ";")[0])
cf, err := h.newCodec(p.ContentType())
if err != nil {
return errors.InternalServerError("go.micro.client", err.Error())
}

View File

@@ -4,6 +4,7 @@ import (
"bufio"
"context"
"fmt"
"io"
"net"
"net/http"
"sync"
@@ -89,7 +90,7 @@ func (h *httpStream) Recv(msg interface{}) error {
}
defer hrsp.Body.Close()
return parseRsp(h.context, hrsp, h.cf, msg, h.opts)
return h.parseRsp(h.context, hrsp, h.cf, msg, h.opts)
}
func (h *httpStream) Error() error {
@@ -107,3 +108,39 @@ func (h *httpStream) Close() error {
return h.conn.Close()
}
}
func (h *httpStream) parseRsp(ctx context.Context, hrsp *http.Response, cf codec.Codec, rsp interface{}, opts client.CallOptions) error {
var err error
// fast path return
if hrsp.StatusCode == http.StatusNoContent {
return nil
}
if hrsp.StatusCode < 400 {
if err = cf.ReadBody(hrsp.Body, rsp); err != nil {
return errors.InternalServerError("go.micro.client", err.Error())
}
return nil
}
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 err == nil {
buf, err := io.ReadAll(hrsp.Body)
if err != nil {
errors.InternalServerError("go.micro.client", err.Error())
}
return errors.New("go.micro.client", string(buf), int32(hrsp.StatusCode))
}
if cerr := cf.ReadBody(hrsp.Body, err); cerr != nil {
err = errors.InternalServerError("go.micro.client", cerr.Error())
}
return err
}

41
util.go
View File

@@ -3,14 +3,13 @@ package http
import (
"context"
"fmt"
"io/ioutil"
"io"
"net/http"
"reflect"
"strings"
"sync"
"github.com/unistack-org/micro/v3/client"
"github.com/unistack-org/micro/v3/codec"
"github.com/unistack-org/micro/v3/errors"
rutil "github.com/unistack-org/micro/v3/util/reflect"
util "github.com/unistack-org/micro/v3/util/router"
@@ -159,49 +158,47 @@ func newTemplate(path string) (util.Template, error) {
return tpl, nil
}
func parseRsp(ctx context.Context, hrsp *http.Response, cf codec.Codec, rsp interface{}, opts client.CallOptions) error {
func (h *httpClient) parseRsp(ctx context.Context, hrsp *http.Response, rsp interface{}, opts client.CallOptions) error {
// fast path return
if hrsp.StatusCode == http.StatusNoContent {
return nil
}
b, err := ioutil.ReadAll(hrsp.Body)
ct := DefaultContentType
if htype := hrsp.Header.Get("Content-Type"); htype != "" {
ct = htype
}
cf, err := h.newCodec(ct)
if err != nil {
return errors.InternalServerError("go.micro.client", err.Error())
}
if hrsp.StatusCode < 400 {
// unmarshal only if body not nil
if len(b) > 0 {
// unmarshal
if err := cf.Unmarshal(b, rsp); err != nil {
if err := cf.ReadBody(hrsp.Body, rsp); err != nil {
return errors.InternalServerError("go.micro.client", err.Error())
}
}
return nil
}
errmap, ok := opts.Context.Value(errorMapKey{}).(map[string]interface{})
if !ok || errmap == nil {
// user not provide map of errors
// id: req.Service() ??
return errors.New("go.micro.client", string(b), int32(hrsp.StatusCode))
}
if ok && errmap != nil {
if err, ok = errmap[fmt.Sprintf("%d", hrsp.StatusCode)].(error); !ok {
err, ok = errmap["default"].(error)
}
if !ok {
return errors.New("go.micro.client", string(b), int32(hrsp.StatusCode))
}
if err == nil {
buf, err := io.ReadAll(hrsp.Body)
if err != nil {
errors.InternalServerError("go.micro.client", err.Error())
}
return errors.New("go.micro.client", string(buf), int32(hrsp.StatusCode))
}
if len(b) > 0 {
if cerr := cf.Unmarshal(b, err); cerr != nil {
if cerr := cf.ReadBody(hrsp.Body, err); cerr != nil {
err = errors.InternalServerError("go.micro.client", cerr.Error())
}
} else {
err = errors.New("go.micro.client", string(b), int32(hrsp.StatusCode))
}
return err
}