protocol-grpc 0.14.0 → 0.16.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/context/getting-started.md +4 -1
- data/design.md +19 -19
- data/lib/protocol/grpc/error.rb +1 -1
- data/lib/protocol/grpc/header/timeout.rb +19 -14
- data/lib/protocol/grpc/metadata.rb +25 -6
- data/lib/protocol/grpc/status.rb +33 -19
- data/lib/protocol/grpc/version.rb +1 -1
- data/readme.md +11 -8
- data/releases.md +11 -0
- data.tar.gz.sig +0 -0
- metadata +2 -2
- 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: f2d0b8c7ab88b83ceb64b1cbcd59828bf95416c35b9eafb94892b6c847b4820c
|
|
4
|
+
data.tar.gz: e9283b64b23825657eb739beddb581bd6e3596264e627e7d0cbe5441cd1220de
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 67dfce9bbf3d3cf7255579c5fd0467b879a6c23c07bbecb3556ee7154922aeeb834fcca6ba9f7a5e8fa025c40d8db141a53db78cf8293fa5c932c55ee0dd6d09
|
|
7
|
+
data.tar.gz: 7c90ef5c07d6a142e9bcdd1f5938b9253f6e73902b5ab284fb1c1b3f9d4167540ed09a81c900eff8996146ffe400a43cb03bcf9fda76b92c3c89cf383cffa3a4
|
checksums.yaml.gz.sig
CHANGED
|
Binary file
|
data/context/getting-started.md
CHANGED
|
@@ -19,7 +19,7 @@ $ bundle add protocol-grpc
|
|
|
19
19
|
- A {ruby Protocol::GRPC::Body::Writable} class which handles writing gRPC messages to HTTP request/response bodies with automatic framing and encoding.
|
|
20
20
|
- A {ruby Protocol::GRPC::Middleware} abstract base class for building gRPC server applications.
|
|
21
21
|
- A {ruby Protocol::GRPC::Call} class which represents the request, response, metadata, and deadline for a single gRPC RPC call.
|
|
22
|
-
- A {ruby Protocol::GRPC::Status} module with gRPC status code constants.
|
|
22
|
+
- A {ruby Protocol::GRPC::Status} module with gRPC status code constants and names.
|
|
23
23
|
- A {ruby Protocol::GRPC::Error} hierarchy for gRPC-specific error handling.
|
|
24
24
|
|
|
25
25
|
## Integration
|
|
@@ -103,6 +103,9 @@ if status != Protocol::GRPC::Status::OK
|
|
|
103
103
|
message = Protocol::GRPC::Metadata.extract_message(response.headers)
|
|
104
104
|
raise Protocol::GRPC::Error.for(status, message)
|
|
105
105
|
end
|
|
106
|
+
|
|
107
|
+
# Use canonical gRPC status names for logging, metrics, or serialization:
|
|
108
|
+
Protocol::GRPC::Status::NAMES[status] # => "OK"
|
|
106
109
|
```
|
|
107
110
|
|
|
108
111
|
### Server Middleware
|
data/design.md
CHANGED
|
@@ -496,25 +496,25 @@ module Protocol
|
|
|
496
496
|
DATA_LOSS = 15
|
|
497
497
|
UNAUTHENTICATED = 16
|
|
498
498
|
|
|
499
|
-
# Status code
|
|
500
|
-
|
|
499
|
+
# Status code names
|
|
500
|
+
NAMES = {
|
|
501
501
|
OK => "OK",
|
|
502
|
-
CANCELLED => "
|
|
503
|
-
UNKNOWN => "
|
|
504
|
-
INVALID_ARGUMENT => "
|
|
505
|
-
DEADLINE_EXCEEDED => "
|
|
506
|
-
NOT_FOUND => "
|
|
507
|
-
ALREADY_EXISTS => "
|
|
508
|
-
PERMISSION_DENIED => "
|
|
509
|
-
RESOURCE_EXHAUSTED => "
|
|
510
|
-
FAILED_PRECONDITION => "
|
|
511
|
-
ABORTED => "
|
|
512
|
-
OUT_OF_RANGE => "
|
|
513
|
-
UNIMPLEMENTED => "
|
|
514
|
-
INTERNAL => "
|
|
515
|
-
UNAVAILABLE => "
|
|
516
|
-
DATA_LOSS => "
|
|
517
|
-
UNAUTHENTICATED => "
|
|
502
|
+
CANCELLED => "CANCELLED",
|
|
503
|
+
UNKNOWN => "UNKNOWN",
|
|
504
|
+
INVALID_ARGUMENT => "INVALID_ARGUMENT",
|
|
505
|
+
DEADLINE_EXCEEDED => "DEADLINE_EXCEEDED",
|
|
506
|
+
NOT_FOUND => "NOT_FOUND",
|
|
507
|
+
ALREADY_EXISTS => "ALREADY_EXISTS",
|
|
508
|
+
PERMISSION_DENIED => "PERMISSION_DENIED",
|
|
509
|
+
RESOURCE_EXHAUSTED => "RESOURCE_EXHAUSTED",
|
|
510
|
+
FAILED_PRECONDITION => "FAILED_PRECONDITION",
|
|
511
|
+
ABORTED => "ABORTED",
|
|
512
|
+
OUT_OF_RANGE => "OUT_OF_RANGE",
|
|
513
|
+
UNIMPLEMENTED => "UNIMPLEMENTED",
|
|
514
|
+
INTERNAL => "INTERNAL",
|
|
515
|
+
UNAVAILABLE => "UNAVAILABLE",
|
|
516
|
+
DATA_LOSS => "DATA_LOSS",
|
|
517
|
+
UNAUTHENTICATED => "UNAUTHENTICATED"
|
|
518
518
|
}.freeze
|
|
519
519
|
end
|
|
520
520
|
end
|
|
@@ -585,7 +585,7 @@ module Protocol
|
|
|
585
585
|
@status_code = status_code
|
|
586
586
|
@details = details
|
|
587
587
|
@metadata = metadata
|
|
588
|
-
super(message || Status::
|
|
588
|
+
super(message || Status::NAMES[status_code])
|
|
589
589
|
end
|
|
590
590
|
end
|
|
591
591
|
|
data/lib/protocol/grpc/error.rb
CHANGED
|
@@ -13,24 +13,29 @@ module Protocol
|
|
|
13
13
|
# This header appears only in request headers, not in trailers.
|
|
14
14
|
class Timeout < String
|
|
15
15
|
# The wire format for a gRPC timeout value.
|
|
16
|
-
FORMAT = /\A(?<amount
|
|
16
|
+
FORMAT = /\A(?<amount>\d{1,8})(?<unit>[HMSmun])\z/
|
|
17
17
|
|
|
18
18
|
# Format a timeout duration for the `grpc-timeout` header.
|
|
19
19
|
# @parameter timeout [Numeric] The timeout duration in seconds.
|
|
20
20
|
# @returns [String] The formatted timeout.
|
|
21
21
|
def self.format(timeout)
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
22
|
+
raise ArgumentError, "Timeout must be finite and non-negative!" unless timeout.finite? && timeout >= 0
|
|
23
|
+
raise RangeError, "Timeout exceeds the grpc-timeout wire limit!" if timeout > 99_999_999 * 3600
|
|
24
|
+
return "0n" if timeout.zero?
|
|
25
|
+
|
|
26
|
+
nanoseconds = (timeout * 1_000_000_000).ceil
|
|
27
|
+
units = {"H" => 3_600_000_000_000, "M" => 60_000_000_000, "S" => 1_000_000_000, "m" => 1_000_000, "u" => 1000, "n" => 1}
|
|
28
|
+
|
|
29
|
+
# Prefer an exact representation in the largest possible unit:
|
|
30
|
+
units.each do |unit, scale|
|
|
31
|
+
amount, remainder = nanoseconds.divmod(scale)
|
|
32
|
+
return "#{amount}#{unit}" if remainder.zero? && amount <= 99_999_999
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Otherwise round up in the finest unit that fits the wire limit:
|
|
36
|
+
units.reverse_each do |unit, scale|
|
|
37
|
+
amount = (nanoseconds + scale - 1).div(scale)
|
|
38
|
+
return "#{amount}#{unit}" if amount <= 99_999_999
|
|
34
39
|
end
|
|
35
40
|
end
|
|
36
41
|
|
|
@@ -69,7 +74,7 @@ module Protocol
|
|
|
69
74
|
# @raises [ArgumentError] If the timeout value is invalid.
|
|
70
75
|
def to_seconds
|
|
71
76
|
unless match = FORMAT.match(self)
|
|
72
|
-
raise ArgumentError, "Invalid grpc-timeout: #{self.inspect}"
|
|
77
|
+
raise ArgumentError, "Invalid grpc-timeout: #{self.inspect}!"
|
|
73
78
|
end
|
|
74
79
|
|
|
75
80
|
amount = match[:amount].to_i
|
|
@@ -17,7 +17,7 @@ module Protocol
|
|
|
17
17
|
# @parameter timeout [Numeric | Nil] Optional timeout in seconds.
|
|
18
18
|
# @parameter content_type [String] The request content type.
|
|
19
19
|
# @returns [Protocol::HTTP::Headers] The constructed request headers.
|
|
20
|
-
def self.build(metadata: {}, timeout: nil, content_type: "application/grpc
|
|
20
|
+
def self.build(metadata: {}, timeout: nil, content_type: "application/grpc")
|
|
21
21
|
headers = Protocol::HTTP::Headers.new(policy: Protocol::GRPC::HEADER_POLICY)
|
|
22
22
|
headers["content-type"] = content_type
|
|
23
23
|
headers["te"] = "trailers"
|
|
@@ -52,9 +52,9 @@ module Protocol
|
|
|
52
52
|
# Decode binary headers:
|
|
53
53
|
if key.end_with?("-bin")
|
|
54
54
|
if value.is_a?(String)
|
|
55
|
-
value =
|
|
55
|
+
value = decode_binary(value)
|
|
56
56
|
elsif value.is_a?(Array)
|
|
57
|
-
value = value.map{|item|
|
|
57
|
+
value = value.map{|item| decode_binary(item)}
|
|
58
58
|
end
|
|
59
59
|
end
|
|
60
60
|
|
|
@@ -64,6 +64,24 @@ module Protocol
|
|
|
64
64
|
metadata
|
|
65
65
|
end
|
|
66
66
|
|
|
67
|
+
# Decode a padded or unpadded binary metadata value.
|
|
68
|
+
# @parameter value [String] The base64 encoded value.
|
|
69
|
+
# @returns [String] The decoded bytes.
|
|
70
|
+
# @raises [ArgumentError] If the value has invalid Base64 characters or padding.
|
|
71
|
+
def self.decode_binary(value)
|
|
72
|
+
# Only supply omitted padding; validate existing padding unchanged:
|
|
73
|
+
unless value.end_with?("=")
|
|
74
|
+
case value.bytesize % 4
|
|
75
|
+
when 2
|
|
76
|
+
value += "=="
|
|
77
|
+
when 3
|
|
78
|
+
value += "="
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
Base64.strict_decode64(value)
|
|
83
|
+
end
|
|
84
|
+
|
|
67
85
|
# Extract gRPC status from headers.
|
|
68
86
|
# Returns Status::UNKNOWN if status is not present.
|
|
69
87
|
#
|
|
@@ -107,8 +125,9 @@ module Protocol
|
|
|
107
125
|
# @parameter headers [Protocol::HTTP::Headers]
|
|
108
126
|
# @parameter status [Integer] gRPC status code
|
|
109
127
|
# @parameter message [String | Nil] Optional status message
|
|
110
|
-
# @parameter error [Exception | Nil] Optional error object
|
|
111
|
-
|
|
128
|
+
# @parameter error [Exception | Nil] Optional error object used for the message.
|
|
129
|
+
# @parameter backtrace [Boolean] Whether to include the error backtrace for debugging.
|
|
130
|
+
def self.assign_status!(headers, status: Status::OK, message: nil, error: nil, backtrace: false)
|
|
112
131
|
headers["grpc-status"] = status
|
|
113
132
|
|
|
114
133
|
if error && message.nil?
|
|
@@ -121,7 +140,7 @@ module Protocol
|
|
|
121
140
|
end
|
|
122
141
|
|
|
123
142
|
# Add backtrace from error if available
|
|
124
|
-
if error && error.backtrace && !error.backtrace.empty?
|
|
143
|
+
if backtrace && error && error.backtrace && !error.backtrace.empty?
|
|
125
144
|
# Assign backtrace array directly - Split header will handle it
|
|
126
145
|
headers["backtrace"] = error.backtrace
|
|
127
146
|
end
|
data/lib/protocol/grpc/status.rb
CHANGED
|
@@ -5,8 +5,22 @@
|
|
|
5
5
|
|
|
6
6
|
module Protocol
|
|
7
7
|
module GRPC
|
|
8
|
-
# Provides gRPC status codes and their
|
|
8
|
+
# Provides gRPC status codes and their names.
|
|
9
9
|
module Status
|
|
10
|
+
# Map an HTTP response status when the server did not provide grpc-status.
|
|
11
|
+
# @parameter status [Integer] The HTTP status code.
|
|
12
|
+
# @returns [Integer] The fallback gRPC status code.
|
|
13
|
+
def self.for_http_status(status)
|
|
14
|
+
case status
|
|
15
|
+
when 400 then INTERNAL
|
|
16
|
+
when 401 then UNAUTHENTICATED
|
|
17
|
+
when 403 then PERMISSION_DENIED
|
|
18
|
+
when 404 then UNIMPLEMENTED
|
|
19
|
+
when 429, 502, 503, 504 then UNAVAILABLE
|
|
20
|
+
else UNKNOWN
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
10
24
|
OK = 0
|
|
11
25
|
CANCELLED = 1
|
|
12
26
|
UNKNOWN = 2
|
|
@@ -25,25 +39,25 @@ module Protocol
|
|
|
25
39
|
DATA_LOSS = 15
|
|
26
40
|
UNAUTHENTICATED = 16
|
|
27
41
|
|
|
28
|
-
# Status code
|
|
29
|
-
|
|
42
|
+
# Status code names, as defined by the gRPC specification.
|
|
43
|
+
NAMES = {
|
|
30
44
|
OK => "OK",
|
|
31
|
-
CANCELLED => "
|
|
32
|
-
UNKNOWN => "
|
|
33
|
-
INVALID_ARGUMENT => "
|
|
34
|
-
DEADLINE_EXCEEDED => "
|
|
35
|
-
NOT_FOUND => "
|
|
36
|
-
ALREADY_EXISTS => "
|
|
37
|
-
PERMISSION_DENIED => "
|
|
38
|
-
RESOURCE_EXHAUSTED => "
|
|
39
|
-
FAILED_PRECONDITION => "
|
|
40
|
-
ABORTED => "
|
|
41
|
-
OUT_OF_RANGE => "
|
|
42
|
-
UNIMPLEMENTED => "
|
|
43
|
-
INTERNAL => "
|
|
44
|
-
UNAVAILABLE => "
|
|
45
|
-
DATA_LOSS => "
|
|
46
|
-
UNAUTHENTICATED => "
|
|
45
|
+
CANCELLED => "CANCELLED",
|
|
46
|
+
UNKNOWN => "UNKNOWN",
|
|
47
|
+
INVALID_ARGUMENT => "INVALID_ARGUMENT",
|
|
48
|
+
DEADLINE_EXCEEDED => "DEADLINE_EXCEEDED",
|
|
49
|
+
NOT_FOUND => "NOT_FOUND",
|
|
50
|
+
ALREADY_EXISTS => "ALREADY_EXISTS",
|
|
51
|
+
PERMISSION_DENIED => "PERMISSION_DENIED",
|
|
52
|
+
RESOURCE_EXHAUSTED => "RESOURCE_EXHAUSTED",
|
|
53
|
+
FAILED_PRECONDITION => "FAILED_PRECONDITION",
|
|
54
|
+
ABORTED => "ABORTED",
|
|
55
|
+
OUT_OF_RANGE => "OUT_OF_RANGE",
|
|
56
|
+
UNIMPLEMENTED => "UNIMPLEMENTED",
|
|
57
|
+
INTERNAL => "INTERNAL",
|
|
58
|
+
UNAVAILABLE => "UNAVAILABLE",
|
|
59
|
+
DATA_LOSS => "DATA_LOSS",
|
|
60
|
+
UNAUTHENTICATED => "UNAUTHENTICATED"
|
|
47
61
|
}.freeze
|
|
48
62
|
end
|
|
49
63
|
end
|
data/readme.md
CHANGED
|
@@ -28,6 +28,17 @@ Please see the [project documentation](https://socketry.github.io/protocol-grpc/
|
|
|
28
28
|
|
|
29
29
|
Please see the [project releases](https://socketry.github.io/protocol-grpc/releases/index) for all releases.
|
|
30
30
|
|
|
31
|
+
### v0.16.0
|
|
32
|
+
|
|
33
|
+
- Preserve timeout precision within the eight-digit wire limit, rounding up when necessary.
|
|
34
|
+
- Accept padded and unpadded binary metadata.
|
|
35
|
+
- **Breaking**: Error backtraces are no longer sent to clients by default. Pass `backtrace: true` to `Metadata.assign_status!` to explicitly enable them for debugging.
|
|
36
|
+
- Default request metadata to `application/grpc` and provide `Status.for_http_status` for responses without `grpc-status`.
|
|
37
|
+
|
|
38
|
+
### v0.15.0
|
|
39
|
+
|
|
40
|
+
- **Breaking**: Removed `Protocol::GRPC::Status::DESCRIPTIONS`. Use `Protocol::GRPC::Status::NAMES` for canonical gRPC status names.
|
|
41
|
+
|
|
31
42
|
### v0.14.0
|
|
32
43
|
|
|
33
44
|
- **Breaking**: Removed the unused `Protocol::GRPC::Call#cancel!` and `Protocol::GRPC::Call#cancelled?` methods.
|
|
@@ -67,14 +78,6 @@ Please see the [project releases](https://socketry.github.io/protocol-grpc/relea
|
|
|
67
78
|
- **Breaking**: Removed `Call#response_headers` method. Use `call.response.headers` directly.
|
|
68
79
|
- Added `RPC#streaming?` method to check if an RPC is streaming.
|
|
69
80
|
|
|
70
|
-
### v0.2.0
|
|
71
|
-
|
|
72
|
-
- `RPC#method` is always defined (snake case).
|
|
73
|
-
|
|
74
|
-
### v0.1.0
|
|
75
|
-
|
|
76
|
-
- Initial design.
|
|
77
|
-
|
|
78
81
|
## See Also
|
|
79
82
|
|
|
80
83
|
- [async-grpc](https://github.com/socketry/async-grpc) — Asynchronous gRPC client and server implementation using this interface.
|
data/releases.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# Releases
|
|
2
2
|
|
|
3
|
+
## v0.16.0
|
|
4
|
+
|
|
5
|
+
- Preserve timeout precision within the eight-digit wire limit, rounding up when necessary.
|
|
6
|
+
- Accept padded and unpadded binary metadata.
|
|
7
|
+
- **Breaking**: Error backtraces are no longer sent to clients by default. Pass `backtrace: true` to `Metadata.assign_status!` to explicitly enable them for debugging.
|
|
8
|
+
- Default request metadata to `application/grpc` and provide `Status.for_http_status` for responses without `grpc-status`.
|
|
9
|
+
|
|
10
|
+
## v0.15.0
|
|
11
|
+
|
|
12
|
+
- **Breaking**: Removed `Protocol::GRPC::Status::DESCRIPTIONS`. Use `Protocol::GRPC::Status::NAMES` for canonical gRPC status names.
|
|
13
|
+
|
|
3
14
|
## v0.14.0
|
|
4
15
|
|
|
5
16
|
- **Breaking**: Removed the unused `Protocol::GRPC::Call#cancel!` and `Protocol::GRPC::Call#cancelled?` methods.
|
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.16.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Samuel Williams
|
|
@@ -145,7 +145,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
145
145
|
- !ruby/object:Gem::Version
|
|
146
146
|
version: '0'
|
|
147
147
|
requirements: []
|
|
148
|
-
rubygems_version: 4.0.
|
|
148
|
+
rubygems_version: 4.0.16
|
|
149
149
|
specification_version: 4
|
|
150
150
|
summary: Protocol abstractions for gRPC, built on top of protocol-http.
|
|
151
151
|
test_files: []
|
metadata.gz.sig
CHANGED
|
Binary file
|