proto_service_mesh 0.18.0 → 0.19.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fb343f9b59bc8db10b06a8466222d6a808477cc0753480a83384c940a9e9696b
4
- data.tar.gz: cf701b106b97be15a829e464010985bd58c6252ccbd45ca6acff91a0c1621593
3
+ metadata.gz: b3837c53424bc2b59a2808b6586c056dba67b76ff34e4871adfad3978fe9a668
4
+ data.tar.gz: 62dd2ab469307cf2a40e0060cdaede23c1c3e4f397e5c4d9cc4991e441a1f2d9
5
5
  SHA512:
6
- metadata.gz: a3360b9dea587ed75a43062f8f5664bdb5fe7d81cbe74763c113c59f566eb85cfde7dc515b69d22be1b8f65212958010f97244ce2707ae9c7a280d64d6a3650b
7
- data.tar.gz: 93641536186e764ac6e48bc0f0051b1b1dfc04cf4620d68a3f546289a9e2e630451bbc53a57081ccd834f4a3baf47d7243e186bc98417af3203cec6798a2a334
6
+ metadata.gz: e8552e6c52b9d1cb9f5e2e6d3b99f541659fe5f7bc29bb2e855da3e3e2c88b42c83b5147c068897a7449abee61d6a178a31be29a3e681b4b48865cfaa02a509d
7
+ data.tar.gz: 8982b84372dcc2e2a4ccd8b96e6f31ebcf89cde3590866088d5c4a1d403a9b7d232924d2445e0c56bf3b6b688ca540dccb25191338a95eebf31f97a3cc47b9a2
@@ -28,15 +28,19 @@ module ProtoServiceMesh
28
28
 
29
29
  # Sends +request+ to a route and returns the decoded response, whose
30
30
  # mesh_metadata is the reply's metadata without keys that start with
31
- # OPTION_PREFIX. Raises MeshError for a reply carrying Grpc-Status, or
32
- # INTERNAL when either payload does not decode; either error's
33
- # mesh_metadata is the same reply metadata.
31
+ # OPTION_PREFIX. Raises MeshError for a reply carrying Mesh-Status, or
32
+ # Grpc-Status when the reply has no Mesh-Status, or INTERNAL when either
33
+ # payload does not decode; either error's mesh_metadata is the same reply
34
+ # metadata. A reply that carries Grpc-Status or Content-Type without its
35
+ # Mesh- replacement comes from an older version, and the first such reply
36
+ # on the target writes a warning.
34
37
  def self.request(rpc, request)
35
38
  message, options = outbound(rpc, request)
36
39
  reply = client_for(rpc.target).request(message, options)
37
40
  reply_metadata = Wire.without_options(reply.metadata)
41
+ Wire.legacy_content_type?(rpc.target, reply.metadata)
38
42
  begin
39
- if reply_metadata.key?(Wire::GRPC_STATUS_KEY)
43
+ if reply_metadata.key?(Wire::MESH_STATUS_KEY) || Wire.legacy_status?(rpc.target, reply.metadata)
40
44
  raise MeshError.from_proto(decode(Google::Rpc::Status, reply.payload))
41
45
  end
42
46
 
@@ -6,7 +6,9 @@ module ProtoServiceMesh
6
6
  # serves, taking the decoded request, whose mesh_metadata is the inbound
7
7
  # message metadata. A route method returns the response; the response's
8
8
  # mesh_metadata, without keys that start with OPTION_PREFIX, is the reply's
9
- # metadata. The same holds for a raised MeshError.
9
+ # metadata. The same holds for a raised MeshError. A request that carries
10
+ # Content-Type without Mesh-Content-Type comes from an older version, and its
11
+ # reply also carries Content-Type and, for a MeshError, Grpc-Status.
10
12
  class RPCService
11
13
  extend RpcDSL
12
14
 
@@ -41,10 +43,11 @@ module ProtoServiceMesh
41
43
 
42
44
  def endpoint_handler(rpc)
43
45
  lambda do |message|
46
+ legacy = Wire.legacy_content_type?(rpc.target, message.metadata)
44
47
  request = begin
45
48
  decode_inbound(rpc, message)
46
49
  rescue Google::Protobuf::ParseError => e
47
- next status_reply(message, MeshError.new(:INTERNAL, e.message))
50
+ next status_reply(message, MeshError.new(:INTERNAL, e.message), legacy)
48
51
  end
49
52
 
50
53
  response = public_send(rpc.name, request)
@@ -52,17 +55,18 @@ module ProtoServiceMesh
52
55
  raise TypeError, "#{rpc.name} returned #{response.class}, expected #{rpc.output}"
53
56
  end
54
57
 
55
- metadata = Wire.metadata(Wire.without_options(response.mesh_metadata).except(Wire::GRPC_STATUS_KEY))
58
+ metadata = Wire.metadata(Wire.without_options(response.mesh_metadata), legacy: legacy)
56
59
  ServiceMesh::Message.new(target: message.target, metadata: metadata, payload: response.to_proto)
57
60
  rescue MeshError => e
58
- status_reply(message, e)
61
+ status_reply(message, e, legacy)
59
62
  rescue => e
60
- status_reply(message, MeshError.new(:UNKNOWN, e.message))
63
+ status_reply(message, MeshError.new(:UNKNOWN, e.message), legacy)
61
64
  end
62
65
  end
63
66
 
64
67
  def subscriber_handler(rpc)
65
68
  lambda do |message|
