Module: Protocol::GRPC::Metadata

Defined in:
lib/protocol/grpc/metadata.rb

Overview

Provides operations for building and extracting gRPC metadata.

Class Method Summary collapse

Class Method Details

.assign_status!(headers, status: Status::OK, message: nil, error: nil) ⇒ Object Also known as: add_status!

Assign gRPC status, message, and optional backtrace to headers.

Whether these become headers or trailers is controlled by the protocol layer.



111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/protocol/grpc/metadata.rb', line 111

def self.assign_status!(headers, status: Status::OK, message: nil, error: nil)
	headers["grpc-status"] = status
	
	if error && message.nil?
		# If message is not provided but error is, use error message
		message = error.message
	end
	
	if message
		headers["grpc-message"] = message
	end
	
	# Add backtrace from error if available
	if error && error.backtrace && !error.backtrace.empty?
		# Assign backtrace array directly - Split header will handle it
		headers["backtrace"] = error.backtrace
	end
	
	return headers
end

.build(metadata: {}, timeout: nil, content_type: "application/grpc+proto") ⇒ Object

Build gRPC request headers containing the given metadata.



20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# File 'lib/protocol/grpc/metadata.rb', line 20

def self.build(metadata: {}, timeout: nil, content_type: "application/grpc+proto")
	headers = Protocol::HTTP::Headers.new(policy: Protocol::GRPC::HEADER_POLICY)
	headers["content-type"] = content_type
	headers["te"] = "trailers"
	
	if timeout
		# Coerced to proper format by header policy:
		headers["grpc-timeout"] = timeout
	end
	
	.each do |key, value|
		# Binary headers end with -bin and are base64 encoded:
		headers[key] = if key.end_with?("-bin")
			Base64.strict_encode64(value)
		else
			value.to_s
		end
	end
	
	headers
end

.extract(headers) ⇒ Object

Extract application metadata from gRPC headers.



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/protocol/grpc/metadata.rb', line 45

def self.extract(headers)
	 = {}
	
	headers.to_h.each do |key, value|
		# Skip reserved headers:
		next if key.start_with?("grpc-") || key == "content-type" || key == "te"
		
		# Decode binary headers:
		if key.end_with?("-bin")
			if value.is_a?(String)
				value = Base64.strict_decode64(value)
			elsif value.is_a?(Array)
				value = value.map{|item| Base64.strict_decode64(item)}
			end
		end
		
		[key] = value
	end
	
	
end

.extract_message(headers) ⇒ Object

Extract gRPC status message from headers. Returns Nil if message is not present.



92
93
94
95
96
97
98
99
100
101
# File 'lib/protocol/grpc/metadata.rb', line 92

def self.extract_message(headers)
	# Ensure policy is set - setting policy clears the index (@indexed = nil)
	# The index will be rebuilt automatically on next access via to_h
	headers.policy = Protocol::GRPC::HEADER_POLICY unless headers.policy == Protocol::GRPC::HEADER_POLICY
	
	message = headers["grpc-message"]
	return nil unless message
	
	return message.decode
end

.extract_status(headers) ⇒ Object

Extract gRPC status from headers. Returns Status::UNKNOWN if status is not present.

Note: In Protocol::HTTP::Headers, trailers are merged into the headers so users just access headers regardless of whether it was sent as an initial header or trailer.



76
77
78
79
80
81
82
83
84
85
# File 'lib/protocol/grpc/metadata.rb', line 76

def self.extract_status(headers)
	# Ensure policy is set - setting policy clears the index (@indexed = nil)
	# The index will be rebuilt automatically on next access via to_h
	headers.policy = Protocol::GRPC::HEADER_POLICY unless headers.policy == Protocol::GRPC::HEADER_POLICY
	
	status = headers["grpc-status"]
	return Status::UNKNOWN unless status
	
	return status.to_i
end