grpc

package module
v0.0.0-...-befa80f Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 27, 2026 License: BSD-3-Clause Imports: 15 Imported by: 0

README

go-ruby-grpc/grpc

grpc — go-ruby-grpc

Docs License Go Coverage

A pure-Go (no cgo), MRI-faithful reimplementation of the surface of Ruby's grpc gem — the GRPC object model a Ruby program uses to build servers and stubs — without any Ruby runtime and without the gem's C extension (upstream grpc ships as a heavy C extension around the gRPC-core C library).

It does not reimplement HTTP/2 or the gRPC wire protocol. It is a Ruby-faithful API layer on top of google.golang.org/grpc and google.golang.org/protobuf, the official pure-Go gRPC and protobuf runtimes. Every byte on the wire is produced by the canonical Go gRPC stack, so a server built here interoperates with any conformant gRPC peer and a stub built here can call any conformant gRPC server — by construction.

It is the gRPC binding for go-embedded-ruby, and it reuses go-ruby-protobuf (the pure-Go google-protobuf gem) for the message layer. It is a sibling of go-ruby-oauth2, go-ruby-regexp and go-ruby-net-http.

The network is a host seam. The core server logic and client stub never touch a socket: both the listener and the dialer come from an injected Transport. NetTransport is the production transport (real TCP); MemTransport is an in-process, bufconn-backed transport that carries a real HTTP/2 gRPC session over an in-memory pipe. So the whole stack is exercised end-to-end in tests without binding a port — mirroring the host seam the OIDC/OAuth2 bindings use for their HTTP round-trip.

Features

Faithful port of the grpc gem's server and client surface:

  • GRPC::RpcServerRpcServer#add_http2_portAddHTTP2Port, #handleHandle, #run / #run_till_terminatedRun / RunTillTerminated, #stopStop.
  • GRPC::ClientStubClientStub — all four cardinalities: #request_responseRequestResponse, #client_streamerClientStreamer, #server_streamerServerStreamer, #bidi_streamerBidiStreamer; per-call deadlines and metadata (a Hash).
  • GRPC::ActiveCallActiveCallSend, Read, EachRemoteRead, Metadata, Deadline over one call.
  • GRPC::Core::StatusCodes → the StatusCode constants (OK, InvalidArgument, DeadlineExceeded, …, all 17), with the gem's SCREAMING_SNAKE names.
  • GRPC::BadStatus*BadStatus (code, details, trailing metadata, to_status), and GRPC::Core::CallError*CallError; full error mapping to and from the gRPC runtime.
  • Message-agnostic, exactly like the gem: each call carries a Marshal / Unmarshal function (the marshal/unmarshal procs a generated *_services_pb.rb attaches to a RpcDesc). Messages from go-ruby-protobuf drop straight in via its Encode / Decode.

CGO-free, 100% test coverage, -race clean, gofmt + go vet clean, and green across the six 64-bit Go targets (amd64, arm64, riscv64, loong64, ppc64le, s390x — including the big-endian s390x).

Install

go get github.com/go-ruby-grpc/grpc

Usage

package main

import (
	"fmt"

	grpc "github.com/go-ruby-grpc/grpc"
)

func main() {
	tr := grpc.NewMemTransport() // or grpc.NetTransport{} in production

	// --- server: mirrors GRPC::RpcServer ---
	srv := grpc.NewRpcServer(grpc.WithTransport(tr))
	srv.AddHTTP2Port("localhost:50051", ":this_port_is_insecure")
	srv.Handle(grpc.Service{
		Name: "helloworld.Greeter",
		Methods: []grpc.Method{{
			Name:             "SayHello",
			Type:             grpc.Unary,
			RequestUnmarshal: func(b []byte) (any, error) { return string(b), nil },
			ResponseMarshal:  func(m any) ([]byte, error) { return []byte(m.(string)), nil },
			UnaryHandler: func(req any, call *grpc.ActiveCall) (any, error) {
				return "Hello " + req.(string), nil
			},
		}},
	})
	go srv.Run()
	defer srv.Stop()

	// --- client: mirrors GRPC::ClientStub ---
	stub, _ := grpc.NewClientStub("localhost:50051", ":this_channel_is_insecure",
		grpc.WithStubTransport(tr))
	defer stub.Close()

	resp, _ := stub.RequestResponse("/helloworld.Greeter/SayHello", "world", grpc.CallOptions{
		Marshal:   func(m any) ([]byte, error) { return []byte(m.(string)), nil },
		Unmarshal: func(b []byte) (any, error) { return string(b), nil },
		Metadata:  grpc.Metadata{"x-trace": "abc"},
	})
	fmt.Println(resp) // Hello world
}

