From d0e5a92286d13f29751d19a3870e52d3bb0f7d0b Mon Sep 17 00:00:00 2001 From: Ilnur Date: Thu, 2 May 2024 01:32:07 +0400 Subject: [PATCH] VM CreateHandlers (#7) * add implementation for method CreateHandlers * Implement ABCI endpoints (#13) * implemetn ibc response from lendslidecore * introduce status rpc method (#14) Co-authored-by: Ivan Sukach * use cometbft json rpc handler --------- Co-authored-by: ivansukach <47761294+ivansukach@users.noreply.github.com> Co-authored-by: Ivan Sukach Co-authored-by: ramil * remove landslidecore dependency add routes for abci_query and abci_info * update pb * Add all RPC methods to Route map (#17) * Add all RPC methods to Route map * rpc methods signature updated * MempoolService interface (#18) * MempoolService interface * run Github Actions on any PR --------- Co-authored-by: ramil * RPC tests for Health and Status functions --------- Co-authored-by: ramil Co-authored-by: chist100 <44050804+chist100@users.noreply.github.com> Co-authored-by: ivansukach <47761294+ivansukach@users.noreply.github.com> Co-authored-by: Ivan Sukach Co-authored-by: Vasyl Naumenko --- .github/workflows/buf.yml | 1 - .github/workflows/go.yml | 1 - go.mod | 2 +- grpcutils/client.go | 101 ++ grpcutils/server.go | 103 ++ .../closer => grpcutils}/server_closer.go | 2 +- grpcutils/util.go | 102 ++ http/conn/conn_client.go | 116 ++ http/conn/conn_server.go | 86 ++ http/http_client.go | 218 +++ http/http_server.go | 204 +++ http/reader/reader_client.go | 35 + http/reader/reader_server.go | 34 + http/responsewriter/locked_writer.go | 72 + http/responsewriter/writer_client.go | 130 ++ http/responsewriter/writer_server.go | 117 ++ http/writer/writer_client.go | 33 + http/writer/writer_server.go | 33 + jsonrpc/http_json_handler.go | 266 ++++ jsonrpc/http_json_handler_test.go | 281 ++++ jsonrpc/rpc_func.go | 149 ++ proto/http/http.pb.go | 1238 +++++++++++++++++ proto/http/http.proto | 172 +++ proto/http/http_grpc.pb.go | 157 +++ .../http/responsewriter/responsewriter.pb.go | 518 +++++++ .../http/responsewriter/responsewriter.proto | 60 + .../responsewriter/responsewriter_grpc.pb.go | 229 +++ proto/io/reader/reader.pb.go | 228 +++ proto/io/reader/reader.proto | 22 + proto/io/reader/reader_grpc.pb.go | 107 ++ proto/io/writer/writer.pb.go | 229 +++ proto/io/writer/writer.proto | 23 + proto/io/writer/writer_grpc.pb.go | 109 ++ proto/net/conn/conn.pb.go | 465 +++++++ proto/net/conn/conn.proto | 55 + proto/net/conn/conn_grpc.pb.go | 311 +++++ vm/rpc.go | 676 +++++++++ vm/rpc_test.go | 59 + vm/vm.go | 40 +- 39 files changed, 6776 insertions(+), 8 deletions(-) create mode 100644 grpcutils/client.go create mode 100644 grpcutils/server.go rename {vm/types/closer => grpcutils}/server_closer.go (97%) create mode 100644 grpcutils/util.go create mode 100644 http/conn/conn_client.go create mode 100644 http/conn/conn_server.go create mode 100644 http/http_client.go create mode 100644 http/http_server.go create mode 100644 http/reader/reader_client.go create mode 100644 http/reader/reader_server.go create mode 100644 http/responsewriter/locked_writer.go create mode 100644 http/responsewriter/writer_client.go create mode 100644 http/responsewriter/writer_server.go create mode 100644 http/writer/writer_client.go create mode 100644 http/writer/writer_server.go create mode 100644 jsonrpc/http_json_handler.go create mode 100644 jsonrpc/http_json_handler_test.go create mode 100644 jsonrpc/rpc_func.go create mode 100644 proto/http/http.pb.go create mode 100644 proto/http/http.proto create mode 100644 proto/http/http_grpc.pb.go create mode 100644 proto/http/responsewriter/responsewriter.pb.go create mode 100644 proto/http/responsewriter/responsewriter.proto create mode 100644 proto/http/responsewriter/responsewriter_grpc.pb.go create mode 100644 proto/io/reader/reader.pb.go create mode 100644 proto/io/reader/reader.proto create mode 100644 proto/io/reader/reader_grpc.pb.go create mode 100644 proto/io/writer/writer.pb.go create mode 100644 proto/io/writer/writer.proto create mode 100644 proto/io/writer/writer_grpc.pb.go create mode 100644 proto/net/conn/conn.pb.go create mode 100644 proto/net/conn/conn.proto create mode 100644 proto/net/conn/conn_grpc.pb.go create mode 100644 vm/rpc.go create mode 100644 vm/rpc_test.go diff --git a/.github/workflows/buf.yml b/.github/workflows/buf.yml index 79ea702..27fba00 100644 --- a/.github/workflows/buf.yml +++ b/.github/workflows/buf.yml @@ -4,7 +4,6 @@ on: push: branches: [ "main" ] pull_request: - branches: [ "main" ] jobs: buf-lint: diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index aa599b1..a18454c 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -7,7 +7,6 @@ on: push: branches: [ "main" ] pull_request: - branches: [ "main" ] jobs: diff --git a/go.mod b/go.mod index eef3736..8d2a7c1 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,7 @@ require ( github.com/prometheus/client_golang v1.14.0 github.com/prometheus/client_model v0.3.0 github.com/stretchr/testify v1.8.4 + google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 google.golang.org/grpc v1.62.0 google.golang.org/protobuf v1.32.0 ) @@ -62,6 +63,5 @@ require ( golang.org/x/sync v0.6.0 // indirect golang.org/x/sys v0.16.0 // indirect golang.org/x/text v0.14.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/grpcutils/client.go b/grpcutils/client.go new file mode 100644 index 0000000..c35fcf4 --- /dev/null +++ b/grpcutils/client.go @@ -0,0 +1,101 @@ +package grpcutils + +import ( + "math" + "time" + + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + "google.golang.org/grpc/keepalive" +) + +const ( + // After a duration of this time if the client doesn't see any activity it + // pings the server to see if the transport is still alive. + // If set below 10s, a minimum value of 10s will be used instead. + // grpc-go default infinity + defaultClientKeepAliveTime = 30 * time.Second + // After having pinged for keepalive check, the client waits for a duration + // of Timeout and if no activity is seen even after that the connection is + // closed. grpc-go default 20s + defaultClientKeepAliveTimeOut = 10 * time.Second + // If true, client sends keepalive pings even with no active RPCs. If false, + // when there are no active RPCs, Time and Timeout will be ignored and no + // keepalive pings will be sent. grpc-go default false + defaultPermitWithoutStream = true + // WaitForReady configures the action to take when an RPC is attempted on + // broken connections or unreachable servers. If waitForReady is false and + // the connection is in the TRANSIENT_FAILURE state, the RPC will fail + // immediately. Otherwise, the RPC client will block the call until a + // connection is available (or the call is canceled or times out) and will + // retry the call if it fails due to a transient error. gRPC will not retry + // if data was written to the wire unless the server indicates it did not + // process the data. Please refer to + // https://github.com/grpc/grpc/blob/master/doc/wait-for-ready.md. + // + // gRPC default behavior is to NOT "wait for ready". + defaultWaitForReady = true +) + +var DefaultDialOptions = []grpc.DialOption{ + grpc.WithDefaultCallOptions( + grpc.MaxCallRecvMsgSize(math.MaxInt), + grpc.MaxCallSendMsgSize(math.MaxInt), + grpc.WaitForReady(defaultWaitForReady), + ), + grpc.WithKeepaliveParams(keepalive.ClientParameters{ + Time: defaultClientKeepAliveTime, + Timeout: defaultClientKeepAliveTimeOut, + PermitWithoutStream: defaultPermitWithoutStream, + }), + grpc.WithTransportCredentials(insecure.NewCredentials()), +} + +// gRPC clients created from this ClientConn will wait forever for the Server to +// become Ready. If you desire a dial timeout ensure context is properly plumbed +// to the client and use context.WithTimeout. +// +// Dial returns a gRPC ClientConn with the dial options as defined by +// DefaultDialOptions. DialOption can also optionally be passed. +func Dial(addr string, opts ...DialOption) (*grpc.ClientConn, error) { + return grpc.Dial("passthrough:///"+addr, newDialOpts(opts...)...) +} + +// DialOptions are options which can be applied to a gRPC client in addition to +// the defaults set by DefaultDialOptions. +type DialOptions struct { + opts []grpc.DialOption +} + +// append(DefaultDialOptions, ...) will always allocate a new slice and will +// not overwrite any potential data that may have previously been appended to +// DefaultServerOptions https://go.dev/ref/spec#Composite_literals +func newDialOpts(opts ...DialOption) []grpc.DialOption { + d := &DialOptions{opts: DefaultDialOptions} + d.applyOpts(opts) + return d.opts +} + +func (d *DialOptions) applyOpts(opts []DialOption) { + for _, opt := range opts { + opt(d) + } +} + +type DialOption func(*DialOptions) + +// WithChainUnaryInterceptor takes a list of unary client interceptors which +// are added to the dial options. +func WithChainUnaryInterceptor(interceptors ...grpc.UnaryClientInterceptor) DialOption { + return func(d *DialOptions) { + d.opts = append(d.opts, grpc.WithChainUnaryInterceptor(interceptors...)) + } +} + +// WithChainStreamInterceptor takes a list of stream client interceptors which +// are added to the dial options. +func WithChainStreamInterceptor(interceptors ...grpc.StreamClientInterceptor) DialOption { + return func(d *DialOptions) { + d.opts = append(d.opts, grpc.WithChainStreamInterceptor(interceptors...)) + } +} diff --git a/grpcutils/server.go b/grpcutils/server.go new file mode 100644 index 0000000..9cc9759 --- /dev/null +++ b/grpcutils/server.go @@ -0,0 +1,103 @@ +package grpcutils + +import ( + "math" + "net" + "time" + + "google.golang.org/grpc" + "google.golang.org/grpc/keepalive" +) + +const ( + // MinTime is the minimum amount of time a client should wait before sending + // a keepalive ping. grpc-go default 5 mins + defaultServerKeepAliveMinTime = 5 * time.Second + // After a duration of this time if the server doesn't see any activity it + // pings the client to see if the transport is still alive. + // If set below 1s, a minimum value of 1s will be used instead. + // grpc-go default 2h + defaultServerKeepAliveInterval = 2 * time.Hour + // After having pinged for keepalive check, the server waits for a duration + // of Timeout and if no activity is seen even after that the connection is + // closed. grpc-go default 20s + defaultServerKeepAliveTimeout = 20 * time.Second +) + +var DefaultServerOptions = []grpc.ServerOption{ + grpc.MaxRecvMsgSize(math.MaxInt), + grpc.MaxSendMsgSize(math.MaxInt), + grpc.MaxConcurrentStreams(math.MaxUint32), + grpc.KeepaliveEnforcementPolicy(keepalive.EnforcementPolicy{ + MinTime: defaultServerKeepAliveMinTime, + PermitWithoutStream: defaultPermitWithoutStream, + }), + grpc.KeepaliveParams(keepalive.ServerParameters{ + Time: defaultServerKeepAliveInterval, + Timeout: defaultServerKeepAliveTimeout, + }), +} + +// NewServer will return a gRPC server with server options as defined by +// DefaultServerOptions. ServerOption can also optionally be passed. +func NewServer(opts ...ServerOption) *grpc.Server { + return grpc.NewServer(newServerOpts(opts)...) +} + +type ServerOptions struct { + opts []grpc.ServerOption +} + +// append(DefaultServerOptions, ...) will always allocate a new slice and will +// not overwrite any potential data that may have previously been appended to +// DefaultServerOptions https://go.dev/ref/spec#Composite_literals +func newServerOpts(opts []ServerOption) []grpc.ServerOption { + s := &ServerOptions{opts: DefaultServerOptions} + s.applyOpts(opts) + return s.opts +} + +func (s *ServerOptions) applyOpts(opts []ServerOption) { + for _, opt := range opts { + opt(s) + } +} + +// ServerOption are options which can be applied to a gRPC server in addition to +// the defaults set by DefaultServerOPtions. +type ServerOption func(*ServerOptions) + +// WithUnaryInterceptor adds a single unary interceptor to the gRPC server +// options. +func WithUnaryInterceptor(unaryInterceptor grpc.UnaryServerInterceptor) ServerOption { + return func(s *ServerOptions) { + s.opts = append(s.opts, grpc.UnaryInterceptor(unaryInterceptor)) + } +} + +// WithStreamInterceptor adds a single stream interceptor to the gRPC server +// options. +func WithStreamInterceptor(streamInterceptor grpc.StreamServerInterceptor) ServerOption { + return func(s *ServerOptions) { + s.opts = append(s.opts, grpc.StreamInterceptor(streamInterceptor)) + } +} + +// NewListener returns a TCP listener listening against the next available port +// on the system bound to localhost. +func NewListener() (net.Listener, error) { + return net.Listen("tcp", "127.0.0.1:") +} + +// Serve will start a gRPC server and block until it errors or is shutdown. +func Serve(listener net.Listener, grpcServer *grpc.Server) { + // TODO: While errors will be reported later, it could be useful to somehow + // log this if it is the primary error. + // + // There is nothing to with the error returned by serve here. Later requests + // will propegate their error if they occur. + _ = grpcServer.Serve(listener) + + // Similarly, there is nothing to with an error when the listener is closed. + _ = listener.Close() +} diff --git a/vm/types/closer/server_closer.go b/grpcutils/server_closer.go similarity index 97% rename from vm/types/closer/server_closer.go rename to grpcutils/server_closer.go index 9d18912..47ddb03 100644 --- a/vm/types/closer/server_closer.go +++ b/grpcutils/server_closer.go @@ -1,4 +1,4 @@ -package closer +package grpcutils import ( "sync" diff --git a/grpcutils/util.go b/grpcutils/util.go new file mode 100644 index 0000000..476a82c --- /dev/null +++ b/grpcutils/util.go @@ -0,0 +1,102 @@ +// Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package grpcutils + +import ( + "fmt" + "net/http" + "time" + + "google.golang.org/grpc/status" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/anypb" + + httppb "github.com/consideritdone/landslidevm/proto/http" + spb "google.golang.org/genproto/googleapis/rpc/status" + tspb "google.golang.org/protobuf/types/known/timestamppb" +) + +func Errorf(code int, tmpl string, args ...interface{}) error { + return GetGRPCErrorFromHTTPResponse(&httppb.HandleSimpleHTTPResponse{ + Code: int32(code), + Body: []byte(fmt.Sprintf(tmpl, args...)), + }) +} + +// GetGRPCErrorFromHTTPRespone takes an HandleSimpleHTTPResponse as input and returns a gRPC error. +func GetGRPCErrorFromHTTPResponse(resp *httppb.HandleSimpleHTTPResponse) error { + a, err := anypb.New(resp) + if err != nil { + return err + } + + return status.ErrorProto(&spb.Status{ + Code: resp.Code, + Message: string(resp.Body), + Details: []*anypb.Any{a}, + }) +} + +// GetHTTPResponseFromError takes an gRPC error as input and returns a gRPC +// HandleSimpleHTTPResponse. +func GetHTTPResponseFromError(err error) (*httppb.HandleSimpleHTTPResponse, bool) { + s, ok := status.FromError(err) + if !ok { + return nil, false + } + + status := s.Proto() + if len(status.Details) != 1 { + return nil, false + } + + var resp httppb.HandleSimpleHTTPResponse + if err := anypb.UnmarshalTo(status.Details[0], &resp, proto.UnmarshalOptions{}); err != nil { + return nil, false + } + + return &resp, true +} + +// GetHTTPHeader takes an http.Header as input and returns a slice of Header. +func GetHTTPHeader(hs http.Header) []*httppb.Element { + result := make([]*httppb.Element, 0, len(hs)) + for k, vs := range hs { + result = append(result, &httppb.Element{ + Key: k, + Values: vs, + }) + } + return result +} + +// MergeHTTPHeader takes a slice of Header and merges with http.Header map. +func MergeHTTPHeader(hs []*httppb.Element, header http.Header) { + for _, h := range hs { + header[h.Key] = h.Values + } +} + +// TimestampAsTime validates timestamppb timestamp and returns time.Time. +func TimestampAsTime(ts *tspb.Timestamp) (time.Time, error) { + if err := ts.CheckValid(); err != nil { + return time.Time{}, fmt.Errorf("invalid timestamp: %w", err) + } + return ts.AsTime(), nil +} + +// TimestampFromTime converts time.Time to a timestamppb timestamp. +func TimestampFromTime(time time.Time) *tspb.Timestamp { + return tspb.New(time) +} + +// EnsureValidResponseCode ensures that the response code is valid otherwise it returns 500. +func EnsureValidResponseCode(code int) int { + // Response code outside of this range is invalid and could panic. + // ref. https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html + if code < 100 || code > 599 { + return http.StatusInternalServerError + } + return code +} diff --git a/http/conn/conn_client.go b/http/conn/conn_client.go new file mode 100644 index 0000000..642ab40 --- /dev/null +++ b/http/conn/conn_client.go @@ -0,0 +1,116 @@ +package conn + +import ( + "context" + "errors" + "io" + "net" + "time" + + "google.golang.org/protobuf/types/known/emptypb" + + connpb "github.com/consideritdone/landslidevm/proto/net/conn" +) + +var _ net.Conn = (*Client)(nil) + +// Client is an implementation of a connection that talks over RPC. +type Client struct { + client connpb.ConnClient + local net.Addr + remote net.Addr + toClose []io.Closer +} + +// NewClient returns a connection connected to a remote connection +func NewClient(client connpb.ConnClient, local, remote net.Addr, toClose ...io.Closer) *Client { + return &Client{ + client: client, + local: local, + remote: remote, + toClose: toClose, + } +} + +func (c *Client) Read(p []byte) (int, error) { + resp, err := c.client.Read(context.Background(), &connpb.ReadRequest{ + Length: int32(len(p)), + }) + if err != nil { + return 0, err + } + + copy(p, resp.Read) + + if resp.Error != nil { + err = errors.New(*resp.Error) + } + return len(resp.Read), err +} + +func (c *Client) Write(b []byte) (int, error) { + resp, err := c.client.Write(context.Background(), &connpb.WriteRequest{ + Payload: b, + }) + if err != nil { + return 0, err + } + + if resp.Error != nil { + err = errors.New(*resp.Error) + } + return int(resp.Length), err +} + +func (c *Client) Close() error { + errs := make([]error, len(c.toClose)+1) + + _, err := c.client.Close(context.Background(), &emptypb.Empty{}) + errs[0] = err + + for i, toClose := range c.toClose { + errs[i+1] = toClose.Close() + } + return errors.Join(errs...) +} + +func (c *Client) LocalAddr() net.Addr { + return c.local +} + +func (c *Client) RemoteAddr() net.Addr { + return c.remote +} + +func (c *Client) SetDeadline(t time.Time) error { + bytes, err := t.MarshalBinary() + if err != nil { + return err + } + _, err = c.client.SetDeadline(context.Background(), &connpb.SetDeadlineRequest{ + Time: bytes, + }) + return err +} + +func (c *Client) SetReadDeadline(t time.Time) error { + bytes, err := t.MarshalBinary() + if err != nil { + return err + } + _, err = c.client.SetReadDeadline(context.Background(), &connpb.SetDeadlineRequest{ + Time: bytes, + }) + return err +} + +func (c *Client) SetWriteDeadline(t time.Time) error { + bytes, err := t.MarshalBinary() + if err != nil { + return err + } + _, err = c.client.SetWriteDeadline(context.Background(), &connpb.SetDeadlineRequest{ + Time: bytes, + }) + return err +} diff --git a/http/conn/conn_server.go b/http/conn/conn_server.go new file mode 100644 index 0000000..a4a4a2e --- /dev/null +++ b/http/conn/conn_server.go @@ -0,0 +1,86 @@ +package conn + +import ( + "context" + "net" + "time" + + "google.golang.org/protobuf/types/known/emptypb" + + "github.com/consideritdone/landslidevm/grpcutils" + + connpb "github.com/consideritdone/landslidevm/proto/net/conn" +) + +var _ connpb.ConnServer = (*Server)(nil) + +// Server is an http.Conn that is managed over RPC. +type Server struct { + connpb.UnsafeConnServer + conn net.Conn + closer *grpcutils.ServerCloser +} + +// NewServer returns an http.Conn managed remotely +func NewServer(conn net.Conn, closer *grpcutils.ServerCloser) *Server { + return &Server{ + conn: conn, + closer: closer, + } +} + +func (s *Server) Read(_ context.Context, req *connpb.ReadRequest) (*connpb.ReadResponse, error) { + buf := make([]byte, int(req.Length)) + n, err := s.conn.Read(buf) + resp := &connpb.ReadResponse{ + Read: buf[:n], + } + if err != nil { + errStr := err.Error() + resp.Error = &errStr + } + return resp, nil +} + +func (s *Server) Write(_ context.Context, req *connpb.WriteRequest) (*connpb.WriteResponse, error) { + n, err := s.conn.Write(req.Payload) + if err != nil { + return nil, err + } + return &connpb.WriteResponse{ + Length: int32(n), + }, nil +} + +func (s *Server) Close(context.Context, *emptypb.Empty) (*emptypb.Empty, error) { + err := s.conn.Close() + s.closer.Stop() + return &emptypb.Empty{}, err +} + +func (s *Server) SetDeadline(_ context.Context, req *connpb.SetDeadlineRequest) (*emptypb.Empty, error) { + deadline := time.Time{} + err := deadline.UnmarshalBinary(req.Time) + if err != nil { + return nil, err + } + return &emptypb.Empty{}, s.conn.SetDeadline(deadline) +} + +func (s *Server) SetReadDeadline(_ context.Context, req *connpb.SetDeadlineRequest) (*emptypb.Empty, error) { + deadline := time.Time{} + err := deadline.UnmarshalBinary(req.Time) + if err != nil { + return nil, err + } + return &emptypb.Empty{}, s.conn.SetReadDeadline(deadline) +} + +func (s *Server) SetWriteDeadline(_ context.Context, req *connpb.SetDeadlineRequest) (*emptypb.Empty, error) { + deadline := time.Time{} + err := deadline.UnmarshalBinary(req.Time) + if err != nil { + return nil, err + } + return &emptypb.Empty{}, s.conn.SetWriteDeadline(deadline) +} diff --git a/http/http_client.go b/http/http_client.go new file mode 100644 index 0000000..175569a --- /dev/null +++ b/http/http_client.go @@ -0,0 +1,218 @@ +package http + +import ( + "io" + "net/http" + + "github.com/consideritdone/landslidevm/grpcutils" + "github.com/consideritdone/landslidevm/http/responsewriter" + + httppb "github.com/consideritdone/landslidevm/proto/http" + responsewriterpb "github.com/consideritdone/landslidevm/proto/http/responsewriter" +) + +var _ http.Handler = (*Client)(nil) + +// Client is an http.Handler that talks over RPC. +type Client struct { + client httppb.HTTPClient +} + +// NewClient returns an HTTP handler database instance connected to a remote +// HTTP handler instance +func NewClient(client httppb.HTTPClient) *Client { + return &Client{ + client: client, + } +} + +func (c *Client) ServeHTTP(w http.ResponseWriter, r *http.Request) { + // rfc2616#section-14.42: The Upgrade general-header allows the client + // to specify a communication protocols it supports and would like to + // use. Upgrade (e.g. websockets) is a more expensive transaction and + // if not required use the less expensive HTTPSimple. + if !isUpgradeRequest(r) { + c.serveHTTPSimple(w, r) + return + } + + closer := grpcutils.ServerCloser{} + defer closer.GracefulStop() + + // Wrap [w] with a lock to ensure that it is accessed in a thread-safe manner. + w = responsewriter.NewLockedWriter(w) + + serverListener, err := grpcutils.NewListener() + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + server := grpcutils.NewServer() + closer.Add(server) + responsewriterpb.RegisterWriterServer(server, responsewriter.NewServer(w)) + + // Start responsewriter gRPC service. + go grpcutils.Serve(serverListener, server) + + body, err := io.ReadAll(r.Body) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + req := &httppb.HTTPRequest{ + ResponseWriter: &httppb.ResponseWriter{ + ServerAddr: serverListener.Addr().String(), + Header: make([]*httppb.Element, 0, len(r.Header)), + }, + Request: &httppb.Request{ + Method: r.Method, + Proto: r.Proto, + ProtoMajor: int32(r.ProtoMajor), + ProtoMinor: int32(r.ProtoMinor), + Header: make([]*httppb.Element, 0, len(r.Header)), + Body: body, + ContentLength: r.ContentLength, + TransferEncoding: r.TransferEncoding, + Host: r.Host, + Form: make([]*httppb.Element, 0, len(r.Form)), + PostForm: make([]*httppb.Element, 0, len(r.PostForm)), + RemoteAddr: r.RemoteAddr, + RequestUri: r.RequestURI, + }, + } + for key, values := range w.Header() { + req.ResponseWriter.Header = append(req.ResponseWriter.Header, &httppb.Element{ + Key: key, + Values: values, + }) + } + for key, values := range r.Header { + req.Request.Header = append(req.Request.Header, &httppb.Element{ + Key: key, + Values: values, + }) + } + for key, values := range r.Form { + req.Request.Form = append(req.Request.Form, &httppb.Element{ + Key: key, + Values: values, + }) + } + for key, values := range r.PostForm { + req.Request.PostForm = append(req.Request.PostForm, &httppb.Element{ + Key: key, + Values: values, + }) + } + + if r.URL != nil { + req.Request.Url = &httppb.URL{ + Scheme: r.URL.Scheme, + Opaque: r.URL.Opaque, + Host: r.URL.Host, + Path: r.URL.Path, + RawPath: r.URL.RawPath, + ForceQuery: r.URL.ForceQuery, + RawQuery: r.URL.RawQuery, + Fragment: r.URL.Fragment, + } + + if r.URL.User != nil { + pwd, set := r.URL.User.Password() + req.Request.Url.User = &httppb.Userinfo{ + Username: r.URL.User.Username(), + Password: pwd, + PasswordSet: set, + } + } + } + + if r.TLS != nil { + req.Request.Tls = &httppb.ConnectionState{ + Version: uint32(r.TLS.Version), + HandshakeComplete: r.TLS.HandshakeComplete, + DidResume: r.TLS.DidResume, + CipherSuite: uint32(r.TLS.CipherSuite), + NegotiatedProtocol: r.TLS.NegotiatedProtocol, + ServerName: r.TLS.ServerName, + PeerCertificates: &httppb.Certificates{ + Cert: make([][]byte, len(r.TLS.PeerCertificates)), + }, + VerifiedChains: make([]*httppb.Certificates, len(r.TLS.VerifiedChains)), + SignedCertificateTimestamps: r.TLS.SignedCertificateTimestamps, + OcspResponse: r.TLS.OCSPResponse, + } + for i, cert := range r.TLS.PeerCertificates { + req.Request.Tls.PeerCertificates.Cert[i] = cert.Raw + } + for i, chain := range r.TLS.VerifiedChains { + req.Request.Tls.VerifiedChains[i] = &httppb.Certificates{ + Cert: make([][]byte, len(chain)), + } + for j, cert := range chain { + req.Request.Tls.VerifiedChains[i].Cert[j] = cert.Raw + } + } + } + + _, err = c.client.Handle(r.Context(), req) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + } +} + +// serveHTTPSimple converts an http request to a gRPC HTTPRequest and returns the +// response to the client. Protocol upgrade requests (websockets) are not supported +// and should use ServeHTTP. Based on https://www.weave.works/blog/turtles-way-http-grpc. +func (c *Client) serveHTTPSimple(w http.ResponseWriter, r *http.Request) { + req, err := getHTTPSimpleRequest(r) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + resp, err := c.client.HandleSimple(r.Context(), req) + if err != nil { + // Some errors will actually contain a valid resp, just need to unpack it + var ok bool + resp, ok = grpcutils.GetHTTPResponseFromError(err) + if !ok { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + } + + if err := convertWriteResponse(w, resp); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } +} + +// getHTTPSimpleRequest takes an http request as input and returns a gRPC HandleSimpleHTTPRequest. +func getHTTPSimpleRequest(r *http.Request) (*httppb.HandleSimpleHTTPRequest, error) { + body, err := io.ReadAll(r.Body) + if err != nil { + return nil, err + } + return &httppb.HandleSimpleHTTPRequest{ + Method: r.Method, + Url: r.RequestURI, + Body: body, + Headers: grpcutils.GetHTTPHeader(r.Header), + }, nil +} + +// convertWriteResponse converts a gRPC HandleSimpleHTTPResponse to an HTTP response. +func convertWriteResponse(w http.ResponseWriter, resp *httppb.HandleSimpleHTTPResponse) error { + grpcutils.MergeHTTPHeader(resp.Headers, w.Header()) + w.WriteHeader(grpcutils.EnsureValidResponseCode(int(resp.Code))) + _, err := w.Write(resp.Body) + return err +} + +// isUpgradeRequest returns true if the upgrade key exists in header and value is non empty. +func isUpgradeRequest(req *http.Request) bool { + return req.Header.Get("Upgrade") != "" +} diff --git a/http/http_server.go b/http/http_server.go new file mode 100644 index 0000000..47b1213 --- /dev/null +++ b/http/http_server.go @@ -0,0 +1,204 @@ +package http + +import ( + "bytes" + "context" + "crypto/tls" + "crypto/x509" + "net/http" + "net/url" + + "google.golang.org/protobuf/types/known/emptypb" + + "github.com/consideritdone/landslidevm/grpcutils" + "github.com/consideritdone/landslidevm/http/responsewriter" + + httppb "github.com/consideritdone/landslidevm/proto/http" + responsewriterpb "github.com/consideritdone/landslidevm/proto/http/responsewriter" +) + +var ( + _ httppb.HTTPServer = (*Server)(nil) + _ http.ResponseWriter = (*ResponseWriter)(nil) +) + +// Server is an http.Handler that is managed over RPC. +type Server struct { + httppb.UnsafeHTTPServer + handler http.Handler +} + +// NewServer returns an http.Handler instance managed remotely +func NewServer(handler http.Handler) *Server { + return &Server{ + handler: handler, + } +} + +func (s *Server) Handle(ctx context.Context, req *httppb.HTTPRequest) (*emptypb.Empty, error) { + clientConn, err := grpcutils.Dial(req.ResponseWriter.ServerAddr) + if err != nil { + return nil, err + } + + writerHeaders := make(http.Header) + for _, elem := range req.ResponseWriter.Header { + writerHeaders[elem.Key] = elem.Values + } + + writer := responsewriter.NewClient(writerHeaders, responsewriterpb.NewWriterClient(clientConn)) + + // create the request with the current context + request, err := http.NewRequestWithContext( + ctx, + req.Request.Method, + req.Request.RequestUri, + bytes.NewBuffer(req.Request.Body), + ) + if err != nil { + return nil, err + } + + if req.Request.Url != nil { + request.URL = &url.URL{ + Scheme: req.Request.Url.Scheme, + Opaque: req.Request.Url.Opaque, + Host: req.Request.Url.Host, + Path: req.Request.Url.Path, + RawPath: req.Request.Url.RawPath, + ForceQuery: req.Request.Url.ForceQuery, + RawQuery: req.Request.Url.RawQuery, + Fragment: req.Request.Url.Fragment, + } + if req.Request.Url.User != nil { + if req.Request.Url.User.PasswordSet { + request.URL.User = url.UserPassword(req.Request.Url.User.Username, req.Request.Url.User.Password) + } else { + request.URL.User = url.User(req.Request.Url.User.Username) + } + } + } + + request.Proto = req.Request.Proto + request.ProtoMajor = int(req.Request.ProtoMajor) + request.ProtoMinor = int(req.Request.ProtoMinor) + request.Header = make(http.Header, len(req.Request.Header)) + for _, elem := range req.Request.Header { + request.Header[elem.Key] = elem.Values + } + request.ContentLength = req.Request.ContentLength + request.TransferEncoding = req.Request.TransferEncoding + request.Host = req.Request.Host + request.Form = make(url.Values, len(req.Request.Form)) + for _, elem := range req.Request.Form { + request.Form[elem.Key] = elem.Values + } + request.PostForm = make(url.Values, len(req.Request.PostForm)) + for _, elem := range req.Request.PostForm { + request.PostForm[elem.Key] = elem.Values + } + request.Trailer = make(http.Header) + request.RemoteAddr = req.Request.RemoteAddr + request.RequestURI = req.Request.RequestUri + + if req.Request.Tls != nil { + request.TLS = &tls.ConnectionState{ + Version: uint16(req.Request.Tls.Version), + HandshakeComplete: req.Request.Tls.HandshakeComplete, + DidResume: req.Request.Tls.DidResume, + CipherSuite: uint16(req.Request.Tls.CipherSuite), + NegotiatedProtocol: req.Request.Tls.NegotiatedProtocol, + NegotiatedProtocolIsMutual: true, // always true per https://pkg.go.dev/crypto/tls#ConnectionState + ServerName: req.Request.Tls.ServerName, + PeerCertificates: make([]*x509.Certificate, len(req.Request.Tls.PeerCertificates.Cert)), + VerifiedChains: make([][]*x509.Certificate, len(req.Request.Tls.VerifiedChains)), + SignedCertificateTimestamps: req.Request.Tls.SignedCertificateTimestamps, + OCSPResponse: req.Request.Tls.OcspResponse, + } + for i, certBytes := range req.Request.Tls.PeerCertificates.Cert { + cert, err := x509.ParseCertificate(certBytes) + if err != nil { + return nil, err + } + request.TLS.PeerCertificates[i] = cert + } + for i, chain := range req.Request.Tls.VerifiedChains { + request.TLS.VerifiedChains[i] = make([]*x509.Certificate, len(chain.Cert)) + for j, certBytes := range chain.Cert { + cert, err := x509.ParseCertificate(certBytes) + if err != nil { + return nil, err + } + request.TLS.VerifiedChains[i][j] = cert + } + } + } + + s.handler.ServeHTTP(writer, request) + + return &emptypb.Empty{}, clientConn.Close() +} + +// HandleSimple handles http requests over http2 using a simple request response model. +// Websockets are not supported. Based on https://www.weave.works/blog/turtles-way-http-grpc/ +func (s *Server) HandleSimple(ctx context.Context, r *httppb.HandleSimpleHTTPRequest) (*httppb.HandleSimpleHTTPResponse, error) { + req, err := http.NewRequest(r.Method, r.Url, bytes.NewBuffer(r.Body)) + if err != nil { + return nil, err + } + + grpcutils.MergeHTTPHeader(r.Headers, req.Header) + + req = req.WithContext(ctx) + req.RequestURI = r.Url + req.ContentLength = int64(len(r.Body)) + + w := newResponseWriter() + s.handler.ServeHTTP(w, req) + + resp := &httppb.HandleSimpleHTTPResponse{ + Code: int32(w.statusCode), + Headers: grpcutils.GetHTTPHeader(w.Header()), + Body: w.body.Bytes(), + } + + if w.statusCode == http.StatusInternalServerError { + return nil, grpcutils.GetGRPCErrorFromHTTPResponse(resp) + } + return resp, nil +} + +type ResponseWriter struct { + body *bytes.Buffer + header http.Header + statusCode int +} + +// newResponseWriter returns very basic implementation of the http.ResponseWriter +func newResponseWriter() *ResponseWriter { + return &ResponseWriter{ + body: new(bytes.Buffer), + header: make(http.Header), + statusCode: http.StatusOK, + } +} + +func (w *ResponseWriter) Header() http.Header { + return w.header +} + +func (w *ResponseWriter) Write(buf []byte) (int, error) { + return w.body.Write(buf) +} + +func (w *ResponseWriter) WriteHeader(code int) { + w.statusCode = code +} + +func (w *ResponseWriter) StatusCode() int { + return w.statusCode +} + +func (w *ResponseWriter) Body() *bytes.Buffer { + return w.body +} diff --git a/http/reader/reader_client.go b/http/reader/reader_client.go new file mode 100644 index 0000000..d3feca3 --- /dev/null +++ b/http/reader/reader_client.go @@ -0,0 +1,35 @@ +package reader + +import ( + "context" + "errors" + "io" + + readerpb "github.com/consideritdone/landslidevm/proto/io/reader" +) + +var _ io.Reader = (*Client)(nil) + +// Client is a reader that talks over RPC. +type Client struct{ client readerpb.ReaderClient } + +// NewClient returns a reader connected to a remote reader +func NewClient(client readerpb.ReaderClient) *Client { + return &Client{client: client} +} + +func (c *Client) Read(p []byte) (int, error) { + resp, err := c.client.Read(context.Background(), &readerpb.ReadRequest{ + Length: int32(len(p)), + }) + if err != nil { + return 0, err + } + + copy(p, resp.Read) + + if resp.Error != nil { + err = errors.New(*resp.Error) + } + return len(resp.Read), err +} diff --git a/http/reader/reader_server.go b/http/reader/reader_server.go new file mode 100644 index 0000000..a587350 --- /dev/null +++ b/http/reader/reader_server.go @@ -0,0 +1,34 @@ +package reader + +import ( + "context" + "io" + + readerpb "github.com/consideritdone/landslidevm/proto/io/reader" +) + +var _ readerpb.ReaderServer = (*Server)(nil) + +// Server is an io.Reader that is managed over RPC. +type Server struct { + readerpb.UnsafeReaderServer + reader io.Reader +} + +// NewServer returns an io.Reader instance managed remotely +func NewServer(reader io.Reader) *Server { + return &Server{reader: reader} +} + +func (s *Server) Read(_ context.Context, req *readerpb.ReadRequest) (*readerpb.ReadResponse, error) { + buf := make([]byte, int(req.Length)) + n, err := s.reader.Read(buf) + resp := &readerpb.ReadResponse{ + Read: buf[:n], + } + if err != nil { + errStr := err.Error() + resp.Error = &errStr + } + return resp, nil +} diff --git a/http/responsewriter/locked_writer.go b/http/responsewriter/locked_writer.go new file mode 100644 index 0000000..60a373c --- /dev/null +++ b/http/responsewriter/locked_writer.go @@ -0,0 +1,72 @@ +package responsewriter + +import ( + "bufio" + "net" + "net/http" + "sync" +) + +var ( + _ http.ResponseWriter = (*lockedWriter)(nil) + _ http.Flusher = (*lockedWriter)(nil) + _ http.Hijacker = (*lockedWriter)(nil) +) + +type lockedWriter struct { + lock sync.Mutex + writer http.ResponseWriter + headerWritten bool +} + +func NewLockedWriter(w http.ResponseWriter) http.ResponseWriter { + return &lockedWriter{writer: w} +} + +func (lw *lockedWriter) Header() http.Header { + lw.lock.Lock() + defer lw.lock.Unlock() + + return lw.writer.Header() +} + +func (lw *lockedWriter) Write(b []byte) (int, error) { + lw.lock.Lock() + defer lw.lock.Unlock() + + lw.headerWritten = true + return lw.writer.Write(b) +} + +func (lw *lockedWriter) WriteHeader(statusCode int) { + lw.lock.Lock() + defer lw.lock.Unlock() + + // Skip writing the header if it has already been written once. + if lw.headerWritten { + return + } + lw.headerWritten = true + lw.writer.WriteHeader(statusCode) +} + +func (lw *lockedWriter) Flush() { + lw.lock.Lock() + defer lw.lock.Unlock() + + flusher, ok := lw.writer.(http.Flusher) + if ok { + flusher.Flush() + } +} + +func (lw *lockedWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) { + lw.lock.Lock() + defer lw.lock.Unlock() + + hijacker, ok := lw.writer.(http.Hijacker) + if !ok { + return nil, nil, errUnsupportedHijacking + } + return hijacker.Hijack() +} diff --git a/http/responsewriter/writer_client.go b/http/responsewriter/writer_client.go new file mode 100644 index 0000000..161e620 --- /dev/null +++ b/http/responsewriter/writer_client.go @@ -0,0 +1,130 @@ +package responsewriter + +import ( + "bufio" + "context" + "net" + "net/http" + + "google.golang.org/protobuf/types/known/emptypb" + + "github.com/consideritdone/landslidevm/grpcutils" + "github.com/consideritdone/landslidevm/http/conn" + "github.com/consideritdone/landslidevm/http/reader" + "github.com/consideritdone/landslidevm/http/writer" + + responsewriterpb "github.com/consideritdone/landslidevm/proto/http/responsewriter" + readerpb "github.com/consideritdone/landslidevm/proto/io/reader" + writerpb "github.com/consideritdone/landslidevm/proto/io/writer" + connpb "github.com/consideritdone/landslidevm/proto/net/conn" +) + +var ( + _ http.ResponseWriter = (*Client)(nil) + _ http.Flusher = (*Client)(nil) + _ http.Hijacker = (*Client)(nil) +) + +// Client is an http.ResponseWriter that talks over RPC. +type Client struct { + client responsewriterpb.WriterClient + header http.Header +} + +// NewClient returns a response writer connected to a remote response writer +func NewClient(header http.Header, client responsewriterpb.WriterClient) *Client { + return &Client{ + client: client, + header: header, + } +} + +func (c *Client) Header() http.Header { + return c.header +} + +func (c *Client) Write(payload []byte) (int, error) { + req := &responsewriterpb.WriteRequest{ + Headers: make([]*responsewriterpb.Header, 0, len(c.header)), + Payload: payload, + } + for key, values := range c.header { + req.Headers = append(req.Headers, &responsewriterpb.Header{ + Key: key, + Values: values, + }) + } + resp, err := c.client.Write(context.Background(), req) + if err != nil { + return 0, err + } + return int(resp.Written), nil +} + +func (c *Client) WriteHeader(statusCode int) { + req := &responsewriterpb.WriteHeaderRequest{ + Headers: make([]*responsewriterpb.Header, 0, len(c.header)), + StatusCode: int32(statusCode), + } + for key, values := range c.header { + req.Headers = append(req.Headers, &responsewriterpb.Header{ + Key: key, + Values: values, + }) + } + // TODO: Is there a way to handle the error here? + _, _ = c.client.WriteHeader(context.Background(), req) +} + +func (c *Client) Flush() { + // TODO: is there a way to handle the error here? + _, _ = c.client.Flush(context.Background(), &emptypb.Empty{}) +} + +type addr struct { + network string + str string +} + +func (a *addr) Network() string { + return a.network +} + +func (a *addr) String() string { + return a.str +} + +func (c *Client) Hijack() (net.Conn, *bufio.ReadWriter, error) { + resp, err := c.client.Hijack(context.Background(), &emptypb.Empty{}) + if err != nil { + return nil, nil, err + } + + clientConn, err := grpcutils.Dial(resp.ServerAddr) + if err != nil { + return nil, nil, err + } + + conn := conn.NewClient( + connpb.NewConnClient(clientConn), + &addr{ + network: resp.LocalNetwork, + str: resp.LocalString, + }, + &addr{ + network: resp.RemoteNetwork, + str: resp.RemoteString, + }, + clientConn, + ) + + reader := reader.NewClient(readerpb.NewReaderClient(clientConn)) + writer := writer.NewClient(writerpb.NewWriterClient(clientConn)) + + readWriter := bufio.NewReadWriter( + bufio.NewReader(reader), + bufio.NewWriter(writer), + ) + + return conn, readWriter, nil +} diff --git a/http/responsewriter/writer_server.go b/http/responsewriter/writer_server.go new file mode 100644 index 0000000..b84e546 --- /dev/null +++ b/http/responsewriter/writer_server.go @@ -0,0 +1,117 @@ +package responsewriter + +import ( + "context" + "errors" + "net/http" + + "google.golang.org/protobuf/types/known/emptypb" + + "github.com/consideritdone/landslidevm/grpcutils" + "github.com/consideritdone/landslidevm/http/conn" + "github.com/consideritdone/landslidevm/http/reader" + "github.com/consideritdone/landslidevm/http/writer" + + responsewriterpb "github.com/consideritdone/landslidevm/proto/http/responsewriter" + readerpb "github.com/consideritdone/landslidevm/proto/io/reader" + writerpb "github.com/consideritdone/landslidevm/proto/io/writer" + connpb "github.com/consideritdone/landslidevm/proto/net/conn" +) + +var ( + errUnsupportedFlushing = errors.New("response writer doesn't support flushing") + errUnsupportedHijacking = errors.New("response writer doesn't support hijacking") + + _ responsewriterpb.WriterServer = (*Server)(nil) +) + +// Server is an http.ResponseWriter that is managed over RPC. +type Server struct { + responsewriterpb.UnsafeWriterServer + writer http.ResponseWriter +} + +// NewServer returns an http.ResponseWriter instance managed remotely +func NewServer(writer http.ResponseWriter) *Server { + return &Server{ + writer: writer, + } +} + +func (s *Server) Write( + _ context.Context, + req *responsewriterpb.WriteRequest, +) (*responsewriterpb.WriteResponse, error) { + headers := s.writer.Header() + clear(headers) + for _, header := range req.Headers { + headers[header.Key] = header.Values + } + + n, err := s.writer.Write(req.Payload) + if err != nil { + return nil, err + } + return &responsewriterpb.WriteResponse{ + Written: int32(n), + }, nil +} + +func (s *Server) WriteHeader( + _ context.Context, + req *responsewriterpb.WriteHeaderRequest, +) (*emptypb.Empty, error) { + headers := s.writer.Header() + clear(headers) + for _, header := range req.Headers { + headers[header.Key] = header.Values + } + s.writer.WriteHeader(grpcutils.EnsureValidResponseCode(int(req.StatusCode))) + return &emptypb.Empty{}, nil +} + +func (s *Server) Flush(context.Context, *emptypb.Empty) (*emptypb.Empty, error) { + flusher, ok := s.writer.(http.Flusher) + if !ok { + return nil, errUnsupportedFlushing + } + flusher.Flush() + return &emptypb.Empty{}, nil +} + +func (s *Server) Hijack(context.Context, *emptypb.Empty) (*responsewriterpb.HijackResponse, error) { + hijacker, ok := s.writer.(http.Hijacker) + if !ok { + return nil, errUnsupportedHijacking + } + connection, readWriter, err := hijacker.Hijack() + if err != nil { + return nil, err + } + + serverListener, err := grpcutils.NewListener() + if err != nil { + return nil, err + } + + server := grpcutils.NewServer() + closer := grpcutils.ServerCloser{} + closer.Add(server) + + connpb.RegisterConnServer(server, conn.NewServer(connection, &closer)) + readerpb.RegisterReaderServer(server, reader.NewServer(readWriter)) + writerpb.RegisterWriterServer(server, writer.NewServer(readWriter)) + + go grpcutils.Serve(serverListener, server) + + local := connection.LocalAddr() + remote := connection.RemoteAddr() + + return &responsewriterpb.HijackResponse{ + LocalNetwork: local.Network(), + LocalString: local.String(), + RemoteNetwork: remote.Network(), + RemoteString: remote.String(), + ServerAddr: serverListener.Addr().String(), + }, nil +} diff --git a/http/writer/writer_client.go b/http/writer/writer_client.go new file mode 100644 index 0000000..77967d2 --- /dev/null +++ b/http/writer/writer_client.go @@ -0,0 +1,33 @@ +package writer + +import ( + "context" + "errors" + "io" + + writerpb "github.com/consideritdone/landslidevm/proto/io/writer" +) + +var _ io.Writer = (*Client)(nil) + +// Client is an io.Writer that talks over RPC. +type Client struct{ client writerpb.WriterClient } + +// NewClient returns a writer connected to a remote writer +func NewClient(client writerpb.WriterClient) *Client { + return &Client{client: client} +} + +func (c *Client) Write(p []byte) (int, error) { + resp, err := c.client.Write(context.Background(), &writerpb.WriteRequest{ + Payload: p, + }) + if err != nil { + return 0, err + } + + if resp.Error != nil { + err = errors.New(*resp.Error) + } + return int(resp.Written), err +} diff --git a/http/writer/writer_server.go b/http/writer/writer_server.go new file mode 100644 index 0000000..97eb7d7 --- /dev/null +++ b/http/writer/writer_server.go @@ -0,0 +1,33 @@ +package writer + +import ( + "context" + "io" + + writerpb "github.com/consideritdone/landslidevm/proto/io/writer" +) + +var _ writerpb.WriterServer = (*Server)(nil) + +// Server is an http.Handler that is managed over RPC. +type Server struct { + writerpb.UnsafeWriterServer + writer io.Writer +} + +// NewServer returns an http.Handler instance managed remotely +func NewServer(writer io.Writer) *Server { + return &Server{writer: writer} +} + +func (s *Server) Write(_ context.Context, req *writerpb.WriteRequest) (*writerpb.WriteResponse, error) { + n, err := s.writer.Write(req.Payload) + resp := &writerpb.WriteResponse{ + Written: int32(n), + } + if err != nil { + errStr := err.Error() + resp.Error = &errStr + } + return resp, nil +} diff --git a/jsonrpc/http_json_handler.go b/jsonrpc/http_json_handler.go new file mode 100644 index 0000000..51c8259 --- /dev/null +++ b/jsonrpc/http_json_handler.go @@ -0,0 +1,266 @@ +package jsonrpc + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "net/http" + "reflect" + "sort" + + cmtjson "github.com/cometbft/cometbft/libs/json" + "github.com/cometbft/cometbft/libs/log" + "github.com/cometbft/cometbft/rpc/jsonrpc/server" + types "github.com/cometbft/cometbft/rpc/jsonrpc/types" +) + +// HTTP + JSON handler + +// jsonrpc calls grab the given method's function info and runs reflect.Call +func makeJSONRPCHandler(funcMap map[string]*RPCFunc, logger log.Logger) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + logger.Info("reading body") + b, err := io.ReadAll(r.Body) + if err != nil { + res := types.RPCInvalidRequestError(nil, + fmt.Errorf("error reading request body: %w", err), + ) + if wErr := server.WriteRPCResponseHTTPError(w, http.StatusBadRequest, res); wErr != nil { + logger.Error("failed to write response", "err", wErr) + } + return + } + + // if its an empty request (like from a browser), just display a list of + // functions + if len(b) == 0 { + writeListOfEndpoints(w, r, funcMap) + return + } + + logger.Info("request", "body", string(b)) + + // first try to unmarshal the incoming request as an array of RPC requests + var ( + requests []types.RPCRequest + responses []types.RPCResponse + ) + if err := json.Unmarshal(b, &requests); err != nil { + // next, try to unmarshal as a single request + var request types.RPCRequest + if err := json.Unmarshal(b, &request); err != nil { + res := types.RPCParseError(fmt.Errorf("error unmarshaling request: %w", err)) + if wErr := server.WriteRPCResponseHTTPError(w, http.StatusInternalServerError, res); wErr != nil { + logger.Error("failed to write response", "err", wErr) + } + return + } + requests = []types.RPCRequest{request} + } + + // Set the default response cache to true unless + // 1. Any RPC request error. + // 2. Any RPC request doesn't allow to be cached. + // 3. Any RPC request has the height argument and the value is 0 (the default). + cache := true + + logger.Info("process requests", "qty", len(requests)) + for _, request := range requests { + request := request + + // A Notification is a Request object without an "id" member. + // The Server MUST NOT reply to a Notification, including those that are within a batch request. + if request.ID == nil { + logger.Debug( + "HTTPJSONRPC received a notification, skipping... (please send a non-empty ID if you want to call a method)", + "req", request, + ) + continue + } + //if len(r.URL.Path) > 1 { + // responses = append( + // responses, + // types.RPCInvalidRequestError(request.ID, fmt.Errorf("path %s is invalid", r.URL.Path)), + // ) + // cache = false + // continue + //} + rpcFunc, ok := funcMap[request.Method] + if !ok || (rpcFunc.ws) { + responses = append(responses, types.RPCMethodNotFoundError(request.ID)) + cache = false + continue + } + ctx := &types.Context{JSONReq: &request, HTTPReq: r} + args := []reflect.Value{reflect.ValueOf(ctx)} + + // real handler argument count and defined for RPC is mismatched + if len(rpcFunc.args)-1 != len(rpcFunc.argNames) { + responses = append( + responses, + types.RPCInternalError(request.ID, fmt.Errorf("rpc function expects %v parameters, defined %v (%v)", + len(rpcFunc.args)-1, len(rpcFunc.argNames), rpcFunc.argNames)), + ) + cache = false + continue + } + + if len(request.Params) > 0 { + fnArgs, err := jsonParamsToArgs(rpcFunc, request.Params) + if err != nil { + responses = append( + responses, + types.RPCInvalidParamsError(request.ID, fmt.Errorf("error converting json params to arguments: %w", err)), + ) + cache = false + continue + } + args = append(args, fnArgs...) + } + + if cache && !rpcFunc.cacheableWithArgs(args) { + cache = false + } + + logger.Info("calling func", "method", request.Method, "args", args) + returns := rpcFunc.f.Call(args) + result, err := unreflectResult(returns) + logger.Info("result of calling func for %s: err: %s", request.Method, err) + + if err != nil { + responses = append(responses, types.RPCInternalError(request.ID, err)) + continue + } + responses = append(responses, types.NewRPCSuccessResponse(request.ID, result)) + } + + if len(responses) > 0 { + var wErr error + if cache { + wErr = server.WriteCacheableRPCResponseHTTP(w, responses...) + } else { + wErr = server.WriteRPCResponseHTTP(w, responses...) + } + if wErr != nil { + logger.Error("failed to write responses", "err", wErr) + } + } + } +} + +func mapParamsToArgs( + rpcFunc *RPCFunc, + params map[string]json.RawMessage, + argsOffset int, +) ([]reflect.Value, error) { + values := make([]reflect.Value, len(rpcFunc.argNames)) + for i, argName := range rpcFunc.argNames { + argType := rpcFunc.args[i+argsOffset] + + if p, ok := params[argName]; ok && p != nil && len(p) > 0 { + val := reflect.New(argType) + err := cmtjson.Unmarshal(p, val.Interface()) + if err != nil { + return nil, err + } + values[i] = val.Elem() + } else { // use default for that type + values[i] = reflect.Zero(argType) + } + } + + return values, nil +} + +func arrayParamsToArgs( + rpcFunc *RPCFunc, + params []json.RawMessage, + argsOffset int, +) ([]reflect.Value, error) { + if len(rpcFunc.argNames) != len(params) { + return nil, fmt.Errorf("expected %v parameters (%v), got %v (%v)", + len(rpcFunc.argNames), rpcFunc.argNames, len(params), params) + } + + values := make([]reflect.Value, len(params)) + for i, p := range params { + argType := rpcFunc.args[i+argsOffset] + val := reflect.New(argType) + err := cmtjson.Unmarshal(p, val.Interface()) + if err != nil { + return nil, err + } + values[i] = val.Elem() + } + return values, nil +} + +// raw is unparsed json (from json.RawMessage) encoding either a map or an +// array. +// +// Example: +// +// rpcFunc.args = [rpctypes.Context string] +// rpcFunc.argNames = ["arg"] +func jsonParamsToArgs(rpcFunc *RPCFunc, raw []byte) ([]reflect.Value, error) { + const argsOffset = 1 + + // TODO: Make more efficient, perhaps by checking the first character for '{' or '['? + // First, try to get the map. + var m map[string]json.RawMessage + err := json.Unmarshal(raw, &m) + if err == nil { + return mapParamsToArgs(rpcFunc, m, argsOffset) + } + + // Otherwise, try an array. + var a []json.RawMessage + err = json.Unmarshal(raw, &a) + if err == nil { + return arrayParamsToArgs(rpcFunc, a, argsOffset) + } + + // Otherwise, bad format, we cannot parse + return nil, fmt.Errorf("unknown type for JSON params: %v. Expected map or array", err) +} + +// writes a list of available rpc endpoints as an html page +func writeListOfEndpoints(w http.ResponseWriter, r *http.Request, funcMap map[string]*RPCFunc) { + noArgNames := []string{} + argNames := []string{} + for name, funcData := range funcMap { + if len(funcData.args) == 0 { + noArgNames = append(noArgNames, name) + } else { + argNames = append(argNames, name) + } + } + sort.Strings(noArgNames) + sort.Strings(argNames) + buf := new(bytes.Buffer) + buf.WriteString("") + buf.WriteString("
Available endpoints:
") + + for _, name := range noArgNames { + link := fmt.Sprintf("//%s/%s", r.Host, name) + buf.WriteString(fmt.Sprintf("%s
", link, link)) + } + + buf.WriteString("
Endpoints that require arguments:
") + for _, name := range argNames { + link := fmt.Sprintf("//%s/%s?", r.Host, name) + funcData := funcMap[name] + for i, argName := range funcData.argNames { + link += argName + "=_" + if i < len(funcData.argNames)-1 { + link += "&" + } + } + buf.WriteString(fmt.Sprintf("%s
", link, link)) + } + buf.WriteString("") + w.Header().Set("Content-Type", "text/html") + w.WriteHeader(200) + w.Write(buf.Bytes()) //nolint: errcheck +} diff --git a/jsonrpc/http_json_handler_test.go b/jsonrpc/http_json_handler_test.go new file mode 100644 index 0000000..6d28ffe --- /dev/null +++ b/jsonrpc/http_json_handler_test.go @@ -0,0 +1,281 @@ +package jsonrpc + +import ( + "bytes" + "encoding/json" + "io" + "net/http" + "net/http/httptest" + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/cometbft/cometbft/libs/log" + types "github.com/cometbft/cometbft/rpc/jsonrpc/types" +) + +func testMux() *http.ServeMux { + funcMap := map[string]*RPCFunc{ + "c": NewRPCFunc(func(ctx *types.Context, s string, i int) (string, error) { return "foo", nil }, "s,i"), + "block": NewRPCFunc(func(ctx *types.Context, h int) (string, error) { return "block", nil }, "height", Cacheable("height")), + "z": NewRPCFunc(func(ctx *types.Context, h int) (string, error) { return "block", nil }, ""), + } + mux := http.NewServeMux() + buf := new(bytes.Buffer) + logger := log.NewTMLogger(buf) + RegisterRPCFuncs(mux, funcMap, logger) + + return mux +} + +func statusOK(code int) bool { return code >= 200 && code <= 299 } + +// Ensure that nefarious/unintended inputs to `params` +// do not crash our RPC handlers. +// See Issue https://github.com/tendermint/tendermint/issues/708. +func TestRPCParams(t *testing.T) { + mux := testMux() + tests := []struct { + payload string + wantErr string + expectedID interface{} + }{ + // bad + {`{"jsonrpc": "2.0", "id": "0"}`, "Method not found", types.JSONRPCStringID("0")}, + {`{"jsonrpc": "2.0", "method": "y", "id": "0"}`, "Method not found", types.JSONRPCStringID("0")}, + // id not captured in JSON parsing failures + {`{"method": "c", "id": "0", "params": a}`, "invalid character", nil}, + {`{"method": "c", "id": "0", "params": ["a"]}`, "got 1", types.JSONRPCStringID("0")}, + {`{"method": "c", "id": "0", "params": ["a", "b"]}`, "invalid character", types.JSONRPCStringID("0")}, + {`{"method": "c", "id": "0", "params": [1, 1]}`, "of type string", types.JSONRPCStringID("0")}, + {`{"method": "z", "id": "0", "params": []}`, "rpc function expects", types.JSONRPCStringID("0")}, + + // no ID - notification + // {`{"jsonrpc": "2.0", "method": "c", "params": ["a", "10"]}`, false, nil}, + + // good + {`{"jsonrpc": "2.0", "method": "c", "id": "0", "params": null}`, "", types.JSONRPCStringID("0")}, + {`{"method": "c", "id": "0", "params": {}}`, "", types.JSONRPCStringID("0")}, + {`{"method": "c", "id": "0", "params": ["a", "10"]}`, "", types.JSONRPCStringID("0")}, + } + + for i, tt := range tests { + req, _ := http.NewRequest("POST", "http://localhost/", strings.NewReader(tt.payload)) + rec := httptest.NewRecorder() + mux.ServeHTTP(rec, req) + res := rec.Result() + defer res.Body.Close() + // Always expecting back a JSONRPCResponse + assert.NotZero(t, res.StatusCode, "#%d: should always return code", i) + blob, err := io.ReadAll(res.Body) + if err != nil { + t.Errorf("#%d: err reading body: %v", i, err) + continue + } + + recv := new(types.RPCResponse) + assert.Nil(t, json.Unmarshal(blob, recv), "#%d: expecting successful parsing of an RPCResponse:\nblob: %s", i, blob) + assert.NotEqual(t, recv, new(types.RPCResponse), "#%d: not expecting a blank RPCResponse", i) + assert.Equal(t, tt.expectedID, recv.ID, "#%d: expected ID not matched in RPCResponse", i) + if tt.wantErr == "" { + assert.Nil(t, recv.Error, "#%d: not expecting an error", i) + } else { + assert.True(t, recv.Error.Code < 0, "#%d: not expecting a positive JSONRPC code", i) + // The wanted error is either in the message or the data + assert.Contains(t, recv.Error.Message+recv.Error.Data, tt.wantErr, "#%d: expected substring", i) + } + } +} + +func TestJSONRPCID(t *testing.T) { + mux := testMux() + tests := []struct { + payload string + wantErr bool + expectedID interface{} + }{ + // good id + {`{"jsonrpc": "2.0", "method": "c", "id": "0", "params": ["a", "10"]}`, false, types.JSONRPCStringID("0")}, + {`{"jsonrpc": "2.0", "method": "c", "id": "abc", "params": ["a", "10"]}`, false, types.JSONRPCStringID("abc")}, + {`{"jsonrpc": "2.0", "method": "c", "id": 0, "params": ["a", "10"]}`, false, types.JSONRPCIntID(0)}, + {`{"jsonrpc": "2.0", "method": "c", "id": 1, "params": ["a", "10"]}`, false, types.JSONRPCIntID(1)}, + {`{"jsonrpc": "2.0", "method": "c", "id": 1.3, "params": ["a", "10"]}`, false, types.JSONRPCIntID(1)}, + {`{"jsonrpc": "2.0", "method": "c", "id": -1, "params": ["a", "10"]}`, false, types.JSONRPCIntID(-1)}, + + // bad id + {`{"jsonrpc": "2.0", "method": "c", "id": {}, "params": ["a", "10"]}`, true, nil}, + {`{"jsonrpc": "2.0", "method": "c", "id": [], "params": ["a", "10"]}`, true, nil}, + } + + for i, tt := range tests { + req, _ := http.NewRequest("POST", "http://localhost/", strings.NewReader(tt.payload)) + rec := httptest.NewRecorder() + mux.ServeHTTP(rec, req) + res := rec.Result() + // Always expecting back a JSONRPCResponse + assert.NotZero(t, res.StatusCode, "#%d: should always return code", i) + blob, err := io.ReadAll(res.Body) + if err != nil { + t.Errorf("#%d: err reading body: %v", i, err) + continue + } + res.Body.Close() + + recv := new(types.RPCResponse) + err = json.Unmarshal(blob, recv) + assert.Nil(t, err, "#%d: expecting successful parsing of an RPCResponse:\nblob: %s", i, blob) + if !tt.wantErr { + assert.NotEqual(t, recv, new(types.RPCResponse), "#%d: not expecting a blank RPCResponse", i) + assert.Equal(t, tt.expectedID, recv.ID, "#%d: expected ID not matched in RPCResponse", i) + assert.Nil(t, recv.Error, "#%d: not expecting an error", i) + } else { + assert.True(t, recv.Error.Code < 0, "#%d: not expecting a positive JSONRPC code", i) + } + } +} + +func TestRPCNotification(t *testing.T) { + mux := testMux() + body := strings.NewReader(`{"jsonrpc": "2.0"}`) + req, _ := http.NewRequest("POST", "http://localhost/", body) + rec := httptest.NewRecorder() + mux.ServeHTTP(rec, req) + res := rec.Result() + + // Always expecting back a JSONRPCResponse + require.True(t, statusOK(res.StatusCode), "should always return 2XX") + blob, err := io.ReadAll(res.Body) + res.Body.Close() + require.Nil(t, err, "reading from the body should not give back an error") + require.Equal(t, len(blob), 0, "a notification SHOULD NOT be responded to by the server") +} + +func TestRPCNotificationInBatch(t *testing.T) { + mux := testMux() + tests := []struct { + payload string + expectCount int + }{ + { + `[ + {"jsonrpc": "2.0"}, + {"jsonrpc": "2.0","method":"c","id":"abc","params":["a","10"]} + ]`, + 1, + }, + { + `[ + {"jsonrpc": "2.0"}, + {"jsonrpc": "2.0","method":"c","id":"abc","params":["a","10"]}, + {"jsonrpc": "2.0"}, + {"jsonrpc": "2.0","method":"c","id":"abc","params":["a","10"]} + ]`, + 2, + }, + } + for i, tt := range tests { + req, _ := http.NewRequest("POST", "http://localhost/", strings.NewReader(tt.payload)) + rec := httptest.NewRecorder() + mux.ServeHTTP(rec, req) + res := rec.Result() + // Always expecting back a JSONRPCResponse + assert.True(t, statusOK(res.StatusCode), "#%d: should always return 2XX", i) + blob, err := io.ReadAll(res.Body) + if err != nil { + t.Errorf("#%d: err reading body: %v", i, err) + continue + } + res.Body.Close() + + var responses []types.RPCResponse + // try to unmarshal an array first + err = json.Unmarshal(blob, &responses) + if err != nil { + // if we were actually expecting an array, but got an error + if tt.expectCount > 1 { + t.Errorf("#%d: expected an array, couldn't unmarshal it\nblob: %s", i, blob) + continue + } + // we were expecting an error here, so let's unmarshal a single response + var response types.RPCResponse + err = json.Unmarshal(blob, &response) + if err != nil { + t.Errorf("#%d: expected successful parsing of an RPCResponse\nblob: %s", i, blob) + continue + } + // have a single-element result + responses = []types.RPCResponse{response} + } + if tt.expectCount != len(responses) { + t.Errorf("#%d: expected %d response(s), but got %d\nblob: %s", i, tt.expectCount, len(responses), blob) + continue + } + for _, response := range responses { + assert.NotEqual(t, response, new(types.RPCResponse), "#%d: not expecting a blank RPCResponse", i) + } + } +} + +//func TestUnknownRPCPath(t *testing.T) { +// mux := testMux() +// req, err := http.NewRequest("GET", "http://localhost/unknownrpcpath", nil) +// require.NoError(t, err) +// rec := httptest.NewRecorder() +// mux.ServeHTTP(rec, req) +// res := rec.Result() +// +// // Always expecting back a 404 error +// require.Equal(t, http.StatusNotFound, res.StatusCode, "should always return 404") +// res.Body.Close() +//} + +func TestRPCResponseCache(t *testing.T) { + mux := testMux() + body := strings.NewReader(`{"jsonrpc": "2.0","method":"block","id": 0, "params": ["1"]}`) + req, _ := http.NewRequest("Get", "http://localhost/", body) + rec := httptest.NewRecorder() + mux.ServeHTTP(rec, req) + res := rec.Result() + + // Always expecting back a JSONRPCResponse + require.True(t, statusOK(res.StatusCode), "should always return 2XX") + require.Equal(t, "public, max-age=86400", res.Header.Get("Cache-control")) + + _, err := io.ReadAll(res.Body) + res.Body.Close() + require.Nil(t, err, "reading from the body should not give back an error") + + // send a request with default height. + body = strings.NewReader(`{"jsonrpc": "2.0","method":"block","id": 0, "params": ["0"]}`) + req, _ = http.NewRequest("Get", "http://localhost/", body) + rec = httptest.NewRecorder() + mux.ServeHTTP(rec, req) + res = rec.Result() + + // Always expecting back a JSONRPCResponse + require.True(t, statusOK(res.StatusCode), "should always return 2XX") + require.Equal(t, "", res.Header.Get("Cache-control")) + + _, err = io.ReadAll(res.Body) + + res.Body.Close() + require.Nil(t, err, "reading from the body should not give back an error") + + // send a request with default height, but as empty set of parameters. + body = strings.NewReader(`{"jsonrpc": "2.0","method":"block","id": 0, "params": []}`) + req, _ = http.NewRequest("Get", "http://localhost/", body) + rec = httptest.NewRecorder() + mux.ServeHTTP(rec, req) + res = rec.Result() + + // Always expecting back a JSONRPCResponse + require.True(t, statusOK(res.StatusCode), "should always return 2XX") + require.Equal(t, "", res.Header.Get("Cache-control")) + + _, err = io.ReadAll(res.Body) + + res.Body.Close() + require.Nil(t, err, "reading from the body should not give back an error") +} diff --git a/jsonrpc/rpc_func.go b/jsonrpc/rpc_func.go new file mode 100644 index 0000000..9bfa51c --- /dev/null +++ b/jsonrpc/rpc_func.go @@ -0,0 +1,149 @@ +package jsonrpc + +import ( + "fmt" + "net/http" + "reflect" + "strings" + + "github.com/cometbft/cometbft/libs/log" +) + +// RegisterRPCFuncs adds a route for each function in the funcMap, as well as +// general jsonrpc and websocket handlers for all functions. "result" is the +// interface on which the result objects are registered, and is popualted with +// every RPCResponse +func RegisterRPCFuncs(mux *http.ServeMux, funcMap map[string]*RPCFunc, logger log.Logger) { + // JSONRPC endpoints + mux.HandleFunc("/", makeJSONRPCHandler(funcMap, logger)) +} + +type Option func(*RPCFunc) + +// Cacheable enables returning a cache control header from RPC functions to +// which it is applied. +// +// `noCacheDefArgs` is a list of argument names that, if omitted or set to +// their defaults when calling the RPC function, will skip the response +// caching. +func Cacheable(noCacheDefArgs ...string) Option { + return func(r *RPCFunc) { + r.cacheable = true + r.noCacheDefArgs = make(map[string]interface{}) + for _, arg := range noCacheDefArgs { + r.noCacheDefArgs[arg] = nil + } + } +} + +// Ws enables WebSocket communication. +func Ws() Option { + return func(r *RPCFunc) { + r.ws = true + } +} + +// RPCFunc contains the introspected type information for a function +type RPCFunc struct { + f reflect.Value // underlying rpc function + args []reflect.Type // type of each function arg + returns []reflect.Type // type of each return arg + argNames []string // name of each argument + cacheable bool // enable cache control + ws bool // enable websocket communication + noCacheDefArgs map[string]interface{} // a lookup table of args that, if not supplied or are set to default values, cause us to not cache +} + +// NewRPCFunc wraps a function for introspection. +// f is the function, args are comma separated argument names +func NewRPCFunc(f interface{}, args string, options ...Option) *RPCFunc { + return newRPCFunc(f, args, options...) +} + +// NewWSRPCFunc wraps a function for introspection and use in the websockets. +func NewWSRPCFunc(f interface{}, args string, options ...Option) *RPCFunc { + options = append(options, Ws()) + return newRPCFunc(f, args, options...) +} + +// cacheableWithArgs returns whether or not a call to this function is cacheable, +// given the specified arguments. +func (f *RPCFunc) cacheableWithArgs(args []reflect.Value) bool { + if !f.cacheable { + return false + } + // Skip the context variable common to all RPC functions + for i := 1; i < len(f.args); i++ { + // f.argNames does not include the context variable + argName := f.argNames[i-1] + if _, hasDefault := f.noCacheDefArgs[argName]; hasDefault { + // Argument with default value was not supplied + if i >= len(args) { + return false + } + // Argument with default value is set to its zero value + if args[i].IsZero() { + return false + } + } + } + return true +} + +func newRPCFunc(f interface{}, args string, options ...Option) *RPCFunc { + var argNames []string + if args != "" { + argNames = strings.Split(args, ",") + } + + r := &RPCFunc{ + f: reflect.ValueOf(f), + args: funcArgTypes(f), + returns: funcReturnTypes(f), + argNames: argNames, + } + + for _, opt := range options { + opt(r) + } + + return r +} + +// return a function's argument types +func funcArgTypes(f interface{}) []reflect.Type { + t := reflect.TypeOf(f) + n := t.NumIn() + typez := make([]reflect.Type, n) + for i := 0; i < n; i++ { + typez[i] = t.In(i) + } + return typez +} + +// return a function's return types +func funcReturnTypes(f interface{}) []reflect.Type { + t := reflect.TypeOf(f) + n := t.NumOut() + typez := make([]reflect.Type, n) + for i := 0; i < n; i++ { + typez[i] = t.Out(i) + } + return typez +} + +//------------------------------------------------------------- + +// NOTE: assume returns is result struct and error. If error is not nil, return it +func unreflectResult(returns []reflect.Value) (interface{}, error) { + errV := returns[1] + if errV.Interface() != nil { + return nil, fmt.Errorf("%v", errV.Interface()) + } + rv := returns[0] + // the result is a registered interface, + // we need a pointer to it so we can marshal with type byte + rvp := reflect.New(rv.Type()) + rvp.Elem().Set(rv) + return rvp.Interface(), nil +} diff --git a/proto/http/http.pb.go b/proto/http/http.pb.go new file mode 100644 index 0000000..1ec49ba --- /dev/null +++ b/proto/http/http.pb.go @@ -0,0 +1,1238 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.33.0 +// protoc (unknown) +// source: http/http.proto + +package http + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + emptypb "google.golang.org/protobuf/types/known/emptypb" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// URL is a net.URL see: https://pkg.go.dev/net/url#URL +type URL struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // scheme is the url scheme name + Scheme string `protobuf:"bytes,1,opt,name=scheme,proto3" json:"scheme,omitempty"` + // opaque is encoded opaque data + Opaque string `protobuf:"bytes,2,opt,name=opaque,proto3" json:"opaque,omitempty"` + // user is username and password information + User *Userinfo `protobuf:"bytes,3,opt,name=user,proto3" json:"user,omitempty"` + // host can be in the format host or host:port + Host string `protobuf:"bytes,4,opt,name=host,proto3" json:"host,omitempty"` + // path (relative paths may omit leading slash) + Path string `protobuf:"bytes,5,opt,name=path,proto3" json:"path,omitempty"` + // raw_path is encoded path hint (see EscapedPath method) + RawPath string `protobuf:"bytes,6,opt,name=raw_path,json=rawPath,proto3" json:"raw_path,omitempty"` + // force is append a query ('?') even if RawQuery is empty + ForceQuery bool `protobuf:"varint,7,opt,name=force_query,json=forceQuery,proto3" json:"force_query,omitempty"` + // raw_query is encoded query values, without '?' + RawQuery string `protobuf:"bytes,8,opt,name=raw_query,json=rawQuery,proto3" json:"raw_query,omitempty"` + // fragment is fragment for references, without '#' + Fragment string `protobuf:"bytes,9,opt,name=fragment,proto3" json:"fragment,omitempty"` +} + +func (x *URL) Reset() { + *x = URL{} + if protoimpl.UnsafeEnabled { + mi := &file_http_http_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *URL) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*URL) ProtoMessage() {} + +func (x *URL) ProtoReflect() protoreflect.Message { + mi := &file_http_http_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use URL.ProtoReflect.Descriptor instead. +func (*URL) Descriptor() ([]byte, []int) { + return file_http_http_proto_rawDescGZIP(), []int{0} +} + +func (x *URL) GetScheme() string { + if x != nil { + return x.Scheme + } + return "" +} + +func (x *URL) GetOpaque() string { + if x != nil { + return x.Opaque + } + return "" +} + +func (x *URL) GetUser() *Userinfo { + if x != nil { + return x.User + } + return nil +} + +func (x *URL) GetHost() string { + if x != nil { + return x.Host + } + return "" +} + +func (x *URL) GetPath() string { + if x != nil { + return x.Path + } + return "" +} + +func (x *URL) GetRawPath() string { + if x != nil { + return x.RawPath + } + return "" +} + +func (x *URL) GetForceQuery() bool { + if x != nil { + return x.ForceQuery + } + return false +} + +func (x *URL) GetRawQuery() string { + if x != nil { + return x.RawQuery + } + return "" +} + +func (x *URL) GetFragment() string { + if x != nil { + return x.Fragment + } + return "" +} + +// UserInfo is net.Userinfo see: https://pkg.go.dev/net/url#Userinfo +type Userinfo struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // username is the username for the user + Username string `protobuf:"bytes,1,opt,name=username,proto3" json:"username,omitempty"` + // password is the password for the user + Password string `protobuf:"bytes,2,opt,name=password,proto3" json:"password,omitempty"` + // password_set is a boolean which is true if the passord is set + PasswordSet bool `protobuf:"varint,3,opt,name=password_set,json=passwordSet,proto3" json:"password_set,omitempty"` +} + +func (x *Userinfo) Reset() { + *x = Userinfo{} + if protoimpl.UnsafeEnabled { + mi := &file_http_http_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Userinfo) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Userinfo) ProtoMessage() {} + +func (x *Userinfo) ProtoReflect() protoreflect.Message { + mi := &file_http_http_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Userinfo.ProtoReflect.Descriptor instead. +func (*Userinfo) Descriptor() ([]byte, []int) { + return file_http_http_proto_rawDescGZIP(), []int{1} +} + +func (x *Userinfo) GetUsername() string { + if x != nil { + return x.Username + } + return "" +} + +func (x *Userinfo) GetPassword() string { + if x != nil { + return x.Password + } + return "" +} + +func (x *Userinfo) GetPasswordSet() bool { + if x != nil { + return x.PasswordSet + } + return false +} + +type Element struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // key is a element key in a key value pair + Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` + // values are a list of strings coresponding to the key + Values []string `protobuf:"bytes,2,rep,name=values,proto3" json:"values,omitempty"` +} + +func (x *Element) Reset() { + *x = Element{} + if protoimpl.UnsafeEnabled { + mi := &file_http_http_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Element) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Element) ProtoMessage() {} + +func (x *Element) ProtoReflect() protoreflect.Message { + mi := &file_http_http_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Element.ProtoReflect.Descriptor instead. +func (*Element) Descriptor() ([]byte, []int) { + return file_http_http_proto_rawDescGZIP(), []int{2} +} + +func (x *Element) GetKey() string { + if x != nil { + return x.Key + } + return "" +} + +func (x *Element) GetValues() []string { + if x != nil { + return x.Values + } + return nil +} + +type Certificates struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // cert is the certificate body + Cert [][]byte `protobuf:"bytes,1,rep,name=cert,proto3" json:"cert,omitempty"` +} + +func (x *Certificates) Reset() { + *x = Certificates{} + if protoimpl.UnsafeEnabled { + mi := &file_http_http_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Certificates) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Certificates) ProtoMessage() {} + +func (x *Certificates) ProtoReflect() protoreflect.Message { + mi := &file_http_http_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Certificates.ProtoReflect.Descriptor instead. +func (*Certificates) Descriptor() ([]byte, []int) { + return file_http_http_proto_rawDescGZIP(), []int{3} +} + +func (x *Certificates) GetCert() [][]byte { + if x != nil { + return x.Cert + } + return nil +} + +// ConnectionState is tls.ConnectionState see: https://pkg.go.dev/crypto/tls#ConnectionState +type ConnectionState struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // version is the TLS version used by the connection (e.g. VersionTLS12) + Version uint32 `protobuf:"varint,1,opt,name=version,proto3" json:"version,omitempty"` + // handshake_complete is true if the handshake has concluded + HandshakeComplete bool `protobuf:"varint,2,opt,name=handshake_complete,json=handshakeComplete,proto3" json:"handshake_complete,omitempty"` + // did_resume is true if this connection was successfully resumed from a + // previous session with a session ticket or similar mechanism + DidResume bool `protobuf:"varint,3,opt,name=did_resume,json=didResume,proto3" json:"did_resume,omitempty"` + // cipher_suite is the cipher suite negotiated for the connection + CipherSuite uint32 `protobuf:"varint,4,opt,name=cipher_suite,json=cipherSuite,proto3" json:"cipher_suite,omitempty"` + // negotiated_protocol is the application protocol negotiated with ALPN + NegotiatedProtocol string `protobuf:"bytes,5,opt,name=negotiated_protocol,json=negotiatedProtocol,proto3" json:"negotiated_protocol,omitempty"` + // server_name is the value of the Server Name Indication extension sent by + // the client + ServerName string `protobuf:"bytes,6,opt,name=server_name,json=serverName,proto3" json:"server_name,omitempty"` + // peer_certificates are the parsed certificates sent by the peer, in the + // order in which they were sent + PeerCertificates *Certificates `protobuf:"bytes,7,opt,name=peer_certificates,json=peerCertificates,proto3" json:"peer_certificates,omitempty"` + // verified_chains is a list of one or more chains where the first element is + // PeerCertificates[0] and the last element is from Config.RootCAs (on the + // client side) or Config.ClientCAs (on the server side). + VerifiedChains []*Certificates `protobuf:"bytes,8,rep,name=verified_chains,json=verifiedChains,proto3" json:"verified_chains,omitempty"` + // signed_certificate_timestamps is a list of SCTs provided by the peer + // through the TLS handshake for the leaf certificate, if any + SignedCertificateTimestamps [][]byte `protobuf:"bytes,9,rep,name=signed_certificate_timestamps,json=signedCertificateTimestamps,proto3" json:"signed_certificate_timestamps,omitempty"` + // ocsp_response is a stapled Online Certificate Status Protocol (OCSP) + // response provided by the peer for the leaf certificate, if any. + OcspResponse []byte `protobuf:"bytes,10,opt,name=ocsp_response,json=ocspResponse,proto3" json:"ocsp_response,omitempty"` +} + +func (x *ConnectionState) Reset() { + *x = ConnectionState{} + if protoimpl.UnsafeEnabled { + mi := &file_http_http_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ConnectionState) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ConnectionState) ProtoMessage() {} + +func (x *ConnectionState) ProtoReflect() protoreflect.Message { + mi := &file_http_http_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ConnectionState.ProtoReflect.Descriptor instead. +func (*ConnectionState) Descriptor() ([]byte, []int) { + return file_http_http_proto_rawDescGZIP(), []int{4} +} + +func (x *ConnectionState) GetVersion() uint32 { + if x != nil { + return x.Version + } + return 0 +} + +func (x *ConnectionState) GetHandshakeComplete() bool { + if x != nil { + return x.HandshakeComplete + } + return false +} + +func (x *ConnectionState) GetDidResume() bool { + if x != nil { + return x.DidResume + } + return false +} + +func (x *ConnectionState) GetCipherSuite() uint32 { + if x != nil { + return x.CipherSuite + } + return 0 +} + +func (x *ConnectionState) GetNegotiatedProtocol() string { + if x != nil { + return x.NegotiatedProtocol + } + return "" +} + +func (x *ConnectionState) GetServerName() string { + if x != nil { + return x.ServerName + } + return "" +} + +func (x *ConnectionState) GetPeerCertificates() *Certificates { + if x != nil { + return x.PeerCertificates + } + return nil +} + +func (x *ConnectionState) GetVerifiedChains() []*Certificates { + if x != nil { + return x.VerifiedChains + } + return nil +} + +func (x *ConnectionState) GetSignedCertificateTimestamps() [][]byte { + if x != nil { + return x.SignedCertificateTimestamps + } + return nil +} + +func (x *ConnectionState) GetOcspResponse() []byte { + if x != nil { + return x.OcspResponse + } + return nil +} + +// Request is an http.Request see: https://pkg.go.dev/net/http#Request +type Request struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // method specifies the HTTP method (GET, POST, PUT, etc.) + Method string `protobuf:"bytes,1,opt,name=method,proto3" json:"method,omitempty"` + // url specifies either the URI being requested (for server requests) + // or the URL to access (for client requests) + Url *URL `protobuf:"bytes,2,opt,name=url,proto3" json:"url,omitempty"` + // proto is the protocol version for incoming server requests + Proto string `protobuf:"bytes,3,opt,name=proto,proto3" json:"proto,omitempty"` + // proto_major is the major version + ProtoMajor int32 `protobuf:"varint,4,opt,name=proto_major,json=protoMajor,proto3" json:"proto_major,omitempty"` + // proto_minor is the minor version + ProtoMinor int32 `protobuf:"varint,5,opt,name=proto_minor,json=protoMinor,proto3" json:"proto_minor,omitempty"` + // header contains the request header fields either received + // by the server or to be sent by the client + Header []*Element `protobuf:"bytes,6,rep,name=header,proto3" json:"header,omitempty"` + // body is the request payload in bytes + Body []byte `protobuf:"bytes,7,opt,name=body,proto3" json:"body,omitempty"` + // content_length records the length of the associated content + ContentLength int64 `protobuf:"varint,8,opt,name=content_length,json=contentLength,proto3" json:"content_length,omitempty"` + // transfer_encoding lists the transfer encodings from outermost to + // innermost + TransferEncoding []string `protobuf:"bytes,9,rep,name=transfer_encoding,json=transferEncoding,proto3" json:"transfer_encoding,omitempty"` + // host specifies the host on which the URL is sought + Host string `protobuf:"bytes,10,opt,name=host,proto3" json:"host,omitempty"` + // form contains the parsed form data, including both the URL + // field's query parameters and the PATCH, POST, or PUT form data + Form []*Element `protobuf:"bytes,11,rep,name=form,proto3" json:"form,omitempty"` + // post_form contains the parsed form data from PATCH, POST + // or PUT body parameters + PostForm []*Element `protobuf:"bytes,12,rep,name=post_form,json=postForm,proto3" json:"post_form,omitempty"` + // trailer_keys specifies additional headers that are sent after the request + TrailerKeys []string `protobuf:"bytes,13,rep,name=trailer_keys,json=trailerKeys,proto3" json:"trailer_keys,omitempty"` + // remote_addr allows HTTP servers and other software to record + // the network address that sent the request + RemoteAddr string `protobuf:"bytes,14,opt,name=remote_addr,json=remoteAddr,proto3" json:"remote_addr,omitempty"` + // request_uri is the unmodified request-target + RequestUri string `protobuf:"bytes,15,opt,name=request_uri,json=requestUri,proto3" json:"request_uri,omitempty"` + // tls connection state + Tls *ConnectionState `protobuf:"bytes,16,opt,name=tls,proto3" json:"tls,omitempty"` +} + +func (x *Request) Reset() { + *x = Request{} + if protoimpl.UnsafeEnabled { + mi := &file_http_http_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Request) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Request) ProtoMessage() {} + +func (x *Request) ProtoReflect() protoreflect.Message { + mi := &file_http_http_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Request.ProtoReflect.Descriptor instead. +func (*Request) Descriptor() ([]byte, []int) { + return file_http_http_proto_rawDescGZIP(), []int{5} +} + +func (x *Request) GetMethod() string { + if x != nil { + return x.Method + } + return "" +} + +func (x *Request) GetUrl() *URL { + if x != nil { + return x.Url + } + return nil +} + +func (x *Request) GetProto() string { + if x != nil { + return x.Proto + } + return "" +} + +func (x *Request) GetProtoMajor() int32 { + if x != nil { + return x.ProtoMajor + } + return 0 +} + +func (x *Request) GetProtoMinor() int32 { + if x != nil { + return x.ProtoMinor + } + return 0 +} + +func (x *Request) GetHeader() []*Element { + if x != nil { + return x.Header + } + return nil +} + +func (x *Request) GetBody() []byte { + if x != nil { + return x.Body + } + return nil +} + +func (x *Request) GetContentLength() int64 { + if x != nil { + return x.ContentLength + } + return 0 +} + +func (x *Request) GetTransferEncoding() []string { + if x != nil { + return x.TransferEncoding + } + return nil +} + +func (x *Request) GetHost() string { + if x != nil { + return x.Host + } + return "" +} + +func (x *Request) GetForm() []*Element { + if x != nil { + return x.Form + } + return nil +} + +func (x *Request) GetPostForm() []*Element { + if x != nil { + return x.PostForm + } + return nil +} + +func (x *Request) GetTrailerKeys() []string { + if x != nil { + return x.TrailerKeys + } + return nil +} + +func (x *Request) GetRemoteAddr() string { + if x != nil { + return x.RemoteAddr + } + return "" +} + +func (x *Request) GetRequestUri() string { + if x != nil { + return x.RequestUri + } + return "" +} + +func (x *Request) GetTls() *ConnectionState { + if x != nil { + return x.Tls + } + return nil +} + +type ResponseWriter struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // header returns the header map that will be sent by + // WriteHeader. + Header []*Element `protobuf:"bytes,1,rep,name=header,proto3" json:"header,omitempty"` + // server_addr is the address of the gRPC server hosting the Writer service + ServerAddr string `protobuf:"bytes,2,opt,name=server_addr,json=serverAddr,proto3" json:"server_addr,omitempty"` +} + +func (x *ResponseWriter) Reset() { + *x = ResponseWriter{} + if protoimpl.UnsafeEnabled { + mi := &file_http_http_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ResponseWriter) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ResponseWriter) ProtoMessage() {} + +func (x *ResponseWriter) ProtoReflect() protoreflect.Message { + mi := &file_http_http_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ResponseWriter.ProtoReflect.Descriptor instead. +func (*ResponseWriter) Descriptor() ([]byte, []int) { + return file_http_http_proto_rawDescGZIP(), []int{6} +} + +func (x *ResponseWriter) GetHeader() []*Element { + if x != nil { + return x.Header + } + return nil +} + +func (x *ResponseWriter) GetServerAddr() string { + if x != nil { + return x.ServerAddr + } + return "" +} + +type HTTPRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // response_writer is used by an HTTP handler to construct an HTTP response + ResponseWriter *ResponseWriter `protobuf:"bytes,1,opt,name=response_writer,json=responseWriter,proto3" json:"response_writer,omitempty"` + // request is an http request + Request *Request `protobuf:"bytes,2,opt,name=request,proto3" json:"request,omitempty"` +} + +func (x *HTTPRequest) Reset() { + *x = HTTPRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_http_http_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *HTTPRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*HTTPRequest) ProtoMessage() {} + +func (x *HTTPRequest) ProtoReflect() protoreflect.Message { + mi := &file_http_http_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use HTTPRequest.ProtoReflect.Descriptor instead. +func (*HTTPRequest) Descriptor() ([]byte, []int) { + return file_http_http_proto_rawDescGZIP(), []int{7} +} + +func (x *HTTPRequest) GetResponseWriter() *ResponseWriter { + if x != nil { + return x.ResponseWriter + } + return nil +} + +func (x *HTTPRequest) GetRequest() *Request { + if x != nil { + return x.Request + } + return nil +} + +type HandleSimpleHTTPRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // method specifies the HTTP method (GET, POST, PUT, etc.) + Method string `protobuf:"bytes,1,opt,name=method,proto3" json:"method,omitempty"` + // url specifies either the URI being requested + Url string `protobuf:"bytes,2,opt,name=url,proto3" json:"url,omitempty"` + // headers contains the request header fields either received + // by the server or to be sent by the client + Headers []*Element `protobuf:"bytes,3,rep,name=headers,proto3" json:"headers,omitempty"` + // body is the request payload in bytes + Body []byte `protobuf:"bytes,4,opt,name=body,proto3" json:"body,omitempty"` +} + +func (x *HandleSimpleHTTPRequest) Reset() { + *x = HandleSimpleHTTPRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_http_http_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *HandleSimpleHTTPRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*HandleSimpleHTTPRequest) ProtoMessage() {} + +func (x *HandleSimpleHTTPRequest) ProtoReflect() protoreflect.Message { + mi := &file_http_http_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use HandleSimpleHTTPRequest.ProtoReflect.Descriptor instead. +func (*HandleSimpleHTTPRequest) Descriptor() ([]byte, []int) { + return file_http_http_proto_rawDescGZIP(), []int{8} +} + +func (x *HandleSimpleHTTPRequest) GetMethod() string { + if x != nil { + return x.Method + } + return "" +} + +func (x *HandleSimpleHTTPRequest) GetUrl() string { + if x != nil { + return x.Url + } + return "" +} + +func (x *HandleSimpleHTTPRequest) GetHeaders() []*Element { + if x != nil { + return x.Headers + } + return nil +} + +func (x *HandleSimpleHTTPRequest) GetBody() []byte { + if x != nil { + return x.Body + } + return nil +} + +type HandleSimpleHTTPResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // code is the response code + Code int32 `protobuf:"varint,1,opt,name=code,proto3" json:"code,omitempty"` + // headers contains the request header fields either received + // by the server or to be sent by the client + Headers []*Element `protobuf:"bytes,2,rep,name=headers,proto3" json:"headers,omitempty"` + // body is the response payload in bytes + Body []byte `protobuf:"bytes,3,opt,name=body,proto3" json:"body,omitempty"` +} + +func (x *HandleSimpleHTTPResponse) Reset() { + *x = HandleSimpleHTTPResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_http_http_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *HandleSimpleHTTPResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*HandleSimpleHTTPResponse) ProtoMessage() {} + +func (x *HandleSimpleHTTPResponse) ProtoReflect() protoreflect.Message { + mi := &file_http_http_proto_msgTypes[9] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use HandleSimpleHTTPResponse.ProtoReflect.Descriptor instead. +func (*HandleSimpleHTTPResponse) Descriptor() ([]byte, []int) { + return file_http_http_proto_rawDescGZIP(), []int{9} +} + +func (x *HandleSimpleHTTPResponse) GetCode() int32 { + if x != nil { + return x.Code + } + return 0 +} + +func (x *HandleSimpleHTTPResponse) GetHeaders() []*Element { + if x != nil { + return x.Headers + } + return nil +} + +func (x *HandleSimpleHTTPResponse) GetBody() []byte { + if x != nil { + return x.Body + } + return nil +} + +var File_http_http_proto protoreflect.FileDescriptor + +var file_http_http_proto_rawDesc = []byte{ + 0x0a, 0x0f, 0x68, 0x74, 0x74, 0x70, 0x2f, 0x68, 0x74, 0x74, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x12, 0x04, 0x68, 0x74, 0x74, 0x70, 0x1a, 0x1b, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xf6, 0x01, 0x0a, 0x03, 0x55, 0x52, 0x4c, 0x12, 0x16, 0x0a, 0x06, + 0x73, 0x63, 0x68, 0x65, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x63, + 0x68, 0x65, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x70, 0x61, 0x71, 0x75, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6f, 0x70, 0x61, 0x71, 0x75, 0x65, 0x12, 0x22, 0x0a, 0x04, + 0x75, 0x73, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x68, 0x74, 0x74, + 0x70, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x69, 0x6e, 0x66, 0x6f, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, + 0x12, 0x12, 0x0a, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x68, 0x6f, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x19, 0x0a, 0x08, 0x72, 0x61, 0x77, 0x5f, + 0x70, 0x61, 0x74, 0x68, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x72, 0x61, 0x77, 0x50, + 0x61, 0x74, 0x68, 0x12, 0x1f, 0x0a, 0x0b, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x5f, 0x71, 0x75, 0x65, + 0x72, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x51, + 0x75, 0x65, 0x72, 0x79, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x61, 0x77, 0x5f, 0x71, 0x75, 0x65, 0x72, + 0x79, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x61, 0x77, 0x51, 0x75, 0x65, 0x72, + 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x09, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x22, 0x65, 0x0a, + 0x08, 0x55, 0x73, 0x65, 0x72, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, + 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, + 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, + 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, + 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x5f, 0x73, 0x65, + 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, + 0x64, 0x53, 0x65, 0x74, 0x22, 0x33, 0x0a, 0x07, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x12, + 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, + 0x79, 0x12, 0x16, 0x0a, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, + 0x09, 0x52, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x22, 0x22, 0x0a, 0x0c, 0x43, 0x65, 0x72, + 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x65, 0x72, + 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x04, 0x63, 0x65, 0x72, 0x74, 0x22, 0xd5, 0x03, + 0x0a, 0x0f, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x2d, 0x0a, 0x12, 0x68, + 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x68, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, + 0x6b, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x64, 0x69, + 0x64, 0x5f, 0x72, 0x65, 0x73, 0x75, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, + 0x64, 0x69, 0x64, 0x52, 0x65, 0x73, 0x75, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x69, 0x70, + 0x68, 0x65, 0x72, 0x5f, 0x73, 0x75, 0x69, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x0b, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x53, 0x75, 0x69, 0x74, 0x65, 0x12, 0x2f, 0x0a, 0x13, + 0x6e, 0x65, 0x67, 0x6f, 0x74, 0x69, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x63, 0x6f, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x6e, 0x65, 0x67, 0x6f, 0x74, + 0x69, 0x61, 0x74, 0x65, 0x64, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x1f, 0x0a, + 0x0b, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0a, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x3f, + 0x0a, 0x11, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, + 0x74, 0x65, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x68, 0x74, 0x74, 0x70, + 0x2e, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x52, 0x10, 0x70, + 0x65, 0x65, 0x72, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x12, + 0x3b, 0x0a, 0x0f, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x65, 0x64, 0x5f, 0x63, 0x68, 0x61, 0x69, + 0x6e, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x2e, + 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x52, 0x0e, 0x76, 0x65, + 0x72, 0x69, 0x66, 0x69, 0x65, 0x64, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x73, 0x12, 0x42, 0x0a, 0x1d, + 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, + 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x73, 0x18, 0x09, 0x20, + 0x03, 0x28, 0x0c, 0x52, 0x1b, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x43, 0x65, 0x72, 0x74, 0x69, + 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x73, + 0x12, 0x23, 0x0a, 0x0d, 0x6f, 0x63, 0x73, 0x70, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x6f, 0x63, 0x73, 0x70, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x96, 0x04, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x1b, 0x0a, 0x03, 0x75, 0x72, 0x6c, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x2e, 0x55, 0x52, + 0x4c, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1f, 0x0a, 0x0b, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x5f, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x4d, 0x61, 0x6a, 0x6f, 0x72, 0x12, 0x1f, 0x0a, + 0x0b, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x5f, 0x6d, 0x69, 0x6e, 0x6f, 0x72, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x05, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x4d, 0x69, 0x6e, 0x6f, 0x72, 0x12, 0x25, + 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0d, + 0x2e, 0x68, 0x74, 0x74, 0x70, 0x2e, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x06, 0x68, + 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x07, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x12, 0x25, 0x0a, 0x0e, 0x63, 0x6f, 0x6e, + 0x74, 0x65, 0x6e, 0x74, 0x5f, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x18, 0x08, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, + 0x12, 0x2b, 0x0a, 0x11, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x5f, 0x65, 0x6e, 0x63, + 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x18, 0x09, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x74, 0x72, 0x61, + 0x6e, 0x73, 0x66, 0x65, 0x72, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x12, 0x0a, + 0x04, 0x68, 0x6f, 0x73, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x6f, 0x73, + 0x74, 0x12, 0x21, 0x0a, 0x04, 0x66, 0x6f, 0x72, 0x6d, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x0d, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x2e, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x04, + 0x66, 0x6f, 0x72, 0x6d, 0x12, 0x2a, 0x0a, 0x09, 0x70, 0x6f, 0x73, 0x74, 0x5f, 0x66, 0x6f, 0x72, + 0x6d, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x2e, 0x45, + 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x08, 0x70, 0x6f, 0x73, 0x74, 0x46, 0x6f, 0x72, 0x6d, + 0x12, 0x21, 0x0a, 0x0c, 0x74, 0x72, 0x61, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x6b, 0x65, 0x79, 0x73, + 0x18, 0x0d, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x74, 0x72, 0x61, 0x69, 0x6c, 0x65, 0x72, 0x4b, + 0x65, 0x79, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x61, 0x64, + 0x64, 0x72, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, + 0x41, 0x64, 0x64, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, + 0x75, 0x72, 0x69, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x55, 0x72, 0x69, 0x12, 0x27, 0x0a, 0x03, 0x74, 0x6c, 0x73, 0x18, 0x10, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x03, 0x74, 0x6c, 0x73, 0x22, 0x58, + 0x0a, 0x0e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x57, 0x72, 0x69, 0x74, 0x65, 0x72, + 0x12, 0x25, 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x0d, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x2e, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x52, + 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x41, 0x64, 0x64, 0x72, 0x22, 0x75, 0x0a, 0x0b, 0x48, 0x54, 0x54, 0x50, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3d, 0x0a, 0x0f, 0x72, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x14, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x57, 0x72, 0x69, 0x74, 0x65, 0x72, 0x52, 0x0e, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x57, 0x72, 0x69, 0x74, 0x65, 0x72, 0x12, 0x27, 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x2e, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, + 0x80, 0x01, 0x0a, 0x17, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, + 0x48, 0x54, 0x54, 0x50, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6d, + 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6d, 0x65, 0x74, + 0x68, 0x6f, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x27, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, + 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x2e, 0x45, 0x6c, + 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x12, 0x12, + 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x62, 0x6f, + 0x64, 0x79, 0x22, 0x6b, 0x0a, 0x18, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x53, 0x69, 0x6d, 0x70, + 0x6c, 0x65, 0x48, 0x54, 0x54, 0x50, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, + 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x63, 0x6f, + 0x64, 0x65, 0x12, 0x27, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x2e, 0x45, 0x6c, 0x65, 0x6d, 0x65, + 0x6e, 0x74, 0x52, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x62, + 0x6f, 0x64, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x32, + 0x8a, 0x01, 0x0a, 0x04, 0x48, 0x54, 0x54, 0x50, 0x12, 0x33, 0x0a, 0x06, 0x48, 0x61, 0x6e, 0x64, + 0x6c, 0x65, 0x12, 0x11, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x4d, 0x0a, + 0x0c, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x12, 0x1d, 0x2e, + 0x68, 0x74, 0x74, 0x70, 0x2e, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x53, 0x69, 0x6d, 0x70, 0x6c, + 0x65, 0x48, 0x54, 0x54, 0x50, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x68, + 0x74, 0x74, 0x70, 0x2e, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, + 0x48, 0x54, 0x54, 0x50, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x32, 0x5a, 0x30, + 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x69, + 0x64, 0x65, 0x72, 0x69, 0x74, 0x64, 0x6f, 0x6e, 0x65, 0x2f, 0x6c, 0x61, 0x6e, 0x64, 0x73, 0x6c, + 0x69, 0x64, 0x65, 0x76, 0x6d, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x68, 0x74, 0x74, 0x70, + 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_http_http_proto_rawDescOnce sync.Once + file_http_http_proto_rawDescData = file_http_http_proto_rawDesc +) + +func file_http_http_proto_rawDescGZIP() []byte { + file_http_http_proto_rawDescOnce.Do(func() { + file_http_http_proto_rawDescData = protoimpl.X.CompressGZIP(file_http_http_proto_rawDescData) + }) + return file_http_http_proto_rawDescData +} + +var file_http_http_proto_msgTypes = make([]protoimpl.MessageInfo, 10) +var file_http_http_proto_goTypes = []interface{}{ + (*URL)(nil), // 0: http.URL + (*Userinfo)(nil), // 1: http.Userinfo + (*Element)(nil), // 2: http.Element + (*Certificates)(nil), // 3: http.Certificates + (*ConnectionState)(nil), // 4: http.ConnectionState + (*Request)(nil), // 5: http.Request + (*ResponseWriter)(nil), // 6: http.ResponseWriter + (*HTTPRequest)(nil), // 7: http.HTTPRequest + (*HandleSimpleHTTPRequest)(nil), // 8: http.HandleSimpleHTTPRequest + (*HandleSimpleHTTPResponse)(nil), // 9: http.HandleSimpleHTTPResponse + (*emptypb.Empty)(nil), // 10: google.protobuf.Empty +} +var file_http_http_proto_depIdxs = []int32{ + 1, // 0: http.URL.user:type_name -> http.Userinfo + 3, // 1: http.ConnectionState.peer_certificates:type_name -> http.Certificates + 3, // 2: http.ConnectionState.verified_chains:type_name -> http.Certificates + 0, // 3: http.Request.url:type_name -> http.URL + 2, // 4: http.Request.header:type_name -> http.Element + 2, // 5: http.Request.form:type_name -> http.Element + 2, // 6: http.Request.post_form:type_name -> http.Element + 4, // 7: http.Request.tls:type_name -> http.ConnectionState + 2, // 8: http.ResponseWriter.header:type_name -> http.Element + 6, // 9: http.HTTPRequest.response_writer:type_name -> http.ResponseWriter + 5, // 10: http.HTTPRequest.request:type_name -> http.Request + 2, // 11: http.HandleSimpleHTTPRequest.headers:type_name -> http.Element + 2, // 12: http.HandleSimpleHTTPResponse.headers:type_name -> http.Element + 7, // 13: http.HTTP.Handle:input_type -> http.HTTPRequest + 8, // 14: http.HTTP.HandleSimple:input_type -> http.HandleSimpleHTTPRequest + 10, // 15: http.HTTP.Handle:output_type -> google.protobuf.Empty + 9, // 16: http.HTTP.HandleSimple:output_type -> http.HandleSimpleHTTPResponse + 15, // [15:17] is the sub-list for method output_type + 13, // [13:15] is the sub-list for method input_type + 13, // [13:13] is the sub-list for extension type_name + 13, // [13:13] is the sub-list for extension extendee + 0, // [0:13] is the sub-list for field type_name +} + +func init() { file_http_http_proto_init() } +func file_http_http_proto_init() { + if File_http_http_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_http_http_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*URL); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_http_http_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Userinfo); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_http_http_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Element); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_http_http_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Certificates); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_http_http_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ConnectionState); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_http_http_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Request); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_http_http_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ResponseWriter); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_http_http_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*HTTPRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_http_http_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*HandleSimpleHTTPRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_http_http_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*HandleSimpleHTTPResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_http_http_proto_rawDesc, + NumEnums: 0, + NumMessages: 10, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_http_http_proto_goTypes, + DependencyIndexes: file_http_http_proto_depIdxs, + MessageInfos: file_http_http_proto_msgTypes, + }.Build() + File_http_http_proto = out.File + file_http_http_proto_rawDesc = nil + file_http_http_proto_goTypes = nil + file_http_http_proto_depIdxs = nil +} diff --git a/proto/http/http.proto b/proto/http/http.proto new file mode 100644 index 0000000..163bfb2 --- /dev/null +++ b/proto/http/http.proto @@ -0,0 +1,172 @@ +syntax = "proto3"; + +package http; + +import "google/protobuf/empty.proto"; + +option go_package = "github.com/consideritdone/landslidevm/proto/http"; + +service HTTP { + // Handle wraps http1 over http2 and provides support for websockets by implementing + // net conn and responsewriter in http2. + rpc Handle(HTTPRequest) returns (google.protobuf.Empty); + // HandleSimple wraps http1 requests over http2 similar to Handle but only passes headers + // and body bytes. Because the request and response are single protos with no inline + // gRPC servers the CPU cost as well as file descriptor overhead is less + // (no additional goroutines). + rpc HandleSimple(HandleSimpleHTTPRequest) returns (HandleSimpleHTTPResponse); +} + +// URL is a net.URL see: https://pkg.go.dev/net/url#URL +message URL { + // scheme is the url scheme name + string scheme = 1; + // opaque is encoded opaque data + string opaque = 2; + // user is username and password information + Userinfo user = 3; + // host can be in the format host or host:port + string host = 4; + // path (relative paths may omit leading slash) + string path = 5; + // raw_path is encoded path hint (see EscapedPath method) + string raw_path = 6; + // force is append a query ('?') even if RawQuery is empty + bool force_query = 7; + // raw_query is encoded query values, without '?' + string raw_query = 8; + // fragment is fragment for references, without '#' + string fragment = 9; +} + +// UserInfo is net.Userinfo see: https://pkg.go.dev/net/url#Userinfo +message Userinfo { + // username is the username for the user + string username = 1; + // password is the password for the user + string password = 2; + // password_set is a boolean which is true if the passord is set + bool password_set = 3; +} + +message Element { + // key is a element key in a key value pair + string key = 1; + // values are a list of strings coresponding to the key + repeated string values = 2; +} + +message Certificates { + // cert is the certificate body + repeated bytes cert = 1; +} + +// ConnectionState is tls.ConnectionState see: https://pkg.go.dev/crypto/tls#ConnectionState +message ConnectionState { + // version is the TLS version used by the connection (e.g. VersionTLS12) + uint32 version = 1; + // handshake_complete is true if the handshake has concluded + bool handshake_complete = 2; + // did_resume is true if this connection was successfully resumed from a + // previous session with a session ticket or similar mechanism + bool did_resume = 3; + // cipher_suite is the cipher suite negotiated for the connection + uint32 cipher_suite = 4; + // negotiated_protocol is the application protocol negotiated with ALPN + string negotiated_protocol = 5; + // server_name is the value of the Server Name Indication extension sent by + // the client + string server_name = 6; + // peer_certificates are the parsed certificates sent by the peer, in the + // order in which they were sent + Certificates peer_certificates = 7; + // verified_chains is a list of one or more chains where the first element is + // PeerCertificates[0] and the last element is from Config.RootCAs (on the + // client side) or Config.ClientCAs (on the server side). + repeated Certificates verified_chains = 8; + // signed_certificate_timestamps is a list of SCTs provided by the peer + // through the TLS handshake for the leaf certificate, if any + repeated bytes signed_certificate_timestamps = 9; + // ocsp_response is a stapled Online Certificate Status Protocol (OCSP) + // response provided by the peer for the leaf certificate, if any. + bytes ocsp_response = 10; +} + +// Request is an http.Request see: https://pkg.go.dev/net/http#Request +message Request { + // method specifies the HTTP method (GET, POST, PUT, etc.) + string method = 1; + // url specifies either the URI being requested (for server requests) + // or the URL to access (for client requests) + URL url = 2; + // proto is the protocol version for incoming server requests + string proto = 3; + // proto_major is the major version + int32 proto_major = 4; + // proto_minor is the minor version + int32 proto_minor = 5; + // header contains the request header fields either received + // by the server or to be sent by the client + repeated Element header = 6; + // body is the request payload in bytes + bytes body = 7; + // content_length records the length of the associated content + int64 content_length = 8; + // transfer_encoding lists the transfer encodings from outermost to + // innermost + repeated string transfer_encoding = 9; + // host specifies the host on which the URL is sought + string host = 10; + // form contains the parsed form data, including both the URL + // field's query parameters and the PATCH, POST, or PUT form data + repeated Element form = 11; + // post_form contains the parsed form data from PATCH, POST + // or PUT body parameters + repeated Element post_form = 12; + // trailer_keys specifies additional headers that are sent after the request + repeated string trailer_keys = 13; + // remote_addr allows HTTP servers and other software to record + // the network address that sent the request + string remote_addr = 14; + // request_uri is the unmodified request-target + string request_uri = 15; + // tls connection state + ConnectionState tls = 16; +} + +message ResponseWriter { + // header returns the header map that will be sent by + // WriteHeader. + repeated Element header = 1; + // server_addr is the address of the gRPC server hosting the Writer service + string server_addr = 2; +} + +message HTTPRequest { + // response_writer is used by an HTTP handler to construct an HTTP response + ResponseWriter response_writer = 1; + // request is an http request + Request request = 2; +} + +message HandleSimpleHTTPRequest { + // method specifies the HTTP method (GET, POST, PUT, etc.) + string method = 1; + // url specifies either the URI being requested + string url = 2; + // headers contains the request header fields either received + // by the server or to be sent by the client + repeated Element headers = 3; + // body is the request payload in bytes + bytes body = 4; +} + +message HandleSimpleHTTPResponse { + // code is the response code + int32 code = 1; + // headers contains the request header fields either received + // by the server or to be sent by the client + repeated Element headers = 2; + // body is the response payload in bytes + bytes body = 3; +} diff --git a/proto/http/http_grpc.pb.go b/proto/http/http_grpc.pb.go new file mode 100644 index 0000000..5fd94b0 --- /dev/null +++ b/proto/http/http_grpc.pb.go @@ -0,0 +1,157 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.3.0 +// - protoc (unknown) +// source: http/http.proto + +package http + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + emptypb "google.golang.org/protobuf/types/known/emptypb" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +const ( + HTTP_Handle_FullMethodName = "/http.HTTP/Handle" + HTTP_HandleSimple_FullMethodName = "/http.HTTP/HandleSimple" +) + +// HTTPClient is the client API for HTTP service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type HTTPClient interface { + // Handle wraps http1 over http2 and provides support for websockets by implementing + // net conn and responsewriter in http2. + Handle(ctx context.Context, in *HTTPRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) + // HandleSimple wraps http1 requests over http2 similar to Handle but only passes headers + // and body bytes. Because the request and response are single protos with no inline + // gRPC servers the CPU cost as well as file descriptor overhead is less + // (no additional goroutines). + HandleSimple(ctx context.Context, in *HandleSimpleHTTPRequest, opts ...grpc.CallOption) (*HandleSimpleHTTPResponse, error) +} + +type hTTPClient struct { + cc grpc.ClientConnInterface +} + +func NewHTTPClient(cc grpc.ClientConnInterface) HTTPClient { + return &hTTPClient{cc} +} + +func (c *hTTPClient) Handle(ctx context.Context, in *HTTPRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { + out := new(emptypb.Empty) + err := c.cc.Invoke(ctx, HTTP_Handle_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *hTTPClient) HandleSimple(ctx context.Context, in *HandleSimpleHTTPRequest, opts ...grpc.CallOption) (*HandleSimpleHTTPResponse, error) { + out := new(HandleSimpleHTTPResponse) + err := c.cc.Invoke(ctx, HTTP_HandleSimple_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// HTTPServer is the server API for HTTP service. +// All implementations should embed UnimplementedHTTPServer +// for forward compatibility +type HTTPServer interface { + // Handle wraps http1 over http2 and provides support for websockets by implementing + // net conn and responsewriter in http2. + Handle(context.Context, *HTTPRequest) (*emptypb.Empty, error) + // HandleSimple wraps http1 requests over http2 similar to Handle but only passes headers + // and body bytes. Because the request and response are single protos with no inline + // gRPC servers the CPU cost as well as file descriptor overhead is less + // (no additional goroutines). + HandleSimple(context.Context, *HandleSimpleHTTPRequest) (*HandleSimpleHTTPResponse, error) +} + +// UnimplementedHTTPServer should be embedded to have forward compatible implementations. +type UnimplementedHTTPServer struct { +} + +func (UnimplementedHTTPServer) Handle(context.Context, *HTTPRequest) (*emptypb.Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method Handle not implemented") +} +func (UnimplementedHTTPServer) HandleSimple(context.Context, *HandleSimpleHTTPRequest) (*HandleSimpleHTTPResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method HandleSimple not implemented") +} + +// UnsafeHTTPServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to HTTPServer will +// result in compilation errors. +type UnsafeHTTPServer interface { + mustEmbedUnimplementedHTTPServer() +} + +func RegisterHTTPServer(s grpc.ServiceRegistrar, srv HTTPServer) { + s.RegisterService(&HTTP_ServiceDesc, srv) +} + +func _HTTP_Handle_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(HTTPRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(HTTPServer).Handle(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: HTTP_Handle_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(HTTPServer).Handle(ctx, req.(*HTTPRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _HTTP_HandleSimple_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(HandleSimpleHTTPRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(HTTPServer).HandleSimple(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: HTTP_HandleSimple_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(HTTPServer).HandleSimple(ctx, req.(*HandleSimpleHTTPRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// HTTP_ServiceDesc is the grpc.ServiceDesc for HTTP service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var HTTP_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "http.HTTP", + HandlerType: (*HTTPServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Handle", + Handler: _HTTP_Handle_Handler, + }, + { + MethodName: "HandleSimple", + Handler: _HTTP_HandleSimple_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "http/http.proto", +} diff --git a/proto/http/responsewriter/responsewriter.pb.go b/proto/http/responsewriter/responsewriter.pb.go new file mode 100644 index 0000000..8e1e708 --- /dev/null +++ b/proto/http/responsewriter/responsewriter.pb.go @@ -0,0 +1,518 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.33.0 +// protoc (unknown) +// source: http/responsewriter/responsewriter.proto + +package responsewriter + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + emptypb "google.golang.org/protobuf/types/known/emptypb" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type Header struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // key is a element key in a key value pair + Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` + // values are a list of strings coresponding to the key + Values []string `protobuf:"bytes,2,rep,name=values,proto3" json:"values,omitempty"` +} + +func (x *Header) Reset() { + *x = Header{} + if protoimpl.UnsafeEnabled { + mi := &file_http_responsewriter_responsewriter_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Header) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Header) ProtoMessage() {} + +func (x *Header) ProtoReflect() protoreflect.Message { + mi := &file_http_responsewriter_responsewriter_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Header.ProtoReflect.Descriptor instead. +func (*Header) Descriptor() ([]byte, []int) { + return file_http_responsewriter_responsewriter_proto_rawDescGZIP(), []int{0} +} + +func (x *Header) GetKey() string { + if x != nil { + return x.Key + } + return "" +} + +func (x *Header) GetValues() []string { + if x != nil { + return x.Values + } + return nil +} + +type WriteRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // headers represents the key-value pairs in an HTTP header + Headers []*Header `protobuf:"bytes,1,rep,name=headers,proto3" json:"headers,omitempty"` + // payload is the write request in bytes + Payload []byte `protobuf:"bytes,2,opt,name=payload,proto3" json:"payload,omitempty"` +} + +func (x *WriteRequest) Reset() { + *x = WriteRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_http_responsewriter_responsewriter_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *WriteRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WriteRequest) ProtoMessage() {} + +func (x *WriteRequest) ProtoReflect() protoreflect.Message { + mi := &file_http_responsewriter_responsewriter_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WriteRequest.ProtoReflect.Descriptor instead. +func (*WriteRequest) Descriptor() ([]byte, []int) { + return file_http_responsewriter_responsewriter_proto_rawDescGZIP(), []int{1} +} + +func (x *WriteRequest) GetHeaders() []*Header { + if x != nil { + return x.Headers + } + return nil +} + +func (x *WriteRequest) GetPayload() []byte { + if x != nil { + return x.Payload + } + return nil +} + +type WriteResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // written is the number of bytes written in body + Written int32 `protobuf:"varint,1,opt,name=written,proto3" json:"written,omitempty"` +} + +func (x *WriteResponse) Reset() { + *x = WriteResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_http_responsewriter_responsewriter_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *WriteResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WriteResponse) ProtoMessage() {} + +func (x *WriteResponse) ProtoReflect() protoreflect.Message { + mi := &file_http_responsewriter_responsewriter_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WriteResponse.ProtoReflect.Descriptor instead. +func (*WriteResponse) Descriptor() ([]byte, []int) { + return file_http_responsewriter_responsewriter_proto_rawDescGZIP(), []int{2} +} + +func (x *WriteResponse) GetWritten() int32 { + if x != nil { + return x.Written + } + return 0 +} + +type WriteHeaderRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // headers represents the key-value pairs in an HTTP header + Headers []*Header `protobuf:"bytes,1,rep,name=headers,proto3" json:"headers,omitempty"` + // status_code must be a valid HTTP 1xx-5xx status code + StatusCode int32 `protobuf:"varint,2,opt,name=status_code,json=statusCode,proto3" json:"status_code,omitempty"` +} + +func (x *WriteHeaderRequest) Reset() { + *x = WriteHeaderRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_http_responsewriter_responsewriter_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *WriteHeaderRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WriteHeaderRequest) ProtoMessage() {} + +func (x *WriteHeaderRequest) ProtoReflect() protoreflect.Message { + mi := &file_http_responsewriter_responsewriter_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WriteHeaderRequest.ProtoReflect.Descriptor instead. +func (*WriteHeaderRequest) Descriptor() ([]byte, []int) { + return file_http_responsewriter_responsewriter_proto_rawDescGZIP(), []int{3} +} + +func (x *WriteHeaderRequest) GetHeaders() []*Header { + if x != nil { + return x.Headers + } + return nil +} + +func (x *WriteHeaderRequest) GetStatusCode() int32 { + if x != nil { + return x.StatusCode + } + return 0 +} + +type HijackResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // local_network is the name of the network (for example, "tcp", "udp") + LocalNetwork string `protobuf:"bytes,1,opt,name=local_network,json=localNetwork,proto3" json:"local_network,omitempty"` + // local_string is string form of address + LocalString string `protobuf:"bytes,2,opt,name=local_string,json=localString,proto3" json:"local_string,omitempty"` + // remote_network is the name of the network (for example, "tcp", "udp") + RemoteNetwork string `protobuf:"bytes,3,opt,name=remote_network,json=remoteNetwork,proto3" json:"remote_network,omitempty"` + // remote_string is string form of address + RemoteString string `protobuf:"bytes,4,opt,name=remote_string,json=remoteString,proto3" json:"remote_string,omitempty"` + // server_addr is the address of the gRPC server serving the Conn, Reader + // and Writer services which facilitate Hijacking + ServerAddr string `protobuf:"bytes,5,opt,name=server_addr,json=serverAddr,proto3" json:"server_addr,omitempty"` +} + +func (x *HijackResponse) Reset() { + *x = HijackResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_http_responsewriter_responsewriter_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *HijackResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*HijackResponse) ProtoMessage() {} + +func (x *HijackResponse) ProtoReflect() protoreflect.Message { + mi := &file_http_responsewriter_responsewriter_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use HijackResponse.ProtoReflect.Descriptor instead. +func (*HijackResponse) Descriptor() ([]byte, []int) { + return file_http_responsewriter_responsewriter_proto_rawDescGZIP(), []int{4} +} + +func (x *HijackResponse) GetLocalNetwork() string { + if x != nil { + return x.LocalNetwork + } + return "" +} + +func (x *HijackResponse) GetLocalString() string { + if x != nil { + return x.LocalString + } + return "" +} + +func (x *HijackResponse) GetRemoteNetwork() string { + if x != nil { + return x.RemoteNetwork + } + return "" +} + +func (x *HijackResponse) GetRemoteString() string { + if x != nil { + return x.RemoteString + } + return "" +} + +func (x *HijackResponse) GetServerAddr() string { + if x != nil { + return x.ServerAddr + } + return "" +} + +var File_http_responsewriter_responsewriter_proto protoreflect.FileDescriptor + +var file_http_responsewriter_responsewriter_proto_rawDesc = []byte{ + 0x0a, 0x28, 0x68, 0x74, 0x74, 0x70, 0x2f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x77, + 0x72, 0x69, 0x74, 0x65, 0x72, 0x2f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x77, 0x72, + 0x69, 0x74, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x13, 0x68, 0x74, 0x74, 0x70, + 0x2e, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x77, 0x72, 0x69, 0x74, 0x65, 0x72, 0x1a, + 0x1b, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2f, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x32, 0x0a, 0x06, + 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, + 0x22, 0x5f, 0x0a, 0x0c, 0x57, 0x72, 0x69, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x35, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x1b, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x2e, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x77, 0x72, 0x69, 0x74, 0x65, 0x72, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x07, + 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, + 0x61, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, + 0x64, 0x22, 0x29, 0x0a, 0x0d, 0x57, 0x72, 0x69, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x77, 0x72, 0x69, 0x74, 0x74, 0x65, 0x6e, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x05, 0x52, 0x07, 0x77, 0x72, 0x69, 0x74, 0x74, 0x65, 0x6e, 0x22, 0x6c, 0x0a, 0x12, + 0x57, 0x72, 0x69, 0x74, 0x65, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x35, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x2e, 0x72, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x77, 0x72, 0x69, 0x74, 0x65, 0x72, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, + 0x52, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, + 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x43, 0x6f, 0x64, 0x65, 0x22, 0xc5, 0x01, 0x0a, 0x0e, 0x48, + 0x69, 0x6a, 0x61, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x23, 0x0a, + 0x0d, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x4e, 0x65, 0x74, 0x77, 0x6f, + 0x72, 0x6b, 0x12, 0x21, 0x0a, 0x0c, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x73, 0x74, 0x72, 0x69, + 0x6e, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x53, + 0x74, 0x72, 0x69, 0x6e, 0x67, 0x12, 0x25, 0x0a, 0x0e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, + 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x72, + 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x23, 0x0a, 0x0d, + 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0c, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x53, 0x74, 0x72, 0x69, 0x6e, + 0x67, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x61, 0x64, 0x64, 0x72, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x41, 0x64, + 0x64, 0x72, 0x32, 0xa8, 0x02, 0x0a, 0x06, 0x57, 0x72, 0x69, 0x74, 0x65, 0x72, 0x12, 0x4e, 0x0a, + 0x05, 0x57, 0x72, 0x69, 0x74, 0x65, 0x12, 0x21, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x2e, 0x72, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x77, 0x72, 0x69, 0x74, 0x65, 0x72, 0x2e, 0x57, 0x72, 0x69, + 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x68, 0x74, 0x74, 0x70, + 0x2e, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x77, 0x72, 0x69, 0x74, 0x65, 0x72, 0x2e, + 0x57, 0x72, 0x69, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4e, 0x0a, + 0x0b, 0x57, 0x72, 0x69, 0x74, 0x65, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x27, 0x2e, 0x68, + 0x74, 0x74, 0x70, 0x2e, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x77, 0x72, 0x69, 0x74, + 0x65, 0x72, 0x2e, 0x57, 0x72, 0x69, 0x74, 0x65, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x37, 0x0a, + 0x05, 0x46, 0x6c, 0x75, 0x73, 0x68, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x16, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x45, 0x0a, 0x06, 0x48, 0x69, 0x6a, 0x61, 0x63, 0x6b, + 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x23, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x2e, + 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x77, 0x72, 0x69, 0x74, 0x65, 0x72, 0x2e, 0x48, + 0x69, 0x6a, 0x61, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x41, 0x5a, + 0x3f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x6f, 0x6e, 0x73, + 0x69, 0x64, 0x65, 0x72, 0x69, 0x74, 0x64, 0x6f, 0x6e, 0x65, 0x2f, 0x6c, 0x61, 0x6e, 0x64, 0x73, + 0x6c, 0x69, 0x64, 0x65, 0x76, 0x6d, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x68, 0x74, 0x74, + 0x70, 0x2f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x77, 0x72, 0x69, 0x74, 0x65, 0x72, + 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_http_responsewriter_responsewriter_proto_rawDescOnce sync.Once + file_http_responsewriter_responsewriter_proto_rawDescData = file_http_responsewriter_responsewriter_proto_rawDesc +) + +func file_http_responsewriter_responsewriter_proto_rawDescGZIP() []byte { + file_http_responsewriter_responsewriter_proto_rawDescOnce.Do(func() { + file_http_responsewriter_responsewriter_proto_rawDescData = protoimpl.X.CompressGZIP(file_http_responsewriter_responsewriter_proto_rawDescData) + }) + return file_http_responsewriter_responsewriter_proto_rawDescData +} + +var file_http_responsewriter_responsewriter_proto_msgTypes = make([]protoimpl.MessageInfo, 5) +var file_http_responsewriter_responsewriter_proto_goTypes = []interface{}{ + (*Header)(nil), // 0: http.responsewriter.Header + (*WriteRequest)(nil), // 1: http.responsewriter.WriteRequest + (*WriteResponse)(nil), // 2: http.responsewriter.WriteResponse + (*WriteHeaderRequest)(nil), // 3: http.responsewriter.WriteHeaderRequest + (*HijackResponse)(nil), // 4: http.responsewriter.HijackResponse + (*emptypb.Empty)(nil), // 5: google.protobuf.Empty +} +var file_http_responsewriter_responsewriter_proto_depIdxs = []int32{ + 0, // 0: http.responsewriter.WriteRequest.headers:type_name -> http.responsewriter.Header + 0, // 1: http.responsewriter.WriteHeaderRequest.headers:type_name -> http.responsewriter.Header + 1, // 2: http.responsewriter.Writer.Write:input_type -> http.responsewriter.WriteRequest + 3, // 3: http.responsewriter.Writer.WriteHeader:input_type -> http.responsewriter.WriteHeaderRequest + 5, // 4: http.responsewriter.Writer.Flush:input_type -> google.protobuf.Empty + 5, // 5: http.responsewriter.Writer.Hijack:input_type -> google.protobuf.Empty + 2, // 6: http.responsewriter.Writer.Write:output_type -> http.responsewriter.WriteResponse + 5, // 7: http.responsewriter.Writer.WriteHeader:output_type -> google.protobuf.Empty + 5, // 8: http.responsewriter.Writer.Flush:output_type -> google.protobuf.Empty + 4, // 9: http.responsewriter.Writer.Hijack:output_type -> http.responsewriter.HijackResponse + 6, // [6:10] is the sub-list for method output_type + 2, // [2:6] is the sub-list for method input_type + 2, // [2:2] is the sub-list for extension type_name + 2, // [2:2] is the sub-list for extension extendee + 0, // [0:2] is the sub-list for field type_name +} + +func init() { file_http_responsewriter_responsewriter_proto_init() } +func file_http_responsewriter_responsewriter_proto_init() { + if File_http_responsewriter_responsewriter_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_http_responsewriter_responsewriter_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Header); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_http_responsewriter_responsewriter_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*WriteRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_http_responsewriter_responsewriter_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*WriteResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_http_responsewriter_responsewriter_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*WriteHeaderRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_http_responsewriter_responsewriter_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*HijackResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_http_responsewriter_responsewriter_proto_rawDesc, + NumEnums: 0, + NumMessages: 5, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_http_responsewriter_responsewriter_proto_goTypes, + DependencyIndexes: file_http_responsewriter_responsewriter_proto_depIdxs, + MessageInfos: file_http_responsewriter_responsewriter_proto_msgTypes, + }.Build() + File_http_responsewriter_responsewriter_proto = out.File + file_http_responsewriter_responsewriter_proto_rawDesc = nil + file_http_responsewriter_responsewriter_proto_goTypes = nil + file_http_responsewriter_responsewriter_proto_depIdxs = nil +} diff --git a/proto/http/responsewriter/responsewriter.proto b/proto/http/responsewriter/responsewriter.proto new file mode 100644 index 0000000..ea7f21d --- /dev/null +++ b/proto/http/responsewriter/responsewriter.proto @@ -0,0 +1,60 @@ +syntax = "proto3"; + +package http.responsewriter; + +import "google/protobuf/empty.proto"; + +option go_package = "github.com/consideritdone/landslidevm/proto/http/responsewriter"; + +// Writer is an http.ResponseWriter see: https://pkg.go.dev/net/http#ResponseWriter +service Writer { + // Write writes the data to the connection as part of an HTTP reply + rpc Write(WriteRequest) returns (WriteResponse); + // WriteHeader sends an HTTP response header with the provided + // status code + rpc WriteHeader(WriteHeaderRequest) returns (google.protobuf.Empty); + // Flush is a no-op + rpc Flush(google.protobuf.Empty) returns (google.protobuf.Empty); + // Hijack lets the caller take over the connection + rpc Hijack(google.protobuf.Empty) returns (HijackResponse); +} + +message Header { + // key is a element key in a key value pair + string key = 1; + // values are a list of strings coresponding to the key + repeated string values = 2; +} + +message WriteRequest { + // headers represents the key-value pairs in an HTTP header + repeated Header headers = 1; + // payload is the write request in bytes + bytes payload = 2; +} + +message WriteResponse { + // written is the number of bytes written in body + int32 written = 1; +} + +message WriteHeaderRequest { + // headers represents the key-value pairs in an HTTP header + repeated Header headers = 1; + // status_code must be a valid HTTP 1xx-5xx status code + int32 status_code = 2; +} + +message HijackResponse { + // local_network is the name of the network (for example, "tcp", "udp") + string local_network = 1; + // local_string is string form of address + string local_string = 2; + // remote_network is the name of the network (for example, "tcp", "udp") + string remote_network = 3; + // remote_string is string form of address + string remote_string = 4; + // server_addr is the address of the gRPC server serving the Conn, Reader + // and Writer services which facilitate Hijacking + string server_addr = 5; +} diff --git a/proto/http/responsewriter/responsewriter_grpc.pb.go b/proto/http/responsewriter/responsewriter_grpc.pb.go new file mode 100644 index 0000000..146a284 --- /dev/null +++ b/proto/http/responsewriter/responsewriter_grpc.pb.go @@ -0,0 +1,229 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.3.0 +// - protoc (unknown) +// source: http/responsewriter/responsewriter.proto + +package responsewriter + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + emptypb "google.golang.org/protobuf/types/known/emptypb" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +const ( + Writer_Write_FullMethodName = "/http.responsewriter.Writer/Write" + Writer_WriteHeader_FullMethodName = "/http.responsewriter.Writer/WriteHeader" + Writer_Flush_FullMethodName = "/http.responsewriter.Writer/Flush" + Writer_Hijack_FullMethodName = "/http.responsewriter.Writer/Hijack" +) + +// WriterClient is the client API for Writer service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type WriterClient interface { + // Write writes the data to the connection as part of an HTTP reply + Write(ctx context.Context, in *WriteRequest, opts ...grpc.CallOption) (*WriteResponse, error) + // WriteHeader sends an HTTP response header with the provided + // status code + WriteHeader(ctx context.Context, in *WriteHeaderRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) + // Flush is a no-op + Flush(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*emptypb.Empty, error) + // Hijack lets the caller take over the connection + Hijack(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*HijackResponse, error) +} + +type writerClient struct { + cc grpc.ClientConnInterface +} + +func NewWriterClient(cc grpc.ClientConnInterface) WriterClient { + return &writerClient{cc} +} + +func (c *writerClient) Write(ctx context.Context, in *WriteRequest, opts ...grpc.CallOption) (*WriteResponse, error) { + out := new(WriteResponse) + err := c.cc.Invoke(ctx, Writer_Write_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *writerClient) WriteHeader(ctx context.Context, in *WriteHeaderRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { + out := new(emptypb.Empty) + err := c.cc.Invoke(ctx, Writer_WriteHeader_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *writerClient) Flush(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*emptypb.Empty, error) { + out := new(emptypb.Empty) + err := c.cc.Invoke(ctx, Writer_Flush_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *writerClient) Hijack(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*HijackResponse, error) { + out := new(HijackResponse) + err := c.cc.Invoke(ctx, Writer_Hijack_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// WriterServer is the server API for Writer service. +// All implementations should embed UnimplementedWriterServer +// for forward compatibility +type WriterServer interface { + // Write writes the data to the connection as part of an HTTP reply + Write(context.Context, *WriteRequest) (*WriteResponse, error) + // WriteHeader sends an HTTP response header with the provided + // status code + WriteHeader(context.Context, *WriteHeaderRequest) (*emptypb.Empty, error) + // Flush is a no-op + Flush(context.Context, *emptypb.Empty) (*emptypb.Empty, error) + // Hijack lets the caller take over the connection + Hijack(context.Context, *emptypb.Empty) (*HijackResponse, error) +} + +// UnimplementedWriterServer should be embedded to have forward compatible implementations. +type UnimplementedWriterServer struct { +} + +func (UnimplementedWriterServer) Write(context.Context, *WriteRequest) (*WriteResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Write not implemented") +} +func (UnimplementedWriterServer) WriteHeader(context.Context, *WriteHeaderRequest) (*emptypb.Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method WriteHeader not implemented") +} +func (UnimplementedWriterServer) Flush(context.Context, *emptypb.Empty) (*emptypb.Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method Flush not implemented") +} +func (UnimplementedWriterServer) Hijack(context.Context, *emptypb.Empty) (*HijackResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Hijack not implemented") +} + +// UnsafeWriterServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to WriterServer will +// result in compilation errors. +type UnsafeWriterServer interface { + mustEmbedUnimplementedWriterServer() +} + +func RegisterWriterServer(s grpc.ServiceRegistrar, srv WriterServer) { + s.RegisterService(&Writer_ServiceDesc, srv) +} + +func _Writer_Write_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(WriteRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(WriterServer).Write(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Writer_Write_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(WriterServer).Write(ctx, req.(*WriteRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Writer_WriteHeader_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(WriteHeaderRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(WriterServer).WriteHeader(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Writer_WriteHeader_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(WriterServer).WriteHeader(ctx, req.(*WriteHeaderRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Writer_Flush_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(emptypb.Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(WriterServer).Flush(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Writer_Flush_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(WriterServer).Flush(ctx, req.(*emptypb.Empty)) + } + return interceptor(ctx, in, info, handler) +} + +func _Writer_Hijack_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(emptypb.Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(WriterServer).Hijack(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Writer_Hijack_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(WriterServer).Hijack(ctx, req.(*emptypb.Empty)) + } + return interceptor(ctx, in, info, handler) +} + +// Writer_ServiceDesc is the grpc.ServiceDesc for Writer service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var Writer_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "http.responsewriter.Writer", + HandlerType: (*WriterServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Write", + Handler: _Writer_Write_Handler, + }, + { + MethodName: "WriteHeader", + Handler: _Writer_WriteHeader_Handler, + }, + { + MethodName: "Flush", + Handler: _Writer_Flush_Handler, + }, + { + MethodName: "Hijack", + Handler: _Writer_Hijack_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "http/responsewriter/responsewriter.proto", +} diff --git a/proto/io/reader/reader.pb.go b/proto/io/reader/reader.pb.go new file mode 100644 index 0000000..0fd3051 --- /dev/null +++ b/proto/io/reader/reader.pb.go @@ -0,0 +1,228 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.33.0 +// protoc (unknown) +// source: io/reader/reader.proto + +package reader + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type ReadRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // length is the request in bytes + Length int32 `protobuf:"varint,1,opt,name=length,proto3" json:"length,omitempty"` +} + +func (x *ReadRequest) Reset() { + *x = ReadRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_io_reader_reader_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ReadRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ReadRequest) ProtoMessage() {} + +func (x *ReadRequest) ProtoReflect() protoreflect.Message { + mi := &file_io_reader_reader_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ReadRequest.ProtoReflect.Descriptor instead. +func (*ReadRequest) Descriptor() ([]byte, []int) { + return file_io_reader_reader_proto_rawDescGZIP(), []int{0} +} + +func (x *ReadRequest) GetLength() int32 { + if x != nil { + return x.Length + } + return 0 +} + +type ReadResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // read is the payload in bytes + Read []byte `protobuf:"bytes,1,opt,name=read,proto3" json:"read,omitempty"` + // error is an error message + Error *string `protobuf:"bytes,2,opt,name=error,proto3,oneof" json:"error,omitempty"` +} + +func (x *ReadResponse) Reset() { + *x = ReadResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_io_reader_reader_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ReadResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ReadResponse) ProtoMessage() {} + +func (x *ReadResponse) ProtoReflect() protoreflect.Message { + mi := &file_io_reader_reader_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ReadResponse.ProtoReflect.Descriptor instead. +func (*ReadResponse) Descriptor() ([]byte, []int) { + return file_io_reader_reader_proto_rawDescGZIP(), []int{1} +} + +func (x *ReadResponse) GetRead() []byte { + if x != nil { + return x.Read + } + return nil +} + +func (x *ReadResponse) GetError() string { + if x != nil && x.Error != nil { + return *x.Error + } + return "" +} + +var File_io_reader_reader_proto protoreflect.FileDescriptor + +var file_io_reader_reader_proto_rawDesc = []byte{ + 0x0a, 0x16, 0x69, 0x6f, 0x2f, 0x72, 0x65, 0x61, 0x64, 0x65, 0x72, 0x2f, 0x72, 0x65, 0x61, 0x64, + 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x09, 0x69, 0x6f, 0x2e, 0x72, 0x65, 0x61, + 0x64, 0x65, 0x72, 0x22, 0x25, 0x0a, 0x0b, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x05, 0x52, 0x06, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x47, 0x0a, 0x0c, 0x52, 0x65, + 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x72, 0x65, + 0x61, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x72, 0x65, 0x61, 0x64, 0x12, 0x19, + 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, + 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x88, 0x01, 0x01, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x65, 0x72, + 0x72, 0x6f, 0x72, 0x32, 0x41, 0x0a, 0x06, 0x52, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x37, 0x0a, + 0x04, 0x52, 0x65, 0x61, 0x64, 0x12, 0x16, 0x2e, 0x69, 0x6f, 0x2e, 0x72, 0x65, 0x61, 0x64, 0x65, + 0x72, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, + 0x69, 0x6f, 0x2e, 0x72, 0x65, 0x61, 0x64, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x37, 0x5a, 0x35, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x69, 0x64, 0x65, 0x72, 0x69, 0x74, 0x64, + 0x6f, 0x6e, 0x65, 0x2f, 0x6c, 0x61, 0x6e, 0x64, 0x73, 0x6c, 0x69, 0x64, 0x65, 0x76, 0x6d, 0x2f, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x69, 0x6f, 0x2f, 0x72, 0x65, 0x61, 0x64, 0x65, 0x72, 0x62, + 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_io_reader_reader_proto_rawDescOnce sync.Once + file_io_reader_reader_proto_rawDescData = file_io_reader_reader_proto_rawDesc +) + +func file_io_reader_reader_proto_rawDescGZIP() []byte { + file_io_reader_reader_proto_rawDescOnce.Do(func() { + file_io_reader_reader_proto_rawDescData = protoimpl.X.CompressGZIP(file_io_reader_reader_proto_rawDescData) + }) + return file_io_reader_reader_proto_rawDescData +} + +var file_io_reader_reader_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_io_reader_reader_proto_goTypes = []interface{}{ + (*ReadRequest)(nil), // 0: io.reader.ReadRequest + (*ReadResponse)(nil), // 1: io.reader.ReadResponse +} +var file_io_reader_reader_proto_depIdxs = []int32{ + 0, // 0: io.reader.Reader.Read:input_type -> io.reader.ReadRequest + 1, // 1: io.reader.Reader.Read:output_type -> io.reader.ReadResponse + 1, // [1:2] is the sub-list for method output_type + 0, // [0:1] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_io_reader_reader_proto_init() } +func file_io_reader_reader_proto_init() { + if File_io_reader_reader_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_io_reader_reader_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ReadRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_io_reader_reader_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ReadResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + file_io_reader_reader_proto_msgTypes[1].OneofWrappers = []interface{}{} + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_io_reader_reader_proto_rawDesc, + NumEnums: 0, + NumMessages: 2, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_io_reader_reader_proto_goTypes, + DependencyIndexes: file_io_reader_reader_proto_depIdxs, + MessageInfos: file_io_reader_reader_proto_msgTypes, + }.Build() + File_io_reader_reader_proto = out.File + file_io_reader_reader_proto_rawDesc = nil + file_io_reader_reader_proto_goTypes = nil + file_io_reader_reader_proto_depIdxs = nil +} diff --git a/proto/io/reader/reader.proto b/proto/io/reader/reader.proto new file mode 100644 index 0000000..34c56ed --- /dev/null +++ b/proto/io/reader/reader.proto @@ -0,0 +1,22 @@ +syntax = "proto3"; + +package io.reader; + +option go_package = "github.com/consideritdone/landslidevm/proto/io/reader"; + +// Reader is an io.Reader see: https://pkg.go.dev/io#Reader +service Reader { + rpc Read(ReadRequest) returns (ReadResponse); +} + +message ReadRequest { + // length is the request in bytes + int32 length = 1; +} + +message ReadResponse { + // read is the payload in bytes + bytes read = 1; + // error is an error message + optional string error = 2; +} diff --git a/proto/io/reader/reader_grpc.pb.go b/proto/io/reader/reader_grpc.pb.go new file mode 100644 index 0000000..bd9f300 --- /dev/null +++ b/proto/io/reader/reader_grpc.pb.go @@ -0,0 +1,107 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.3.0 +// - protoc (unknown) +// source: io/reader/reader.proto + +package reader + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +const ( + Reader_Read_FullMethodName = "/io.reader.Reader/Read" +) + +// ReaderClient is the client API for Reader service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type ReaderClient interface { + Read(ctx context.Context, in *ReadRequest, opts ...grpc.CallOption) (*ReadResponse, error) +} + +type readerClient struct { + cc grpc.ClientConnInterface +} + +func NewReaderClient(cc grpc.ClientConnInterface) ReaderClient { + return &readerClient{cc} +} + +func (c *readerClient) Read(ctx context.Context, in *ReadRequest, opts ...grpc.CallOption) (*ReadResponse, error) { + out := new(ReadResponse) + err := c.cc.Invoke(ctx, Reader_Read_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// ReaderServer is the server API for Reader service. +// All implementations should embed UnimplementedReaderServer +// for forward compatibility +type ReaderServer interface { + Read(context.Context, *ReadRequest) (*ReadResponse, error) +} + +// UnimplementedReaderServer should be embedded to have forward compatible implementations. +type UnimplementedReaderServer struct { +} + +func (UnimplementedReaderServer) Read(context.Context, *ReadRequest) (*ReadResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Read not implemented") +} + +// UnsafeReaderServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to ReaderServer will +// result in compilation errors. +type UnsafeReaderServer interface { + mustEmbedUnimplementedReaderServer() +} + +func RegisterReaderServer(s grpc.ServiceRegistrar, srv ReaderServer) { + s.RegisterService(&Reader_ServiceDesc, srv) +} + +func _Reader_Read_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ReadRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ReaderServer).Read(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Reader_Read_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ReaderServer).Read(ctx, req.(*ReadRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// Reader_ServiceDesc is the grpc.ServiceDesc for Reader service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var Reader_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "io.reader.Reader", + HandlerType: (*ReaderServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Read", + Handler: _Reader_Read_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "io/reader/reader.proto", +} diff --git a/proto/io/writer/writer.pb.go b/proto/io/writer/writer.pb.go new file mode 100644 index 0000000..d71482a --- /dev/null +++ b/proto/io/writer/writer.pb.go @@ -0,0 +1,229 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.33.0 +// protoc (unknown) +// source: io/writer/writer.proto + +package writer + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type WriteRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // payload is the write request in bytes + Payload []byte `protobuf:"bytes,1,opt,name=payload,proto3" json:"payload,omitempty"` +} + +func (x *WriteRequest) Reset() { + *x = WriteRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_io_writer_writer_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *WriteRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WriteRequest) ProtoMessage() {} + +func (x *WriteRequest) ProtoReflect() protoreflect.Message { + mi := &file_io_writer_writer_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WriteRequest.ProtoReflect.Descriptor instead. +func (*WriteRequest) Descriptor() ([]byte, []int) { + return file_io_writer_writer_proto_rawDescGZIP(), []int{0} +} + +func (x *WriteRequest) GetPayload() []byte { + if x != nil { + return x.Payload + } + return nil +} + +type WriteResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // written is the length of payload in bytes + Written int32 `protobuf:"varint,1,opt,name=written,proto3" json:"written,omitempty"` + // error is an error message + Error *string `protobuf:"bytes,2,opt,name=error,proto3,oneof" json:"error,omitempty"` +} + +func (x *WriteResponse) Reset() { + *x = WriteResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_io_writer_writer_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *WriteResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WriteResponse) ProtoMessage() {} + +func (x *WriteResponse) ProtoReflect() protoreflect.Message { + mi := &file_io_writer_writer_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WriteResponse.ProtoReflect.Descriptor instead. +func (*WriteResponse) Descriptor() ([]byte, []int) { + return file_io_writer_writer_proto_rawDescGZIP(), []int{1} +} + +func (x *WriteResponse) GetWritten() int32 { + if x != nil { + return x.Written + } + return 0 +} + +func (x *WriteResponse) GetError() string { + if x != nil && x.Error != nil { + return *x.Error + } + return "" +} + +var File_io_writer_writer_proto protoreflect.FileDescriptor + +var file_io_writer_writer_proto_rawDesc = []byte{ + 0x0a, 0x16, 0x69, 0x6f, 0x2f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x72, 0x2f, 0x77, 0x72, 0x69, 0x74, + 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x09, 0x69, 0x6f, 0x2e, 0x77, 0x72, 0x69, + 0x74, 0x65, 0x72, 0x22, 0x28, 0x0a, 0x0c, 0x57, 0x72, 0x69, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x22, 0x4e, 0x0a, + 0x0d, 0x57, 0x72, 0x69, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, + 0x0a, 0x07, 0x77, 0x72, 0x69, 0x74, 0x74, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, + 0x07, 0x77, 0x72, 0x69, 0x74, 0x74, 0x65, 0x6e, 0x12, 0x19, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, + 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, + 0x88, 0x01, 0x01, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x32, 0x44, 0x0a, + 0x06, 0x57, 0x72, 0x69, 0x74, 0x65, 0x72, 0x12, 0x3a, 0x0a, 0x05, 0x57, 0x72, 0x69, 0x74, 0x65, + 0x12, 0x17, 0x2e, 0x69, 0x6f, 0x2e, 0x77, 0x72, 0x69, 0x74, 0x65, 0x72, 0x2e, 0x57, 0x72, 0x69, + 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x69, 0x6f, 0x2e, 0x77, + 0x72, 0x69, 0x74, 0x65, 0x72, 0x2e, 0x57, 0x72, 0x69, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x42, 0x37, 0x5a, 0x35, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x69, 0x64, 0x65, 0x72, 0x69, 0x74, 0x64, 0x6f, 0x6e, 0x65, + 0x2f, 0x6c, 0x61, 0x6e, 0x64, 0x73, 0x6c, 0x69, 0x64, 0x65, 0x76, 0x6d, 0x2f, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x2f, 0x69, 0x6f, 0x2f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x72, 0x62, 0x06, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_io_writer_writer_proto_rawDescOnce sync.Once + file_io_writer_writer_proto_rawDescData = file_io_writer_writer_proto_rawDesc +) + +func file_io_writer_writer_proto_rawDescGZIP() []byte { + file_io_writer_writer_proto_rawDescOnce.Do(func() { + file_io_writer_writer_proto_rawDescData = protoimpl.X.CompressGZIP(file_io_writer_writer_proto_rawDescData) + }) + return file_io_writer_writer_proto_rawDescData +} + +var file_io_writer_writer_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_io_writer_writer_proto_goTypes = []interface{}{ + (*WriteRequest)(nil), // 0: io.writer.WriteRequest + (*WriteResponse)(nil), // 1: io.writer.WriteResponse +} +var file_io_writer_writer_proto_depIdxs = []int32{ + 0, // 0: io.writer.Writer.Write:input_type -> io.writer.WriteRequest + 1, // 1: io.writer.Writer.Write:output_type -> io.writer.WriteResponse + 1, // [1:2] is the sub-list for method output_type + 0, // [0:1] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_io_writer_writer_proto_init() } +func file_io_writer_writer_proto_init() { + if File_io_writer_writer_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_io_writer_writer_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*WriteRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_io_writer_writer_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*WriteResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + file_io_writer_writer_proto_msgTypes[1].OneofWrappers = []interface{}{} + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_io_writer_writer_proto_rawDesc, + NumEnums: 0, + NumMessages: 2, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_io_writer_writer_proto_goTypes, + DependencyIndexes: file_io_writer_writer_proto_depIdxs, + MessageInfos: file_io_writer_writer_proto_msgTypes, + }.Build() + File_io_writer_writer_proto = out.File + file_io_writer_writer_proto_rawDesc = nil + file_io_writer_writer_proto_goTypes = nil + file_io_writer_writer_proto_depIdxs = nil +} diff --git a/proto/io/writer/writer.proto b/proto/io/writer/writer.proto new file mode 100644 index 0000000..db7a6bf --- /dev/null +++ b/proto/io/writer/writer.proto @@ -0,0 +1,23 @@ +syntax = "proto3"; + +package io.writer; + +option go_package = "github.com/consideritdone/landslidevm/proto/io/writer"; + +// Writer see: io.Writer https://pkg.go.dev/io#Writer +service Writer { + // Write writes len(p) bytes from p to the underlying data stream. + rpc Write(WriteRequest) returns (WriteResponse); +} + +message WriteRequest { + // payload is the write request in bytes + bytes payload = 1; +} + +message WriteResponse { + // written is the length of payload in bytes + int32 written = 1; + // error is an error message + optional string error = 2; +} diff --git a/proto/io/writer/writer_grpc.pb.go b/proto/io/writer/writer_grpc.pb.go new file mode 100644 index 0000000..79588ab --- /dev/null +++ b/proto/io/writer/writer_grpc.pb.go @@ -0,0 +1,109 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.3.0 +// - protoc (unknown) +// source: io/writer/writer.proto + +package writer + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +const ( + Writer_Write_FullMethodName = "/io.writer.Writer/Write" +) + +// WriterClient is the client API for Writer service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type WriterClient interface { + // Write writes len(p) bytes from p to the underlying data stream. + Write(ctx context.Context, in *WriteRequest, opts ...grpc.CallOption) (*WriteResponse, error) +} + +type writerClient struct { + cc grpc.ClientConnInterface +} + +func NewWriterClient(cc grpc.ClientConnInterface) WriterClient { + return &writerClient{cc} +} + +func (c *writerClient) Write(ctx context.Context, in *WriteRequest, opts ...grpc.CallOption) (*WriteResponse, error) { + out := new(WriteResponse) + err := c.cc.Invoke(ctx, Writer_Write_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// WriterServer is the server API for Writer service. +// All implementations should embed UnimplementedWriterServer +// for forward compatibility +type WriterServer interface { + // Write writes len(p) bytes from p to the underlying data stream. + Write(context.Context, *WriteRequest) (*WriteResponse, error) +} + +// UnimplementedWriterServer should be embedded to have forward compatible implementations. +type UnimplementedWriterServer struct { +} + +func (UnimplementedWriterServer) Write(context.Context, *WriteRequest) (*WriteResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Write not implemented") +} + +// UnsafeWriterServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to WriterServer will +// result in compilation errors. +type UnsafeWriterServer interface { + mustEmbedUnimplementedWriterServer() +} + +func RegisterWriterServer(s grpc.ServiceRegistrar, srv WriterServer) { + s.RegisterService(&Writer_ServiceDesc, srv) +} + +func _Writer_Write_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(WriteRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(WriterServer).Write(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Writer_Write_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(WriterServer).Write(ctx, req.(*WriteRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// Writer_ServiceDesc is the grpc.ServiceDesc for Writer service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var Writer_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "io.writer.Writer", + HandlerType: (*WriterServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Write", + Handler: _Writer_Write_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "io/writer/writer.proto", +} diff --git a/proto/net/conn/conn.pb.go b/proto/net/conn/conn.pb.go new file mode 100644 index 0000000..78502ed --- /dev/null +++ b/proto/net/conn/conn.pb.go @@ -0,0 +1,465 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.33.0 +// protoc (unknown) +// source: net/conn/conn.proto + +package conn + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + emptypb "google.golang.org/protobuf/types/known/emptypb" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type ReadRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // length of the request in bytes + Length int32 `protobuf:"varint,1,opt,name=length,proto3" json:"length,omitempty"` +} + +func (x *ReadRequest) Reset() { + *x = ReadRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_net_conn_conn_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ReadRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ReadRequest) ProtoMessage() {} + +func (x *ReadRequest) ProtoReflect() protoreflect.Message { + mi := &file_net_conn_conn_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ReadRequest.ProtoReflect.Descriptor instead. +func (*ReadRequest) Descriptor() ([]byte, []int) { + return file_net_conn_conn_proto_rawDescGZIP(), []int{0} +} + +func (x *ReadRequest) GetLength() int32 { + if x != nil { + return x.Length + } + return 0 +} + +type ReadResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // read is the payload in bytes + Read []byte `protobuf:"bytes,1,opt,name=read,proto3" json:"read,omitempty"` + // error is an error message + Error *string `protobuf:"bytes,2,opt,name=error,proto3,oneof" json:"error,omitempty"` +} + +func (x *ReadResponse) Reset() { + *x = ReadResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_net_conn_conn_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ReadResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ReadResponse) ProtoMessage() {} + +func (x *ReadResponse) ProtoReflect() protoreflect.Message { + mi := &file_net_conn_conn_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ReadResponse.ProtoReflect.Descriptor instead. +func (*ReadResponse) Descriptor() ([]byte, []int) { + return file_net_conn_conn_proto_rawDescGZIP(), []int{1} +} + +func (x *ReadResponse) GetRead() []byte { + if x != nil { + return x.Read + } + return nil +} + +func (x *ReadResponse) GetError() string { + if x != nil && x.Error != nil { + return *x.Error + } + return "" +} + +type WriteRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // payload is the write request in bytes + Payload []byte `protobuf:"bytes,1,opt,name=payload,proto3" json:"payload,omitempty"` +} + +func (x *WriteRequest) Reset() { + *x = WriteRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_net_conn_conn_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *WriteRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WriteRequest) ProtoMessage() {} + +func (x *WriteRequest) ProtoReflect() protoreflect.Message { + mi := &file_net_conn_conn_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WriteRequest.ProtoReflect.Descriptor instead. +func (*WriteRequest) Descriptor() ([]byte, []int) { + return file_net_conn_conn_proto_rawDescGZIP(), []int{2} +} + +func (x *WriteRequest) GetPayload() []byte { + if x != nil { + return x.Payload + } + return nil +} + +type WriteResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // length of the response in bytes + Length int32 `protobuf:"varint,1,opt,name=length,proto3" json:"length,omitempty"` + // error is an error message + Error *string `protobuf:"bytes,2,opt,name=error,proto3,oneof" json:"error,omitempty"` +} + +func (x *WriteResponse) Reset() { + *x = WriteResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_net_conn_conn_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *WriteResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WriteResponse) ProtoMessage() {} + +func (x *WriteResponse) ProtoReflect() protoreflect.Message { + mi := &file_net_conn_conn_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WriteResponse.ProtoReflect.Descriptor instead. +func (*WriteResponse) Descriptor() ([]byte, []int) { + return file_net_conn_conn_proto_rawDescGZIP(), []int{3} +} + +func (x *WriteResponse) GetLength() int32 { + if x != nil { + return x.Length + } + return 0 +} + +func (x *WriteResponse) GetError() string { + if x != nil && x.Error != nil { + return *x.Error + } + return "" +} + +type SetDeadlineRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // time represents an instant in time in bytes + Time []byte `protobuf:"bytes,1,opt,name=time,proto3" json:"time,omitempty"` +} + +func (x *SetDeadlineRequest) Reset() { + *x = SetDeadlineRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_net_conn_conn_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SetDeadlineRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SetDeadlineRequest) ProtoMessage() {} + +func (x *SetDeadlineRequest) ProtoReflect() protoreflect.Message { + mi := &file_net_conn_conn_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SetDeadlineRequest.ProtoReflect.Descriptor instead. +func (*SetDeadlineRequest) Descriptor() ([]byte, []int) { + return file_net_conn_conn_proto_rawDescGZIP(), []int{4} +} + +func (x *SetDeadlineRequest) GetTime() []byte { + if x != nil { + return x.Time + } + return nil +} + +var File_net_conn_conn_proto protoreflect.FileDescriptor + +var file_net_conn_conn_proto_rawDesc = []byte{ + 0x0a, 0x13, 0x6e, 0x65, 0x74, 0x2f, 0x63, 0x6f, 0x6e, 0x6e, 0x2f, 0x63, 0x6f, 0x6e, 0x6e, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08, 0x6e, 0x65, 0x74, 0x2e, 0x63, 0x6f, 0x6e, 0x6e, 0x1a, + 0x1b, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2f, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x25, 0x0a, 0x0b, + 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6c, + 0x65, 0x6e, 0x67, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x6c, 0x65, 0x6e, + 0x67, 0x74, 0x68, 0x22, 0x47, 0x0a, 0x0c, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x72, 0x65, 0x61, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x04, 0x72, 0x65, 0x61, 0x64, 0x12, 0x19, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x88, + 0x01, 0x01, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x28, 0x0a, 0x0c, + 0x57, 0x72, 0x69, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, + 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, + 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x22, 0x4c, 0x0a, 0x0d, 0x57, 0x72, 0x69, 0x74, 0x65, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x65, 0x6e, 0x67, 0x74, + 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x12, + 0x19, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, + 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x88, 0x01, 0x01, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x65, + 0x72, 0x72, 0x6f, 0x72, 0x22, 0x28, 0x0a, 0x12, 0x53, 0x65, 0x74, 0x44, 0x65, 0x61, 0x64, 0x6c, + 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x69, + 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x32, 0x88, + 0x03, 0x0a, 0x04, 0x43, 0x6f, 0x6e, 0x6e, 0x12, 0x35, 0x0a, 0x04, 0x52, 0x65, 0x61, 0x64, 0x12, + 0x15, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x63, 0x6f, 0x6e, 0x6e, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x63, 0x6f, 0x6e, + 0x6e, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x38, + 0x0a, 0x05, 0x57, 0x72, 0x69, 0x74, 0x65, 0x12, 0x16, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x63, 0x6f, + 0x6e, 0x6e, 0x2e, 0x57, 0x72, 0x69, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x17, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x63, 0x6f, 0x6e, 0x6e, 0x2e, 0x57, 0x72, 0x69, 0x74, 0x65, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x37, 0x0a, 0x05, 0x43, 0x6c, 0x6f, 0x73, + 0x65, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, + 0x79, 0x12, 0x43, 0x0a, 0x0b, 0x53, 0x65, 0x74, 0x44, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, + 0x12, 0x1c, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x63, 0x6f, 0x6e, 0x6e, 0x2e, 0x53, 0x65, 0x74, 0x44, + 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x47, 0x0a, 0x0f, 0x53, 0x65, 0x74, 0x52, 0x65, 0x61, + 0x64, 0x44, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x1c, 0x2e, 0x6e, 0x65, 0x74, 0x2e, + 0x63, 0x6f, 0x6e, 0x6e, 0x2e, 0x53, 0x65, 0x74, 0x44, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, + 0x48, 0x0a, 0x10, 0x53, 0x65, 0x74, 0x57, 0x72, 0x69, 0x74, 0x65, 0x44, 0x65, 0x61, 0x64, 0x6c, + 0x69, 0x6e, 0x65, 0x12, 0x1c, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x63, 0x6f, 0x6e, 0x6e, 0x2e, 0x53, + 0x65, 0x74, 0x44, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x42, 0x36, 0x5a, 0x34, 0x67, 0x69, 0x74, + 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x69, 0x64, 0x65, 0x72, + 0x69, 0x74, 0x64, 0x6f, 0x6e, 0x65, 0x2f, 0x6c, 0x61, 0x6e, 0x64, 0x73, 0x6c, 0x69, 0x64, 0x65, + 0x76, 0x6d, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x6e, 0x65, 0x74, 0x2f, 0x63, 0x6f, 0x6e, + 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_net_conn_conn_proto_rawDescOnce sync.Once + file_net_conn_conn_proto_rawDescData = file_net_conn_conn_proto_rawDesc +) + +func file_net_conn_conn_proto_rawDescGZIP() []byte { + file_net_conn_conn_proto_rawDescOnce.Do(func() { + file_net_conn_conn_proto_rawDescData = protoimpl.X.CompressGZIP(file_net_conn_conn_proto_rawDescData) + }) + return file_net_conn_conn_proto_rawDescData +} + +var file_net_conn_conn_proto_msgTypes = make([]protoimpl.MessageInfo, 5) +var file_net_conn_conn_proto_goTypes = []interface{}{ + (*ReadRequest)(nil), // 0: net.conn.ReadRequest + (*ReadResponse)(nil), // 1: net.conn.ReadResponse + (*WriteRequest)(nil), // 2: net.conn.WriteRequest + (*WriteResponse)(nil), // 3: net.conn.WriteResponse + (*SetDeadlineRequest)(nil), // 4: net.conn.SetDeadlineRequest + (*emptypb.Empty)(nil), // 5: google.protobuf.Empty +} +var file_net_conn_conn_proto_depIdxs = []int32{ + 0, // 0: net.conn.Conn.Read:input_type -> net.conn.ReadRequest + 2, // 1: net.conn.Conn.Write:input_type -> net.conn.WriteRequest + 5, // 2: net.conn.Conn.Close:input_type -> google.protobuf.Empty + 4, // 3: net.conn.Conn.SetDeadline:input_type -> net.conn.SetDeadlineRequest + 4, // 4: net.conn.Conn.SetReadDeadline:input_type -> net.conn.SetDeadlineRequest + 4, // 5: net.conn.Conn.SetWriteDeadline:input_type -> net.conn.SetDeadlineRequest + 1, // 6: net.conn.Conn.Read:output_type -> net.conn.ReadResponse + 3, // 7: net.conn.Conn.Write:output_type -> net.conn.WriteResponse + 5, // 8: net.conn.Conn.Close:output_type -> google.protobuf.Empty + 5, // 9: net.conn.Conn.SetDeadline:output_type -> google.protobuf.Empty + 5, // 10: net.conn.Conn.SetReadDeadline:output_type -> google.protobuf.Empty + 5, // 11: net.conn.Conn.SetWriteDeadline:output_type -> google.protobuf.Empty + 6, // [6:12] is the sub-list for method output_type + 0, // [0:6] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_net_conn_conn_proto_init() } +func file_net_conn_conn_proto_init() { + if File_net_conn_conn_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_net_conn_conn_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ReadRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_net_conn_conn_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ReadResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_net_conn_conn_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*WriteRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_net_conn_conn_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*WriteResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_net_conn_conn_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SetDeadlineRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + file_net_conn_conn_proto_msgTypes[1].OneofWrappers = []interface{}{} + file_net_conn_conn_proto_msgTypes[3].OneofWrappers = []interface{}{} + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_net_conn_conn_proto_rawDesc, + NumEnums: 0, + NumMessages: 5, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_net_conn_conn_proto_goTypes, + DependencyIndexes: file_net_conn_conn_proto_depIdxs, + MessageInfos: file_net_conn_conn_proto_msgTypes, + }.Build() + File_net_conn_conn_proto = out.File + file_net_conn_conn_proto_rawDesc = nil + file_net_conn_conn_proto_goTypes = nil + file_net_conn_conn_proto_depIdxs = nil +} diff --git a/proto/net/conn/conn.proto b/proto/net/conn/conn.proto new file mode 100644 index 0000000..3609473 --- /dev/null +++ b/proto/net/conn/conn.proto @@ -0,0 +1,55 @@ +syntax = "proto3"; + +package net.conn; + +import "google/protobuf/empty.proto"; + +option go_package = "github.com/consideritdone/landslidevm/proto/net/conn"; + +// Conn is a net.Conn see: https://pkg.go.dev/net#Conn +service Conn { + // Read reads data from the connection. + rpc Read(ReadRequest) returns (ReadResponse); + // Write writes data to the connection. + rpc Write(WriteRequest) returns (WriteResponse); + // Close closes the connection. + rpc Close(google.protobuf.Empty) returns (google.protobuf.Empty); + // SetDeadline sets the read and write deadlines associated + // with the connection. + rpc SetDeadline(SetDeadlineRequest) returns (google.protobuf.Empty); + // SetReadDeadline sets the deadline for future Read calls + // and any currently-blocked Read call. + rpc SetReadDeadline(SetDeadlineRequest) returns (google.protobuf.Empty); + // SetWriteDeadline sets the deadline for future Write calls + // and any currently-blocked Write call. + rpc SetWriteDeadline(SetDeadlineRequest) returns (google.protobuf.Empty); +} + +message ReadRequest { + // length of the request in bytes + int32 length = 1; +} + +message ReadResponse { + // read is the payload in bytes + bytes read = 1; + // error is an error message + optional string error = 2; +} + +message WriteRequest { + // payload is the write request in bytes + bytes payload = 1; +} + +message WriteResponse { + // length of the response in bytes + int32 length = 1; + // error is an error message + optional string error = 2; +} + +message SetDeadlineRequest { + // time represents an instant in time in bytes + bytes time = 1; +} diff --git a/proto/net/conn/conn_grpc.pb.go b/proto/net/conn/conn_grpc.pb.go new file mode 100644 index 0000000..5915962 --- /dev/null +++ b/proto/net/conn/conn_grpc.pb.go @@ -0,0 +1,311 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.3.0 +// - protoc (unknown) +// source: net/conn/conn.proto + +package conn + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + emptypb "google.golang.org/protobuf/types/known/emptypb" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +const ( + Conn_Read_FullMethodName = "/net.conn.Conn/Read" + Conn_Write_FullMethodName = "/net.conn.Conn/Write" + Conn_Close_FullMethodName = "/net.conn.Conn/Close" + Conn_SetDeadline_FullMethodName = "/net.conn.Conn/SetDeadline" + Conn_SetReadDeadline_FullMethodName = "/net.conn.Conn/SetReadDeadline" + Conn_SetWriteDeadline_FullMethodName = "/net.conn.Conn/SetWriteDeadline" +) + +// ConnClient is the client API for Conn service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type ConnClient interface { + // Read reads data from the connection. + Read(ctx context.Context, in *ReadRequest, opts ...grpc.CallOption) (*ReadResponse, error) + // Write writes data to the connection. + Write(ctx context.Context, in *WriteRequest, opts ...grpc.CallOption) (*WriteResponse, error) + // Close closes the connection. + Close(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*emptypb.Empty, error) + // SetDeadline sets the read and write deadlines associated + // with the connection. + SetDeadline(ctx context.Context, in *SetDeadlineRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) + // SetReadDeadline sets the deadline for future Read calls + // and any currently-blocked Read call. + SetReadDeadline(ctx context.Context, in *SetDeadlineRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) + // SetWriteDeadline sets the deadline for future Write calls + // and any currently-blocked Write call. + SetWriteDeadline(ctx context.Context, in *SetDeadlineRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) +} + +type connClient struct { + cc grpc.ClientConnInterface +} + +func NewConnClient(cc grpc.ClientConnInterface) ConnClient { + return &connClient{cc} +} + +func (c *connClient) Read(ctx context.Context, in *ReadRequest, opts ...grpc.CallOption) (*ReadResponse, error) { + out := new(ReadResponse) + err := c.cc.Invoke(ctx, Conn_Read_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *connClient) Write(ctx context.Context, in *WriteRequest, opts ...grpc.CallOption) (*WriteResponse, error) { + out := new(WriteResponse) + err := c.cc.Invoke(ctx, Conn_Write_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *connClient) Close(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*emptypb.Empty, error) { + out := new(emptypb.Empty) + err := c.cc.Invoke(ctx, Conn_Close_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *connClient) SetDeadline(ctx context.Context, in *SetDeadlineRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { + out := new(emptypb.Empty) + err := c.cc.Invoke(ctx, Conn_SetDeadline_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *connClient) SetReadDeadline(ctx context.Context, in *SetDeadlineRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { + out := new(emptypb.Empty) + err := c.cc.Invoke(ctx, Conn_SetReadDeadline_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *connClient) SetWriteDeadline(ctx context.Context, in *SetDeadlineRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { + out := new(emptypb.Empty) + err := c.cc.Invoke(ctx, Conn_SetWriteDeadline_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// ConnServer is the server API for Conn service. +// All implementations should embed UnimplementedConnServer +// for forward compatibility +type ConnServer interface { + // Read reads data from the connection. + Read(context.Context, *ReadRequest) (*ReadResponse, error) + // Write writes data to the connection. + Write(context.Context, *WriteRequest) (*WriteResponse, error) + // Close closes the connection. + Close(context.Context, *emptypb.Empty) (*emptypb.Empty, error) + // SetDeadline sets the read and write deadlines associated + // with the connection. + SetDeadline(context.Context, *SetDeadlineRequest) (*emptypb.Empty, error) + // SetReadDeadline sets the deadline for future Read calls + // and any currently-blocked Read call. + SetReadDeadline(context.Context, *SetDeadlineRequest) (*emptypb.Empty, error) + // SetWriteDeadline sets the deadline for future Write calls + // and any currently-blocked Write call. + SetWriteDeadline(context.Context, *SetDeadlineRequest) (*emptypb.Empty, error) +} + +// UnimplementedConnServer should be embedded to have forward compatible implementations. +type UnimplementedConnServer struct { +} + +func (UnimplementedConnServer) Read(context.Context, *ReadRequest) (*ReadResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Read not implemented") +} +func (UnimplementedConnServer) Write(context.Context, *WriteRequest) (*WriteResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Write not implemented") +} +func (UnimplementedConnServer) Close(context.Context, *emptypb.Empty) (*emptypb.Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method Close not implemented") +} +func (UnimplementedConnServer) SetDeadline(context.Context, *SetDeadlineRequest) (*emptypb.Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method SetDeadline not implemented") +} +func (UnimplementedConnServer) SetReadDeadline(context.Context, *SetDeadlineRequest) (*emptypb.Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method SetReadDeadline not implemented") +} +func (UnimplementedConnServer) SetWriteDeadline(context.Context, *SetDeadlineRequest) (*emptypb.Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method SetWriteDeadline not implemented") +} + +// UnsafeConnServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to ConnServer will +// result in compilation errors. +type UnsafeConnServer interface { + mustEmbedUnimplementedConnServer() +} + +func RegisterConnServer(s grpc.ServiceRegistrar, srv ConnServer) { + s.RegisterService(&Conn_ServiceDesc, srv) +} + +func _Conn_Read_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ReadRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ConnServer).Read(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Conn_Read_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ConnServer).Read(ctx, req.(*ReadRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Conn_Write_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(WriteRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ConnServer).Write(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Conn_Write_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ConnServer).Write(ctx, req.(*WriteRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Conn_Close_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(emptypb.Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ConnServer).Close(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Conn_Close_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ConnServer).Close(ctx, req.(*emptypb.Empty)) + } + return interceptor(ctx, in, info, handler) +} + +func _Conn_SetDeadline_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SetDeadlineRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ConnServer).SetDeadline(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Conn_SetDeadline_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ConnServer).SetDeadline(ctx, req.(*SetDeadlineRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Conn_SetReadDeadline_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SetDeadlineRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ConnServer).SetReadDeadline(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Conn_SetReadDeadline_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ConnServer).SetReadDeadline(ctx, req.(*SetDeadlineRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Conn_SetWriteDeadline_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SetDeadlineRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ConnServer).SetWriteDeadline(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Conn_SetWriteDeadline_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ConnServer).SetWriteDeadline(ctx, req.(*SetDeadlineRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// Conn_ServiceDesc is the grpc.ServiceDesc for Conn service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var Conn_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "net.conn.Conn", + HandlerType: (*ConnServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Read", + Handler: _Conn_Read_Handler, + }, + { + MethodName: "Write", + Handler: _Conn_Write_Handler, + }, + { + MethodName: "Close", + Handler: _Conn_Close_Handler, + }, + { + MethodName: "SetDeadline", + Handler: _Conn_SetDeadline_Handler, + }, + { + MethodName: "SetReadDeadline", + Handler: _Conn_SetReadDeadline_Handler, + }, + { + MethodName: "SetWriteDeadline", + Handler: _Conn_SetWriteDeadline_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "net/conn/conn.proto", +} diff --git a/vm/rpc.go b/vm/rpc.go new file mode 100644 index 0000000..b653576 --- /dev/null +++ b/vm/rpc.go @@ -0,0 +1,676 @@ +package vm + +import ( + "context" + "errors" + "fmt" + "sort" + "time" + + abci "github.com/cometbft/cometbft/abci/types" + tmbytes "github.com/cometbft/cometbft/libs/bytes" + tmmath "github.com/cometbft/cometbft/libs/math" + tmquery "github.com/cometbft/cometbft/libs/pubsub/query" + mempl "github.com/cometbft/cometbft/mempool" + "github.com/cometbft/cometbft/p2p" + "github.com/cometbft/cometbft/proxy" + ctypes "github.com/cometbft/cometbft/rpc/core/types" + rpctypes "github.com/cometbft/cometbft/rpc/jsonrpc/types" + "github.com/cometbft/cometbft/store" + "github.com/cometbft/cometbft/types" + "github.com/cometbft/cometbft/version" + + "github.com/consideritdone/landslidevm/jsonrpc" +) + +type RPC struct { + vm *LandslideVM +} + +func NewRPC(vm *LandslideVM) *RPC { + return &RPC{vm} +} + +func (rpc *RPC) Routes() map[string]*jsonrpc.RPCFunc { + return map[string]*jsonrpc.RPCFunc{ + // subscribe/unsubscribe are reserved for websocket events. + // "subscribe": jsonrpc.NewWSRPCFunc(rpc.Subscribe, "query"), + // "unsubscribe": jsonrpc.NewWSRPCFunc(rpc.Unsubscribe, "query"), + // "unsubscribe_all": jsonrpc.NewWSRPCFunc(rpc.UnsubscribeAll, ""), + + // info AP + "health": jsonrpc.NewRPCFunc(rpc.Health, ""), + "status": jsonrpc.NewRPCFunc(rpc.Status, ""), + "net_info": jsonrpc.NewRPCFunc(rpc.NetInfo, ""), + "blockchain": jsonrpc.NewRPCFunc(rpc.BlockchainInfo, "minHeight,maxHeight", jsonrpc.Cacheable()), + "genesis": jsonrpc.NewRPCFunc(rpc.Genesis, "", jsonrpc.Cacheable()), + "genesis_chunked": jsonrpc.NewRPCFunc(rpc.GenesisChunked, "chunk", jsonrpc.Cacheable()), + "block": jsonrpc.NewRPCFunc(rpc.Block, "height", jsonrpc.Cacheable("height")), + "block_by_hash": jsonrpc.NewRPCFunc(rpc.BlockByHash, "hash", jsonrpc.Cacheable()), + "block_results": jsonrpc.NewRPCFunc(rpc.BlockResults, "height", jsonrpc.Cacheable("height")), + "commit": jsonrpc.NewRPCFunc(rpc.Commit, "height", jsonrpc.Cacheable("height")), + // "header": jsonrpc.NewRPCFunc(rpc.Header, "height", jsonrpc.Cacheable("height")), + // "header_by_hash": jsonrpc.NewRPCFunc(rpc.HeaderByHash, "hash", jsonrpc.Cacheable()), + "check_tx": jsonrpc.NewRPCFunc(rpc.CheckTx, "tx"), + "tx": jsonrpc.NewRPCFunc(rpc.Tx, "hash,prove", jsonrpc.Cacheable()), + // "consensus_state": jsonrpc.NewRPCFunc(rpc.GetConsensusState, ""), + "unconfirmed_txs": jsonrpc.NewRPCFunc(rpc.UnconfirmedTxs, "limit"), + "num_unconfirmed_txs": jsonrpc.NewRPCFunc(rpc.NumUnconfirmedTxs, ""), + "tx_search": jsonrpc.NewRPCFunc(rpc.TxSearch, "query,prove,page,per_page,order_by"), + "block_search": jsonrpc.NewRPCFunc(rpc.BlockSearch, "query,page,per_page,order_by"), + "validators": jsonrpc.NewRPCFunc(rpc.Validators, "height,page,per_page", jsonrpc.Cacheable("height")), + "dump_consensus_state": jsonrpc.NewRPCFunc(rpc.DumpConsensusState, ""), + "consensus_params": jsonrpc.NewRPCFunc(rpc.ConsensusParams, "height", jsonrpc.Cacheable("height")), + + // tx broadcast API + // "broadcast_tx_commit": jsonrpc.NewRPCFunc(rpc.BroadcastTxCommit, "tx"), + "broadcast_tx_sync": jsonrpc.NewRPCFunc(rpc.BroadcastTxSync, "tx"), + "broadcast_tx_async": jsonrpc.NewRPCFunc(rpc.BroadcastTxAsync, "tx"), + + // abci API + "abci_query": jsonrpc.NewRPCFunc(rpc.ABCIQuery, "path,data,height,prove"), + "abci_info": jsonrpc.NewRPCFunc(rpc.ABCIInfo, "", jsonrpc.Cacheable()), + + // evidence API + // "broadcast_evidence": jsonrpc.NewRPCFunc(rpc.BroadcastEvidence, "evidence"), + } +} + +// UnconfirmedTxs gets unconfirmed transactions (maximum ?limit entries) +// including their number. +func (rpc *RPC) UnconfirmedTxs(_ *rpctypes.Context, limitPtr *int) (*ctypes.ResultUnconfirmedTxs, error) { + // reuse per_page validator + limit := validatePerPage(limitPtr) + txs := rpc.vm.mempool.ReapMaxTxs(limit) + return &ctypes.ResultUnconfirmedTxs{ + Count: len(txs), + Total: rpc.vm.mempool.Size(), + TotalBytes: rpc.vm.mempool.SizeBytes(), + Txs: txs, + }, nil +} + +// NumUnconfirmedTxs gets number of unconfirmed transactions. +func (rpc *RPC) NumUnconfirmedTxs(*rpctypes.Context) (*ctypes.ResultUnconfirmedTxs, error) { + return &ctypes.ResultUnconfirmedTxs{ + Count: rpc.vm.mempool.Size(), + Total: rpc.vm.mempool.Size(), + TotalBytes: rpc.vm.mempool.SizeBytes(), + }, nil +} + +// CheckTx checks the transaction without executing it. The transaction won't +// be added to the mempool either. +func (rpc *RPC) CheckTx(_ *rpctypes.Context, tx types.Tx) (*ctypes.ResultCheckTx, error) { + res, err := rpc.vm.app.Mempool().CheckTx(context.TODO(), &abci.RequestCheckTx{Tx: tx}) + if err != nil { + return nil, err + } + return &ctypes.ResultCheckTx{ResponseCheckTx: *res}, nil +} + +func (rpc *RPC) ABCIInfo(_ context.Context) (*ctypes.ResultABCIInfo, error) { + resInfo, err := rpc.vm.app.Query().Info(context.TODO(), proxy.RequestInfo) + if err != nil { + return nil, err + } + return &ctypes.ResultABCIInfo{Response: *resInfo}, nil +} + +func (rpc *RPC) ABCIQuery( + _ *rpctypes.Context, + path string, + data tmbytes.HexBytes, + height int64, + prove bool, +) (*ctypes.ResultABCIQuery, error) { + resQuery, err := rpc.vm.app.Query().Query(context.TODO(), &abci.RequestQuery{ + Path: path, + Data: data, + Height: height, + Prove: prove, + }) + if err != nil { + return nil, err + } + + return &ctypes.ResultABCIQuery{Response: *resQuery}, nil +} + +func (rpc *RPC) BroadcastTxAsync(_ context.Context, tx types.Tx) (*ctypes.ResultBroadcastTx, error) { + err := rpc.vm.mempool.CheckTx(tx, nil, mempl.TxInfo{}) + if err != nil { + return nil, err + } + return &ctypes.ResultBroadcastTx{Hash: tx.Hash()}, nil +} + +func (rpc *RPC) BroadcastTxSync(_ context.Context, tx types.Tx) (*ctypes.ResultBroadcastTx, error) { + resCh := make(chan *abci.ResponseCheckTx, 1) + err := rpc.vm.mempool.CheckTx(tx, func(res *abci.ResponseCheckTx) { + resCh <- res + }, mempl.TxInfo{}) + if err != nil { + return nil, err + } + res := <-resCh + return &ctypes.ResultBroadcastTx{ + Code: res.GetCode(), + Data: res.GetData(), + Log: res.GetLog(), + Codespace: res.GetCodespace(), + Hash: tx.Hash(), + }, nil +} + +// filterMinMax returns error if either min or max are negative or min > max +// if 0, use blockstore base for min, latest block height for max +// enforce limit. +func filterMinMax(base, height, min, max, limit int64) (int64, int64, error) { + // filter negatives + if min < 0 || max < 0 { + return min, max, fmt.Errorf("heights must be non-negative") + } + + // adjust for default values + if min == 0 { + min = 1 + } + if max == 0 { + max = height + } + + // limit max to the height + max = tmmath.MinInt64(height, max) + + // limit min to the base + min = tmmath.MaxInt64(base, min) + + // limit min to within `limit` of max + // so the total number of blocks returned will be `limit` + min = tmmath.MaxInt64(min, max-limit+1) + + if min > max { + return min, max, fmt.Errorf("min height %d can't be greater than max height %d", min, max) + } + return min, max, nil +} + +func (rpc *RPC) BlockchainInfo( + _ *rpctypes.Context, + minHeight, maxHeight int64, +) (*ctypes.ResultBlockchainInfo, error) { + // maximum 20 block metas + const limit int64 = 20 + var err error + minHeight, maxHeight, err = filterMinMax( + rpc.vm.blockStore.Base(), + rpc.vm.blockStore.Height(), + minHeight, + maxHeight, + limit) + if err != nil { + return nil, err + } + rpc.vm.logger.Debug("BlockchainInfoHandler", "maxHeight", maxHeight, "minHeight", minHeight) + + var blockMetas []*types.BlockMeta + for height := maxHeight; height >= minHeight; height-- { + blockMeta := rpc.vm.blockStore.LoadBlockMeta(height) + blockMetas = append(blockMetas, blockMeta) + } + + return &ctypes.ResultBlockchainInfo{ + LastHeight: rpc.vm.blockStore.Height(), + BlockMetas: blockMetas, + }, nil +} + +func (rpc *RPC) Genesis(_ *rpctypes.Context) (*ctypes.ResultGenesis, error) { + if len(rpc.vm.genChunks) > 1 { + return nil, errors.New("genesis response is large, please use the genesis_chunked API instead") + } + + return &ctypes.ResultGenesis{Genesis: rpc.vm.genesis}, nil +} + +func (rpc *RPC) GenesisChunked(_ *rpctypes.Context, chunk uint) (*ctypes.ResultGenesisChunk, error) { + if rpc.vm.genChunks == nil { + return nil, fmt.Errorf("service configuration error, genesis chunks are not initialized") + } + + if len(rpc.vm.genChunks) == 0 { + return nil, fmt.Errorf("service configuration error, there are no chunks") + } + + id := int(chunk) + + if id > len(rpc.vm.genChunks)-1 { + return nil, fmt.Errorf("there are %d chunks, %d is invalid", len(rpc.vm.genChunks)-1, id) + } + + return &ctypes.ResultGenesisChunk{ + TotalChunks: len(rpc.vm.genChunks), + ChunkNumber: id, + Data: rpc.vm.genChunks[id], + }, nil +} + +// ToDo: no peers, because it's vm +func (rpc *RPC) NetInfo(_ *rpctypes.Context) (*ctypes.ResultNetInfo, error) { + return nil, nil +} + +// ToDo: we doesn't have consensusState +func (rpc *RPC) DumpConsensusState(_ *rpctypes.Context) (*ctypes.ResultDumpConsensusState, error) { + return nil, nil +} + +// ToDo: we doesn't have consensusState +func (rpc *RPC) GetConsensusState(_ *rpctypes.Context) (*ctypes.ResultConsensusState, error) { + return nil, nil +} + +func (rpc *RPC) ConsensusParams( + _ *rpctypes.Context, + heightPtr *int64, +) (*ctypes.ResultConsensusParams, error) { + return &ctypes.ResultConsensusParams{ + BlockHeight: rpc.vm.blockStore.Height(), + ConsensusParams: *rpc.vm.genesis.ConsensusParams, + }, nil +} + +func (rpc *RPC) Health(*rpctypes.Context) (*ctypes.ResultHealth, error) { + return &ctypes.ResultHealth{}, nil +} + +// bsHeight can be either latest committed or uncommitted (+1) height. +func getHeight(bs *store.BlockStore, heightPtr *int64) (int64, error) { + bsHeight := bs.Height() + if heightPtr != nil { + height := *heightPtr + if height <= 0 { + return 0, fmt.Errorf("height must be greater than 0, but got %d", height) + } + if height > bsHeight { + return 0, fmt.Errorf("height %d must be less than or equal to the current blockchain height %d", height, bsHeight) + } + bsBase := bs.Base() + if height < bsBase { + return 0, fmt.Errorf("height %d is not available, lowest height is %d", height, bsBase) + } + return height, nil + } + return bsHeight, nil +} + +func (rpc *RPC) Block(_ *rpctypes.Context, heightPtr *int64) (*ctypes.ResultBlock, error) { + height, err := getHeight(rpc.vm.blockStore, heightPtr) + if err != nil { + return nil, err + } + block := rpc.vm.blockStore.LoadBlock(height) + blockMeta := rpc.vm.blockStore.LoadBlockMeta(height) + + if blockMeta == nil { + return &ctypes.ResultBlock{BlockID: types.BlockID{}, Block: block}, nil + } + return &ctypes.ResultBlock{BlockID: blockMeta.BlockID, Block: block}, nil +} + +func (rpc *RPC) BlockByHash(_ *rpctypes.Context, hash []byte) (*ctypes.ResultBlock, error) { + block := rpc.vm.blockStore.LoadBlockByHash(hash) + if block == nil { + return &ctypes.ResultBlock{BlockID: types.BlockID{}, Block: nil}, nil + } + blockMeta := rpc.vm.blockStore.LoadBlockMeta(block.Height) + return &ctypes.ResultBlock{BlockID: blockMeta.BlockID, Block: block}, nil +} + +func (rpc *RPC) BlockResults(_ *rpctypes.Context, heightPtr *int64) (*ctypes.ResultBlockResults, error) { + // height, err := getHeight(rpc.vm.blockStore, args.Height) + // if err != nil { + // return err + // } + + // TODO make IBC reply it realised in landslidevm, but not realised in comet bft + // results, err := rpc.vm.stateStore. + // if err != nil { + // return err + // } + + // reply.Height = height + // reply.TxsResults = results.DeliverTxs + // reply.BeginBlockEvents = results.BeginBlock.Events + // reply.EndBlockEvents = results.EndBlock.Events + // reply.ValidatorUpdates = results.EndBlock.ValidatorUpdates + // reply.ConsensusParamUpdates = results.EndBlock.ConsensusParamUpdates + return nil, nil +} + +func (rpc *RPC) Commit(_ *rpctypes.Context, heightPtr *int64) (*ctypes.ResultCommit, error) { + height, err := getHeight(rpc.vm.blockStore, heightPtr) + if err != nil { + return nil, err + } + + blockMeta := rpc.vm.blockStore.LoadBlockMeta(height) + if blockMeta == nil { + return nil, nil + } + + header := blockMeta.Header + commit := rpc.vm.blockStore.LoadBlockCommit(height) + + return ctypes.NewResultCommit(&header, commit, !(height == rpc.vm.blockStore.Height())), nil +} + +var ( + // see README + defaultPerPage = 30 + maxPerPage = 100 +) + +func validatePerPage(perPagePtr *int) int { + if perPagePtr == nil { // no per_page parameter + return defaultPerPage + } + + perPage := *perPagePtr + if perPage < 1 { + return defaultPerPage + } else if perPage > maxPerPage { + return maxPerPage + } + return perPage +} + +func validatePage(pagePtr *int, perPage, totalCount int) (int, error) { + if perPage < 1 { + panic(fmt.Sprintf("zero or negative perPage: %d", perPage)) + } + + if pagePtr == nil { // no page parameter + return 1, nil + } + + pages := ((totalCount - 1) / perPage) + 1 + if pages == 0 { + pages = 1 // one page (even if it's empty) + } + page := *pagePtr + if page <= 0 || page > pages { + return 1, fmt.Errorf("page should be within [1, %d] range, given %d", pages, page) + } + + return page, nil +} + +func validateSkipCount(page, perPage int) int { + skipCount := (page - 1) * perPage + if skipCount < 0 { + return 0 + } + return skipCount +} + +func (rpc *RPC) Validators( + _ *rpctypes.Context, + heightPtr *int64, + pagePtr, perPagePtr *int, +) (*ctypes.ResultValidators, error) { + height, err := getHeight(rpc.vm.blockStore, heightPtr) + if err != nil { + return nil, err + } + + validators, err := rpc.vm.stateStore.LoadValidators(height) + if err != nil { + return nil, err + } + + totalCount := len(validators.Validators) + perPage := validatePerPage(perPagePtr) + page, err := validatePage(pagePtr, perPage, totalCount) + if err != nil { + return nil, err + } + + skipCount := validateSkipCount(page, perPage) + + v := validators.Validators[skipCount : skipCount+tmmath.MinInt(perPage, totalCount-skipCount)] + + return &ctypes.ResultValidators{ + BlockHeight: height, + Validators: v, + Count: len(v), + Total: totalCount, + }, nil +} + +func (rpc *RPC) Tx(_ *rpctypes.Context, hash []byte, prove bool) (*ctypes.ResultTx, error) { + r, err := rpc.vm.txIndexer.Get(hash) + if err != nil { + return nil, err + } + + if r == nil { + return nil, fmt.Errorf("tx (%X) not found", hash) + } + + height := r.Height + index := r.Index + + var proof types.TxProof + if prove { + block := rpc.vm.blockStore.LoadBlock(height) + proof = block.Data.Txs.Proof(int(index)) // XXX: overflow on 32-bit machines + } + + return &ctypes.ResultTx{ + Hash: hash, + Height: r.Height, + Index: r.Index, + TxResult: r.Result, + Tx: r.Tx, + Proof: proof, + }, nil +} + +func (rpc *RPC) TxSearch( + ctx *rpctypes.Context, + query string, + prove bool, + pagePtr, perPagePtr *int, + orderBy string, +) (*ctypes.ResultTxSearch, error) { + q, err := tmquery.New(query) + if err != nil { + return nil, err + } + + results, err := rpc.vm.txIndexer.Search(ctx.Context(), q) + if err != nil { + return nil, err + } + + // sort results (must be done before pagination) + switch orderBy { + case "desc": + sort.Slice(results, func(i, j int) bool { + if results[i].Height == results[j].Height { + return results[i].Index > results[j].Index + } + return results[i].Height > results[j].Height + }) + case "asc", "": + sort.Slice(results, func(i, j int) bool { + if results[i].Height == results[j].Height { + return results[i].Index < results[j].Index + } + return results[i].Height < results[j].Height + }) + default: + return nil, errors.New("expected order_by to be either `asc` or `desc` or empty") + } + + // paginate results + totalCount := len(results) + perPage := validatePerPage(perPagePtr) + + page, err := validatePage(pagePtr, perPage, totalCount) + if err != nil { + return nil, err + } + + skipCount := validateSkipCount(page, perPage) + pageSize := tmmath.MinInt(perPage, totalCount-skipCount) + + apiResults := make([]*ctypes.ResultTx, 0, pageSize) + for i := skipCount; i < skipCount+pageSize; i++ { + r := results[i] + + var proof types.TxProof + if prove { + block := rpc.vm.blockStore.LoadBlock(r.Height) + proof = block.Data.Txs.Proof(int(r.Index)) // XXX: overflow on 32-bit machines + } + + apiResults = append(apiResults, &ctypes.ResultTx{ + Hash: types.Tx(r.Tx).Hash(), + Height: r.Height, + Index: r.Index, + TxResult: r.Result, + Tx: r.Tx, + Proof: proof, + }) + } + + return &ctypes.ResultTxSearch{Txs: apiResults, TotalCount: totalCount}, nil +} + +func (rpc *RPC) BlockSearch( + ctx *rpctypes.Context, + query string, + pagePtr, perPagePtr *int, + orderBy string, +) (*ctypes.ResultBlockSearch, error) { + q, err := tmquery.New(query) + if err != nil { + return nil, err + } + + results, err := rpc.vm.blockIndexer.Search(ctx.Context(), q) + if err != nil { + return nil, err + } + + // sort results (must be done before pagination) + switch orderBy { + case "desc", "": + sort.Slice(results, func(i, j int) bool { return results[i] > results[j] }) + + case "asc": + sort.Slice(results, func(i, j int) bool { return results[i] < results[j] }) + + default: + return nil, errors.New("expected order_by to be either `asc` or `desc` or empty") + } + + // paginate results + totalCount := len(results) + perPage := validatePerPage(perPagePtr) + + page, err := validatePage(pagePtr, perPage, totalCount) + if err != nil { + return nil, err + } + + skipCount := validateSkipCount(page, perPage) + pageSize := tmmath.MinInt(perPage, totalCount-skipCount) + + apiResults := make([]*ctypes.ResultBlock, 0, pageSize) + for i := skipCount; i < skipCount+pageSize; i++ { + block := rpc.vm.blockStore.LoadBlock(results[i]) + if block != nil { + blockMeta := rpc.vm.blockStore.LoadBlockMeta(block.Height) + if blockMeta != nil { + apiResults = append(apiResults, &ctypes.ResultBlock{ + Block: block, + BlockID: blockMeta.BlockID, + }) + } + } + } + + return &ctypes.ResultBlockSearch{Blocks: apiResults, TotalCount: totalCount}, nil +} + +func (rpc *RPC) Status(_ *rpctypes.Context) (*ctypes.ResultStatus, error) { + var ( + earliestBlockHeight int64 + earliestBlockHash tmbytes.HexBytes + earliestAppHash tmbytes.HexBytes + earliestBlockTimeNano int64 + ) + + if earliestBlockMeta := rpc.vm.blockStore.LoadBaseMeta(); earliestBlockMeta != nil { + earliestBlockHeight = earliestBlockMeta.Header.Height + earliestAppHash = earliestBlockMeta.Header.AppHash + earliestBlockHash = earliestBlockMeta.BlockID.Hash + earliestBlockTimeNano = earliestBlockMeta.Header.Time.UnixNano() + } + + var ( + latestBlockHash tmbytes.HexBytes + latestAppHash tmbytes.HexBytes + latestBlockTimeNano int64 + + latestHeight = rpc.vm.blockStore.Height() + ) + + if latestHeight != 0 { + if latestBlockMeta := rpc.vm.blockStore.LoadBlockMeta(latestHeight); latestBlockMeta != nil { + latestBlockHash = latestBlockMeta.BlockID.Hash + latestAppHash = latestBlockMeta.Header.AppHash + latestBlockTimeNano = latestBlockMeta.Header.Time.UnixNano() + } + } + + result := &ctypes.ResultStatus{ + NodeInfo: p2p.DefaultNodeInfo{ + ProtocolVersion: p2p.NewProtocolVersion( + version.P2PProtocol, + version.BlockProtocol, + 0, + ), + DefaultNodeID: p2p.ID(rpc.vm.appOpts.NodeId), + ListenAddr: "", + Network: fmt.Sprintf("%d", rpc.vm.appOpts.NetworkId), + Version: version.TMCoreSemVer, + Channels: nil, + Moniker: "", + Other: p2p.DefaultNodeInfoOther{}, + }, + SyncInfo: ctypes.SyncInfo{ + LatestBlockHash: latestBlockHash, + LatestAppHash: latestAppHash, + LatestBlockHeight: latestHeight, + LatestBlockTime: time.Unix(0, latestBlockTimeNano), + EarliestBlockHash: earliestBlockHash, + EarliestAppHash: earliestAppHash, + EarliestBlockHeight: earliestBlockHeight, + EarliestBlockTime: time.Unix(0, earliestBlockTimeNano), + CatchingUp: false, + }, + // TODO: use internal app validators instead + ValidatorInfo: ctypes.ValidatorInfo{ + Address: proposerPubKey.Address(), + PubKey: proposerPubKey, + VotingPower: 0, + }, + } + + return result, nil +} diff --git a/vm/rpc_test.go b/vm/rpc_test.go new file mode 100644 index 0000000..77456c0 --- /dev/null +++ b/vm/rpc_test.go @@ -0,0 +1,59 @@ +package vm + +import ( + "context" + "net/http" + "testing" + "time" + + ctypes "github.com/cometbft/cometbft/rpc/core/types" + "github.com/cometbft/cometbft/rpc/jsonrpc/client" + "github.com/stretchr/testify/require" + + "github.com/consideritdone/landslidevm/jsonrpc" +) + +func setupRPC(t *testing.T) (*http.Server, *LandslideVM, *client.Client) { + vm := newFreshKvApp(t) + vmLnd := vm.(*LandslideVM) + mux := http.NewServeMux() + jsonrpc.RegisterRPCFuncs(mux, NewRPC(vmLnd).Routes(), vmLnd.logger) + + address := "127.0.0.1:44444" + server := &http.Server{Addr: address, Handler: mux} + go func() { + server.ListenAndServe() + //panic(err) + //require.NoError(t, err) + }() + + // wait for servers to start + time.Sleep(time.Second * 2) + + client, err := client.New("tcp://" + address) + require.NoError(t, err) + + return server, vmLnd, client +} + +func TestHealth(t *testing.T) { + server, _, client := setupRPC(t) + defer server.Close() + + result := new(ctypes.ResultHealth) + _, err := client.Call(context.Background(), "health", map[string]interface{}{}, result) + require.NoError(t, err) + + t.Logf("Health result %+v", result) +} + +func TestStatus(t *testing.T) { + server, _, client := setupRPC(t) + defer server.Close() + + result := new(ctypes.ResultStatus) + _, err := client.Call(context.Background(), "status", map[string]interface{}{}, result) + require.NoError(t, err) + + t.Logf("Status result %+v", result) +} diff --git a/vm/vm.go b/vm/vm.go index d72066a..b1d7e58 100644 --- a/vm/vm.go +++ b/vm/vm.go @@ -6,6 +6,7 @@ import ( "encoding/json" "errors" "fmt" + http2 "net/http" "os" "slices" "sync" @@ -15,6 +16,7 @@ import ( abcitypes "github.com/cometbft/cometbft/abci/types" "github.com/cometbft/cometbft/config" "github.com/cometbft/cometbft/consensus" + "github.com/cometbft/cometbft/crypto/secp256k1" "github.com/cometbft/cometbft/libs/log" "github.com/cometbft/cometbft/mempool" "github.com/cometbft/cometbft/node" @@ -35,6 +37,10 @@ import ( "google.golang.org/protobuf/types/known/timestamppb" "github.com/consideritdone/landslidevm/database" + "github.com/consideritdone/landslidevm/grpcutils" + "github.com/consideritdone/landslidevm/http" + "github.com/consideritdone/landslidevm/jsonrpc" + httppb "github.com/consideritdone/landslidevm/proto/http" "github.com/consideritdone/landslidevm/proto/rpcdb" vmpb "github.com/consideritdone/landslidevm/proto/vm" vmtypes "github.com/consideritdone/landslidevm/vm/types" @@ -56,7 +62,9 @@ var ( dbPrefixTxIndexer = []byte("tx-indexer") dbPrefixBlockIndexer = []byte("block-indexer") + //TODO: use internal app validators instead proposerAddress = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} + proposerPubKey = secp256k1.PubKey{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} Version = "0.0.0" @@ -87,13 +95,14 @@ type ( allowShutdown *vmtypes.Atomic[bool] processMetrics prometheus.Gatherer - serverCloser closer.ServerCloser + serverCloser grpcutils.ServerCloser connCloser closer.Closer database dbm.DB databaseClient rpcdb.DatabaseClient appCreator AppCreator app proxy.AppConns + appOpts *AppCreatorOpts logger log.Logger blockStore *store.BlockStore @@ -183,7 +192,7 @@ func (vm *LandslideVM) Initialize(_ context.Context, req *vmpb.InitializeRequest dbStateStore := dbm.NewPrefixDB(vm.database, dbPrefixStateStore) vm.stateStore = state.NewStore(dbStateStore, state.StoreOptions{DiscardABCIResponses: false}) - app, err := vm.appCreator(&AppCreatorOpts{ + vm.appOpts = &AppCreatorOpts{ NetworkId: req.NetworkId, SubnetId: req.SubnetId, ChainId: req.CChainId, @@ -195,7 +204,8 @@ func (vm *LandslideVM) Initialize(_ context.Context, req *vmpb.InitializeRequest GenesisBytes: req.GenesisBytes, UpgradeBytes: req.UpgradeBytes, ConfigBytes: req.ConfigBytes, - }) + } + app, err := vm.appCreator(vm.appOpts) if err != nil { return nil, err } @@ -375,7 +385,29 @@ func (vm *LandslideVM) Shutdown(context.Context, *emptypb.Empty) (*emptypb.Empty // CreateHandlers creates the HTTP handlers for custom chain network calls. func (vm *LandslideVM) CreateHandlers(context.Context, *emptypb.Empty) (*vmpb.CreateHandlersResponse, error) { - return nil, nil + server := grpcutils.NewServer() + vm.serverCloser.Add(server) + + mux := http2.NewServeMux() + jsonrpc.RegisterRPCFuncs(mux, NewRPC(vm).Routes(), vm.logger) + + httppb.RegisterHTTPServer(server, http.NewServer(mux)) + + listener, err := grpcutils.NewListener() + if err != nil { + return nil, err + } + + go grpcutils.Serve(listener, server) + + return &vmpb.CreateHandlersResponse{ + Handlers: []*vmpb.Handler{ + { + Prefix: "/rpc", + ServerAddr: listener.Addr().String(), + }, + }, + }, nil } func (vm *LandslideVM) Connected(context.Context, *vmpb.ConnectedRequest) (*emptypb.Empty, error) {