mirror of
https://github.com/taigrr/wasm-experiments
synced 2025-01-18 04:03:21 -08:00
Use custom for of grpc-go and unify client and server
This commit is contained in:
1
vendor/github.com/johanbrandhorst/grpc-wasm/.gitignore
generated
vendored
1
vendor/github.com/johanbrandhorst/grpc-wasm/.gitignore
generated
vendored
@@ -1 +0,0 @@
|
||||
.vscode
|
||||
5
vendor/github.com/johanbrandhorst/grpc-wasm/Makefile
generated
vendored
5
vendor/github.com/johanbrandhorst/grpc-wasm/Makefile
generated
vendored
@@ -1,5 +0,0 @@
|
||||
update-plugin:
|
||||
# https://stackoverflow.com/a/39317180
|
||||
svn export https://github.com/golang/protobuf/trunk/protoc-gen-go
|
||||
rm -rf protoc-gen-wasm
|
||||
mv protoc-gen-go protoc-gen-wasm
|
||||
2
vendor/github.com/johanbrandhorst/grpc-wasm/README.md
generated
vendored
2
vendor/github.com/johanbrandhorst/grpc-wasm/README.md
generated
vendored
@@ -1,2 +0,0 @@
|
||||
# gRPC-WASM
|
||||
gRPC-Web implementation in Go. Built as a drop-in alternative to google.golang.org/grpc.
|
||||
22
vendor/github.com/johanbrandhorst/grpc-wasm/calloptions.go
generated
vendored
22
vendor/github.com/johanbrandhorst/grpc-wasm/calloptions.go
generated
vendored
@@ -1,22 +0,0 @@
|
||||
// +build js,wasm
|
||||
|
||||
package grpc
|
||||
|
||||
// CallOption configures a Call before it starts or extracts information from
|
||||
// a Call after it completes.
|
||||
type CallOption interface {
|
||||
// before is called before the call is sent to any server. If before
|
||||
// returns a non-nil error, the RPC fails with that error.
|
||||
before(*callInfo) error
|
||||
|
||||
// after is called after the call has completed. after cannot return an
|
||||
// error, so any failures should be reported via output parameters.
|
||||
after(*callInfo)
|
||||
}
|
||||
|
||||
type callInfo struct {
|
||||
}
|
||||
|
||||
func defaultCallInfo() *callInfo {
|
||||
return &callInfo{}
|
||||
}
|
||||
234
vendor/github.com/johanbrandhorst/grpc-wasm/clientconn.go
generated
vendored
234
vendor/github.com/johanbrandhorst/grpc-wasm/clientconn.go
generated
vendored
@@ -1,234 +0,0 @@
|
||||
// +build js,wasm
|
||||
|
||||
package grpc
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
spb "google.golang.org/genproto/googleapis/rpc/status"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
type ClientConn struct {
|
||||
target string
|
||||
}
|
||||
|
||||
// Dial creates a client connection to the target. The target string should
|
||||
// be a URL with scheme HTTP or HTTPS, or a FQDN to infer the scheme.
|
||||
func Dial(target string, opts ...DialOption) (*ClientConn, error) {
|
||||
return DialContext(context.Background(), target, opts...)
|
||||
}
|
||||
|
||||
func DialContext(ctx context.Context, target string, opts ...DialOption) (conn *ClientConn, err error) {
|
||||
return &ClientConn{
|
||||
target: target,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (cc *ClientConn) NewStream(ctx context.Context, desc *StreamDesc, method string, opts ...CallOption) (ClientStream, error) {
|
||||
if desc.ClientStreams {
|
||||
return nil, status.Error(codes.Unimplemented, "client-side streaming is not supported by grpc-web")
|
||||
}
|
||||
|
||||
endpoint := cc.target + "/" + method
|
||||
if cc.target == "" {
|
||||
endpoint = method
|
||||
}
|
||||
|
||||
return newStream(ctx, endpoint)
|
||||
}
|
||||
|
||||
func (cc *ClientConn) Invoke(ctx context.Context, method string, args, reply interface{}, opts ...CallOption) error {
|
||||
b, err := proto.Marshal(args.(proto.Message))
|
||||
if err != nil {
|
||||
return status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
|
||||
bufHeader := make([]byte, 5)
|
||||
|
||||
// Write length of b into buf
|
||||
binary.BigEndian.PutUint32(bufHeader[1:], uint32(len(b)))
|
||||
|
||||
endpoint := cc.target + "/" + method
|
||||
if cc.target == "" {
|
||||
endpoint = method
|
||||
}
|
||||
req, err := http.NewRequest(
|
||||
"POST",
|
||||
endpoint,
|
||||
bytes.NewBuffer(append(bufHeader, b...)),
|
||||
)
|
||||
if err != nil {
|
||||
return status.Error(codes.Unavailable, err.Error())
|
||||
}
|
||||
req = req.WithContext(ctx)
|
||||
addHeaders(req)
|
||||
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
st := statusFromHeaders(resp.Header)
|
||||
if st.Code() != codes.OK {
|
||||
return st.Err()
|
||||
}
|
||||
|
||||
msgHeader := make([]byte, 5)
|
||||
for {
|
||||
_, err := resp.Body.Read(msgHeader)
|
||||
if err != nil {
|
||||
return status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
// 1 in MSB signifies that this is the trailer. Break loop.
|
||||
// https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-WEB.md#protocol-differences-vs-grpc-over-http2
|
||||
if msgHeader[0]>>7 == 1 {
|
||||
break
|
||||
}
|
||||
|
||||
msgLen := binary.BigEndian.Uint32(msgHeader[1:])
|
||||
|
||||
msg := make([]byte, msgLen)
|
||||
_, err = resp.Body.Read(msg)
|
||||
if err != nil {
|
||||
return status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
err = proto.Unmarshal(msg, reply.(proto.Message))
|
||||
if err != nil {
|
||||
return status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
if msgHeader[0]&1 == 0 {
|
||||
trailers, err := readTrailers(resp.Body)
|
||||
if err != nil {
|
||||
return status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
st = statusFromHeaders(trailers)
|
||||
} else {
|
||||
// TODO(johanbrandhorst): Support compressed trailers
|
||||
}
|
||||
|
||||
return st.Err()
|
||||
}
|
||||
|
||||
func addHeaders(req *http.Request) {
|
||||
// TODO: Add more headers
|
||||
// https://github.com/grpc/grpc-go/blob/590da37e2dfb4705d8ebd9574ce4cb75295d9674/transport/http2_client.go#L356
|
||||
req.Header.Add("content-type", "application/grpc-web+proto")
|
||||
req.Header.Add("x-grpc-web", "1")
|
||||
if dl, ok := req.Context().Deadline(); ok {
|
||||
timeout := dl.Sub(time.Now())
|
||||
req.Header.Add("grpc-timeout", encodeTimeout(timeout))
|
||||
}
|
||||
md, ok := metadata.FromOutgoingContext(req.Context())
|
||||
if ok {
|
||||
for h, vs := range md {
|
||||
for _, v := range vs {
|
||||
req.Header.Add(h, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const maxTimeoutValue int64 = 100000000 - 1
|
||||
|
||||
// Copied from grpc-go
|
||||
// https://github.com/grpc/grpc-go/blob/590da37e2dfb4705d8ebd9574ce4cb75295d9674/transport/http_util.go#L388
|
||||
// div does integer division and round-up the result. Note that this is
|
||||
// equivalent to (d+r-1)/r but has less chance to overflow.
|
||||
func div(d, r time.Duration) int64 {
|
||||
if m := d % r; m > 0 {
|
||||
return int64(d/r + 1)
|
||||
}
|
||||
return int64(d / r)
|
||||
}
|
||||
|
||||
// Copied from grpc-go
|
||||
// https://github.com/grpc/grpc-go/blob/590da37e2dfb4705d8ebd9574ce4cb75295d9674/transport/http_util.go#L398
|
||||
func encodeTimeout(t time.Duration) string {
|
||||
if t <= 0 {
|
||||
return "0n"
|
||||
}
|
||||
if d := div(t, time.Nanosecond); d <= maxTimeoutValue {
|
||||
return strconv.FormatInt(d, 10) + "n"
|
||||
}
|
||||
if d := div(t, time.Microsecond); d <= maxTimeoutValue {
|
||||
return strconv.FormatInt(d, 10) + "u"
|
||||
}
|
||||
if d := div(t, time.Millisecond); d <= maxTimeoutValue {
|
||||
return strconv.FormatInt(d, 10) + "m"
|
||||
}
|
||||
if d := div(t, time.Second); d <= maxTimeoutValue {
|
||||
return strconv.FormatInt(d, 10) + "S"
|
||||
}
|
||||
if d := div(t, time.Minute); d <= maxTimeoutValue {
|
||||
return strconv.FormatInt(d, 10) + "M"
|
||||
}
|
||||
// Note that maxTimeoutValue * time.Hour > MaxInt64.
|
||||
return strconv.FormatInt(div(t, time.Hour), 10) + "H"
|
||||
}
|
||||
|
||||
// Copied from grpc-go
|
||||
// https://github.com/grpc/grpc-go/blob/b94ea975f3beb73799fac17cc24ee923fcd3cb5c/transport/http_util.go#L213
|
||||
func decodeBinHeader(v string) ([]byte, error) {
|
||||
if len(v)%4 == 0 {
|
||||
// Input was padded, or padding was not necessary.
|
||||
return base64.StdEncoding.DecodeString(v)
|
||||
}
|
||||
return base64.RawStdEncoding.DecodeString(v)
|
||||
}
|
||||
|
||||
func readTrailers(in io.Reader) (http.Header, error) {
|
||||
s := bufio.NewScanner(in)
|
||||
trailers := http.Header{}
|
||||
for s.Scan() {
|
||||
v := s.Text()
|
||||
kv := strings.SplitN(v, ": ", 2)
|
||||
if len(kv) != 2 {
|
||||
return nil, errors.New("malformed header: " + v)
|
||||
}
|
||||
trailers.Add(kv[0], kv[1])
|
||||
}
|
||||
|
||||
return trailers, s.Err()
|
||||
}
|
||||
|
||||
func statusFromHeaders(h http.Header) *status.Status {
|
||||
details := h.Get("grpc-status-details-bin")
|
||||
if details != "" {
|
||||
b, err := decodeBinHeader(details)
|
||||
if err != nil {
|
||||
return status.New(codes.Internal, "malformed grps-status-details-bin header: "+err.Error())
|
||||
}
|
||||
s := &spb.Status{}
|
||||
err = proto.Unmarshal(b, s)
|
||||
if err != nil {
|
||||
return status.New(codes.Internal, "malformed grps-status-details-bin header: "+err.Error())
|
||||
}
|
||||
return status.FromProto(s)
|
||||
}
|
||||
sh := h.Get("grpc-status")
|
||||
if sh != "" {
|
||||
val, err := strconv.Atoi(sh)
|
||||
if err != nil {
|
||||
return status.New(codes.Internal, "malformed grpc-status header: "+err.Error())
|
||||
}
|
||||
return status.New(codes.Code(val), h.Get("grpc-message"))
|
||||
}
|
||||
return status.New(codes.OK, "")
|
||||
}
|
||||
194
vendor/github.com/johanbrandhorst/grpc-wasm/clientstream.go
generated
vendored
194
vendor/github.com/johanbrandhorst/grpc-wasm/clientstream.go
generated
vendored
@@ -1,194 +0,0 @@
|
||||
// +build js,wasm
|
||||
|
||||
package grpc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// ClientStream defines the interface a client stream has to satisfy.
|
||||
type ClientStream grpc.ClientStream
|
||||
|
||||
// ServerStream defines the interface a server stream has to satisfy.
|
||||
type ServerStream grpc.ServerStream
|
||||
|
||||
// StreamHandler defines the handler called by gRPC server to complete the
|
||||
// execution of a streaming RPC. If a StreamHandler returns an error, it
|
||||
// should be produced by the status package, or else gRPC will use
|
||||
// codes.Unknown as the status code and err.Error() as the status message
|
||||
// of the RPC.
|
||||
type StreamHandler func(srv interface{}, stream ServerStream) error
|
||||
|
||||
// StreamDesc represents a streaming RPC service's method specification.
|
||||
type StreamDesc struct {
|
||||
StreamName string
|
||||
Handler StreamHandler
|
||||
|
||||
// At least one of these is true.
|
||||
ServerStreams bool
|
||||
ClientStreams bool
|
||||
}
|
||||
|
||||
type methodHandler func(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor UnaryServerInterceptor) (interface{}, error)
|
||||
|
||||
// MethodDesc represents an RPC service's method specification.
|
||||
type MethodDesc struct {
|
||||
MethodName string
|
||||
Handler methodHandler
|
||||
}
|
||||
|
||||
// ServiceDesc represents an RPC service's specification.
|
||||
type ServiceDesc struct {
|
||||
ServiceName string
|
||||
// The pointer to the service interface. Used to check whether the user
|
||||
// provided implementation satisfies the interface requirements.
|
||||
HandlerType interface{}
|
||||
Methods []MethodDesc
|
||||
Streams []StreamDesc
|
||||
Metadata interface{}
|
||||
}
|
||||
|
||||
type clientStream struct {
|
||||
ctx context.Context
|
||||
req *http.Request
|
||||
errCh chan error
|
||||
msgCh chan []byte
|
||||
}
|
||||
|
||||
func newStream(ctx context.Context, endpoint string) (*clientStream, error) {
|
||||
cs := &clientStream{
|
||||
ctx: ctx,
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(
|
||||
"POST",
|
||||
endpoint,
|
||||
nil,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, status.New(codes.Unavailable, err.Error()).Err()
|
||||
}
|
||||
|
||||
cs.req = req.WithContext(ctx)
|
||||
return cs, nil
|
||||
}
|
||||
|
||||
func (c *clientStream) Header() (metadata.MD, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (c *clientStream) Trailer() metadata.MD {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *clientStream) Context() context.Context {
|
||||
return c.ctx
|
||||
}
|
||||
|
||||
func (c *clientStream) RecvMsg(reply interface{}) error {
|
||||
select {
|
||||
case <-c.ctx.Done():
|
||||
return c.ctx.Err()
|
||||
case err := <-c.errCh:
|
||||
return err
|
||||
case msg, ok := <-c.msgCh:
|
||||
if !ok {
|
||||
return io.EOF
|
||||
}
|
||||
err := proto.Unmarshal(msg, reply.(proto.Message))
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func (c *clientStream) SendMsg(req interface{}) error {
|
||||
msg, err := proto.Marshal(req.(proto.Message))
|
||||
if err != nil {
|
||||
return status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
|
||||
bufHeader := make([]byte, 5)
|
||||
|
||||
// Write length of b into buf
|
||||
binary.BigEndian.PutUint32(bufHeader[1:], uint32(len(msg)))
|
||||
|
||||
c.req.Body = ioutil.NopCloser(bytes.NewBuffer(append(bufHeader, msg...)))
|
||||
addHeaders(c.req)
|
||||
|
||||
resp, err := http.DefaultClient.Do(c.req)
|
||||
if err != nil {
|
||||
return status.Error(codes.Unavailable, err.Error())
|
||||
}
|
||||
|
||||
st := statusFromHeaders(resp.Header)
|
||||
if st.Code() != codes.OK {
|
||||
resp.Body.Close()
|
||||
return st.Err()
|
||||
}
|
||||
|
||||
c.errCh = make(chan error, 1)
|
||||
c.msgCh = make(chan []byte, 1)
|
||||
|
||||
// Read response asynchronously
|
||||
go func() {
|
||||
defer resp.Body.Close()
|
||||
|
||||
msgHeader := make([]byte, 5)
|
||||
for {
|
||||
_, err := io.ReadFull(resp.Body, msgHeader)
|
||||
if err != nil {
|
||||
c.errCh <- status.Error(codes.Internal, err.Error())
|
||||
return
|
||||
}
|
||||
// 1 in MSB signifies that this is the trailer. Break loop.
|
||||
// https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-WEB.md#protocol-differences-vs-grpc-over-http2
|
||||
if msgHeader[0]>>7 == 1 {
|
||||
break
|
||||
}
|
||||
|
||||
msgLen := binary.BigEndian.Uint32(msgHeader[1:])
|
||||
|
||||
msg := make([]byte, msgLen)
|
||||
_, err = io.ReadFull(resp.Body, msg)
|
||||
if err != nil {
|
||||
c.errCh <- status.Error(codes.Internal, err.Error())
|
||||
return
|
||||
}
|
||||
c.msgCh <- msg
|
||||
}
|
||||
|
||||
if msgHeader[0]&1 == 0 {
|
||||
trailers, err := readTrailers(resp.Body)
|
||||
if err != nil {
|
||||
c.errCh <- status.Error(codes.Internal, err.Error())
|
||||
return
|
||||
}
|
||||
st = statusFromHeaders(trailers)
|
||||
} else {
|
||||
// TODO(johanbrandhorst): Support compressed trailers
|
||||
}
|
||||
|
||||
if st.Code() != codes.OK {
|
||||
c.errCh <- st.Err()
|
||||
return
|
||||
}
|
||||
|
||||
close(c.msgCh)
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *clientStream) CloseSend() error {
|
||||
return nil
|
||||
}
|
||||
11
vendor/github.com/johanbrandhorst/grpc-wasm/dialoptions.go
generated
vendored
11
vendor/github.com/johanbrandhorst/grpc-wasm/dialoptions.go
generated
vendored
@@ -1,11 +0,0 @@
|
||||
// +build js,wasm
|
||||
|
||||
package grpc
|
||||
|
||||
// DialOption configures how we set up the connection.
|
||||
type DialOption func(*dialOptions)
|
||||
|
||||
// dialOptions configure a Dial call. dialOptions are set by the DialOption
|
||||
// values passed to Dial.
|
||||
type dialOptions struct {
|
||||
}
|
||||
17
vendor/github.com/johanbrandhorst/grpc-wasm/doc.go
generated
vendored
17
vendor/github.com/johanbrandhorst/grpc-wasm/doc.go
generated
vendored
@@ -1,17 +0,0 @@
|
||||
// +build js,wasm
|
||||
|
||||
package grpc
|
||||
|
||||
// The SupportPackageIsVersion variables are referenced from generated protocol
|
||||
// buffer files to ensure compatibility with the gRPC version used. The latest
|
||||
// support package version is 4.
|
||||
//
|
||||
// Older versions are kept for compatibility. They may be removed if
|
||||
// compatibility cannot be maintained.
|
||||
//
|
||||
// These constants should not be referenced from any other code.
|
||||
//
|
||||
// Note: grpc-wasm is compatible with Version4+ files only.
|
||||
const (
|
||||
SupportPackageIsVersion4 = true
|
||||
)
|
||||
26
vendor/github.com/johanbrandhorst/grpc-wasm/interceptor.go
generated
vendored
26
vendor/github.com/johanbrandhorst/grpc-wasm/interceptor.go
generated
vendored
@@ -1,26 +0,0 @@
|
||||
// +build js,wasm
|
||||
|
||||
package grpc
|
||||
|
||||
import "context"
|
||||
|
||||
// UnaryServerInfo consists of various information about a unary RPC on
|
||||
// server side. All per-rpc information may be mutated by the interceptor.
|
||||
type UnaryServerInfo struct {
|
||||
// Server is the service implementation the user provides. This is read-only.
|
||||
Server interface{}
|
||||
// FullMethod is the full RPC method string, i.e., /package.service/method.
|
||||
FullMethod string
|
||||
}
|
||||
|
||||
// UnaryHandler defines the handler invoked by UnaryServerInterceptor to complete the normal
|
||||
// execution of a unary RPC. If a UnaryHandler returns an error, it should be produced by the
|
||||
// status package, or else gRPC will use codes.Unknown as the status code and err.Error() as
|
||||
// the status message of the RPC.
|
||||
type UnaryHandler func(ctx context.Context, req interface{}) (interface{}, error)
|
||||
|
||||
// UnaryServerInterceptor provides a hook to intercept the execution of a unary RPC on the server. info
|
||||
// contains all the information of this RPC the interceptor can operate on. And handler is the wrapper
|
||||
// of the service method implementation. It is the responsibility of the interceptor to invoke handler
|
||||
// to complete the RPC.
|
||||
type UnaryServerInterceptor func(ctx context.Context, req interface{}, info *UnaryServerInfo, handler UnaryHandler) (resp interface{}, err error)
|
||||
8
vendor/github.com/johanbrandhorst/grpc-wasm/server.go
generated
vendored
8
vendor/github.com/johanbrandhorst/grpc-wasm/server.go
generated
vendored
@@ -1,8 +0,0 @@
|
||||
// +build js,wasm
|
||||
|
||||
package grpc
|
||||
|
||||
// Server is a gRPC server to serve RPC requests.
|
||||
type Server struct{}
|
||||
|
||||
func (s *Server) RegisterService(sd *ServiceDesc, ss interface{}) {}
|
||||
Reference in New Issue
Block a user