Streaming

ClientStreamer, ServerStreamer and BidiStreamer mirror the gem's streaming helpers; a streaming handler uses ActiveCall.Read / EachRemoteRead to consume requests and ActiveCall.Send to emit responses.

Generated services & codegen

Real gem usage rarely builds a Service by hand: a .proto's service block is compiled by grpc_tools_ruby_protoc into a *_services_pb.rb that declares a GRPC::GenericService base class and its Stub. This package ports both halves.

  • GRPC::GenericServiceGenericServiceNewGenericService(name) then RPC(...) per rpc (the gem's rpc :Name, In, Out macro). BuildService pairs the declarations with handlers to yield a Service to Handle; StubClass derives the client stub (the gem's rpc_stub_class). The generated stub carries each rpc's marshal/unmarshal, so a caller supplies only the request:

    gs := grpc.NewGenericService("helloworld.Greeter").
        RPC(grpc.RpcDesc{Name: "SayHello", Type: grpc.Unary,
            RequestMarshal: enc, RequestUnmarshal: dec,
            ResponseMarshal: enc, ResponseUnmarshal: dec})
    
    svc, _ := gs.BuildService(grpc.Handlers{
        "SayHello": func(req any, call *grpc.ActiveCall) (any, error) {
            return "Hello " + req.(string), nil
        }})
    srv.Handle(svc)
    
    stub := gs.StubClass(clientStub)
    resp, _ := stub.RequestResponse("SayHello", "world", grpc.CallOptions{})
    
  • grpc_tools_ruby_protocGenerateRubyServices — given a .proto's service block (ServiceFile / ServiceGen / MethodGen), it emits the exact *_services_pb.rb source, byte-for-byte as the gem's generator. All four cardinalities (stream(...) on the request and/or response), multiple services per file, dotted and underscored packages, nested and cross-package message types, and package-less files are reproduced. The generated Ruby loads unchanged and binds this runtime through go-embedded-ruby.

    src, _ := grpc.GenerateRubyServices(grpc.ServiceFile{
        ProtoFile: "helloworld.proto", Package: "helloworld",
        Services: []grpc.ServiceGen{{Name: "Greeter", Methods: []grpc.MethodGen{
            {Name: "SayHello", InputType: "helloworld.HelloRequest",
                OutputType: "helloworld.HelloReply"}}}},
    })
    

The generator is checked against the real grpc_tools_ruby_protoc (the grpc-tools gem) as a differential oracle: for each .proto, our output must equal the binary's to the byte; the test skips only when the gem is not installed, and inline goldens still pin the format in that case.

Residual (named, not silent): message-.proto parsing and message codegen stay in go-ruby-protobuf (which also builds descriptors at runtime rather than parsing .proto text); a Ruby constant for a message imported from another package with a nested type is approximated (the common same-package and flat cross-package cases are byte-exact); and TLS/ChannelCredentials, xDS, channelz and health/reflection services remain follow-ups.

Status codes & errors

_ = grpc.NewBadStatus(grpc.InvalidArgument, "bad argument", grpc.Metadata{})
// err.Error() == "3:bad argument"; err.Code.Name() == "INVALID_ARGUMENT"

A handler returns a *BadStatus to fail a call with a specific code; the stub raises the matching *BadStatus on the client side. Any other handler error maps to UNKNOWN, exactly as the gem surfaces a bare exception.

Mapping to the gem

gem this package
GRPC::RpcServer.new NewRpcServer(...)
#add_http2_port(addr, creds) (*RpcServer).AddHTTP2Port
#handle(service) (*RpcServer).Handle
#run / #run_till_terminated (*RpcServer).Run / RunTillTerminated
#stop (*RpcServer).Stop
GRPC::ClientStub.new(host, creds) NewClientStub(host, creds, ...)
#request_response (*ClientStub).RequestResponse
#client_streamer (*ClientStub).ClientStreamer
#server_streamer (*ClientStub).ServerStreamer
#bidi_streamer (*ClientStub).BidiStreamer
GRPC::ActiveCall *ActiveCall
GRPC::Core::StatusCodes::* the StatusCode constants
GRPC::BadStatus *BadStatus
GRPC::Core::CallError *CallError
metadata (a Hash) Metadata (map[string]string)
generated marshal/unmarshal procs Marshaler / Unmarshaler per call
GRPC::GenericService *GenericService
rpc :Name, In, Out (*GenericService).RPC
Service.rpc_stub_class (*GenericService).StubClass*GenericStub
grpc_tools_ruby_protoc GenerateRubyServices

