Bump reva deps (#8412)
* bump dependencies Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de> * bump reva and add config options Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de> --------- Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>
This commit is contained in:
+1
-1
@@ -5,7 +5,7 @@ import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// Handler represents a HTTP handler that manages a request
|
||||
// Handler represents a HTTP handler that manages a request.
|
||||
type Handler interface {
|
||||
// standard http handler
|
||||
http.Handler
|
||||
|
||||
+12
-8
@@ -7,20 +7,23 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
DefaultMaxRecvSize int64 = 1024 * 1024 * 100 // 10Mb
|
||||
// DefaultMaxRecvSize is 10MiB.
|
||||
DefaultMaxRecvSize int64 = 1024 * 1024 * 100
|
||||
)
|
||||
|
||||
// Options is the list of api Options.
|
||||
type Options struct {
|
||||
MaxRecvSize int64
|
||||
Namespace string
|
||||
Router router.Router
|
||||
Client client.Client
|
||||
Logger logger.Logger
|
||||
Namespace string
|
||||
MaxRecvSize int64
|
||||
}
|
||||
|
||||
// Option is a api Option.
|
||||
type Option func(o *Options)
|
||||
|
||||
// NewOptions fills in the blanks
|
||||
// NewOptions fills in the blanks.
|
||||
func NewOptions(opts ...Option) Options {
|
||||
options := Options{
|
||||
Logger: logger.DefaultLogger,
|
||||
@@ -45,34 +48,35 @@ func NewOptions(opts ...Option) Options {
|
||||
return options
|
||||
}
|
||||
|
||||
// WithNamespace specifies the namespace for the handler
|
||||
// WithNamespace specifies the namespace for the handler.
|
||||
func WithNamespace(s string) Option {
|
||||
return func(o *Options) {
|
||||
o.Namespace = s
|
||||
}
|
||||
}
|
||||
|
||||
// WithRouter specifies a router to be used by the handler
|
||||
// WithRouter specifies a router to be used by the handler.
|
||||
func WithRouter(r router.Router) Option {
|
||||
return func(o *Options) {
|
||||
o.Router = r
|
||||
}
|
||||
}
|
||||
|
||||
// WithClient sets the client for the handler.
|
||||
func WithClient(c client.Client) Option {
|
||||
return func(o *Options) {
|
||||
o.Client = c
|
||||
}
|
||||
}
|
||||
|
||||
// WithMaxRecvSize specifies max body size
|
||||
// WithMaxRecvSize specifies max body size.
|
||||
func WithMaxRecvSize(size int64) Option {
|
||||
return func(o *Options) {
|
||||
o.MaxRecvSize = size
|
||||
}
|
||||
}
|
||||
|
||||
// WithLogger specifies the logger
|
||||
// WithLogger specifies the logger.
|
||||
func WithLogger(l logger.Logger) Option {
|
||||
return func(o *Options) {
|
||||
o.Logger = l
|
||||
|
||||
+88
-50
@@ -11,7 +11,6 @@ import (
|
||||
|
||||
jsonpatch "github.com/evanphx/json-patch/v5"
|
||||
"github.com/oxtoacart/bpool"
|
||||
|
||||
"go-micro.dev/v4/api/handler"
|
||||
"go-micro.dev/v4/api/internal/proto"
|
||||
"go-micro.dev/v4/api/router"
|
||||
@@ -29,19 +28,20 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
// Handler is the name of this handler.
|
||||
Handler = "rpc"
|
||||
packageID = "go.micro.api"
|
||||
)
|
||||
|
||||
var (
|
||||
// supported json codecs
|
||||
// supported json codecs.
|
||||
jsonCodecs = []string{
|
||||
"application/grpc+json",
|
||||
"application/json",
|
||||
"application/json-rpc",
|
||||
}
|
||||
|
||||
// support proto codecs
|
||||
// support proto codecs.
|
||||
protoCodecs = []string{
|
||||
"application/grpc",
|
||||
"application/grpc+proto",
|
||||
@@ -66,7 +66,7 @@ func (b *buffer) Write(_ []byte) (int, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// strategy is a hack for selection
|
||||
// strategy is a hack for selection.
|
||||
func strategy(services []*registry.Service) selector.Strategy {
|
||||
return func(_ []*registry.Service) selector.Next {
|
||||
// ignore input to this function, use services above
|
||||
@@ -77,6 +77,7 @@ func strategy(services []*registry.Service) selector.Strategy {
|
||||
func (h *rpcHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
logger := h.opts.Logger
|
||||
bsize := handler.DefaultMaxRecvSize
|
||||
|
||||
if h.opts.MaxRecvSize > 0 {
|
||||
bsize = h.opts.MaxRecvSize
|
||||
}
|
||||
@@ -84,6 +85,7 @@ func (h *rpcHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
r.Body = http.MaxBytesReader(w, r.Body, bsize)
|
||||
|
||||
defer r.Body.Close()
|
||||
|
||||
var service *router.Route
|
||||
|
||||
if h.opts.Router != nil {
|
||||
@@ -94,8 +96,10 @@ func (h *rpcHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
if werr != nil {
|
||||
logger.Log(log.ErrorLevel, werr)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
service = s
|
||||
} else {
|
||||
// we have no way of routing the request
|
||||
@@ -106,18 +110,18 @@ func (h *rpcHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
ct := r.Header.Get("Content-Type")
|
||||
contentType := r.Header.Get("Content-Type")
|
||||
|
||||
// Strip charset from Content-Type (like `application/json; charset=UTF-8`)
|
||||
if idx := strings.IndexRune(ct, ';'); idx >= 0 {
|
||||
ct = ct[:idx]
|
||||
if idx := strings.IndexRune(contentType, ';'); idx >= 0 {
|
||||
contentType = contentType[:idx]
|
||||
}
|
||||
|
||||
// micro client
|
||||
c := h.opts.Client
|
||||
myClient := h.opts.Client
|
||||
|
||||
// create context
|
||||
cx := ctx.FromRequest(r)
|
||||
myContext := ctx.FromRequest(r)
|
||||
// get context from http handler wrappers
|
||||
md, ok := metadata.FromContext(r.Context())
|
||||
if !ok {
|
||||
@@ -133,23 +137,24 @@ func (h *rpcHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// merge context with overwrite
|
||||
cx = metadata.MergeContext(cx, md, true)
|
||||
myContext = metadata.MergeContext(myContext, md, true)
|
||||
|
||||
// set merged context to request
|
||||
*r = *r.Clone(cx)
|
||||
*r = *r.Clone(myContext)
|
||||
// if stream we currently only support json
|
||||
if isStream(r, service) {
|
||||
// drop older context as it can have timeouts and create new
|
||||
// md, _ := metadata.FromContext(cx)
|
||||
//serveWebsocket(context.TODO(), w, r, service, c)
|
||||
if err := serveWebsocket(cx, w, r, service, c); err != nil {
|
||||
// serveWebsocket(context.TODO(), w, r, service, c)
|
||||
if err := serveWebsocket(myContext, w, r, service, myClient); err != nil {
|
||||
logger.Log(log.ErrorLevel, err)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// create strategy
|
||||
so := selector.WithStrategy(strategy(service.Versions))
|
||||
mySelector := selector.WithStrategy(strategy(service.Versions))
|
||||
|
||||
// walk the standard call path
|
||||
// get payload
|
||||
@@ -158,6 +163,7 @@ func (h *rpcHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
if werr := writeError(w, r, err); werr != nil {
|
||||
logger.Log(log.ErrorLevel, werr)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -165,7 +171,7 @@ func (h *rpcHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
switch {
|
||||
// proto codecs
|
||||
case hasCodec(ct, protoCodecs):
|
||||
case hasCodec(contentType, protoCodecs):
|
||||
request := &proto.Message{}
|
||||
// if the extracted payload isn't empty lets use it
|
||||
if len(br) > 0 {
|
||||
@@ -175,18 +181,19 @@ func (h *rpcHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
// create request/response
|
||||
response := &proto.Message{}
|
||||
|
||||
req := c.NewRequest(
|
||||
req := myClient.NewRequest(
|
||||
service.Service,
|
||||
service.Endpoint.Name,
|
||||
request,
|
||||
client.WithContentType(ct),
|
||||
client.WithContentType(contentType),
|
||||
)
|
||||
|
||||
// make the call
|
||||
if err := c.Call(cx, req, response, client.WithSelectOption(so)); err != nil {
|
||||
if err := myClient.Call(myContext, req, response, client.WithSelectOption(mySelector)); err != nil {
|
||||
if werr := writeError(w, r, err); werr != nil {
|
||||
logger.Log(log.ErrorLevel, werr)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -196,13 +203,14 @@ func (h *rpcHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
if werr := writeError(w, r, err); werr != nil {
|
||||
logger.Log(log.ErrorLevel, werr)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
default:
|
||||
// if json codec is not present set to json
|
||||
if !hasCodec(ct, jsonCodecs) {
|
||||
ct = "application/json"
|
||||
if !hasCodec(contentType, jsonCodecs) {
|
||||
contentType = "application/json"
|
||||
}
|
||||
|
||||
// default to trying json
|
||||
@@ -215,17 +223,18 @@ func (h *rpcHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
// create request/response
|
||||
var response json.RawMessage
|
||||
|
||||
req := c.NewRequest(
|
||||
req := myClient.NewRequest(
|
||||
service.Service,
|
||||
service.Endpoint.Name,
|
||||
&request,
|
||||
client.WithContentType(ct),
|
||||
client.WithContentType(contentType),
|
||||
)
|
||||
// make the call
|
||||
if err := c.Call(cx, req, &response, client.WithSelectOption(so)); err != nil {
|
||||
if err := myClient.Call(myContext, req, &response, client.WithSelectOption(mySelector)); err != nil {
|
||||
if werr := writeError(w, r, err); werr != nil {
|
||||
logger.Log(log.ErrorLevel, werr)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -235,6 +244,7 @@ func (h *rpcHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
if werr := writeError(w, r, err); werr != nil {
|
||||
logger.Log(log.ErrorLevel, werr)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -245,8 +255,8 @@ func (h *rpcHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
func (rh *rpcHandler) String() string {
|
||||
return "rpc"
|
||||
func (h *rpcHandler) String() string {
|
||||
return Handler
|
||||
}
|
||||
|
||||
func hasCodec(ct string, codecs []string) bool {
|
||||
@@ -255,49 +265,57 @@ func hasCodec(ct string, codecs []string) bool {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// requestPayload takes a *http.Request.
|
||||
// If the request is a GET the query string parameters are extracted and marshaled to JSON and the raw bytes are returned.
|
||||
// If the request method is a POST the request body is read and returned
|
||||
// If the request method is a POST the request body is read and returned.
|
||||
func requestPayload(r *http.Request) ([]byte, error) {
|
||||
var err error
|
||||
|
||||
// we have to decode json-rpc and proto-rpc because we suck
|
||||
// well actually because there's no proxy codec right now
|
||||
|
||||
ct := r.Header.Get("Content-Type")
|
||||
myCt := r.Header.Get("Content-Type")
|
||||
|
||||
switch {
|
||||
case strings.Contains(ct, "application/json-rpc"):
|
||||
case strings.Contains(myCt, "application/json-rpc"):
|
||||
msg := codec.Message{
|
||||
Type: codec.Request,
|
||||
Header: make(map[string]string),
|
||||
}
|
||||
|
||||
c := jsonrpc.NewCodec(&buffer{r.Body})
|
||||
if err = c.ReadHeader(&msg, codec.Request); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var raw json.RawMessage
|
||||
if err = c.ReadBody(&raw); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ([]byte)(raw), nil
|
||||
case strings.Contains(ct, "application/proto-rpc"), strings.Contains(ct, "application/octet-stream"):
|
||||
case strings.Contains(myCt, "application/proto-rpc"), strings.Contains(myCt, "application/octet-stream"):
|
||||
msg := codec.Message{
|
||||
Type: codec.Request,
|
||||
Header: make(map[string]string),
|
||||
}
|
||||
|
||||
c := protorpc.NewCodec(&buffer{r.Body})
|
||||
if err = c.ReadHeader(&msg, codec.Request); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var raw proto.Message
|
||||
if err = c.ReadBody(&raw); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return raw.Marshal()
|
||||
case strings.Contains(ct, "application/www-x-form-urlencoded"):
|
||||
case strings.Contains(myCt, "application/www-x-form-urlencoded"), strings.Contains(myCt, "application/x-www-form-urlencoded"):
|
||||
if err := r.ParseForm(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -315,6 +333,7 @@ func requestPayload(r *http.Request) ([]byte, error) {
|
||||
|
||||
// otherwise as per usual
|
||||
ctx := r.Context()
|
||||
|
||||
// dont user meadata.FromContext as it mangles names
|
||||
md, ok := metadata.FromContext(ctx)
|
||||
if !ok {
|
||||
@@ -331,9 +350,11 @@ func requestPayload(r *http.Request) ([]byte, error) {
|
||||
// filter own keys
|
||||
if strings.HasPrefix(k, "x-api-field-") {
|
||||
matches[strings.TrimPrefix(k, "x-api-field-")] = v
|
||||
|
||||
delete(md, k)
|
||||
} else if k == "x-api-body" {
|
||||
bodydst = v
|
||||
|
||||
delete(md, k)
|
||||
}
|
||||
}
|
||||
@@ -344,10 +365,12 @@ func requestPayload(r *http.Request) ([]byte, error) {
|
||||
// get fields from url values
|
||||
if len(r.URL.RawQuery) > 0 {
|
||||
umd := make(map[string]interface{})
|
||||
|
||||
err = qson.Unmarshal(&umd, r.URL.RawQuery)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for k, v := range umd {
|
||||
matches[k] = v
|
||||
}
|
||||
@@ -362,24 +385,29 @@ func requestPayload(r *http.Request) ([]byte, error) {
|
||||
req[k] = v
|
||||
continue
|
||||
}
|
||||
|
||||
em := make(map[string]interface{})
|
||||
em[ps[len(ps)-1]] = v
|
||||
|
||||
for i := len(ps) - 2; i > 0; i-- {
|
||||
nm := make(map[string]interface{})
|
||||
nm[ps[i]] = em
|
||||
em = nm
|
||||
}
|
||||
|
||||
if vm, ok := req[ps[0]]; ok {
|
||||
// nested map
|
||||
nm := vm.(map[string]interface{})
|
||||
for vk, vv := range em {
|
||||
nm[vk] = vv
|
||||
}
|
||||
|
||||
req[ps[0]] = nm
|
||||
} else {
|
||||
req[ps[0]] = em
|
||||
}
|
||||
}
|
||||
|
||||
pathbuf := []byte("{}")
|
||||
if len(req) > 0 {
|
||||
pathbuf, err = json.Marshal(req)
|
||||
@@ -389,48 +417,55 @@ func requestPayload(r *http.Request) ([]byte, error) {
|
||||
}
|
||||
|
||||
urlbuf := []byte("{}")
|
||||
|
||||
out, err := jsonpatch.MergeMergePatches(urlbuf, pathbuf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch r.Method {
|
||||
case "GET":
|
||||
case http.MethodGet:
|
||||
// empty response
|
||||
if strings.Contains(ct, "application/json") && string(out) == "{}" {
|
||||
if strings.Contains(myCt, "application/json") && string(out) == "{}" {
|
||||
return out, nil
|
||||
} else if string(out) == "{}" && !strings.Contains(ct, "application/json") {
|
||||
} else if string(out) == "{}" && !strings.Contains(myCt, "application/json") {
|
||||
return []byte{}, nil
|
||||
}
|
||||
|
||||
return out, nil
|
||||
case "PATCH", "POST", "PUT", "DELETE":
|
||||
case http.MethodPatch, http.MethodPost, http.MethodPut, http.MethodDelete:
|
||||
bodybuf := []byte("{}")
|
||||
buf := bufferPool.Get()
|
||||
|
||||
defer bufferPool.Put(buf)
|
||||
if _, err := buf.ReadFrom(r.Body); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if b := buf.Bytes(); len(b) > 0 {
|
||||
bodybuf = b
|
||||
}
|
||||
|
||||
if bodydst == "" || bodydst == "*" {
|
||||
if out, err = jsonpatch.MergeMergePatches(out, bodybuf); err == nil {
|
||||
return out, nil
|
||||
}
|
||||
}
|
||||
|
||||
var jsonbody map[string]interface{}
|
||||
if json.Valid(bodybuf) {
|
||||
if err = json.Unmarshal(bodybuf, &jsonbody); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
dstmap := make(map[string]interface{})
|
||||
ps := strings.Split(bodydst, ".")
|
||||
if len(ps) == 1 {
|
||||
if jsonbody != nil {
|
||||
dstmap[ps[0]] = jsonbody
|
||||
} else {
|
||||
// old unexpected behaviour
|
||||
// old unexpected behavior
|
||||
dstmap[ps[0]] = bodybuf
|
||||
}
|
||||
} else {
|
||||
@@ -438,7 +473,7 @@ func requestPayload(r *http.Request) ([]byte, error) {
|
||||
if jsonbody != nil {
|
||||
em[ps[len(ps)-1]] = jsonbody
|
||||
} else {
|
||||
// old unexpected behaviour
|
||||
// old unexpected behavior
|
||||
em[ps[len(ps)-1]] = bodybuf
|
||||
}
|
||||
for i := len(ps) - 2; i > 0; i-- {
|
||||
@@ -458,41 +493,41 @@ func requestPayload(r *http.Request) ([]byte, error) {
|
||||
return out, nil
|
||||
}
|
||||
|
||||
//fallback to previous unknown behaviour
|
||||
return bodybuf, nil
|
||||
|
||||
}
|
||||
|
||||
return []byte{}, nil
|
||||
}
|
||||
|
||||
func writeError(w http.ResponseWriter, r *http.Request, err error) error {
|
||||
func writeError(rsp http.ResponseWriter, req *http.Request, err error) error {
|
||||
ce := errors.Parse(err.Error())
|
||||
|
||||
switch ce.Code {
|
||||
case 0:
|
||||
// assuming it's totally screwed
|
||||
ce.Code = 500
|
||||
ce.Code = http.StatusInternalServerError
|
||||
ce.Id = packageID
|
||||
ce.Status = http.StatusText(500)
|
||||
ce.Status = http.StatusText(http.StatusInternalServerError)
|
||||
ce.Detail = "error during request: " + ce.Detail
|
||||
w.WriteHeader(500)
|
||||
|
||||
rsp.WriteHeader(http.StatusInternalServerError)
|
||||
default:
|
||||
w.WriteHeader(int(ce.Code))
|
||||
rsp.WriteHeader(int(ce.Code))
|
||||
}
|
||||
|
||||
// response content type
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
rsp.Header().Set("Content-Type", "application/json")
|
||||
|
||||
// Set trailers
|
||||
if strings.Contains(r.Header.Get("Content-Type"), "application/grpc") {
|
||||
w.Header().Set("Trailer", "grpc-status")
|
||||
w.Header().Set("Trailer", "grpc-message")
|
||||
w.Header().Set("grpc-status", "13")
|
||||
w.Header().Set("grpc-message", ce.Detail)
|
||||
if strings.Contains(req.Header.Get("Content-Type"), "application/grpc") {
|
||||
rsp.Header().Set("Trailer", "grpc-status")
|
||||
rsp.Header().Set("Trailer", "grpc-message")
|
||||
rsp.Header().Set("grpc-status", "13")
|
||||
rsp.Header().Set("grpc-message", ce.Detail)
|
||||
}
|
||||
|
||||
_, werr := w.Write([]byte(ce.Error()))
|
||||
_, werr := rsp.Write([]byte(ce.Error()))
|
||||
|
||||
return werr
|
||||
}
|
||||
|
||||
@@ -515,11 +550,14 @@ func writeResponse(w http.ResponseWriter, r *http.Request, rsp []byte) error {
|
||||
|
||||
// write response
|
||||
_, err := w.Write(rsp)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// NewHandler returns a new RPC handler.
|
||||
func NewHandler(opts ...handler.Option) handler.Handler {
|
||||
options := handler.NewOptions(opts...)
|
||||
|
||||
return &rpcHandler{
|
||||
opts: options,
|
||||
}
|
||||
|
||||
+35
-23
@@ -12,41 +12,41 @@ import (
|
||||
"github.com/gobwas/httphead"
|
||||
"github.com/gobwas/ws"
|
||||
"github.com/gobwas/ws/wsutil"
|
||||
|
||||
"go-micro.dev/v4/api/router"
|
||||
"go-micro.dev/v4/client"
|
||||
raw "go-micro.dev/v4/codec/bytes"
|
||||
"go-micro.dev/v4/selector"
|
||||
)
|
||||
|
||||
// serveWebsocket will stream rpc back over websockets assuming json
|
||||
// serveWebsocket will stream rpc back over websockets assuming json.
|
||||
func serveWebsocket(ctx context.Context, w http.ResponseWriter, r *http.Request, service *router.Route, c client.Client) (err error) {
|
||||
var op ws.OpCode
|
||||
var opCode ws.OpCode
|
||||
|
||||
ct := r.Header.Get("Content-Type")
|
||||
myCt := r.Header.Get("Content-Type")
|
||||
// Strip charset from Content-Type (like `application/json; charset=UTF-8`)
|
||||
if idx := strings.IndexRune(ct, ';'); idx >= 0 {
|
||||
ct = ct[:idx]
|
||||
if idx := strings.IndexRune(myCt, ';'); idx >= 0 {
|
||||
myCt = myCt[:idx]
|
||||
}
|
||||
|
||||
// check proto from request
|
||||
switch ct {
|
||||
switch myCt {
|
||||
case "application/json":
|
||||
op = ws.OpText
|
||||
opCode = ws.OpText
|
||||
default:
|
||||
op = ws.OpBinary
|
||||
opCode = ws.OpBinary
|
||||
}
|
||||
|
||||
hdr := make(http.Header)
|
||||
|
||||
if proto, ok := r.Header["Sec-Websocket-Protocol"]; ok {
|
||||
for _, p := range proto {
|
||||
switch p {
|
||||
case "binary":
|
||||
if p == "binary" {
|
||||
hdr["Sec-WebSocket-Protocol"] = []string{"binary"}
|
||||
op = ws.OpBinary
|
||||
opCode = ws.OpBinary
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
payload, err := requestPayload(r)
|
||||
if err != nil {
|
||||
return
|
||||
@@ -67,7 +67,7 @@ func serveWebsocket(ctx context.Context, w http.ResponseWriter, r *http.Request,
|
||||
Header: hdr,
|
||||
}
|
||||
|
||||
conn, rw, _, err := upgrader.Upgrade(r, w)
|
||||
conn, uRw, _, err := upgrader.Upgrade(r, w)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -79,8 +79,9 @@ func serveWebsocket(ctx context.Context, w http.ResponseWriter, r *http.Request,
|
||||
}()
|
||||
|
||||
var request interface{}
|
||||
|
||||
if !bytes.Equal(payload, []byte(`{}`)) {
|
||||
switch ct {
|
||||
switch myCt {
|
||||
case "application/json", "":
|
||||
m := json.RawMessage(payload)
|
||||
request = &m
|
||||
@@ -90,20 +91,25 @@ func serveWebsocket(ctx context.Context, w http.ResponseWriter, r *http.Request,
|
||||
}
|
||||
|
||||
// we always need to set content type for message
|
||||
if ct == "" {
|
||||
ct = "application/json"
|
||||
if myCt == "" {
|
||||
myCt = "application/json"
|
||||
}
|
||||
|
||||
req := c.NewRequest(
|
||||
service.Service,
|
||||
service.Endpoint.Name,
|
||||
request,
|
||||
client.WithContentType(ct),
|
||||
client.WithContentType(myCt),
|
||||
client.StreamingRequest(),
|
||||
)
|
||||
|
||||
cCtx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
|
||||
so := selector.WithStrategy(strategy(service.Versions))
|
||||
|
||||
// create a new stream
|
||||
stream, err := c.Stream(ctx, req, client.WithSelectOption(so))
|
||||
stream, err := c.Stream(cCtx, req, client.WithSelectOption(so))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -115,7 +121,7 @@ func serveWebsocket(ctx context.Context, w http.ResponseWriter, r *http.Request,
|
||||
}
|
||||
|
||||
go func() {
|
||||
if wErr := writeLoop(rw, stream); wErr != nil && err == nil {
|
||||
if wErr := writeLoop(uRw, stream); wErr != nil && err == nil {
|
||||
err = wErr
|
||||
}
|
||||
}()
|
||||
@@ -137,21 +143,23 @@ func serveWebsocket(ctx context.Context, w http.ResponseWriter, r *http.Request,
|
||||
if strings.Contains(err.Error(), "context canceled") {
|
||||
return nil
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// write the response
|
||||
if err = wsutil.WriteServerMessage(rw, op, buf); err != nil {
|
||||
if err = wsutil.WriteServerMessage(uRw, opCode, buf); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = rw.Flush(); err != nil {
|
||||
|
||||
if err = uRw.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// writeLoop
|
||||
// writeLoop.
|
||||
func writeLoop(rw io.ReadWriter, stream client.Stream) error {
|
||||
// close stream when done
|
||||
defer stream.Close()
|
||||
@@ -171,10 +179,12 @@ func writeLoop(rw io.ReadWriter, stream client.Stream) error {
|
||||
case ws.StatusNormalClosure, ws.StatusNoStatusRcvd:
|
||||
// this happens when user close ws connection, or we don't get any status
|
||||
return nil
|
||||
default:
|
||||
return err
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
switch op {
|
||||
default:
|
||||
// not relevant
|
||||
@@ -211,6 +221,7 @@ func isStream(r *http.Request, srv *router.Route) bool {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -222,6 +233,7 @@ func isWebSocket(r *http.Request) bool {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user