starpc 0.51.0 → 0.52.1

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.
Files changed (93) hide show
  1. package/cmd/protoc-gen-es-starpc/typescript.ts +37 -0
  2. package/dist/cmd/protoc-gen-es-starpc/typescript.js +24 -0
  3. package/dist/echo/client-test.d.ts +1 -1
  4. package/dist/echo/client-test.js +110 -2
  5. package/dist/echo/echo_srpc.pb.d.ts +44 -1
  6. package/dist/echo/server.d.ts +9 -8
  7. package/dist/echo/server.js +6 -6
  8. package/dist/integration/cross-language/tcp-packet-stream.d.ts +3 -0
  9. package/dist/integration/cross-language/tcp-packet-stream.js +112 -0
  10. package/dist/integration/cross-language/tcp-packet-stream.test.d.ts +1 -0
  11. package/dist/integration/cross-language/tcp-packet-stream.test.js +121 -0
  12. package/dist/integration/cross-language/ts-client.js +50 -37
  13. package/dist/integration/cross-language/ts-server.js +1 -36
  14. package/dist/mock/mock_srpc.pb.d.ts +14 -1
  15. package/dist/rpcstream/rpcstream.d.ts +5 -1
  16. package/dist/rpcstream/rpcstream.js +75 -28
  17. package/dist/rpcstream/rpcstream.test.d.ts +1 -0
  18. package/dist/rpcstream/rpcstream.test.js +92 -0
  19. package/dist/srpc/channel.js +6 -3
  20. package/dist/srpc/channel.test.js +20 -1
  21. package/dist/srpc/client.js +18 -4
  22. package/dist/srpc/common-rpc.test.js +2 -0
  23. package/dist/srpc/handler.d.ts +12 -3
  24. package/dist/srpc/index.d.ts +2 -0
  25. package/dist/srpc/index.js +1 -0
  26. package/dist/srpc/invoker.d.ts +2 -1
  27. package/dist/srpc/invoker.js +2 -2
  28. package/dist/srpc/packet-codec.test.d.ts +1 -0
  29. package/dist/srpc/packet-codec.test.js +75 -0
  30. package/dist/srpc/packet.d.ts +1 -1
  31. package/dist/srpc/packet.js +11 -1
  32. package/dist/srpc/server-context.d.ts +11 -0
  33. package/dist/srpc/server-context.js +28 -0
  34. package/dist/srpc/server-rpc.js +3 -1
  35. package/dist/srpc/server.js +19 -6
  36. package/dist/srpc/server.test.js +78 -6
  37. package/dist/srpc/stream.d.ts +4 -1
  38. package/dist/srpc/stream.js +62 -3
  39. package/dist/srpc/stream.test.js +110 -1
  40. package/dist/srpc/termination.d.ts +27 -0
  41. package/dist/srpc/termination.js +56 -0
  42. package/dist/srpc/termination.test.d.ts +1 -0
  43. package/dist/srpc/termination.test.js +24 -0
  44. package/dist/srpc/watchdog.test.js +1 -0
  45. package/dist/testdata/packet-codec-vectors.json +64 -0
  46. package/echo/client-test.ts +124 -2
  47. package/echo/echo_pb2.py +40 -0
  48. package/echo/echo_pb2.pyi +13 -0
  49. package/echo/echo_srpc.pb.ts +74 -0
  50. package/echo/echo_srpc.py +306 -0
  51. package/echo/echo_srpc.pyi +85 -0
  52. package/echo/server.ts +24 -5
  53. package/go.mod +2 -2
  54. package/go.sum +14 -0
  55. package/integration/cross-language/go-client/main.go +79 -3
  56. package/integration/cross-language/python-client.py +146 -0
  57. package/integration/cross-language/python-server.py +140 -0
  58. package/integration/cross-language/run.bash +190 -65
  59. package/integration/cross-language/tcp-packet-stream.test.ts +154 -0
  60. package/integration/cross-language/tcp-packet-stream.ts +121 -0
  61. package/integration/cross-language/ts-client.ts +62 -40
  62. package/integration/cross-language/ts-server.ts +1 -45
  63. package/mock/mock_pb2.py +38 -0
  64. package/mock/mock_pb2.pyi +11 -0
  65. package/mock/mock_srpc.pb.ts +19 -1
  66. package/mock/mock_srpc.py +71 -0
  67. package/mock/mock_srpc.pyi +27 -0
  68. package/package.json +20 -6
  69. package/srpc/__init__.py +0 -0
  70. package/srpc/channel.test.ts +21 -1
  71. package/srpc/channel.ts +7 -3
  72. package/srpc/client.ts +20 -4
  73. package/srpc/codec.rs +6 -0
  74. package/srpc/common-rpc.test.ts +2 -0
  75. package/srpc/handler.ts +54 -4
  76. package/srpc/index.ts +7 -0
  77. package/srpc/invoker.ts +23 -6
  78. package/srpc/packet-codec-vectors_test.go +195 -0
  79. package/srpc/packet-codec.test.ts +139 -0
  80. package/srpc/packet-rw.go +9 -2
  81. package/srpc/packet.ts +15 -2
  82. package/srpc/py.typed +0 -0
  83. package/srpc/rpcproto_pb2.py +40 -0
  84. package/srpc/rpcproto_pb2.pyi +40 -0
  85. package/srpc/server-context.ts +55 -0
  86. package/srpc/server-rpc.ts +4 -1
  87. package/srpc/server.test.ts +100 -5
  88. package/srpc/server.ts +22 -6
  89. package/srpc/stream.test.ts +132 -1
  90. package/srpc/stream.ts +65 -9
  91. package/srpc/termination.test.ts +30 -0
  92. package/srpc/termination.ts +70 -0
  93. package/srpc/watchdog.test.ts +1 -0