Tests & coverage

The suite drives every RPC cardinality (unary + the three streaming shapes), status-code and deadline propagation, metadata round-trips and error mapping over the in-memory transport, verified against a real google.golang.org/grpc server and client as the oracle: our stub calls a plain grpc-go server and a plain grpc-go client calls our RpcServer, both over bufconn, so protocol conformance is checked end-to-end — not asserted. A go-ruby-protobuf message is round-tripped through the stack to confirm the message-layer integration.

COVERPKG=$(go list ./... | paste -sd, -)
go test -race -coverpkg="$COVERPKG" -coverprofile=cover.out ./...
go tool cover -func=cover.out | tail -1   # 100.0%

License

BSD-3-Clause — see LICENSE. Copyright the go-ruby-grpc/grpc authors.

WebAssembly

Being pure Go (CGO=0), this library also compiles to WebAssembly — both GOOS=js GOARCH=wasm (browser / Node.js) and GOOS=wasip1 GOARCH=wasm (WASI). CI builds both targets on every push, alongside the six 64-bit native/qemu arches.

GOOS=js     GOARCH=wasm go build ./...   # browser / Node
GOOS=wasip1 GOARCH=wasm go build ./...   # WASI (wasmtime, wasmer, wasmedge, …)

Documentation

Overview

Package grpc is a pure-Go (CGO-free), MRI-faithful reimplementation of the surface of Ruby's grpc gem — the object model a Ruby program sees as the GRPC namespace — without any Ruby runtime and without the gem's C extension (upstream grpc ships as a heavy C extension around the gRPC-core C library).

It does not reimplement HTTP/2 or the gRPC wire protocol: it is a Ruby-faithful API layer built on top of google.golang.org/grpc and google.golang.org/protobuf, the official pure-Go gRPC and protobuf runtimes. Every byte on the wire is therefore produced by the canonical Go gRPC stack, so a server built here interoperates with any conformant gRPC peer and a stub built here can call any conformant gRPC server, by construction.

The network seam

The library never hardwires a socket into its core logic. Both the server's listener and the client's dialer are obtained from an injected Transport. NetTransport is the production transport (real TCP sockets); MemTransport is an in-process, bufconn-backed transport that carries a real HTTP/2 gRPC session over an in-memory pipe, so the whole stack is exercised end-to-end in tests without binding a port. This mirrors how the OIDC/OAuth2 bindings treat the HTTP round-trip as a host seam.

Mapping to the gem

Ruby (grpc)                           Go (this package)
-----------                           -----------------
GRPC::RpcServer.new                   NewRpcServer(...)
  #add_http2_port(addr, creds)          (*RpcServer).AddHTTP2Port
  #handle(service)                      (*RpcServer).Handle
  #run / #run_till_terminated           (*RpcServer).Run / RunTillTerminated
  #stop                                 (*RpcServer).Stop
GRPC::ClientStub.new(host, creds)     NewClientStub(...)
  #request_response                     (*ClientStub).RequestResponse
  #client_streamer                      (*ClientStub).ClientStreamer
  #server_streamer                      (*ClientStub).ServerStreamer
  #bidi_streamer                        (*ClientStub).BidiStreamer
GRPC::ActiveCall                      *ActiveCall
GRPC::Core::StatusCodes               the StatusCode constants (OK, …)
GRPC::BadStatus                       *BadStatus
GRPC::Core::CallError                 *CallError
metadata (a Hash)                     Metadata (map[string]string)
GRPC::GenericService                  *GenericService
  rpc :Name, In, Out                    (*GenericService).RPC
  service.rpc_stub_class                (*GenericService).StubClass → *GenericStub
grpc_tools_ruby_protoc                GenerateRubyServices

Generated services

