mirror of
https://github.com/taigrr/wasm-experiments
synced 2025-01-18 04:03:21 -08:00
Add prototype Invoke method and regenerate files
This commit is contained in:
parent
5922c39730
commit
e48ef08afd
9
Gopkg.lock
generated
9
Gopkg.lock
generated
@ -35,7 +35,7 @@
|
|||||||
branch = "master"
|
branch = "master"
|
||||||
name = "github.com/johanbrandhorst/fetch"
|
name = "github.com/johanbrandhorst/fetch"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
revision = "6a24c959938ff60f3daf26d7a7dc21e363a8470a"
|
revision = "324bdf4e09d4aa48e98660403d9def0504daaaea"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
name = "github.com/lpar/gzipped"
|
name = "github.com/lpar/gzipped"
|
||||||
@ -120,7 +120,10 @@
|
|||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
name = "google.golang.org/genproto"
|
name = "google.golang.org/genproto"
|
||||||
packages = ["googleapis/rpc/status"]
|
packages = [
|
||||||
|
"googleapis/rpc/errdetails",
|
||||||
|
"googleapis/rpc/status"
|
||||||
|
]
|
||||||
revision = "86e600f69ee4704c6efbf6a2a40a5c10700e76c2"
|
revision = "86e600f69ee4704c6efbf6a2a40a5c10700e76c2"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
@ -157,6 +160,6 @@
|
|||||||
[solve-meta]
|
[solve-meta]
|
||||||
analyzer-name = "dep"
|
analyzer-name = "dep"
|
||||||
analyzer-version = 1
|
analyzer-version = 1
|
||||||
inputs-digest = "3c3a60294951567756e771a7e00b7ef536766428e1e0f076dbf2bed4df73411f"
|
inputs-digest = "d688fec22cff0842b1870ada98bed495d65764a2c8212db1e39cf65c0d64f5b7"
|
||||||
solver-name = "gps-cdcl"
|
solver-name = "gps-cdcl"
|
||||||
solver-version = 1
|
solver-version = 1
|
||||||
|
@ -6,6 +6,10 @@
|
|||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
name = "github.com/golang/protobuf"
|
name = "github.com/golang/protobuf"
|
||||||
|
|
||||||
|
[[constraint]]
|
||||||
|
branch = "master"
|
||||||
|
name = "google.golang.org/genproto"
|
||||||
|
|
||||||
[[constraint]]
|
[[constraint]]
|
||||||
version = "1.12.0"
|
version = "1.12.0"
|
||||||
name = "google.golang.org/grpc"
|
name = "google.golang.org/grpc"
|
||||||
|
@ -3,6 +3,7 @@ package backend
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
|
"google.golang.org/genproto/googleapis/rpc/errdetails"
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
"google.golang.org/grpc/status"
|
"google.golang.org/grpc/status"
|
||||||
|
|
||||||
@ -19,7 +20,19 @@ var _ server.BackendServer = (*Backend)(nil)
|
|||||||
|
|
||||||
func (b Backend) GetUser(ctx context.Context, req *server.GetUserRequest) (*server.User, error) {
|
func (b Backend) GetUser(ctx context.Context, req *server.GetUserRequest) (*server.User, error) {
|
||||||
if req.GetUserId() != "1234" {
|
if req.GetUserId() != "1234" {
|
||||||
return nil, status.Error(codes.InvalidArgument, "invalid id")
|
st := status.New(codes.InvalidArgument, "invalid id")
|
||||||
|
detSt, err := st.WithDetails(&errdetails.BadRequest{
|
||||||
|
FieldViolations: []*errdetails.BadRequest_FieldViolation{
|
||||||
|
{
|
||||||
|
Field: "user",
|
||||||
|
Description: "That user does not exist",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err == nil {
|
||||||
|
return nil, detSt.Err()
|
||||||
|
}
|
||||||
|
return nil, st.Err()
|
||||||
}
|
}
|
||||||
return &server.User{
|
return &server.User{
|
||||||
Id: req.GetUserId(),
|
Id: req.GetUserId(),
|
||||||
|
File diff suppressed because one or more lines are too long
@ -1,13 +1,25 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"encoding/base64"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/golang/protobuf/proto"
|
"github.com/golang/protobuf/proto"
|
||||||
|
_ "google.golang.org/genproto/googleapis/rpc/errdetails"
|
||||||
|
spb "google.golang.org/genproto/googleapis/rpc/status"
|
||||||
|
"google.golang.org/grpc/codes"
|
||||||
|
"google.golang.org/grpc/metadata"
|
||||||
|
"google.golang.org/grpc/status"
|
||||||
|
|
||||||
"github.com/johanbrandhorst/fetch"
|
"github.com/johanbrandhorst/fetch"
|
||||||
"github.com/johanbrandhorst/wasm-experiments/grpc/proto/server"
|
"github.com/johanbrandhorst/wasm-experiments/grpc/proto/server"
|
||||||
@ -21,84 +33,220 @@ import (
|
|||||||
//go:generate bash -c "go run assets_generate.go"
|
//go:generate bash -c "go run assets_generate.go"
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
c := http.Client{
|
s := newClientConn("", "web.Backend")
|
||||||
Transport: &fetch.Transport{},
|
req := &server.GetUserRequest{
|
||||||
}
|
|
||||||
b, err := proto.Marshal(&server.GetUserRequest{
|
|
||||||
UserId: "1234",
|
UserId: "1234",
|
||||||
})
|
}
|
||||||
|
resp := new(server.User)
|
||||||
|
err := s.Invoke(context.Background(), "GetUser", req, resp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
st := status.Convert(err)
|
||||||
|
fmt.Println(st.Code(), st.Message(), st.Details())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
fmt.Println(resp.GetId())
|
||||||
|
req.UserId = "123"
|
||||||
|
err = s.Invoke(context.Background(), "GetUser", req, resp)
|
||||||
|
if err != nil {
|
||||||
|
st := status.Convert(err)
|
||||||
|
fmt.Println(st.Code(), st.Message(), st.Details())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Println(resp.GetId())
|
||||||
|
}
|
||||||
|
|
||||||
|
type ClientConn struct {
|
||||||
|
client *http.Client
|
||||||
|
service string
|
||||||
|
host string
|
||||||
|
}
|
||||||
|
|
||||||
|
func newClientConn(host, service string) *ClientConn {
|
||||||
|
return &ClientConn{
|
||||||
|
client: &http.Client{
|
||||||
|
Transport: &fetch.Transport{},
|
||||||
|
},
|
||||||
|
service: service,
|
||||||
|
host: host,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cc *ClientConn) Invoke(ctx context.Context, method string, in, out proto.Message) error {
|
||||||
|
b, err := proto.Marshal(in)
|
||||||
|
if err != nil {
|
||||||
|
return status.Error(codes.Internal, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
bufHeader := make([]byte, 5)
|
bufHeader := make([]byte, 5)
|
||||||
|
|
||||||
// Write length of b into buf
|
// Write length of b into buf
|
||||||
binary.BigEndian.PutUint32(bufHeader[1:], uint32(len(b)))
|
binary.BigEndian.PutUint32(bufHeader[1:], uint32(len(b)))
|
||||||
|
|
||||||
req, err := http.NewRequest("POST", "/web.Backend/GetUser", bytes.NewBuffer(append(bufHeader, b...)))
|
req, err := http.NewRequest(
|
||||||
|
"POST",
|
||||||
|
strings.Join([]string{cc.host, cc.service, method}, "/"),
|
||||||
|
bytes.NewBuffer(append(bufHeader, b...)),
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
return status.Error(codes.Internal, err.Error())
|
||||||
return
|
|
||||||
}
|
}
|
||||||
req.Header.Add("content-type", "application/grpc-web+proto")
|
req = req.WithContext(ctx)
|
||||||
//ctx, _ := context.WithTimeout(context.Background(), time.Second)
|
addHeaders(req)
|
||||||
//req = req.WithContext(ctx)
|
|
||||||
|
|
||||||
resp, err := c.Do(req)
|
resp, err := cc.client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
return status.Error(codes.Internal, err.Error())
|
||||||
return
|
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
st := statusFromHeaders(resp.Header)
|
||||||
|
if st.Code() != codes.OK {
|
||||||
|
return st.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
msgHeader := make([]byte, 5)
|
||||||
for {
|
for {
|
||||||
header := make([]byte, 5)
|
_, err := resp.Body.Read(msgHeader)
|
||||||
_, err := resp.Body.Read(header)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
return status.Error(codes.Internal, err.Error())
|
||||||
return
|
|
||||||
}
|
}
|
||||||
if header[0] == 0x80 {
|
// 1 in MSB signifies that this is the trailer. Break loop.
|
||||||
trailers, err := ioutil.ReadAll(resp.Body)
|
// https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-WEB.md#protocol-differences-vs-grpc-over-http2
|
||||||
if err != nil {
|
if msgHeader[0]>>7 == 1 {
|
||||||
fmt.Println(err)
|
break
|
||||||
return
|
|
||||||
}
|
|
||||||
fmt.Println(string(trailers))
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
length := binary.BigEndian.Uint32(header[1:])
|
msgLen := binary.BigEndian.Uint32(msgHeader[1:])
|
||||||
|
|
||||||
message := make([]byte, length)
|
msg := make([]byte, msgLen)
|
||||||
_, err = resp.Body.Read(message)
|
_, err = resp.Body.Read(msg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
return status.Error(codes.Internal, err.Error())
|
||||||
return
|
|
||||||
}
|
}
|
||||||
/*
|
err = proto.Unmarshal(msg, out)
|
||||||
status := resp.Header.Get("grpc-status")
|
|
||||||
statusCode, err := strconv.Atoi(status)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
code := codes.Code(statusCode)
|
|
||||||
if code != codes.OK {
|
|
||||||
msg := resp.Header.Get("grpc-message")
|
|
||||||
fmt.Println(msg)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
user := new(server.User)
|
|
||||||
err = proto.Unmarshal(message, user)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
return status.Error(codes.Internal, err.Error())
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fmt.Println(user.Id)
|
if msgHeader[0]&1 == 0 {
|
||||||
|
trailers, err := readTrailers(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return status.Error(codes.Internal, err.Error())
|
||||||
|
}
|
||||||
|
st = statusFromHeaders(trailers)
|
||||||
|
if st.Code() != codes.OK {
|
||||||
|
return st.Err()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// TODO(johanbrandhorst): Support compressed trailers
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
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")
|
||||||
|
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, "")
|
||||||
|
}
|
||||||
|
@ -116,8 +116,9 @@ var _ grpc.ClientConn
|
|||||||
// is compatible with the grpc package it is being compiled against.
|
// is compatible with the grpc package it is being compiled against.
|
||||||
const _ = grpc.SupportPackageIsVersion4
|
const _ = grpc.SupportPackageIsVersion4
|
||||||
|
|
||||||
// Client API for Backend service
|
// BackendClient is the client API for Backend service.
|
||||||
|
//
|
||||||
|
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
|
||||||
type BackendClient interface {
|
type BackendClient interface {
|
||||||
GetUser(ctx context.Context, in *GetUserRequest, opts ...grpc.CallOption) (*User, error)
|
GetUser(ctx context.Context, in *GetUserRequest, opts ...grpc.CallOption) (*User, error)
|
||||||
}
|
}
|
||||||
@ -132,15 +133,14 @@ func NewBackendClient(cc *grpc.ClientConn) BackendClient {
|
|||||||
|
|
||||||
func (c *backendClient) GetUser(ctx context.Context, in *GetUserRequest, opts ...grpc.CallOption) (*User, error) {
|
func (c *backendClient) GetUser(ctx context.Context, in *GetUserRequest, opts ...grpc.CallOption) (*User, error) {
|
||||||
out := new(User)
|
out := new(User)
|
||||||
err := grpc.Invoke(ctx, "/web.Backend/GetUser", in, out, c.cc, opts...)
|
err := c.cc.Invoke(ctx, "/web.Backend/GetUser", in, out, opts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Server API for Backend service
|
// BackendServer is the server API for Backend service.
|
||||||
|
|
||||||
type BackendServer interface {
|
type BackendServer interface {
|
||||||
GetUser(context.Context, *GetUserRequest) (*User, error)
|
GetUser(context.Context, *GetUserRequest) (*User, error)
|
||||||
}
|
}
|
||||||
|
@ -116,8 +116,9 @@ var _ grpc.ClientConn
|
|||||||
// is compatible with the grpc package it is being compiled against.
|
// is compatible with the grpc package it is being compiled against.
|
||||||
const _ = grpc.SupportPackageIsVersion4
|
const _ = grpc.SupportPackageIsVersion4
|
||||||
|
|
||||||
// Client API for Backend service
|
// BackendClient is the client API for Backend service.
|
||||||
|
//
|
||||||
|
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
|
||||||
type BackendClient interface {
|
type BackendClient interface {
|
||||||
GetUser(ctx context.Context, in *GetUserRequest, opts ...grpc.CallOption) (*User, error)
|
GetUser(ctx context.Context, in *GetUserRequest, opts ...grpc.CallOption) (*User, error)
|
||||||
}
|
}
|
||||||
@ -132,15 +133,14 @@ func NewBackendClient(cc *grpc.ClientConn) BackendClient {
|
|||||||
|
|
||||||
func (c *backendClient) GetUser(ctx context.Context, in *GetUserRequest, opts ...grpc.CallOption) (*User, error) {
|
func (c *backendClient) GetUser(ctx context.Context, in *GetUserRequest, opts ...grpc.CallOption) (*User, error) {
|
||||||
out := new(User)
|
out := new(User)
|
||||||
err := grpc.Invoke(ctx, "/web.Backend/GetUser", in, out, c.cc, opts...)
|
err := c.cc.Invoke(ctx, "/web.Backend/GetUser", in, out, opts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Server API for Backend service
|
// BackendServer is the server API for Backend service.
|
||||||
|
|
||||||
type BackendServer interface {
|
type BackendServer interface {
|
||||||
GetUser(context.Context, *GetUserRequest) (*User, error)
|
GetUser(context.Context, *GetUserRequest) (*User, error)
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user