69
+ Wire.legacy_content_type?(rpc.target, message.metadata)
66
70
  public_send(rpc.name, decode_inbound(rpc, message))
67
71
  nil
68
72
  end
@@ -81,8 +85,9 @@ module ProtoServiceMesh
81
85
  request
82
86
  end
83
87
 
84
- def status_reply(message, error)
85
- ServiceMesh::Message.new(target: message.target, metadata: Wire.status_metadata(error, Wire.without_options(error.mesh_metadata)), payload: error.proto.to_proto)
88
+ def status_reply(message, error, legacy)
89
+ metadata = Wire.status_metadata(error, Wire.without_options(error.mesh_metadata), legacy: legacy)
90
+ ServiceMesh::Message.new(target: message.target, metadata: metadata, payload: error.proto.to_proto)
86
91
  end
87
92
  end
88
93
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ProtoServiceMesh
4
- VERSION = "0.18.0"
4
+ VERSION = "0.19.0"
5
5
  end
@@ -10,14 +10,56 @@ module ProtoServiceMesh
10
10
 
11
11
  # Metadata keys and values the specification fixes on every message.
12
12
  module Wire
13
- CONTENT_TYPE_KEY = "Content-Type"
13
+ MESH_CONTENT_TYPE_KEY = "Mesh-Content-Type"
14
14
  CONTENT_TYPE = "application/x-protobuf"
15
+ MESH_STATUS_KEY = "Mesh-Status"
16
+
17
+ # Deprecated. The key older versions send in place of
18
+ # MESH_CONTENT_TYPE_KEY.
19
+ CONTENT_TYPE_KEY = "Content-Type"
20
+
21
+ # Deprecated. The key older versions send in place of MESH_STATUS_KEY.
15
22
  GRPC_STATUS_KEY = "Grpc-Status"
16
23
 
17
- # Metadata for a message carrying a payload of the method's type.
18
- def self.metadata(extra = {})
19
- extra.to_h.merge(CONTENT_TYPE_KEY => CONTENT_TYPE)
24
+ # The keys the library sets itself. Values for them in application
25
+ # metadata are dropped.
26
+ KEYS = [MESH_CONTENT_TYPE_KEY, MESH_STATUS_KEY, CONTENT_TYPE_KEY, GRPC_STATUS_KEY].freeze
27
+
28
+ @warned = {}
29
+ @warned_lock = Mutex.new
30
+
31
+ # Metadata for a message carrying a payload of the method's type: +extra+
32
+ # without KEYS, with MESH_CONTENT_TYPE_KEY, and with CONTENT_TYPE_KEY
33
+ # too when +legacy+ is true.
34
+ def self.metadata(extra = {}, legacy: false)
35
+ out = extra.to_h.except(*KEYS).merge(MESH_CONTENT_TYPE_KEY => CONTENT_TYPE)
36
+ out[CONTENT_TYPE_KEY] = CONTENT_TYPE if legacy
37
+ out
38
+ end
39
+
40
+ # Whether +metadata+ comes from an older version: it carries
41
+ # CONTENT_TYPE_KEY and no MESH_CONTENT_TYPE_KEY. The first such message on
42
+ # +target+ writes a warning.
43
+ def self.legacy_content_type?(target, metadata)
44
+ deprecated?(target, metadata, CONTENT_TYPE_KEY, MESH_CONTENT_TYPE_KEY)
45
+ end
46
+
47
+ # Whether reply +metadata+ carries GRPC_STATUS_KEY and no
48
+ # MESH_STATUS_KEY. The first such reply on +target+ writes a warning.
49
+ def self.legacy_status?(target, metadata)
50
+ deprecated?(target, metadata, GRPC_STATUS_KEY, MESH_STATUS_KEY)
51
+ end
52
+
53
+ def self.deprecated?(target, metadata, old, replacement)
54
+ return false unless metadata.key?(old) && !metadata.key?(replacement)
55
+
56
+ first = @warned_lock.synchronize { @warned.key?([old, target.segments]) ? false : @warned[[old, target.segments]] = true }
57
+ if first
58
+ warn "proto_service_mesh: a message on #{target.segments.inspect} carries the deprecated metadata key #{old} in place of #{replacement}; the sender runs an older version"
59
+ end
60
+ true
20
61
  end
62
+ private_class_method :deprecated?
21
63
 
22
64
  # Splits +metadata+ into the message metadata and the transport options:
23
65
  # the keys that start with OPTION_PREFIX become options with the prefix
@@ -40,9 +82,13 @@ module ProtoServiceMesh
40
82
  split_options(metadata).first
41
83
  end
42
84
 
43
- # Metadata for a reply whose payload is an encoded google.rpc.Status.
44
- def self.status_metadata(error, extra = {})
45
- extra.to_h.merge(CONTENT_TYPE_KEY => CONTENT_TYPE, GRPC_STATUS_KEY => error.proto.code.to_s)
85
+ # Metadata for a reply whose payload is an encoded google.rpc.Status:
86
+ # metadata(extra, legacy:) with MESH_STATUS_KEY, and with GRPC_STATUS_KEY
87
+ # too when +legacy+ is true.
88
+ def self.status_metadata(error, extra = {}, legacy: false)
89
+ out = metadata(extra, legacy: legacy).merge(MESH_STATUS_KEY => error.proto.code.to_s)
90
+ out[GRPC_STATUS_KEY] = out[MESH_STATUS_KEY] if legacy
91
+ out
46
92
  end
47
93
  end
48
94
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: proto_service_mesh
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.18.0
4
+ version: 0.19.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bryant Morrill