@@ -5,6 +5,7 @@ describe('Watchdog', () => {
5
5
  vi.useFakeTimers();
6
6
  });
7
7
  afterEach(() => {
8
+ vi.useRealTimers();
8
9
  vi.restoreAllMocks();
9
10
  });
10
11
  it('should call expired callback after timeout', () => {
@@ -0,0 +1,64 @@
1
+ {
2
+ "version": 1,
3
+ "max_message_size": 10000000,
4
+ "frame_prefix": "uint32 little-endian",
5
+ "cases": [
6
+ {
7
+ "name": "call_start_data",
8
+ "packet_hex": "0a120a0373766312066d6574686f641a03616263",
9
+ "frame_hex": "140000000a120a0373766312066d6574686f641a03616263"
10
+ },
11
+ {
12
+ "name": "call_start_absent_empty",
13
+ "packet_hex": "0a0d0a0373766312066d6574686f64",
14
+ "frame_hex": "0f0000000a0d0a0373766312066d6574686f64"
15
+ },
16
+ {
17
+ "name": "call_start_present_empty",
18
+ "packet_hex": "0a0f0a0373766312066d6574686f642001",
19
+ "frame_hex": "110000000a0f0a0373766312066d6574686f642001"
20
+ },
21
+ {
22
+ "name": "call_data_terminal",
23
+ "packet_hex": "12070a036f75741801",
24
+ "frame_hex": "0900000012070a036f75741801"
25
+ },
26
+ {
27
+ "name": "call_data_error",
28
+ "packet_hex": "120a180122066661696c6564",
29
+ "frame_hex": "0c000000120a180122066661696c6564"
30
+ },
31
+ {
32
+ "name": "call_cancel",
33
+ "packet_hex": "1801",
34
+ "frame_hex": "020000001801"
35
+ },
36
+ {
37
+ "name": "malformed_complete",
38
+ "frame_hex": "030000000a01ff",
39
+ "expect_error": "malformed_packet"
40
+ },
41
+ {
42
+ "name": "invalid_zero_length",
43
+ "frame_hex": "00000000",
44
+ "expect_error": "invalid_length"
45
+ },
46
+ {
47
+ "name": "invalid_oversized_prefix",
48
+ "frame_hex": "81969800",
49
+ "expect_error": "oversized_frame"
50
+ },
51
+ {
52
+ "name": "invalid_empty_packet",
53
+ "packet_hex": "",
54
+ "expect_error": "empty_packet"
55
+ },
56
+ { "name": "invalid_empty_service_id", "expect_error": "empty_service_id" },
57
+ { "name": "invalid_empty_method_id", "expect_error": "empty_method_id" },
58
+ {
59
+ "name": "truncated_body",
60
+ "frame_hex": "040000000a01",
61
+ "expect_error": "truncated_frame"
62
+ }
63
+ ]
64
+ }
@@ -2,7 +2,10 @@ import { Client, ERR_RPC_ABORT } from '../srpc/index.js'
2
2
  import { EchoMsg } from './echo.pb.js'
3
3
  import { EchoerClient } from './echo_srpc.pb.js'
4
4
  import { pushable } from 'it-pushable'
5
- import { buildRpcStreamOpenStream } from '../rpcstream/rpcstream.js'
5
+ import {
6
+ buildRpcStreamOpenStream,
7
+ openRpcStream,
8
+ } from '../rpcstream/rpcstream.js'
6
9
  import { Message } from '@aptre/protobuf-es-lite'
7
10
 
8
11
  export async function runClientTest(client: Client) {
@@ -89,8 +92,19 @@ export async function runAbortControllerTest(client: Client) {
89
92
  })
90
93
  }
91
94
 
95
+ function requireError(
96
+ error: unknown,
97
+ label: string,
98
+ fragments: string[],
99
+ ): void {
100
+ const message = error instanceof Error ? error.message : String(error)
101
+ if (!fragments.some((fragment) => message.includes(fragment))) {
102
+ throw new Error(`${label} returned unexpected error: ${message}`)
103
+ }
104
+ }
105
+
92
106
  // runRpcStreamTest tests a RPCStream.
93
- export async function runRpcStreamTest(client: Client) {
107
+ export async function runRpcStreamTest(client: Client, release = false) {
94
108
  console.log('Calling RpcStream to open a RPC stream client...')
95
109
  const service = new EchoerClient(client)
96
110
  const openStreamFn = buildRpcStreamOpenStream(
@@ -106,4 +120,112 @@ export async function runRpcStreamTest(client: Client) {
106
120
 
107
121
  console.log('Running client test over RPC stream...')
108
122
  await runClientTest(proxiedClient)
123
+
124
+ if (release) {
125
+ let unknownRejected = false
126
+ try {
127
+ await openRpcStream('missing', service.RpcStream.bind(service), true)
128
+ } catch (error) {
129
+ unknownRejected = true
130
+ requireError(error, 'unknown component', ['unknown component: missing'])
131
+ }
132
+ if (!unknownRejected) {
133
+ throw new Error('unknown component unexpectedly succeeded')
134
+ }
135
+ }
136
+
137
+ let methodRejected = false
138
+ try {
139
+ await proxiedClient.request('missing.Service', 'Missing', new Uint8Array())
140
+ } catch (error) {
141
+ methodRejected = true
142
+ requireError(error, 'unknown nested method', [
143
+ 'missing.Service',
144
+ 'unimplemented',
145
+ ])
146
+ }
147
+ if (!methodRejected) {
148
+ throw new Error('unknown nested method unexpectedly succeeded')
149
+ }
150
+
151
+ if (release) {
152
+ const terminalService = new EchoerClient(proxiedClient)
153
+ let terminalFailed = false
154
+ try {
155
+ await terminalService.Echo({ body: '__nested_error__' })
156
+ } catch (error) {
157
+ terminalFailed = true
158
+ requireError(error, 'terminal nested error', ['nested terminal error'])
159
+ }
160
+ if (!terminalFailed) {
161
+ throw new Error('terminal nested error unexpectedly succeeded')
162
+ }
163
+ }
164
+
165
+ if (release) {
166
+ const releaseClient = new Client(
167
+ buildRpcStreamOpenStream('release', service.RpcStream.bind(service)),
168
+ )
169
+ let releaseFailed = false
170
+ const releaseService = new EchoerClient(releaseClient)
171
+ try {
172
+ await releaseService.Echo({ body: '__nested_release__' })
173
+ } catch (error) {
174
+ releaseFailed = true
175
+ requireError(error, 'release during active call', [
176
+ 'closed before completion',
177
+ 'stream closed',
178
+ 'abort',
179
+ 'cancel',
180
+ ])
181
+ }
182
+ if (!releaseFailed) {
183
+ throw new Error('release during active call unexpectedly succeeded')
184
+ }
185
+ const releaseStatus = await service.Echo({
186
+ body: '__nested_release_status__',
187
+ })
188
+ if (releaseStatus.body !== 'released') {
189
+ throw new Error(`release completion returned ${releaseStatus.body}`)
190
+ }
191
+ let releasedRejected = false
192
+ try {
193
+ await releaseService.Echo({})
194
+ } catch (error) {
195
+ releasedRejected = true
196
+ requireError(error, 'released component', ['unknown component: release'])
197
+ }
198
+ if (!releasedRejected) {
199
+ throw new Error('released component unexpectedly remained available')
200
+ }
201
+ }
202
+
203
+ const cancelled = new AbortController()
204
+ const cancelledStream = proxiedClient.bidirectionalStreamingRequest(
205
+ 'echo.Echoer',
206
+ 'EchoBidiStream',
207
+ (async function* () {
208
+ yield new Uint8Array()
209
+ await new Promise(() => undefined)
210
+ })(),
211
+ cancelled.signal,
212
+ )
213
+ const cancelledIterator = cancelledStream[Symbol.asyncIterator]()
214
+ const firstNestedResponse = await cancelledIterator.next()
215
+ if (firstNestedResponse.done) {
216
+ throw new Error('nested cancellation call ended before its first response')
217
+ }
218
+ cancelled.abort()
219
+ let cancelRejected = false
220
+ try {
221
+ while (!(await cancelledIterator.next()).done) {
222
+ // Drain until the abort reaches the nested call.
223
+ }
224
+ } catch (error) {
225
+ cancelRejected = true
226
+ requireError(error, 'nested cancellation', [ERR_RPC_ABORT])
227
+ }
228
+ if (!cancelRejected) {
229
+ throw new Error('nested cancellation unexpectedly completed normally')
230
+ }
109
231
  }
@@ -0,0 +1,40 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Generated by the protocol buffer compiler. DO NOT EDIT!
3
+ # NO CHECKED-IN PROTOBUF GENCODE
4
+ # source: github.com/aperturerobotics/starpc/echo/echo.proto
5
+ # Protobuf Python Version: 6.33.4
6
+ """Generated protocol buffer code."""
7
+ from google.protobuf import descriptor as _descriptor
8
+ from google.protobuf import descriptor_pool as _descriptor_pool
9
+ from google.protobuf import runtime_version as _runtime_version
10
+ from google.protobuf import symbol_database as _symbol_database
11
+ from google.protobuf.internal import builder as _builder
12
+ _runtime_version.ValidateProtobufRuntimeVersion(
13
+ _runtime_version.Domain.PUBLIC,
14
+ 6,
15
+ 33,
16
+ 4,
17
+ '',
18
+ 'github.com/aperturerobotics/starpc/echo/echo.proto'
19
+ )
20
+ # @@protoc_insertion_point(imports)
21
+
22
+ _sym_db = _symbol_database.Default()
23
+
24
+
25
+ from rpcstream import rpcstream_pb2 as github_dot_com_dot_aperturerobotics_dot_starpc_dot_rpcstream_dot_rpcstream__pb2
26
+ from google.protobuf import empty_pb2 as google_dot_protobuf_dot_empty__pb2
27
+
28
+
29
+ DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n2github.com/aperturerobotics/starpc/echo/echo.proto\x12\x04\x65\x63ho\x1a<github.com/aperturerobotics/starpc/rpcstream/rpcstream.proto\x1a\x1bgoogle/protobuf/empty.proto\"\x17\n\x07\x45\x63hoMsg\x12\x0c\n\x04\x62ody\x18\x01 \x01(\t2\xd0\x02\n\x06\x45\x63hoer\x12$\n\x04\x45\x63ho\x12\r.echo.EchoMsg\x1a\r.echo.EchoMsg\x12\x32\n\x10\x45\x63hoServerStream\x12\r.echo.EchoMsg\x1a\r.echo.EchoMsg0\x01\x12\x32\n\x10\x45\x63hoClientStream\x12\r.echo.EchoMsg\x1a\r.echo.EchoMsg(\x01\x12\x32\n\x0e\x45\x63hoBidiStream\x12\r.echo.EchoMsg\x1a\r.echo.EchoMsg(\x01\x30\x01\x12G\n\tRpcStream\x12\x1a.rpcstream.RpcStreamPacket\x1a\x1a.rpcstream.RpcStreamPacket(\x01\x30\x01\x12;\n\tDoNothing\x12\x16.google.protobuf.Empty\x1a\x16.google.protobuf.Emptyb\x06proto3')
30
+
31
+ _globals = globals()
32
+ _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
33
+ _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'github.com.aperturerobotics.starpc.echo.echo_pb2', _globals)
34
+ if not _descriptor._USE_C_DESCRIPTORS:
35
+ DESCRIPTOR._loaded_options = None
36
+ _globals['_ECHOMSG']._serialized_start=151
37
+ _globals['_ECHOMSG']._serialized_end=174
38
+ _globals['_ECHOER']._serialized_start=177
39
+ _globals['_ECHOER']._serialized_end=513
40
+ # @@protoc_insertion_point(module_scope)
@@ -0,0 +1,13 @@
1
+ from rpcstream import rpcstream_pb2 as _rpcstream_pb2
2
+ from google.protobuf import empty_pb2 as _empty_pb2
3
+ from google.protobuf import descriptor as _descriptor
4
+ from google.protobuf import message as _message
5
+ from typing import ClassVar as _ClassVar, Optional as _Optional
6
+
7
+ DESCRIPTOR: _descriptor.FileDescriptor
8
+
9
+ class EchoMsg(_message.Message):
10
+ __slots__ = ("body",)
11
+ BODY_FIELD_NUMBER: _ClassVar[int]
12
+ body: str
13
+ def __init__(self, body: _Optional[str] = ...) -> None: ...
@@ -11,6 +11,7 @@ import {
11
11
  buildEncodeMessageTransform,
12
12
  MessageStream,
13
13
  ProtoRpc,
14
+ ServerContext,
14
15
  } from 'starpc'
15
16
 
16
17
  /**
@@ -151,6 +152,79 @@ export interface Echoer {
151
152
  DoNothing(request: Empty, abortSignal?: AbortSignal): Promise<Empty>
152
153
  }
153
154
 
155
+ /**
156
+ * Echoer service returns the given message.
157
+ *
158
+ * @generated from service echo.Echoer
159
+ */
160
+ export interface EchoerHandler {
161
+ /**
162
+ * Echo returns the given message.
163
+ *
164
+ * @generated from rpc echo.Echoer.Echo
165
+ */
166
+ Echo(
167
+ request: EchoMsg,
168
+ abortSignal: AbortSignal,
169
+ context: ServerContext,
170
+ ): Promise<EchoMsg>
171
+
172
+ /**
173
+ * EchoServerStream is an example of a server -> client one-way stream.
174
+ *
175
+ * @generated from rpc echo.Echoer.EchoServerStream
176
+ */
177
+ EchoServerStream(
178
+ request: EchoMsg,
179
+ abortSignal: AbortSignal,
180
+ context: ServerContext,
181
+ ): MessageStream<EchoMsg>
182
+
183
+ /**
184
+ * EchoClientStream is an example of client->server one-way stream.
185
+ *
186
+ * @generated from rpc echo.Echoer.EchoClientStream
187
+ */
188
+ EchoClientStream(
189
+ request: MessageStream<EchoMsg>,
190
+ abortSignal: AbortSignal,
191
+ context: ServerContext,
192
+ ): Promise<EchoMsg>
193
+
194
+ /**
195
+ * EchoBidiStream is an example of a two-way stream.
196
+ *
197
+ * @generated from rpc echo.Echoer.EchoBidiStream
198
+ */
199
+ EchoBidiStream(
200
+ request: MessageStream<EchoMsg>,
201
+ abortSignal: AbortSignal,
202
+ context: ServerContext,
203
+ ): MessageStream<EchoMsg>
204
+
205
+ /**
206
+ * RpcStream opens a nested rpc call stream.
207
+ *
208
+ * @generated from rpc echo.Echoer.RpcStream
209
+ */
210
+ RpcStream(
211
+ request: MessageStream<RpcStreamPacket>,
212
+ abortSignal: AbortSignal,
213
+ context: ServerContext,
214
+ ): MessageStream<RpcStreamPacket>
215
+
216
+ /**
217
+ * DoNothing does nothing.
218
+ *
219
+ * @generated from rpc echo.Echoer.DoNothing
220
+ */
221
+ DoNothing(
222
+ request: Empty,
223
+ abortSignal: AbortSignal,
224
+ context: ServerContext,
225
+ ): Promise<Empty>
226
+ }
227
+
154
228
  export const EchoerServiceName = EchoerDefinition.typeName
155
229
 
156
230
  export class EchoerClient implements Echoer {
@@ -0,0 +1,306 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import AsyncIterable, AsyncIterator
4
+ from typing import Protocol
5
+
6
+ from google.protobuf import empty_pb2 as _google_protobuf_empty_pb2
7
+
8
+ from echo import (
9
+ echo_pb2 as _github_com_aperturerobotics_starpc_echo_echo_pb2,
10
+ )
11
+ from rpcstream import (
12
+ rpcstream_pb2 as _github_com_aperturerobotics_starpc_rpcstream_rpcstream_pb2,
13
+ )
14
+ from starpc.call import Call, CallProtocolError
15
+ from starpc.client import Client
16
+ from starpc.server import ServiceRegistry
17
+ from starpc.service import MethodDescriptor, ServiceDescriptor, bidirectional_bytes
18
+
19
+ ECHOER_SERVICE = ServiceDescriptor(
20
+ "echo.Echoer",
21
+ (
22
+ MethodDescriptor(
23
+ "Echo",
24
+ _github_com_aperturerobotics_starpc_echo_echo_pb2.EchoMsg,
25
+ _github_com_aperturerobotics_starpc_echo_echo_pb2.EchoMsg,
26
+ False,
27
+ False,
28
+ ),
29
+ MethodDescriptor(
30
+ "EchoServerStream",
31
+ _github_com_aperturerobotics_starpc_echo_echo_pb2.EchoMsg,
32
+ _github_com_aperturerobotics_starpc_echo_echo_pb2.EchoMsg,
33
+ False,
34
+ True,
35
+ ),
36
+ MethodDescriptor(
37
+ "EchoClientStream",
38
+ _github_com_aperturerobotics_starpc_echo_echo_pb2.EchoMsg,
39
+ _github_com_aperturerobotics_starpc_echo_echo_pb2.EchoMsg,
40
+ True,
41
+ False,
42
+ ),
43
+ MethodDescriptor(
44
+ "EchoBidiStream",
45
+ _github_com_aperturerobotics_starpc_echo_echo_pb2.EchoMsg,
46
+ _github_com_aperturerobotics_starpc_echo_echo_pb2.EchoMsg,
47
+ True,
48
+ True,
49
+ ),
50
+ MethodDescriptor(
51
+ "RpcStream",
52
+ _github_com_aperturerobotics_starpc_rpcstream_rpcstream_pb2.RpcStreamPacket,
53
+ _github_com_aperturerobotics_starpc_rpcstream_rpcstream_pb2.RpcStreamPacket,
54
+ True,
55
+ True,
56
+ ),
57
+ MethodDescriptor(
58
+ "DoNothing",
59
+ _google_protobuf_empty_pb2.Empty,
60
+ _google_protobuf_empty_pb2.Empty,
61
+ False,
62
+ False,
63
+ ),
64
+ ),
65
+ )
66
+
67
+
68
+ class EchoerClient:
69
+ def __init__(self, client: Client, service: str | None = None) -> None:
70
+ self._client = client
71
+ self._service = service or "echo.Echoer"
72
+
73
+ async def echo(
74
+ self, request: _github_com_aperturerobotics_starpc_echo_echo_pb2.EchoMsg
75
+ ) -> _github_com_aperturerobotics_starpc_echo_echo_pb2.EchoMsg:
76
+ call = await self._client.open_call(
77
+ self._service, "Echo", request.SerializeToString(deterministic=True)
78
+ )
79
+ try:
80
+ data = await call.receive()
81
+ if data is None:
82
+ raise CallProtocolError("missing unary response")
83
+ response = _github_com_aperturerobotics_starpc_echo_echo_pb2.EchoMsg()
84
+ response.ParseFromString(data)
85
+ if await call.receive() is not None:
86
+ raise CallProtocolError("extra unary response")
87
+ return response
88
+ finally:
89
+ await call.aclose()
90
+
91
+ async def echo_server_stream(
92
+ self, request: _github_com_aperturerobotics_starpc_echo_echo_pb2.EchoMsg
93
+ ) -> AsyncIterator[_github_com_aperturerobotics_starpc_echo_echo_pb2.EchoMsg]:
94
+ call = await self._client.open_call(
95
+ self._service,
96
+ "EchoServerStream",
97
+ request.SerializeToString(deterministic=True),
98
+ )
99
+ try:
100
+ while True:
101
+ data = await call.receive()
102
+ if data is None:
103
+ return
104
+ response = _github_com_aperturerobotics_starpc_echo_echo_pb2.EchoMsg()
105
+ response.ParseFromString(data)
106
+ yield response
107
+ finally:
108
+ await call.aclose()
109
+
110
+ async def echo_client_stream(
111
+ self,
112
+ requests: AsyncIterable[
113
+ _github_com_aperturerobotics_starpc_echo_echo_pb2.EchoMsg
114
+ ],
115
+ ) -> _github_com_aperturerobotics_starpc_echo_echo_pb2.EchoMsg:
116
+ call = await self._client.open_call(self._service, "EchoClientStream")
117
+ try:
118
+ async for request in requests:
119
+ await call.send(request.SerializeToString(deterministic=True))
120
+ await call.finish()
121
+ data = await call.receive()
122
+ if data is None:
123
+ raise CallProtocolError("missing client-stream response")
124
+ response = _github_com_aperturerobotics_starpc_echo_echo_pb2.EchoMsg()
125
+ response.ParseFromString(data)
126
+ if await call.receive() is not None:
127
+ raise CallProtocolError("extra client-stream response")
128
+ return response
129
+ finally:
130
+ await call.aclose()
131
+
132
+ async def echo_bidi_stream(
133
+ self,
134
+ requests: AsyncIterable[
135
+ _github_com_aperturerobotics_starpc_echo_echo_pb2.EchoMsg
136
+ ],
137
+ ) -> AsyncIterator[_github_com_aperturerobotics_starpc_echo_echo_pb2.EchoMsg]:
138
+ call = await self._client.open_call(self._service, "EchoBidiStream")
139
+
140
+ async def encoded() -> AsyncIterator[bytes]:
141
+ async for request in requests:
142
+ yield request.SerializeToString(deterministic=True)
143
+
144
+ async for data in bidirectional_bytes(call, encoded()):
145
+ response = _github_com_aperturerobotics_starpc_echo_echo_pb2.EchoMsg()
146
+ response.ParseFromString(data)
147
+ yield response
148
+
149
+ async def rpc_stream(
150
+ self,
151
+ requests: AsyncIterable[
152
+ _github_com_aperturerobotics_starpc_rpcstream_rpcstream_pb2.RpcStreamPacket
153
+ ],
154
+ ) -> AsyncIterator[
155
+ _github_com_aperturerobotics_starpc_rpcstream_rpcstream_pb2.RpcStreamPacket
156
+ ]:
157
+ call = await self._client.open_call(self._service, "RpcStream")
158
+
159
+ async def encoded() -> AsyncIterator[bytes]:
160
+ async for request in requests:
161
+ yield request.SerializeToString(deterministic=True)
162
+
163
+ async for data in bidirectional_bytes(call, encoded()):
164
+ response = _github_com_aperturerobotics_starpc_rpcstream_rpcstream_pb2.RpcStreamPacket()
165
+ response.ParseFromString(data)
166
+ yield response
167
+
168
+ async def do_nothing(
169
+ self, request: _google_protobuf_empty_pb2.Empty
170
+ ) -> _google_protobuf_empty_pb2.Empty:
171
+ call = await self._client.open_call(
172
+ self._service, "DoNothing", request.SerializeToString(deterministic=True)
173
+ )
174
+ try:
175
+ data = await call.receive()
176
+ if data is None:
177
+ raise CallProtocolError("missing unary response")
178
+ response = _google_protobuf_empty_pb2.Empty()
179
+ response.ParseFromString(data)
180
+ if await call.receive() is not None:
181
+ raise CallProtocolError("extra unary response")
182
+ return response
183
+ finally:
184
+ await call.aclose()
185
+
186
+
187
+ class EchoerServer(Protocol):
188
+ async def echo(
189
+ self, request: _github_com_aperturerobotics_starpc_echo_echo_pb2.EchoMsg
190
+ ) -> _github_com_aperturerobotics_starpc_echo_echo_pb2.EchoMsg: ...
191
+ def echo_server_stream(
192
+ self, request: _github_com_aperturerobotics_starpc_echo_echo_pb2.EchoMsg
193
+ ) -> AsyncIterator[_github_com_aperturerobotics_starpc_echo_echo_pb2.EchoMsg]: ...
194
+ async def echo_client_stream(
195
+ self,
196
+ requests: AsyncIterator[
197
+ _github_com_aperturerobotics_starpc_echo_echo_pb2.EchoMsg
198
+ ],
199
+ ) -> _github_com_aperturerobotics_starpc_echo_echo_pb2.EchoMsg: ...
200
+ def echo_bidi_stream(
201
+ self,
202
+ requests: AsyncIterator[
203
+ _github_com_aperturerobotics_starpc_echo_echo_pb2.EchoMsg
204
+ ],
205
+ ) -> AsyncIterator[_github_com_aperturerobotics_starpc_echo_echo_pb2.EchoMsg]: ...
206
+ def rpc_stream(
207
+ self,
208
+ requests: AsyncIterator[
209
+ _github_com_aperturerobotics_starpc_rpcstream_rpcstream_pb2.RpcStreamPacket
210
+ ],
211
+ ) -> AsyncIterator[
212
+ _github_com_aperturerobotics_starpc_rpcstream_rpcstream_pb2.RpcStreamPacket
213
+ ]: ...
214
+ async def do_nothing(
215
+ self, request: _google_protobuf_empty_pb2.Empty
216
+ ) -> _google_protobuf_empty_pb2.Empty: ...
217
+
218
+
219
+ def register_echoer(
220
+ registry: ServiceRegistry,
221
+ implementation: EchoerServer,
222
+ service: str = "echo.Echoer",
223
+ ) -> None:
224
+ async def echo_handler(call: Call) -> None:
225
+ first = await call.receive()
226
+ if first is None:
227
+ raise CallProtocolError("missing initial request")
228
+ request = _github_com_aperturerobotics_starpc_echo_echo_pb2.EchoMsg()
229
+ request.ParseFromString(first)
230
+ response = await implementation.echo(request)
231
+ await call.send(response.SerializeToString(deterministic=True))
232
+
233
+ registry.register(service, "Echo", echo_handler)
234
+
235
+ async def echo_server_stream_handler(call: Call) -> None:
236
+ first = await call.receive()
237
+ if first is None:
238
+ raise CallProtocolError("missing initial request")
239
+ request = _github_com_aperturerobotics_starpc_echo_echo_pb2.EchoMsg()
240
+ request.ParseFromString(first)
241
+ async for response in implementation.echo_server_stream(request):
242
+ await call.send(response.SerializeToString(deterministic=True))
243
+
244
+ registry.register(service, "EchoServerStream", echo_server_stream_handler)
245
+
246
+ async def echo_client_stream_handler(call: Call) -> None:
247
+ async def requests() -> AsyncIterator[
248
+ _github_com_aperturerobotics_starpc_echo_echo_pb2.EchoMsg
249
+ ]:
250
+ while True:
251
+ data = await call.receive()
252
+ if data is None:
253
+ return
254
+ request = _github_com_aperturerobotics_starpc_echo_echo_pb2.EchoMsg()
255
+ request.ParseFromString(data)
256
+ yield request
257
+
258
+ response = await implementation.echo_client_stream(requests())
259
+ await call.send(response.SerializeToString(deterministic=True))
260
+
261
+ registry.register(service, "EchoClientStream", echo_client_stream_handler)
262
+
263
+ async def echo_bidi_stream_handler(call: Call) -> None:
264
+ async def requests() -> AsyncIterator[
265
+ _github_com_aperturerobotics_starpc_echo_echo_pb2.EchoMsg
266
+ ]:
267
+ while True:
268
+ data = await call.receive()
269
+ if data is None:
270
+ return
271
+ request = _github_com_aperturerobotics_starpc_echo_echo_pb2.EchoMsg()
272
+ request.ParseFromString(data)
273
+ yield request
274
+
275
+ async for response in implementation.echo_bidi_stream(requests()):
276
+ await call.send(response.SerializeToString(deterministic=True))
277
+
278
+ registry.register(service, "EchoBidiStream", echo_bidi_stream_handler)
279
+
280
+ async def rpc_stream_handler(call: Call) -> None:
281
+ async def requests() -> AsyncIterator[
282
+ _github_com_aperturerobotics_starpc_rpcstream_rpcstream_pb2.RpcStreamPacket
283
+ ]:
284
+ while True:
285
+ data = await call.receive()
286
+ if data is None:
287
+ return
288
+ request = _github_com_aperturerobotics_starpc_rpcstream_rpcstream_pb2.RpcStreamPacket()
289
+ request.ParseFromString(data)
290
+ yield request
291
+
292
+ async for response in implementation.rpc_stream(requests()):
293
+ await call.send(response.SerializeToString(deterministic=True))
294
+
295
+ registry.register(service, "RpcStream", rpc_stream_handler)
296
+
297
+ async def do_nothing_handler(call: Call) -> None:
298
+ first = await call.receive()
299
+ if first is None:
300
+ raise CallProtocolError("missing initial request")
301
+ request = _google_protobuf_empty_pb2.Empty()
302
+ request.ParseFromString(first)
303
+ response = await implementation.do_nothing(request)
304
+ await call.send(response.SerializeToString(deterministic=True))
305
+
306
+ registry.register(service, "DoNothing", do_nothing_handler)