The generated-service layer mirrors what grpc_tools_ruby_protoc emits: a GenericService is the GRPC::GenericService a generated *_services_pb.rb Service base class includes — it names the service and collects the rpc declarations, then derives the server-side Service to register (GenericService.BuildService) and the client-side stub (GenericService.StubClass, the gem's rpc_stub_class). GenerateRubyServices is the code generator itself: given a .proto's service block it emits the exact *_services_pb.rb source, byte-faithful to grpc_tools_ruby_protoc (asserted against the real binary as the oracle).

Messages

Like the gem, this package is message-agnostic: a call carries a marshal and an unmarshal function, exactly as GRPC::RpcDesc carries the marshal/unmarshal procs a generated *_pb.rb / *_services_pb.rb attaches. Messages produced by github.com/go-ruby-protobuf/protobuf (the pure-Go google-protobuf gem) drop straight in via its Encode/Decode.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func GenerateRubyServices

func GenerateRubyServices(f ServiceFile) (string, error)

GenerateRubyServices emits the *_services_pb.rb Ruby source for f, byte-for-byte as grpc_tools_ruby_protoc would. A service with no methods is skipped (as the gem's generator skips it), while the package modules are still emitted. It errors if the file has no ProtoFile, or a non-empty service or one of its methods has an empty name.

Types

type ActiveCall

type ActiveCall struct {
	// contains filtered or unexported fields
}

ActiveCall mirrors GRPC::ActiveCall: the object a handler or a streaming stub uses to move messages and metadata over a single call. It applies the call's marshal/unmarshal functions so Send/Read speak in messages, not bytes.

The marshal/unmarshal directions are set by whichever side constructs it: on the server Send marshals responses and Read unmarshals requests; on the client it is the reverse.

func (*ActiveCall) Context

func (c *ActiveCall) Context() context.Context

Context exposes the underlying call context for advanced callers.

func (*ActiveCall) Deadline

func (c *ActiveCall) Deadline() (time.Time, bool)

Deadline returns the call deadline and whether one was set, mirroring ActiveCall#deadline.

func (*ActiveCall) EachRemoteRead

func (c *ActiveCall) EachRemoteRead(fn func(msg any) error) error

EachRemoteRead calls fn for every message the peer sends until the stream is half-closed, mirroring ActiveCall#each_remote_read. A non-nil error from fn or from the transport stops the iteration and is returned.

func (*ActiveCall) Metadata

func (c *ActiveCall) Metadata() Metadata

Metadata returns the request metadata the peer sent with the call, mirroring ActiveCall#metadata (a Hash).

func (*ActiveCall) Read

func (c *ActiveCall) Read() (any, error)

Read receives the next message and unmarshals it, mirroring ActiveCall#remote_read. It returns io.EOF when the peer half-closes, so a handler can range over the request stream until EOF.

func (*ActiveCall) Send

func (c *ActiveCall) Send(msg any) error

Send marshals msg and writes it to the call. It mirrors ActiveCall#send / the block-yield a streaming handler uses to emit a response.

type BadStatus

type BadStatus struct {
	Code     StatusCode
	Details  string
	Metadata Metadata
}

BadStatus mirrors GRPC::BadStatus: an operation that finished with a non-OK status. It carries the status code, the detail message and any trailing metadata, and it is the error a client raises when a call fails.

func NewBadStatus

func NewBadStatus(code StatusCode, details string, md Metadata) *BadStatus

NewBadStatus builds a BadStatus, matching GRPC::BadStatus.new(code, details, metadata). A nil metadata becomes an empty Metadata.

func (*BadStatus) Error

func (b *BadStatus) Error() string

Error renders the gem's "<n>:<details>" message, e.g. "3:bad argument".

func (*BadStatus) ToStatus

func (b *BadStatus) ToStatus() (code StatusCode, details string, md Metadata)

ToStatus converts the BadStatus to the gem's Struct::Status shape: a code, its details and trailing metadata. It is what GRPC::BadStatus#to_status returns.

type CallError

type CallError struct {
	Message string
}

CallError mirrors GRPC::Core::CallError: a low-level error from the call machinery itself (as opposed to a non-OK application status). It is raised for misuse such as writing to a finished call.

func NewCallError

func NewCallError(msg string) *CallError

NewCallError builds a CallError with the given message.

func (*CallError) Error

func (e *CallError) Error() string

Error implements error.

type CallOptions

type CallOptions struct {
	Marshal   Marshaler
	Unmarshal Unmarshaler
	Metadata  Metadata
	Deadline  time.Time
}

CallOptions carries the per-call knobs the gem's stub methods accept: the message (un)marshalling, request metadata (a Hash) and a deadline.

type ClientStub

type ClientStub struct {
	// contains filtered or unexported fields
}

ClientStub mirrors GRPC::ClientStub: a handle to a remote service over which typed RPC helpers (request_response, client_streamer, server_streamer, bidi_streamer) are issued. The connection is dialed through the injected Transport, so the stub is exercisable in-process over MemTransport.

func NewClientStub

func NewClientStub(host, creds string, opts ...StubOption) (*ClientStub, error)

NewClientStub mirrors GRPC::ClientStub.new(host, creds). The creds argument is accepted for surface fidelity (pass ":this_channel_is_insecure" for plaintext, as the gem does); TLS is a follow-up. It dials host through the transport.

func (*ClientStub) BidiStreamer

func (s *ClientStub) BidiStreamer(method string, requests []any, opts CallOptions) ([]any, error)

BidiStreamer mirrors GRPC::ClientStub#bidi_streamer: it streams every request and returns every response. Requests are sent first, then responses drained; over a buffered transport this exercises full-duplex handlers.

func (*ClientStub) ClientStreamer

func (s *ClientStub) ClientStreamer(method string, requests []any, opts CallOptions) (any, error)

ClientStreamer mirrors GRPC::ClientStub#client_streamer: it streams every request to method and returns the single response.

func (*ClientStub) Close

func (s *ClientStub) Close() error

Close releases the stub's connection.

func (*ClientStub) RequestResponse

func (s *ClientStub) RequestResponse(method string, req any, opts CallOptions) (any, error)

RequestResponse mirrors GRPC::ClientStub#request_response: a unary call. It marshals req, invokes method (e.g. "/helloworld.Greeter/SayHello"), and returns the unmarshalled response. A non-OK status is returned as *BadStatus.

func (*ClientStub) ServerStreamer

func (s *ClientStub) ServerStreamer(method string, req any, opts CallOptions) ([]any, error)

ServerStreamer mirrors GRPC::ClientStub#server_streamer: it sends one request and returns every response the server streams back.

type GenericService

type GenericService struct {
	// contains filtered or unexported fields
}

GenericService mirrors the GRPC::GenericService mixin a generated *_services_pb.rb Service base class includes: it names the service (the gem's self.service_name) and collects the RPC declarations (the gem's `rpc` class-macro calls). From it, GenericService.BuildService derives the server-side Service to register on an RpcServer, and GenericService.StubClass derives the client-side stub — the gem's rpc_stub_class.

func NewGenericService

func NewGenericService(serviceName string) *GenericService

NewGenericService builds a GenericService for the fully-qualified service name (e.g. "helloworld.Greeter"), mirroring a generated Service base whose self.service_name is set to that name.

func (*GenericService) BuildService

func (g *GenericService) BuildService(h Handlers) (Service, error)

BuildService pairs each declared RpcDesc with its handler from h and returns the runtime Service to register on an RpcServer with Handle. It mirrors implementing a generated Service subclass and passing an instance to GRPC::RpcServer#handle. It errors if the service declares no RPCs, if a declared RPC has no handler, or if a handler's Go type does not match the RPC's cardinality.

func (*GenericService) LookupRPC

func (g *GenericService) LookupRPC(name string) (RpcDesc, bool)

LookupRPC returns the RpcDesc for name and whether it was declared.

func (*GenericService) RPC

RPC declares one RPC on the service, mirroring the gem's `rpc :Name, Input, Output` class macro. It is chainable so a generated service reads as a sequence of RPC declarations. A duplicate name replaces the earlier declaration, as re-declaring an rpc does in the gem.

func (*GenericService) RpcDescs

func (g *GenericService) RpcDescs() []RpcDesc

RpcDescs returns the declared RPCs in declaration order, mirroring the gem's GenericService.rpc_descs.

func (*GenericService) ServiceName

func (g *GenericService) ServiceName() string

ServiceName returns the fully-qualified service name, mirroring the gem's GenericService.service_name.

func (*GenericService) StubClass

func (g *GenericService) StubClass(stub *ClientStub) *GenericStub

StubClass derives the client stub over an existing ClientStub, mirroring `Stub = Service.rpc_stub_class` followed by `Stub.new(host, creds)`.

type GenericStub

type GenericStub struct {
	// contains filtered or unexported fields
}

GenericStub mirrors the Stub class GenericService.rpc_stub_class generates: it wraps a ClientStub and, for each declared RPC, issues the call with the right cardinality and the descriptor's own marshal/unmarshal already applied, so the caller supplies only the request(s) and optional metadata/deadline — exactly the ergonomics of a generated Stub#say_hello(req).

func (*GenericStub) BidiStreamer

func (s *GenericStub) BidiStreamer(rpc string, requests []any, opts CallOptions) ([]any, error)

BidiStreamer issues the bidirectional-streaming RPC named rpc, mirroring a generated bidi stub method. It errors if rpc is unknown or is not a BidiStream RPC.

func (*GenericStub) ClientStreamer

func (s *GenericStub) ClientStreamer(rpc string, requests []any, opts CallOptions) (any, error)

ClientStreamer issues the client-streaming RPC named rpc, mirroring a generated client-streaming stub method. It errors if rpc is unknown or is not a ClientStream RPC.

func (*GenericStub) RequestResponse

func (s *GenericStub) RequestResponse(rpc string, req any, opts CallOptions) (any, error)

RequestResponse issues the unary RPC named rpc, mirroring a generated unary stub method. It errors if rpc is unknown or is not a Unary RPC.

func (*GenericStub) ServerStreamer

func (s *GenericStub) ServerStreamer(rpc string, req any, opts CallOptions) ([]any, error)

ServerStreamer issues the server-streaming RPC named rpc, mirroring a generated server-streaming stub method. It errors if rpc is unknown or is not a ServerStream RPC.

type Handlers

type Handlers map[string]any

Handlers binds each declared RPC name to its handler implementation. Each value must be the handler func matching the RPC's cardinality — the same four shapes Method accepts:

Unary        func(req any, call *ActiveCall) (any, error)
ClientStream func(call *ActiveCall) (any, error)
ServerStream func(req any, call *ActiveCall) error
BidiStream   func(call *ActiveCall) error

This mirrors defining the instance methods of a generated Service subclass.

type Marshaler

type Marshaler func(any) ([]byte, error)

Marshaler serializes a message to bytes. It plays the role of the marshal proc a generated *_services_pb.rb attaches to each GRPC::RpcDesc.

type MemTransport

type MemTransport struct {
	// contains filtered or unexported fields
}

MemTransport is an in-process transport backed by bufconn. A server's Listen registers an in-memory listener under its address; a client's Dial to that same address is wired straight to it. No OS socket or port is used, yet a real gRPC HTTP/2 session runs over the pipe, so servers and stubs are exercised end-to-end. Share one MemTransport between the server and the stubs that call it.

func NewMemTransport

func NewMemTransport() *MemTransport

NewMemTransport creates an empty in-memory transport.

func (*MemTransport) Dial

func (t *MemTransport) Dial(ctx context.Context, addr string) (net.Conn, error)

Dial connects to the in-memory listener registered for addr.

func (*MemTransport) Listen

func (t *MemTransport) Listen(addr string) (net.Listener, error)

Listen registers and returns an in-memory listener for addr. Re-listening on an address that already has a live listener is an error, matching a bind conflict on a real socket.

type Metadata

type Metadata map[string]string

Metadata mirrors the gem's representation of call metadata: a plain Hash of string keys to string values. In the gem a handler reads request metadata from ActiveCall#metadata and sends response metadata with a Hash; this type is that Hash.

gRPC keys are case-insensitive on the wire and the runtime lower-cases them; this type follows suit so a key set as "Foo" reads back as "foo", matching the gem's observed behaviour.

func (Metadata) Keys

func (m Metadata) Keys() []string

Keys returns the metadata keys in sorted order — handy for deterministic iteration and tests.

type Method

type Method struct {
	// Name is the RPC method name as it appears on the wire, e.g. "SayHello".
	Name string
	// Type selects the cardinality and which handler field is invoked.
	Type MethodType
	// RequestUnmarshal decodes an incoming request message.
	RequestUnmarshal Unmarshaler
	// ResponseMarshal encodes an outgoing response message.
	ResponseMarshal Marshaler

	// UnaryHandler serves a Unary method: one request in, one response out.
	UnaryHandler func(req any, call *ActiveCall) (any, error)
	// ClientStreamHandler serves a ClientStream method: it reads requests from
	// call (via Read / EachRemoteRead) and returns the single response.
	ClientStreamHandler func(call *ActiveCall) (any, error)
	// ServerStreamHandler serves a ServerStream method: it receives the single
	// request and emits responses with call.Send.
	ServerStreamHandler func(req any, call *ActiveCall) error
	// BidiStreamHandler serves a BidiStream method: it reads requests and emits
	// responses over the same call.
	BidiStreamHandler func(call *ActiveCall) error
}

Method describes one RPC of a service, mirroring a single GRPC::RpcDesc: its name, cardinality, the request/response (un)marshalling, and the handler. Exactly one handler field is used, selected by Type.

type MethodGen

type MethodGen struct {
	// Name is the RPC name as declared, e.g. "SayHello".
	Name string
	// InputType is the fully-qualified proto name of the request message, e.g.
	// "helloworld.HelloRequest" (a leading dot is tolerated).
	InputType string
	// OutputType is the fully-qualified proto name of the response message.
	OutputType string
	// ClientStreaming marks a streaming request (the input is wrapped in
	// stream(...)).
	ClientStreaming bool
	// ServerStreaming marks a streaming response (the output is wrapped in
	// stream(...)).
	ServerStreaming bool
}

MethodGen describes one RPC in a service block.

type MethodType

type MethodType int

MethodType is the cardinality of an RPC, mirroring the four shapes a GRPC::RpcDesc can take.

const (
	// Unary is a single request, single response (request_response).
	Unary MethodType = iota
	// ClientStream is a stream of requests, single response (client_streamer).
	ClientStream
	// ServerStream is a single request, stream of responses (server_streamer).
	ServerStream
	// BidiStream is a stream of requests and a stream of responses
	// (bidi_streamer).
	BidiStream
)

type NetTransport

type NetTransport struct{}

NetTransport is the production transport: it listens and dials real TCP sockets via the standard library. It is the default when a server or stub is created without an explicit transport.

func (NetTransport) Dial

func (NetTransport) Dial(ctx context.Context, addr string) (net.Conn, error)

Dial opens a TCP connection to addr.

func (NetTransport) Listen

func (NetTransport) Listen(addr string) (net.Listener, error)

Listen binds a TCP socket at addr.

type RpcDesc

type RpcDesc struct {
	// Name is the RPC method name as declared and as it appears on the wire,
	// e.g. "SayHello".
	Name string
	// Type is the cardinality of the RPC.
	Type MethodType
	// RequestMarshal encodes a request message (used by the client stub).
	RequestMarshal Marshaler
	// RequestUnmarshal decodes a request message (used by the server).
	RequestUnmarshal Unmarshaler
	// ResponseMarshal encodes a response message (used by the server).
	ResponseMarshal Marshaler
	// ResponseUnmarshal decodes a response message (used by the client stub).
	ResponseUnmarshal Unmarshaler
}

RpcDesc mirrors GRPC::RpcDesc: one declared RPC of a service — its wire name, cardinality, and the marshal/unmarshal functions for the request and response messages. In the gem a RpcDesc carries the marshal/unmarshal *procs* a message class' marshal_class_method / unmarshal_class_method yield; here, staying message-agnostic like the gem, it carries the equivalent Marshaler / Unmarshaler functions. Messages from github.com/go-ruby-protobuf/protobuf drop straight in via its Encode / Decode.

type RpcServer

type RpcServer struct {
	// contains filtered or unexported fields
}

RpcServer mirrors GRPC::RpcServer: it binds one or more HTTP/2 ports, has services registered on it, and runs until stopped. The actual listener comes from the injected Transport, so the server logic itself never touches a socket and is fully exercisable in-process over MemTransport.

func NewRpcServer

func NewRpcServer(opts ...ServerOption) *RpcServer

NewRpcServer builds an RpcServer, mirroring GRPC::RpcServer.new. Register services with Handle, bind ports with AddHTTP2Port, then Run.

func (*RpcServer) AddHTTP2Port

func (s *RpcServer) AddHTTP2Port(addr, creds string) string

AddHTTP2Port mirrors GRPC::RpcServer#add_http2_port(addr, creds). The credentials argument is accepted for surface fidelity — pass ":this_port_is_insecure" for a plaintext port, as the gem does. TLS credentials are a follow-up; the address is remembered and bound when the server runs. It returns the bound address.

func (*RpcServer) Handle

func (s *RpcServer) Handle(svc Service)

Handle mirrors GRPC::RpcServer#handle(service): it registers a service's RPCs. It must be called before Run.

func (*RpcServer) Run

func (s *RpcServer) Run() error

Run mirrors GRPC::RpcServer#run: it binds every added port and serves, blocking until Stop is called. A bind failure is returned immediately; a clean Stop returns nil. At least one port must have been added, and Run may not be entered twice.

func (*RpcServer) RunTillTerminated

func (s *RpcServer) RunTillTerminated() error

RunTillTerminated mirrors GRPC::RpcServer#run_till_terminated: it runs the server and returns when it is stopped. The gem also installs SIGINT/SIGTERM handlers; wiring signals is a host concern, so here it is an alias for Run that a host can pair with its own signal trap calling Stop.

func (*RpcServer) Running

func (s *RpcServer) Running() bool

Running reports whether Run has been entered and Stop not yet called.

func (*RpcServer) Stop

func (s *RpcServer) Stop()

Stop mirrors GRPC::RpcServer#stop: it gracefully drains and shuts the server down, unblocking Run. It is safe to call repeatedly; only the first call has an effect.

type ServerOption

type ServerOption func(*RpcServer)

ServerOption configures an RpcServer at construction.

func WithTransport

func WithTransport(t Transport) ServerOption

WithTransport injects the network seam. Without it an RpcServer uses NetTransport (real sockets).

type Service

type Service struct {
	Name    string
	Methods []Method
}

Service describes a gRPC service to register on an RpcServer, mirroring the service object passed to GRPC::RpcServer#handle. Name is the fully-qualified service name (e.g. "helloworld.Greeter").

type ServiceFile

type ServiceFile struct {
	// ProtoFile is the source .proto path as it appears after -I, e.g.
	// "helloworld.proto" or "grpc/testing/echo.proto". It sets the header's
	// Source line and the require of the companion messages file.
	ProtoFile string
	// Package is the proto package, e.g. "helloworld" or "grpc.testing" (may be
	// empty for a package-less file).
	Package string
	// Services are the services declared in the file, in declaration order.
	Services []ServiceGen
}

ServiceFile describes one .proto file's service block, the input to GenerateRubyServices — the subset of a FileDescriptorProto the Ruby gRPC generator reads.

type ServiceGen

type ServiceGen struct {
	// Name is the service name as declared, e.g. "Greeter".
	Name string
	// Methods are the RPCs of the service, in declaration order.
	Methods []MethodGen
}

ServiceGen describes one service block.

type StatusCode

type StatusCode uint32

StatusCode mirrors GRPC::Core::StatusCodes — the canonical gRPC status codes. The integer values are wire-fixed and identical to the gem's constants and to google.golang.org/grpc/codes.

const (
	OK                 StatusCode = 0
	Cancelled          StatusCode = 1
	Unknown            StatusCode = 2
	InvalidArgument    StatusCode = 3
	DeadlineExceeded   StatusCode = 4
	NotFound           StatusCode = 5
	AlreadyExists      StatusCode = 6
	PermissionDenied   StatusCode = 7
	ResourceExhausted  StatusCode = 8
	FailedPrecondition StatusCode = 9
	Aborted            StatusCode = 10
	OutOfRange         StatusCode = 11
	Unimplemented      StatusCode = 12
	Internal           StatusCode = 13
	Unavailable        StatusCode = 14
	DataLoss           StatusCode = 15
	Unauthenticated    StatusCode = 16
)

The gRPC status codes, matching GRPC::Core::StatusCodes::* one-to-one.

func (StatusCode) Name

func (c StatusCode) Name() string

Name returns the gem's constant name for the code, e.g. "INVALID_ARGUMENT". Unknown numeric codes render as "CODE(<n>)".

func (StatusCode) String

func (c StatusCode) String() string

String satisfies fmt.Stringer with the gem's constant name.

type StubOption

type StubOption func(*stubConfig)

StubOption configures a ClientStub at construction.

func WithStubTransport

func WithStubTransport(t Transport) StubOption

WithStubTransport injects the network seam for the stub. Without it the stub dials via NetTransport.

func WithTimeout

func WithTimeout(d time.Duration) StubOption

WithTimeout sets a default per-call deadline, mirroring the gem's ClientStub.new(..., timeout:). CallOptions.Deadline overrides it per call.

type Transport

type Transport interface {
	// Listen returns a net.Listener for the given "host:port" address.
	Listen(addr string) (net.Listener, error)
	// Dial returns a connection to the given address, honouring ctx for
	// cancellation and deadlines.
	Dial(ctx context.Context, addr string) (net.Conn, error)
}

Transport is the network seam. The core RpcServer and ClientStub never touch sockets directly: a server obtains its listener from Listen and a client obtains its connections from Dial. Production wiring uses NetTransport (real TCP); tests use MemTransport (an in-process bufconn pipe carrying a real HTTP/2 gRPC session). Injecting the transport keeps the whole stack testable in-process without binding a port, mirroring the host seam used by the OIDC / OAuth2 bindings.

type Unmarshaler

type Unmarshaler func([]byte) (any, error)

Unmarshaler deserializes bytes into a message, mirroring the unmarshal proc a generated *_services_pb.rb attaches to each GRPC::RpcDesc.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL