### Complete Async::GRPC Client-Server Example Source: https://github.com/socketry/async-grpc/blob/main/guides/getting-started/readme.md A full example demonstrating the setup of both an Async::GRPC server and client within a single Async block, including defining the interface, implementing the service, and making an RPC call. ```ruby require "async" require "async/grpc" require "async/http/server" require "async/http/endpoint" # Define interface class GreeterInterface < Protocol::GRPC::Interface rpc :SayHello, request_class: Hello::HelloRequest, response_class: Hello::HelloReply end # Implement service class GreeterService < Async::GRPC::Service def say_hello(input, output, call) request = input.read reply = Hello::HelloReply.new(message: "Hello, #{request.name}!") output.write(reply) end end Async do # Setup server endpoint = Async::HTTP::Endpoint.parse("http://localhost:50051") dispatcher = Async::GRPC::Dispatcher.new dispatcher.register(GreeterService.new(GreeterInterface, "hello.Greeter")) server = Async::HTTP::Server.for(endpoint, dispatcher) # Setup client client_endpoint = Async::HTTP::Endpoint.parse("http://localhost:50051") http_client = Async::HTTP::Client.new(client_endpoint) client = Async::GRPC::Client.new(http_client) stub = client.stub(GreeterInterface, "hello.Greeter") # Make a call request = Hello::HelloRequest.new(name: "World") response = stub.say_hello(request) puts response.message # => "Hello, World!" server.run end ``` -------------------------------- ### Complete Async::GRPC Client-Server Example Source: https://github.com/socketry/async-grpc/blob/main/context/getting-started.md A full example demonstrating the setup of both an asynchronous gRPC server and client within a single Async block, including interface definition, service implementation, and an RPC call. ```ruby require "async" require "async/grpc" require "async/http/server" require "async/http/endpoint" # Define interface class GreeterInterface < Protocol::GRPC::Interface rpc :SayHello, request_class: Hello::HelloRequest, response_class: Hello::HelloReply end # Implement service class GreeterService < Async::GRPC::Service def say_hello(input, output, call) request = input.read reply = Hello::HelloReply.new(message: "Hello, #{request.name}!") output.write(reply) end end Async do # Setup server endpoint = Async::HTTP::Endpoint.parse("http://localhost:50051") dispatcher = Async::GRPC::Dispatcher.new dispatcher.register(GreeterService.new(GreeterInterface, "hello.Greeter")) server = Async::HTTP::Server.for(endpoint, dispatcher) # Setup client client_endpoint = Async::HTTP::Endpoint.parse("http://localhost:50051") http_client = Async::HTTP::Client.new(client_endpoint) client = Async::GRPC::Client.new(http_client) stub = client.stub(GreeterInterface, "hello.Greeter") # Make a call request = Hello::HelloRequest.new(name: "World") response = stub.say_hello(request) puts response.message # => "Hello, World!" server.run end ``` -------------------------------- ### Install Async::GRPC Gem Source: https://github.com/socketry/async-grpc/blob/main/guides/getting-started/readme.md Installs the async-grpc gem and its dependencies, protocol-grpc and async-http, using Bundler. ```bash $ bundle add async-grpc $ bundle add protocol-grpc async-http ``` -------------------------------- ### Run Async::GRPC Server Source: https://github.com/socketry/async-grpc/blob/main/guides/getting-started/readme.md Illustrates how to set up and run an asynchronous gRPC server using Async::HTTP::Server and a registered dispatcher. ```ruby require "async/http/server" require "async/http/endpoint" endpoint = Async::HTTP::Endpoint.parse("http://localhost:50051") server = Async::HTTP::Server.for(endpoint, dispatcher) Async do server.run end ``` -------------------------------- ### Create Async::GRPC Client Source: https://github.com/socketry/async-grpc/blob/main/guides/getting-started/readme.md Demonstrates how to create an asynchronous gRPC client by initializing an Async::HTTP::Client and wrapping it with Async::GRPC::Client. ```ruby require "async/grpc" require "async/http/endpoint" endpoint = Async::HTTP::Endpoint.parse("https://grpc.example.com") http_client = Async::HTTP::Client.new(endpoint) client = Async::GRPC::Client.new(http_client) ``` -------------------------------- ### Register Async::GRPC Service with Dispatcher Source: https://github.com/socketry/async-grpc/blob/main/guides/getting-started/readme.md Shows how to create an Async::GRPC::Dispatcher and register a service implementation with it to handle incoming requests. ```ruby require "async/grpc/dispatcher" dispatcher = Async::GRPC::Dispatcher.new service = GreeterService.new(GreeterInterface, "hello.Greeter") dispatcher.register(service) ``` -------------------------------- ### Handle Streaming RPCs with Async::GRPC Source: https://github.com/socketry/async-grpc/blob/main/guides/getting-started/readme.md Illustrates how to implement server streaming, client streaming, and bidirectional streaming RPCs using Async::GRPC client stubs. ```ruby # Server streaming stub.server_streaming_call(request) do |response| puts response.value end # Client streaming stub.client_streaming_call do |output| output.write(request1) output.write(request2) end # Bidirectional streaming stub.bidirectional_streaming do |input, output| input.each do |request| output.write(process(request)) end end ``` -------------------------------- ### Define and Implement Async::GRPC Service Source: https://github.com/socketry/async-grpc/blob/main/guides/getting-started/readme.md Demonstrates how to define a gRPC interface and implement a concrete service class that handles incoming RPC requests. ```ruby require "async/grpc/service" # Define the interface class GreeterInterface < Protocol::GRPC::Interface rpc :SayHello, request_class: Hello::HelloRequest, response_class: Hello::HelloReply end # Implement the service class GreeterService < Async::GRPC::Service def say_hello(input, output, call) request = input.read reply = Hello::HelloReply.new(message: "Hello, #{request.name}!") output.write(reply) end end ``` -------------------------------- ### gRPC Server Setup Example (Ruby) Source: https://github.com/socketry/async-grpc/blob/main/design.md Illustrates how to set up a gRPC server using async-grpc in Ruby. It shows how to implement service handlers, register them with gRPC middleware, and integrate with Async::HTTP::Server for handling requests. ```ruby require "async" require "async/http/server" require "async/http/endpoint" require "protocol/grpc/middleware" require_relative "my_service_pb" # Implement service handlers class GreeterService def say_hello(input, output, call) hello_request = input.read reply = MyService::HelloReply.new( message: "Hello, #{hello_request.name}!" ) output.write(reply) end def stream_numbers(input, output, call) hello_request = input.read 10.times do |i| reply = MyService::HelloReply.new( message: "Number #{i} for #{hello_request.name}" ) output.write(reply) sleep 0.1 # Simulate work end end end # Setup server endpoint = Async::HTTP::Endpoint.parse( "https://localhost:50051", protocol: Async::HTTP::Protocol::HTTP2 ) Async do # Create gRPC middleware grpc = Protocol::GRPC::Middleware.new grpc.register("my_service.Greeter", GreeterService.new) # Use with Async::HTTP::Server - no wrapper needed! server = Async::HTTP::Server.new(grpc, endpoint) server.run end ``` -------------------------------- ### Use Async::GRPC Stub for RPC Calls Source: https://github.com/socketry/async-grpc/blob/main/guides/getting-started/readme.md Shows how to define a gRPC interface, create a client stub, and make RPC calls. It supports both snake_case and PascalCase method names. ```ruby # Define the interface class GreeterInterface < Protocol::GRPC::Interface rpc :SayHello, request_class: Hello::HelloRequest, response_class: Hello::HelloReply end # Create a stub stub = client.stub(GreeterInterface, "hello.Greeter") # Make RPC calls (accepts both snake_case and PascalCase) response = stub.say_hello(Hello::HelloRequest.new(name: "World")) # or response = stub.SayHello(Hello::HelloRequest.new(name: "World")) ``` -------------------------------- ### Complete Client-Server Example with Async::GRPC Source: https://context7.com/socketry/async-grpc/llms.txt Provides a full example of setting up both a gRPC client and server using Async::GRPC. It includes definitions for message classes, interface definitions, and demonstrates various RPC streaming types. ```ruby require "async" require "async/grpc" require "async/grpc/dispatcher" require "async/http/server" require "async/http/endpoint" require "protocol/grpc/interface" # Message classes class Request attr_accessor :value def initialize(value: nil) = @value = value def to_proto data = (@value || "").dup.force_encoding(Encoding::BINARY) [data.bytesize].pack("N") + data end alias encode to_proto def self.decode(data) length = data[0...4].unpack1("N") value = data[4...(4 + length)].dup.force_encoding(Encoding::UTF_8) new(value: value) end end class Response attr_accessor :result def initialize(result: nil) = @result = result def to_proto data = (@result || "").dup.force_encoding(Encoding::BINARY) [data.bytesize].pack("N") + data end alias encode to_proto def self.decode(data) length = data[0...4].unpack1("N") result = data[4...(4 + length)].dup.force_encoding(Encoding::UTF_8) new(result: result) end end # Interface definition class CalculatorInterface < Protocol::GRPC::Interface rpc :Add, request_class: Request, response_class: Response, streaming: :unary rpc :Sum, request_class: Request, response_class: Response, streaming: :client_streaming rpc :Fibonacci, request_class: Request, response_class: Response, streaming: :server_streaming rpc :Calculate, request_class: Request, response_class: Response, streaming: :bidirectional end ``` -------------------------------- ### Install Async::GRPC and Dependencies Source: https://context7.com/socketry/async-grpc/llms.txt Installs the async-grpc gem along with its core dependencies, protocol-grpc and async-http, using the bundle add command. ```bash bundle add async-grpc protocol-grpc async-http ``` -------------------------------- ### Async::GRPC::Dispatcher Setup and Service Registration Source: https://context7.com/socketry/async-grpc/llms.txt Illustrates how to set up and use the Async::GRPC::Dispatcher to route incoming gRPC requests to registered services. It shows registration with default and custom names, and handling multiple services. ```ruby require "async" require "async/grpc/dispatcher" require "async/http/server" require "async/http/endpoint" # Create dispatcher dispatcher = Async::GRPC::Dispatcher.new # Register services greeter = GreeterService.new(GreeterInterface, "hello.Greeter") dispatcher.register(greeter) # Register with custom name dispatcher.register(greeter, name: "custom.Greeter") # Multiple services class EchoInterface < Protocol::GRPC::Interface rpc :Echo, request_class: HelloRequest, response_class: HelloReply, streaming: :unary end class EchoService < Async::GRPC::Service def echo(input, output, call) request = input.read output.write(HelloReply.new(message: request.name)) end end echo = EchoService.new(EchoInterface, "echo.EchoService") dispatcher.register(echo) # Create and run server endpoint = Async::HTTP::Endpoint.parse("http://0.0.0.0:50051") server = Async::HTTP::Server.for(endpoint, dispatcher) Async do puts "gRPC server listening on port 50051..." server.run end ``` -------------------------------- ### Async::GRPC Logging Interceptor Example Source: https://github.com/socketry/async-grpc/blob/main/design.md An example implementation of a client interceptor that logs the details of gRPC calls, including service, method, and execution duration. It captures start and end times to measure performance and logs errors if the call fails. This demonstrates how to extend the `ClientInterceptor` base class. ```ruby module Async module GRPC # Example: Logging interceptor class LoggingInterceptor < ClientInterceptor def call(service, method, request, call) Console.logger.info(self){"Calling #{service}/#{method}"} start_time = Time.now begin response = yield duration = Time.now - start_time Console.logger.info(self){"Completed #{service}/#{method} in #{duration}s"} response rescue => error Console.logger.error(self){"Failed #{service}/#{method}: #{error.message}"} raise end end end end end ``` -------------------------------- ### Async::GRPC Client Initialization and Usage (Ruby) Source: https://context7.com/socketry/async-grpc/llms.txt Demonstrates various ways to initialize and use the Async::GRPC::Client. It covers automatic closing with a block, manual client management, and setting custom headers for requests. Includes example message classes for demonstration. ```ruby require "async" require "async/grpc" require "async/http/endpoint" # Define a protobuf-like message class class HelloRequest attr_accessor :name def initialize(name: nil) @name = name end def to_proto data = (@name || "").dup.force_encoding(Encoding::BINARY) [data.bytesize].pack("N") + data end alias encode to_proto def self.decode(data) length = data[0...4].unpack1("N") name = data[4...(4 + length)].dup.force_encoding(Encoding::UTF_8) new(name: name) end end class HelloReply attr_accessor :message def initialize(message: nil) @message = message end def to_proto data = (@message || "").dup.force_encoding(Encoding::BINARY) [data.bytesize].pack("N") + data end alias encode to_proto def self.decode(data) length = data[0...4].unpack1("N") message = data[4...(4 + length)].dup.force_encoding(Encoding::UTF_8) new(message: message) end end # Method 1: Using Client.open with block (auto-closes) Async do Async::GRPC::Client.open("http://localhost:50051") do |client| # Use client here end end # Method 2: Manual client management Async do endpoint = Async::HTTP::Endpoint.parse("http://localhost:50051") http_client = Async::HTTP::Client.new(endpoint) client = Async::GRPC::Client.new(http_client) # Use client... client.close end # Method 3: With custom headers Async do endpoint = Async::HTTP::Endpoint.parse("http://localhost:50051") headers = Protocol::HTTP::Headers.new headers["authorization"] = "Bearer token123" Async::GRPC::Client.open(endpoint, headers: headers) do |client| # All requests will include authorization header end end ``` -------------------------------- ### gRPC Client Usage Examples (Ruby) Source: https://github.com/socketry/async-grpc/blob/main/design.md Demonstrates how to use the async-grpc client in Ruby to interact with gRPC services. It covers unary, server streaming, client streaming, and bidirectional streaming RPC calls, including setting up the client and handling responses. ```ruby require "async" require "async/grpc/client" require_relative "my_service_pb" endpoint = Async::HTTP::Endpoint.parse("https://localhost:50051") Async do client = Async::GRPC::Client.new(endpoint) # Unary RPC request = MyService::HelloRequest.new(name: "World") response = client.unary( "my_service.Greeter", "SayHello", request, response_class: MyService::HelloReply ) puts response.message # Server streaming RPC client.server_streaming( "my_service.Greeter", "StreamNumbers", request, response_class: MyService::HelloReply ) do |reply| puts reply.message end # Client streaming RPC response = client.client_streaming( "my_service.Greeter", "RecordRoute", response_class: MyService::RouteSummary ) do |stream| 10.times do |i| point = MyService::Point.new(latitude: i, longitude: i) stream.write(point) end end puts response.point_count # Bidirectional streaming RPC client.bidirectional_streaming( "my_service.Greeter", "RouteChat", response_class: MyService::RouteNote ) do |input, output| # Write in background task = Async do 5.times do |i| note = MyService::RouteNote.new(message: "Note #{i}") input.write(note) sleep 0.1 end input.close_write end # Read responses output.each do |reply| puts reply.message end task.wait end ensure client.close end ``` -------------------------------- ### Run gRPC Server and Client with Async in Ruby Source: https://context7.com/socketry/async-grpc/llms.txt Sets up and runs an Async::GRPC server and demonstrates client interactions. It includes starting the server, registering the service, and making unary, client streaming, server streaming, and bidirectional streaming calls to the calculator service. ```ruby Async do |task| endpoint = Async::HTTP::Endpoint.parse("http://localhost:50051") # Start server dispatcher = Async::GRPC::Dispatcher.new dispatcher.register(CalculatorService.new(CalculatorInterface, "calc.Calculator")) server = Async::HTTP::Server.for(endpoint, dispatcher) server_task = task.async do server.run end # Give server time to start task.sleep(0.1) # Client calls Async::GRPC::Client.open(endpoint) do |client| stub = client.stub(CalculatorInterface, "calc.Calculator") # Unary call response = stub.add(Request.new(value: "1,2,3,4,5")) puts "Sum of 1-5: #{response.result}" # => "15" # Client streaming response = stub.sum do |output| [10, 20, 30].each { |n| output.write(Request.new(value: n.to_s)) } end puts "Total: #{response.result}" # => "60" # Server streaming puts "Fibonacci sequence:" stub.fibonacci(Request.new(value: "10")) do |response| print "#{response.result} " end puts # => "0 1 1 2 3 5 8 13 21 34" # Bidirectional streaming stub.calculate do |output, input| ["2+2", "10*5", "100/4"].each { |expr| output.write(Request.new(value: expr)) } output.close_write results = [] input.each { |response| results << response.result } puts "Results: #{results.join(', ')}" # => "4, 50, 25" end end server_task.stop end ``` -------------------------------- ### Ruby gRPC Channel and Service Setup in google-cloud-spanner Source: https://github.com/socketry/async-grpc/blob/main/spanner_integration.md Demonstrates how the google-cloud-spanner gem establishes a gRPC channel and initializes its service client. It highlights the use of the 'grpc' gem for channel creation and credentials management, and the generated client stubs for making RPC calls. ```ruby def channel require "grpc" GRPC::Core::Channel.new host, chan_args, chan_creds end def chan_creds return credentials if insecure? require "grpc" GRPC::Core::ChannelCredentials.new.compose \ GRPC::Core::CallCredentials.new credentials.client.updater_proc end def service return mocked_service if mocked_service @service ||= \ V1::Spanner::Client.new do |config| config.credentials = channel # <-- Passes gRPC channel config.quota_project = @quota_project config.timeout = timeout if timeout config.endpoint = host if host config.universe_domain = @universe_domain config.lib_name = lib_name_with_prefix config.lib_version = Google::Cloud::Spanner::VERSION config.metadata = { "google-cloud-resource-prefix" => "projects/#{@project}" } end end ``` -------------------------------- ### Async::GRPC Metadata Interceptor Example Source: https://github.com/socketry/async-grpc/blob/main/design.md An example implementation of a client interceptor that adds custom metadata to outgoing gRPC requests. The `MetadataInterceptor` can be initialized with a hash of metadata, which is then merged into the request headers for every call it intercepts. This is useful for passing authentication tokens or other contextual information. ```ruby module Async module GRPC # Example: Metadata interceptor class MetadataInterceptor < ClientInterceptor def initialize(metadata = {}) @metadata = metadata end def call(service, method, request, call) # Add metadata to all calls call.request.headers.merge!(@metadata) yield end end end end ``` -------------------------------- ### Interceptor API Example (Ruby) Source: https://github.com/socketry/async-grpc/blob/main/design.md Demonstrates a basic interceptor pattern for handling gRPC requests and responses. Interceptors can be used for logging, authentication, or other cross-cutting concerns before or after RPC calls. ```ruby class LoggingInterceptor def call(request, call) # Before request result = yield # After response result end end ``` -------------------------------- ### Authentication Hook for gRPC Calls Source: https://github.com/socketry/async-grpc/blob/main/design.md Provides an example of how to use the `updater_proc` from Google Cloud credentials to dynamically generate authentication metadata for gRPC calls. ```ruby # Google Cloud credentials provide updater_proc auth_metadata = credentials.client.updater_proc.call(method_path) # => {"authorization" => "Bearer ya29.a0..."} ``` -------------------------------- ### Define GRPC Interface and Client Stub Source: https://context7.com/socketry/async-grpc/llms.txt Defines a gRPC interface for a Greeter service and demonstrates how to create a client stub to interact with a running gRPC server. It shows examples of unary, server streaming, client streaming, and bidirectional streaming calls, including how to pass metadata and timeouts. ```ruby class GreeterInterface < Protocol::GRPC::Interface rpc :SayHello, request_class: HelloRequest, response_class: HelloReply, streaming: :unary rpc :StreamGreetings, request_class: HelloRequest, response_class: HelloReply, streaming: :server_streaming rpc :CollectNames, request_class: HelloRequest, response_class: HelloReply, streaming: :client_streaming rpc :Chat, request_class: HelloRequest, response_class: HelloReply, streaming: :bidirectional end Async do Async::GRPC::Client.open("http://localhost:50051") do |client| # Create stub from interface stub = client.stub(GreeterInterface, "hello.Greeter") # Unary call - both method name styles work request = HelloRequest.new(name: "World") response = stub.say_hello(request) # snake_case (Ruby convention) response = stub.SayHello(request) # PascalCase (gRPC convention) puts response.message # => "Hello, World!" # With metadata and timeout response = stub.say_hello(request, metadata: { "x-request-id" => "abc123" }, timeout: 30 # seconds ) # Server streaming - receive multiple responses stub.stream_greetings(HelloRequest.new(name: "Stream")) do |response| puts response.message # Called for each response end # Client streaming - send multiple requests response = stub.collect_names do |output| output.write(HelloRequest.new(name: "Alice")) output.write(HelloRequest.new(name: "Bob")) output.write(HelloRequest.new(name: "Charlie")) end puts response.message # => "Received: Alice, Bob, Charlie" # Bidirectional streaming stub.chat do |output, input| # Send messages output.write(HelloRequest.new(name: "Message 1")) output.write(HelloRequest.new(name: "Message 2")) output.close_write # Signal end of sending # Receive responses input.each do |response| puts response.message end end end end ``` -------------------------------- ### Channel Adapter Initialization and Usage Source: https://github.com/socketry/async-grpc/blob/main/design.md Shows the basic instantiation and usage of the `ChannelAdapter` class, which implements the `GRPC::Core::Channel` interface for gRPC communication. ```ruby channel = ChannelAdapter.new(endpoint, channel_args, channel_creds) response = channel.request_response(path, request, marshal, unmarshal, deadline:, metadata:) ``` -------------------------------- ### Define and Register a gRPC Greeter Service in Ruby Source: https://github.com/socketry/async-grpc/blob/main/design.md This snippet demonstrates how to define a gRPC service handler (`GreeterService`) and register it with the `async-grpc` server infrastructure using `Protocol::GRPC::Middleware`. It sets up a basic 'say_hello' RPC. ```ruby require "protocol/grpc/middleware" require_relative "my_service_pb" class GreeterService def say_hello(input, output, call) hello_request = input.read reply = MyService::HelloReply.new(message: "Hello, #{hello_request.name}!") output.write(reply) end end service "grpc.localhost" do include Falcon::Environment::Application middleware do # Just use Protocol::GRPC::Middleware directly! grpc = Protocol::GRPC::Middleware.new grpc.register("my_service.Greeter", GreeterService.new) grpc end scheme "https" protocol {Async::HTTP::Protocol::HTTP2} endpoint do Async::HTTP::Endpoint.for(scheme, "localhost", port: 50051, protocol: protocol) end end ``` -------------------------------- ### Modifying Service Initialization with Regenerated Stubs Source: https://github.com/socketry/async-grpc/blob/main/spanner_integration.md Example of how to modify the service initialization in Ruby when using regenerated client stubs with Async::GRPC::Client. It shows parsing the endpoint, creating an Async::GRPC::Client, and initializing the Spanner client with the new stub. ```ruby require "google/spanner/v1/spanner_grpc" # Our generated stubs def service @service ||= begin endpoint = Async::HTTP::Endpoint.parse("https://#{host}") client = Async::GRPC::Client.new(endpoint) Google::Spanner::V1::SpannerClient.new(client) # Our stub end end ``` -------------------------------- ### Async::GRPC::Client - Making RPC Calls Source: https://context7.com/socketry/async-grpc/llms.txt Demonstrates how to establish a gRPC client connection and make various types of RPC calls (unary, server streaming, client streaming, bidirectional streaming) using a defined interface. ```APIDOC ## Async::GRPC::Client This section details how to use the `Async::GRPC::Client` to interact with gRPC services. ### Method `Async::GRPC::Client.open` ### Endpoint `http://localhost:50051` (example) ### Description Opens a gRPC client connection to the specified address. The connection is managed within the block, and a `stub` object is created from the provided interface and service name. ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```ruby Async do Async::GRPC::Client.open("http://localhost:50051") do |client| # Create stub from interface stub = client.stub(GreeterInterface, "hello.Greeter") # ... RPC calls ... end end ``` ### Response #### Success Response (200) N/A (The block yields a client object) #### Response Example N/A --- ## Making RPC Calls with a Stub Once a client is open and a stub is created, you can make various types of RPC calls. ### Method Stub methods (e.g., `stub.say_hello`, `stub.stream_greetings`) ### Endpoint Defined by the gRPC service implementation. ### Description Allows making different types of gRPC calls, including unary, server streaming, client streaming, and bidirectional streaming. You can also pass metadata and timeouts. ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body Depends on the specific RPC method. ### Request Example ```ruby # Unary call request = HelloRequest.new(name: "World") response = stub.say_hello(request) puts response.message # Unary call with metadata and timeout response = stub.say_hello(request, metadata: { "x-request-id" => "abc123" }, timeout: 30 # seconds ) # Server streaming stub.stream_greetings(HelloRequest.new(name: "Stream")) do |response| puts response.message end # Client streaming response = stub.collect_names do |output| output.write(HelloRequest.new(name: "Alice")) output.write(HelloRequest.new(name: "Bob")) output.close_write end puts response.message # Bidirectional streaming stub.chat do |output, input| output.write(HelloRequest.new(name: "Message 1")) output.write(HelloRequest.new(name: "Message 2")) output.close_write input.each do |response| puts response.message end end ``` ### Response #### Success Response (200) Depends on the specific RPC method. #### Response Example ```ruby # For unary calls # { "message": "Hello, World!" } # For streaming calls, responses are handled via callbacks or iteration. ``` ``` -------------------------------- ### Async::GRPC::Client Initialization Source: https://github.com/socketry/async-grpc/blob/main/design.md Initializes the Async::GRPC::Client with a server endpoint and an optional authority. ```APIDOC ## Async::GRPC::Client#initialize ### Description Initializes the gRPC client with a server endpoint and an optional authority. ### Method `initialize(endpoint, authority: nil)` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```ruby require 'async/grpc' require 'async/http/endpoint' endpoint = Async::HTTP::Endpoint.parse('https://example.com:8080') client = Async::GRPC::Client.new(endpoint, authority: 'example.com') ``` ### Response #### Success Response (N/A) This is a constructor method. #### Response Example N/A ``` -------------------------------- ### Initialize Async::GRPC::Client Source: https://github.com/socketry/async-grpc/blob/main/design.md Initializes the Async::GRPC::Client with an endpoint and an optional authority. It configures the underlying HTTP client to use HTTP/2 protocol. ```ruby module Async module GRPC class Client # @parameter endpoint [Async::HTTP::Endpoint] The server endpoint # @parameter authority [String] The server authority for requests def initialize(endpoint, authority: nil) @client = Async::HTTP::Client.new(endpoint, protocol: Async::HTTP::Protocol::HTTP2) @authority = authority || endpoint.authority end ... end end end ``` -------------------------------- ### Async-gRPC Channel Adapter for Google Cloud Libraries (Ruby) Source: https://github.com/socketry/async-grpc/blob/main/design.md Provides a `ChannelAdapter` class that makes `Async::GRPC::Client` compatible with libraries expecting a `GRPC::Core::Channel` interface. This enables existing Ruby gRPC clients, like those for Google Cloud, to utilize `async-grpc`'s performance benefits. ```ruby module Async module GRPC # Adapter that makes Async::GRPC::Client compatible with # libraries expecting GRPC::Core::Channel class ChannelAdapter def initialize(endpoint, channel_args = {}, channel_creds = nil) @endpoint = endpoint @client = Client.new(endpoint) @channel_creds = channel_creds end # Unary RPC: "/package.Service/Method" def request_response(path, request, marshal, unmarshal, deadline: nil, metadata: {}) service, method = parse_path(path) metadata = add_auth_metadata(metadata, path) if @channel_creds timeout = deadline ? [deadline - Time.now, 0].max : nil response_binary = Async do @client.unary_binary(service, method, marshal.call(request), metadata: metadata, timeout: timeout) end.wait unmarshal.call(response_binary) end # Server streaming def request_stream(path, request, marshal, unmarshal, deadline: nil, metadata: {}) # Returns Enumerator of responses end # Client streaming def stream_request(path, marshal, unmarshal, deadline: nil, metadata: {}) # Returns [input_stream, response_future] end # Bidirectional streaming def stream_stream(path, marshal, unmarshal, deadline: nil, metadata: {}) # Returns [input_stream, output_enumerator] end end end end ``` -------------------------------- ### Integrate Async-gRPC with Google Cloud Spanner (Ruby) Source: https://github.com/socketry/async-grpc/blob/main/design.md Shows how to use the `Async::GRPC::ChannelAdapter` to make Google Cloud libraries, specifically `google-cloud-spanner`, work with `async-grpc`. This involves creating an adapter instance and setting it as the internal channel for the Spanner service. ```ruby require "async/grpc/channel_adapter" require "google/cloud/spanner" endpoint = Async::HTTP::Endpoint.parse("https://spanner.googleapis.com") credentials = Google::Cloud::Spanner::Credentials.default # Create adapter channel = Async::GRPC::ChannelAdapter.new(endpoint, {}, credentials) # Use with Google Cloud libraries service = Google::Cloud::Spanner::Service.new service.instance_variable_set(:@channel, channel) # Now Spanner uses async-grpc! ``` -------------------------------- ### Integrating Protocol::GRPC::Middleware with Async::HTTP::Server Source: https://github.com/socketry/async-grpc/blob/main/design.md Demonstrates how to use `Protocol::GRPC::Middleware` directly with `Async::HTTP::Server` for gRPC services. This approach leverages the automatic async handling provided by `Async::HTTP::Server` and `Protocol::HTTP::Body::Writable`, eliminating the need for a separate `Async::GRPC::Server` class. It involves creating a gRPC middleware instance, registering services, and then running it with an HTTP/2 endpoint. ```ruby require "async" require "async/http/server" require "async/http/endpoint" require "protocol/grpc/middleware" # Create gRPC middleware middleware = Protocol::GRPC::Middleware.new middleware.register("my_service.Greeter", GreeterService.new) # Use with Async::HTTP::Server - it handles everything! endpoint = Async::HTTP::Endpoint.parse( "https://localhost:50051", protocol: Async::HTTP::Protocol::HTTP2 ) server = Async::HTTP::Server.new(middleware, endpoint) Async do server.run end ``` -------------------------------- ### Async::GRPC::Service - Implementing gRPC Services Source: https://context7.com/socketry/async-grpc/llms.txt Details on how to create and implement gRPC services using the `Async::GRPC::Service` base class, including handling different RPC streaming types and errors. ```APIDOC ## Async::GRPC::Service This section covers the implementation of gRPC services using `Async::GRPC::Service`. ### Description The `Service` class is the base for implementing gRPC services. You subclass it and implement methods that match the RPCs defined in your interface, typically using snake_case naming conventions. ### Method Subclassing `Async::GRPC::Service` and implementing RPC handler methods. ### Endpoint N/A (This is for service implementation, not client endpoints) ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body N/A ### Request Example (Service Implementation) ```ruby require "async/grpc/service" require "protocol/grpc/interface" require "protocol/grpc/metadata" require "protocol/grpc/status" # Define interface (matches .proto file) class GreeterInterface < Protocol::GRPC::Interface rpc :SayHello, request_class: HelloRequest, response_class: HelloReply, streaming: :unary rpc :StreamGreetings, request_class: HelloRequest, response_class: HelloReply, streaming: :server_streaming rpc :CollectNames, request_class: HelloRequest, response_class: HelloReply, streaming: :client_streaming rpc :Chat, request_class: HelloRequest, response_class: HelloReply, streaming: :bidirectional rpc :XMLParser, request_class: HelloRequest, response_class: HelloReply, streaming: :unary, method: :xml_parser end # Implement service - use snake_case method names class GreeterService < Async::GRPC::Service # Unary RPC def say_hello(input, output, call) request = input.read reply = HelloReply.new(message: "Hello, #{request.name}!") output.write(reply) end # Server streaming def stream_greetings(input, output, call) request = input.read 5.times do |i| reply = HelloReply.new(message: "Greeting #{i + 1} to #{request.name}") output.write(reply) end end # Client streaming def collect_names(input, output, call) names = [] input.each do |request| names << request.name end reply = HelloReply.new(message: "Received: #{names.join(', ')}") output.write(reply) end # Bidirectional streaming def chat(input, output, call) input.each do |request| reply = HelloReply.new(message: "Echo: #{request.name}") output.write(reply) end end # Returning errors from handlers def error_example(input, output, call) request = input.read Protocol::GRPC::Metadata.add_status!(call.response.headers, status: Protocol::GRPC::Status::INVALID_ARGUMENT, message: "Name cannot be empty") call.response.headers["backtrace"] = caller.first(5).join(", ") end # Handler for RPC with explicit method name def xml_parser(input, output, call) request = input.read output.write(HelloReply.new(message: "Parsed XML")) end end # Create service instance service = GreeterService.new(GreeterInterface, "hello.Greeter") ``` ### Response #### Success Response (200) Depends on the specific RPC method implemented. #### Response Example ```ruby # For unary calls, the handler writes a response using output.write() # For streaming calls, responses are written iteratively. ``` ``` -------------------------------- ### Binary Message Support for gRPC Client Source: https://github.com/socketry/async-grpc/blob/main/design.md Illustrates how to use binary message methods on the gRPC client for efficient data transfer, including unary, server streaming, client streaming, and bidirectional streaming operations with pre-marshaled protobuf data. ```ruby client.unary_binary(service, method, binary_request) # => binary_response client.server_streaming_binary(service, method, binary_request){|binary| do_stuff} client.client_streaming_binary(service, method){|output| output.write(binary)} client.bidirectional_streaming_binary(service, method){|input, output| do_stuff} ``` -------------------------------- ### Monkey-Patching Spanner Service for Mocking with Async GRPC Source: https://github.com/socketry/async-grpc/blob/main/spanner_integration.md Demonstrates a strategy for testing or development by monkey-patching the Spanner service to use a custom mock implementation that integrates with an async-grpc client. This involves adding a `mocked_service` accessor and creating a new service class that implements all RPC methods. ```ruby # In service.rb attr_accessor :mocked_service # Already exists! # Our replacement class AsyncGRPCSpannerService def initialize(client) @client = client end # Implement all Spanner RPC methods def execute_streaming_sql(request, options = {}) # Call via async-grpc end end_transaction(request, options = {}) # ... end # ... implement all 20+ RPC methods end # Usage service = Google::Cloud::Spanner::Service.new service.mocked_service = AsyncGRPCSpannerService.new(async_grpc_client) ``` -------------------------------- ### Access Service Metadata in Async::GRPC Source: https://context7.com/socketry/async-grpc/llms.txt Demonstrates how to retrieve metadata such as service name, interface class, and RPC descriptions from a gRPC service object. This is useful for introspection and understanding the service's capabilities. ```ruby puts service.service_name # => "hello.Greeter" puts service.interface_class # => GreeterInterface puts service.rpc_descriptions # => {"SayHello" => ..., "StreamGreetings" => ...} ``` -------------------------------- ### Basic Unary RPC with Protobuf Objects Source: https://github.com/socketry/async-grpc/blob/main/spanner_integration.md This snippet demonstrates a basic unary RPC call where request and response are handled as protobuf objects. It shows the standard client interaction for sending a request and expecting a typed response. ```ruby client.unary(service, method, request_object, response_class: MyReply) ``` -------------------------------- ### Google Cloud Authentication Metadata Integration Source: https://github.com/socketry/async-grpc/blob/main/design.md Demonstrates how to integrate Google Cloud authentication by obtaining an OAuth2 access token via an updater procedure and adding it to the request metadata for secure gRPC calls. ```ruby # Google's credential format credentials = Google::Cloud::Spanner::Credentials.default updater_proc = credentials.client.updater_proc # For each RPC call: auth_metadata = updater_proc.call(method_path) # => {"authorization" => "Bearer ..."} # Add to request metadata client.unary(service, method, request, metadata: auth_metadata) ```