protocol-grpc 0.9.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/design.md +6 -0
- data/lib/protocol/grpc/body/readable.rb +12 -2
- data/lib/protocol/grpc/body/writable.rb +8 -1
- data/lib/protocol/grpc/header/encoding.rb +63 -0
- data/lib/protocol/grpc/header/timeout.rb +70 -0
- data/lib/protocol/grpc/header.rb +7 -0
- data/lib/protocol/grpc/methods.rb +6 -2
- data/lib/protocol/grpc/middleware.rb +3 -0
- data/lib/protocol/grpc/version.rb +1 -1
- data.tar.gz.sig +0 -0
- metadata +3 -1
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 26f2d23d59b86e4dc0dda296d1ed3d455c52c9741ced1a1c62bd8b24c6d0398e
|
|
4
|
+
data.tar.gz: 4f4b655efdec5d5e1bde611e1904112706a3f1548ee4b5c90d8b24b5a5b2c882
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7f2e77bb2014620f522bf84704dceae110ea22f1c580c9e714d077a3296b1226360910dd5872a8aee6a0d45fb0b273c0a1e074a9c9c7c562f0df4619b50340b0
|
|
7
|
+
data.tar.gz: '039d6f8f513430285ef84722a177c91e501c91c6e10f843b87f2c10df1dbed69b8e4196d44cc73264e1e9b3e749e8c7c75ae6a4da00653973c013588fb0c1f2a'
|
checksums.yaml.gz.sig
CHANGED
|
Binary file
|
data/design.md
CHANGED
|
@@ -266,8 +266,14 @@ module Protocol
|
|
|
266
266
|
# Custom header policy for gRPC
|
|
267
267
|
# Extends Protocol::HTTP::Headers::POLICY with gRPC-specific headers
|
|
268
268
|
HEADER_POLICY = Protocol::HTTP::Headers::POLICY.merge(
|
|
269
|
+
# Request headers:
|
|
270
|
+
"grpc-timeout" => Header::Timeout,
|
|
271
|
+
"grpc-encoding" => Header::Encoding,
|
|
272
|
+
|
|
273
|
+
# Response headers:
|
|
269
274
|
"grpc-status" => Header::Status,
|
|
270
275
|
"grpc-message" => Header::Message,
|
|
276
|
+
|
|
271
277
|
# By default, all other headers follow standard HTTP policy
|
|
272
278
|
# But gRPC allows most metadata to be sent as trailers
|
|
273
279
|
).freeze
|
|
@@ -108,9 +108,19 @@ module Protocol
|
|
|
108
108
|
def decompress(data)
|
|
109
109
|
case @encoding
|
|
110
110
|
when "gzip"
|
|
111
|
-
|
|
111
|
+
# Gzip format: zlib stream with gzip header (RFC 1952)
|
|
112
|
+
# Use MAX_WBITS + 32 to handle gzip header and CRC
|
|
113
|
+
inflater = Zlib::Inflate.new(Zlib::MAX_WBITS + 32)
|
|
114
|
+
result = inflater.inflate(data)
|
|
115
|
+
inflater.close
|
|
116
|
+
result
|
|
112
117
|
when "deflate"
|
|
113
|
-
Zlib
|
|
118
|
+
# Zlib format (RFC 1950) - default window bits handle zlib header
|
|
119
|
+
# This matches HTTP's "deflate" content-encoding
|
|
120
|
+
inflater = Zlib::Inflate.new
|
|
121
|
+
result = inflater.inflate(data)
|
|
122
|
+
inflater.close
|
|
123
|
+
result
|
|
114
124
|
else
|
|
115
125
|
data
|
|
116
126
|
end
|
|
@@ -75,19 +75,26 @@ module Protocol
|
|
|
75
75
|
|
|
76
76
|
protected
|
|
77
77
|
|
|
78
|
-
# Compress data using the
|
|
78
|
+
# Compress data using the specified encoding.
|
|
79
|
+
# Per gRPC spec, compression is per-message and uses standard formats:
|
|
80
|
+
# - gzip: RFC 1952 (gzip format with headers and CRC)
|
|
81
|
+
# - deflate: RFC 1950 (zlib format, not raw deflate, for HTTP compatibility)
|
|
79
82
|
# @parameter data [String] The data to compress
|
|
83
|
+
# @parameter encoding [String | Nil] The encoding to use. If `nil`, uses @encoding.
|
|
80
84
|
# @returns [String] The compressed data
|
|
81
85
|
# @raises [Error] If compression fails
|
|
82
86
|
def compress(data)
|
|
83
87
|
case @encoding
|
|
84
88
|
when "gzip"
|
|
89
|
+
# Use GzipWriter for proper gzip format (includes headers, CRC)
|
|
85
90
|
io = StringIO.new
|
|
86
91
|
gz = Zlib::GzipWriter.new(io, @level)
|
|
87
92
|
gz.write(data)
|
|
88
93
|
gz.close
|
|
89
94
|
io.string
|
|
90
95
|
when "deflate"
|
|
96
|
+
# Use zlib format (RFC 1950) for HTTP compatibility
|
|
97
|
+
# This matches HTTP's "deflate" content-encoding
|
|
91
98
|
Zlib::Deflate.deflate(data, @level)
|
|
92
99
|
else
|
|
93
100
|
data # No compression or identity
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Released under the MIT License.
|
|
4
|
+
# Copyright, 2026, by Samuel Williams.
|
|
5
|
+
|
|
6
|
+
module Protocol
|
|
7
|
+
module GRPC
|
|
8
|
+
module Header
|
|
9
|
+
# The `grpc-encoding` header represents the message compression encoding.
|
|
10
|
+
#
|
|
11
|
+
# The `grpc-encoding` header specifies the compression algorithm used for the message payload.
|
|
12
|
+
# Common values include "identity" (no compression), "gzip", "deflate", etc.
|
|
13
|
+
# This header can appear in both request and response headers, but not in trailers.
|
|
14
|
+
class Encoding < String
|
|
15
|
+
# Parse an encoding from a header value.
|
|
16
|
+
#
|
|
17
|
+
# @parameter value [String] The header value to parse (e.g., "identity", "gzip").
|
|
18
|
+
# @returns [Encoding] A new Encoding instance.
|
|
19
|
+
def self.parse(value)
|
|
20
|
+
new(value)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Coerce a value to an Encoding instance.
|
|
24
|
+
# Used by Protocol::HTTP::Headers when setting header values.
|
|
25
|
+
#
|
|
26
|
+
# @parameter value [Object] The value to coerce (will be converted to string).
|
|
27
|
+
# @returns [Encoding] A new Encoding instance.
|
|
28
|
+
def self.coerce(value)
|
|
29
|
+
new(value.to_s)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Initialize the encoding header with the given value.
|
|
33
|
+
#
|
|
34
|
+
# @parameter value [String] The encoding value (e.g., "identity", "gzip").
|
|
35
|
+
def initialize(value)
|
|
36
|
+
super(value.to_s)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Check if this encoding represents no compression (identity encoding).
|
|
40
|
+
#
|
|
41
|
+
# @returns [Boolean] `true` if the encoding is "identity" or empty.
|
|
42
|
+
def identity?
|
|
43
|
+
self == "identity" || self.empty?
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Merge another encoding value (takes the new value, as encoding should only appear once)
|
|
47
|
+
# @parameter value [String] The new encoding value
|
|
48
|
+
def <<(value)
|
|
49
|
+
replace(value.to_s)
|
|
50
|
+
|
|
51
|
+
return self
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Whether this header is acceptable in HTTP trailers.
|
|
55
|
+
# The `grpc-encoding` header does not appear in trailers.
|
|
56
|
+
# @returns [Boolean] `false`, as grpc-encoding cannot appear in trailers.
|
|
57
|
+
def self.trailer?
|
|
58
|
+
false
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Released under the MIT License.
|
|
4
|
+
# Copyright, 2026, by Samuel Williams.
|
|
5
|
+
|
|
6
|
+
require_relative "../methods"
|
|
7
|
+
|
|
8
|
+
module Protocol
|
|
9
|
+
module GRPC
|
|
10
|
+
module Header
|
|
11
|
+
# The `grpc-timeout` header represents the gRPC request timeout.
|
|
12
|
+
#
|
|
13
|
+
# The `grpc-timeout` header specifies how long the client is willing to wait for an RPC to complete.
|
|
14
|
+
# The format is: value + unit (H=hours, M=minutes, S=seconds, m=milliseconds, u=microseconds, n=nanoseconds).
|
|
15
|
+
# This header appears only in request headers, not in trailers.
|
|
16
|
+
class Timeout < String
|
|
17
|
+
# Parse a timeout from a header value.
|
|
18
|
+
#
|
|
19
|
+
# @parameter value [String] The header value to parse (e.g., "5S", "1000m").
|
|
20
|
+
# @returns [Timeout] A new Timeout instance.
|
|
21
|
+
def self.parse(value)
|
|
22
|
+
new(value)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Coerce a value to a Timeout instance.
|
|
26
|
+
#
|
|
27
|
+
# If a Numeric is provided, it will be formatted as a gRPC timeout string using {Protocol::GRPC::Methods.format_timeout}.
|
|
28
|
+
#
|
|
29
|
+
# @parameter value [String | Numeric] The value to coerce.
|
|
30
|
+
# @returns [Timeout] A new Timeout instance.
|
|
31
|
+
def self.coerce(value)
|
|
32
|
+
if value.is_a?(Numeric)
|
|
33
|
+
return new(Protocol::GRPC::Methods.format_timeout(value))
|
|
34
|
+
else
|
|
35
|
+
return new(value.to_s)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Initialize the timeout header with the given value.
|
|
40
|
+
#
|
|
41
|
+
# @parameter value [String] The timeout value in gRPC format.
|
|
42
|
+
def initialize(value)
|
|
43
|
+
super(value.to_s)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Parse the timeout value to seconds.
|
|
47
|
+
#
|
|
48
|
+
# @returns [Numeric | Nil] Timeout in seconds, or `Nil` if value is invalid.
|
|
49
|
+
def to_seconds
|
|
50
|
+
Protocol::GRPC::Methods.parse_timeout(self)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Merge another timeout value (takes the new value, as timeout should only appear once)
|
|
54
|
+
# @parameter value [String] The new timeout value
|
|
55
|
+
def <<(value)
|
|
56
|
+
replace(value.to_s)
|
|
57
|
+
|
|
58
|
+
return self
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Whether this header is acceptable in HTTP trailers.
|
|
62
|
+
# The `grpc-timeout` header is request-only and does not appear in trailers.
|
|
63
|
+
# @returns [Boolean] `false`, as grpc-timeout cannot appear in trailers.
|
|
64
|
+
def self.trailer?
|
|
65
|
+
false
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
data/lib/protocol/grpc/header.rb
CHANGED
|
@@ -8,6 +8,8 @@ require "protocol/http"
|
|
|
8
8
|
require_relative "status"
|
|
9
9
|
require_relative "header/status"
|
|
10
10
|
require_relative "header/message"
|
|
11
|
+
require_relative "header/timeout"
|
|
12
|
+
require_relative "header/encoding"
|
|
11
13
|
|
|
12
14
|
module Protocol
|
|
13
15
|
module GRPC
|
|
@@ -18,6 +20,11 @@ module Protocol
|
|
|
18
20
|
# Custom header policy for gRPC.
|
|
19
21
|
# Extends Protocol::HTTP::Headers::POLICY with gRPC-specific headers.
|
|
20
22
|
HEADER_POLICY = Protocol::HTTP::Headers::POLICY.merge(
|
|
23
|
+
# Request headers:
|
|
24
|
+
"grpc-timeout" => Header::Timeout,
|
|
25
|
+
"grpc-encoding" => Header::Encoding,
|
|
26
|
+
|
|
27
|
+
# Response headers:
|
|
21
28
|
"grpc-status" => Header::Status,
|
|
22
29
|
"grpc-message" => Header::Message
|
|
23
30
|
# By default, all other headers follow standard HTTP policy, but gRPC allows most metadata to be sent as trailers.
|
|
@@ -32,10 +32,14 @@ module Protocol
|
|
|
32
32
|
# @parameter content_type [String] Content type (default: "application/grpc+proto")
|
|
33
33
|
# @returns [Protocol::HTTP::Headers]
|
|
34
34
|
def self.build_headers(metadata: {}, timeout: nil, content_type: "application/grpc+proto")
|
|
35
|
-
headers = Protocol::HTTP::Headers.new
|
|
35
|
+
headers = Protocol::HTTP::Headers.new(policy: Protocol::GRPC::HEADER_POLICY)
|
|
36
36
|
headers["content-type"] = content_type
|
|
37
37
|
headers["te"] = "trailers"
|
|
38
|
-
|
|
38
|
+
|
|
39
|
+
if timeout
|
|
40
|
+
# Coerced to proper format by header policy:
|
|
41
|
+
headers["grpc-timeout"] = timeout
|
|
42
|
+
end
|
|
39
43
|
|
|
40
44
|
metadata.each do |key, value|
|
|
41
45
|
# Binary headers end with -bin and are base64 encoded:
|
|
@@ -27,6 +27,9 @@ module Protocol
|
|
|
27
27
|
def call(request)
|
|
28
28
|
return super unless grpc_request?(request)
|
|
29
29
|
|
|
30
|
+
# Ensure the request headers are using the gRPC header policy:
|
|
31
|
+
request.headers.policy = Protocol::GRPC::HEADER_POLICY
|
|
32
|
+
|
|
30
33
|
begin
|
|
31
34
|
dispatch(request)
|
|
32
35
|
rescue Error => error
|
data.tar.gz.sig
CHANGED
|
Binary file
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: protocol-grpc
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.10.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Samuel Williams
|
|
@@ -109,8 +109,10 @@ files:
|
|
|
109
109
|
- lib/protocol/grpc/call.rb
|
|
110
110
|
- lib/protocol/grpc/error.rb
|
|
111
111
|
- lib/protocol/grpc/header.rb
|
|
112
|
+
- lib/protocol/grpc/header/encoding.rb
|
|
112
113
|
- lib/protocol/grpc/header/message.rb
|
|
113
114
|
- lib/protocol/grpc/header/status.rb
|
|
115
|
+
- lib/protocol/grpc/header/timeout.rb
|
|
114
116
|
- lib/protocol/grpc/health_check.rb
|
|
115
117
|
- lib/protocol/grpc/interface.rb
|
|
116
118
|
- lib/protocol/grpc/metadata.rb
|
metadata.gz.sig
CHANGED
|
Binary file
|