starpc 0.50.0 → 0.52.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.
- package/cmd/protoc-gen-es-starpc/typescript.ts +37 -0
- package/dist/cmd/protoc-gen-es-starpc/typescript.js +24 -0
- package/dist/echo/echo_srpc.pb.d.ts +44 -1
- package/dist/echo/server.d.ts +9 -8
- package/dist/echo/server.js +6 -6
- package/dist/integration/cross-language/ts-client.js +5 -79
- package/dist/integration/cross-language/ts-server.js +4 -100
- package/dist/mock/mock_srpc.pb.d.ts +14 -1
- package/dist/srpc/channel.js +6 -3
- package/dist/srpc/channel.test.js +20 -1
- package/dist/srpc/client.d.ts +1 -3
- package/dist/srpc/client.js +2 -22
- package/dist/srpc/common-rpc.d.ts +1 -9
- package/dist/srpc/common-rpc.js +4 -62
- package/dist/srpc/handler.d.ts +12 -4
- package/dist/srpc/index.d.ts +2 -4
- package/dist/srpc/index.js +1 -3
- package/dist/srpc/invoker.d.ts +2 -2
- package/dist/srpc/invoker.js +2 -2
- package/dist/srpc/rpcproto.pb.d.ts +0 -44
- package/dist/srpc/rpcproto.pb.js +0 -53
- package/dist/srpc/server-context.d.ts +11 -0
- package/dist/srpc/server-context.js +28 -0
- package/dist/srpc/server-rpc.js +3 -3
- package/dist/srpc/server.test.js +65 -2
- package/dist/srpc/watchdog.test.js +1 -0
- package/echo/echo.go +8 -8
- package/echo/echo.pb.go +12 -36
- package/echo/echo_srpc.pb.cpp +1 -1
- package/echo/echo_srpc.pb.go +1 -1
- package/echo/echo_srpc.pb.hpp +1 -1
- package/echo/echo_srpc.pb.rs +1 -1
- package/echo/echo_srpc.pb.ts +74 -0
- package/echo/server.go +1 -1
- package/echo/server.ts +24 -5
- package/go.mod +1 -1
- package/go.sum +2 -0
- package/integration/cross-language/go-client/main.go +4 -142
- package/integration/cross-language/go-server/main.go +2 -170
- package/integration/cross-language/run.bash +5 -105
- package/integration/cross-language/ts-client.ts +5 -90
- package/integration/cross-language/ts-server.ts +4 -112
- package/mock/mock.go +1 -1
- package/mock/mock.pb.go +12 -36
- package/mock/mock_srpc.pb.cpp +1 -1
- package/mock/mock_srpc.pb.go +1 -1
- package/mock/mock_srpc.pb.hpp +1 -1
- package/mock/mock_srpc.pb.rs +1 -1
- package/mock/mock_srpc.pb.ts +19 -1
- package/package.json +2 -2
- package/srpc/channel.test.ts +21 -1
- package/srpc/channel.ts +7 -3
- package/srpc/client-prefix.go +1 -1
- package/srpc/client-set.go +1 -1
- package/srpc/client.go +1 -1
- package/srpc/client.ts +4 -31
- package/srpc/common-rpc.go +15 -69
- package/srpc/common-rpc.ts +4 -77
- package/srpc/common-rpc_test.go +81 -0
- package/srpc/errors.go +26 -1
- package/srpc/handler.ts +54 -5
- package/srpc/index.ts +7 -4
- package/srpc/invoker-prefix.go +1 -1
- package/srpc/invoker.go +2 -2
- package/srpc/invoker.ts +20 -7
- package/srpc/message.go +1 -1
- package/srpc/msg-stream.go +1 -9
- package/srpc/mux-verbose.go +1 -1
- package/srpc/mux.go +1 -1
- package/srpc/rpcproto.pb.cc +4 -15
- package/srpc/rpcproto.pb.go +102 -419
- package/srpc/rpcproto.pb.h +0 -59
- package/srpc/rpcproto.pb.rs +0 -45
- package/srpc/rpcproto.pb.ts +0 -62
- package/srpc/rpcproto.proto +0 -16
- package/srpc/server-context.ts +55 -0
- package/srpc/server-rpc.go +1 -1
- package/srpc/server-rpc.ts +4 -6
- package/srpc/server.test.ts +94 -1
- package/srpc/stream-pipe.go +1 -1
- package/srpc/stream-rwc.go +1 -1
- package/srpc/stream.go +0 -20
- package/srpc/watchdog.test.ts +1 -0
- package/dist/rpcstream/receipt.test.d.ts +0 -1
- package/dist/rpcstream/receipt.test.js +0 -41
- package/dist/srpc/call-receipt.d.ts +0 -17
- package/dist/srpc/call-receipt.js +0 -106
- package/dist/srpc/call-receipt.test.d.ts +0 -1
- package/dist/srpc/call-receipt.test.js +0 -375
- package/dist/srpc/server-invocation.d.ts +0 -17
- package/dist/srpc/server-invocation.js +0 -37
- package/integration/cross-language/go-server/fixture-owner_test.go +0 -127
- package/srpc/call-receipt-e2e_test.go +0 -111
- package/srpc/call-receipt.go +0 -112
- package/srpc/call-receipt.test.ts +0 -441
- package/srpc/call-receipt.ts +0 -131
- package/srpc/call-receipt_test.go +0 -536
- package/srpc/schema-ownership_test.go +0 -115
- package/srpc/server-invocation.go +0 -40
- package/srpc/server-invocation.ts +0 -76
package/srpc/rpcproto.pb.h
CHANGED
|
@@ -30,7 +30,6 @@
|
|
|
30
30
|
#include "google/protobuf/message_lite.h"
|
|
31
31
|
#include "google/protobuf/repeated_field.h" // IWYU pragma: export
|
|
32
32
|
#include "google/protobuf/extension_set.h" // IWYU pragma: export
|
|
33
|
-
#include "google/protobuf/generated_enum_reflection.h"
|
|
34
33
|
#include "google/protobuf/unknown_field_set.h"
|
|
35
34
|
// @@protoc_insertion_point(includes)
|
|
36
35
|
|
|
@@ -56,8 +55,6 @@ extern "C" {
|
|
|
56
55
|
extern const ::google::protobuf::internal::DescriptorTable descriptor_table_github_2ecom_2faperturerobotics_2fstarpc_2fsrpc_2frpcproto_2eproto;
|
|
57
56
|
} // extern "C"
|
|
58
57
|
namespace srpc {
|
|
59
|
-
enum TerminalKind : int;
|
|
60
|
-
extern const uint32_t TerminalKind_internal_data_[];
|
|
61
58
|
class CallData;
|
|
62
59
|
struct CallDataDefaultTypeInternal;
|
|
63
60
|
extern CallDataDefaultTypeInternal _CallData_default_instance_;
|
|
@@ -73,53 +70,10 @@ extern const ::google::protobuf::internal::ClassDataFull Packet_class_data_;
|
|
|
73
70
|
} // namespace srpc
|
|
74
71
|
namespace google {
|
|
75
72
|
namespace protobuf {
|
|
76
|
-
template <>
|
|
77
|
-
internal::EnumTraitsT<::srpc::TerminalKind_internal_data_>
|
|
78
|
-
internal::EnumTraitsImpl::value<::srpc::TerminalKind>;
|
|
79
73
|
} // namespace protobuf
|
|
80
74
|
} // namespace google
|
|
81
75
|
|
|
82
76
|
namespace srpc {
|
|
83
|
-
enum TerminalKind : int {
|
|
84
|
-
TERMINAL_KIND_UNSPECIFIED = 0,
|
|
85
|
-
TERMINAL_KIND_COMMITTED = 1,
|
|
86
|
-
TERMINAL_KIND_CANCELED = 2,
|
|
87
|
-
TERMINAL_KIND_TRANSPORT_LOST = 3,
|
|
88
|
-
TERMINAL_KIND_CLOSED = 4,
|
|
89
|
-
TERMINAL_KIND_ABANDONED = 5,
|
|
90
|
-
TerminalKind_INT_MIN_SENTINEL_DO_NOT_USE_ =
|
|
91
|
-
::std::numeric_limits<::int32_t>::min(),
|
|
92
|
-
TerminalKind_INT_MAX_SENTINEL_DO_NOT_USE_ =
|
|
93
|
-
::std::numeric_limits<::int32_t>::max(),
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
extern const uint32_t TerminalKind_internal_data_[];
|
|
97
|
-
inline constexpr TerminalKind TerminalKind_MIN =
|
|
98
|
-
static_cast<TerminalKind>(0);
|
|
99
|
-
inline constexpr TerminalKind TerminalKind_MAX =
|
|
100
|
-
static_cast<TerminalKind>(5);
|
|
101
|
-
inline bool TerminalKind_IsValid(int value) {
|
|
102
|
-
return 0 <= value && value <= 5;
|
|
103
|
-
}
|
|
104
|
-
inline constexpr int TerminalKind_ARRAYSIZE = 5 + 1;
|
|
105
|
-
const ::google::protobuf::EnumDescriptor* PROTOBUF_NONNULL TerminalKind_descriptor();
|
|
106
|
-
template <typename T>
|
|
107
|
-
const ::std::string& TerminalKind_Name(T value) {
|
|
108
|
-
static_assert(::std::is_same<T, TerminalKind>::value ||
|
|
109
|
-
::std::is_integral<T>::value,
|
|
110
|
-
"Incorrect type passed to TerminalKind_Name().");
|
|
111
|
-
return TerminalKind_Name(static_cast<TerminalKind>(value));
|
|
112
|
-
}
|
|
113
|
-
template <>
|
|
114
|
-
inline const ::std::string& TerminalKind_Name(TerminalKind value) {
|
|
115
|
-
return ::google::protobuf::internal::NameOfDenseEnum<TerminalKind_descriptor, 0, 5>(
|
|
116
|
-
static_cast<int>(value));
|
|
117
|
-
}
|
|
118
|
-
inline bool TerminalKind_Parse(
|
|
119
|
-
::absl::string_view name, TerminalKind* PROTOBUF_NONNULL value) {
|
|
120
|
-
return ::google::protobuf::internal::ParseNamedEnum<TerminalKind>(TerminalKind_descriptor(), name,
|
|
121
|
-
value);
|
|
122
|
-
}
|
|
123
77
|
|
|
124
78
|
// ===================================================================
|
|
125
79
|
|
|
@@ -1490,19 +1444,6 @@ inline void CallData::set_allocated_error(::std::string* PROTOBUF_NULLABLE value
|
|
|
1490
1444
|
} // namespace srpc
|
|
1491
1445
|
|
|
1492
1446
|
|
|
1493
|
-
namespace google {
|
|
1494
|
-
namespace protobuf {
|
|
1495
|
-
|
|
1496
|
-
template <>
|
|
1497
|
-
struct is_proto_enum<::srpc::TerminalKind> : std::true_type {};
|
|
1498
|
-
template <>
|
|
1499
|
-
inline const EnumDescriptor* PROTOBUF_NONNULL GetEnumDescriptor<::srpc::TerminalKind>() {
|
|
1500
|
-
return ::srpc::TerminalKind_descriptor();
|
|
1501
|
-
}
|
|
1502
|
-
|
|
1503
|
-
} // namespace protobuf
|
|
1504
|
-
} // namespace google
|
|
1505
|
-
|
|
1506
1447
|
// @@protoc_insertion_point(global_scope)
|
|
1507
1448
|
|
|
1508
1449
|
#include "google/protobuf/port_undef.inc"
|
package/srpc/rpcproto.pb.rs
CHANGED
|
@@ -59,49 +59,4 @@ pub struct CallData {
|
|
|
59
59
|
#[prost(string, tag="4")]
|
|
60
60
|
pub error: ::prost::alloc::string::String,
|
|
61
61
|
}
|
|
62
|
-
/// TerminalKind is the terminal state of a held unary invocation.
|
|
63
|
-
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)]
|
|
64
|
-
#[repr(i32)]
|
|
65
|
-
pub enum TerminalKind {
|
|
66
|
-
/// TERMINAL_KIND_UNSPECIFIED is not a valid terminal state.
|
|
67
|
-
Unspecified = 0,
|
|
68
|
-
/// TERMINAL_KIND_COMMITTED identifies an explicit remote CallData completion.
|
|
69
|
-
Committed = 1,
|
|
70
|
-
/// TERMINAL_KIND_CANCELED identifies a remote CallCancel packet.
|
|
71
|
-
Canceled = 2,
|
|
72
|
-
/// TERMINAL_KIND_TRANSPORT_LOST identifies a remote error or transport failure.
|
|
73
|
-
TransportLost = 3,
|
|
74
|
-
/// TERMINAL_KIND_CLOSED identifies a bare remote close without completion.
|
|
75
|
-
Closed = 4,
|
|
76
|
-
/// TERMINAL_KIND_ABANDONED identifies owner-context expiry without a remote terminal.
|
|
77
|
-
Abandoned = 5,
|
|
78
|
-
}
|
|
79
|
-
impl TerminalKind {
|
|
80
|
-
/// String value of the enum field names used in the ProtoBuf definition.
|
|
81
|
-
///
|
|
82
|
-
/// The values are not transformed in any way and thus are considered stable
|
|
83
|
-
/// (if the ProtoBuf definition does not change) and safe for programmatic use.
|
|
84
|
-
pub fn as_str_name(&self) -> &'static str {
|
|
85
|
-
match self {
|
|
86
|
-
Self::Unspecified => "TERMINAL_KIND_UNSPECIFIED",
|
|
87
|
-
Self::Committed => "TERMINAL_KIND_COMMITTED",
|
|
88
|
-
Self::Canceled => "TERMINAL_KIND_CANCELED",
|
|
89
|
-
Self::TransportLost => "TERMINAL_KIND_TRANSPORT_LOST",
|
|
90
|
-
Self::Closed => "TERMINAL_KIND_CLOSED",
|
|
91
|
-
Self::Abandoned => "TERMINAL_KIND_ABANDONED",
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
/// Creates an enum from field names used in the ProtoBuf definition.
|
|
95
|
-
pub fn from_str_name(value: &str) -> ::core::option::Option<Self> {
|
|
96
|
-
match value {
|
|
97
|
-
"TERMINAL_KIND_UNSPECIFIED" => Some(Self::Unspecified),
|
|
98
|
-
"TERMINAL_KIND_COMMITTED" => Some(Self::Committed),
|
|
99
|
-
"TERMINAL_KIND_CANCELED" => Some(Self::Canceled),
|
|
100
|
-
"TERMINAL_KIND_TRANSPORT_LOST" => Some(Self::TransportLost),
|
|
101
|
-
"TERMINAL_KIND_CLOSED" => Some(Self::Closed),
|
|
102
|
-
"TERMINAL_KIND_ABANDONED" => Some(Self::Abandoned),
|
|
103
|
-
_ => None,
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
62
|
// @@protoc_insertion_point(module)
|
package/srpc/rpcproto.pb.ts
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
// @generated from file github.com/aperturerobotics/starpc/srpc/rpcproto.proto (package srpc, syntax proto3)
|
|
3
3
|
/* eslint-disable */
|
|
4
4
|
|
|
5
|
-
import { createEnumType } from '@aptre/protobuf-es-lite/enum'
|
|
6
5
|
import type { MessageType } from '@aptre/protobuf-es-lite/message'
|
|
7
6
|
import { createMessageType } from '@aptre/protobuf-es-lite/message'
|
|
8
7
|
import { ScalarType } from '@aptre/protobuf-es-lite/scalar'
|
|
@@ -10,67 +9,6 @@ import type { PartialFieldInfo } from '@aptre/protobuf-es-lite/field'
|
|
|
10
9
|
|
|
11
10
|
export const protobufPackage = 'srpc'
|
|
12
11
|
|
|
13
|
-
/**
|
|
14
|
-
* TerminalKind is the terminal state of a held unary invocation.
|
|
15
|
-
*
|
|
16
|
-
* @generated from enum srpc.TerminalKind
|
|
17
|
-
*/
|
|
18
|
-
export enum TerminalKind {
|
|
19
|
-
/**
|
|
20
|
-
* TERMINAL_KIND_UNSPECIFIED is not a valid terminal state.
|
|
21
|
-
*
|
|
22
|
-
* @generated from enum value: TERMINAL_KIND_UNSPECIFIED = 0;
|
|
23
|
-
*/
|
|
24
|
-
UNSPECIFIED = 0,
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* TERMINAL_KIND_COMMITTED identifies an explicit remote CallData completion.
|
|
28
|
-
*
|
|
29
|
-
* @generated from enum value: TERMINAL_KIND_COMMITTED = 1;
|
|
30
|
-
*/
|
|
31
|
-
COMMITTED = 1,
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* TERMINAL_KIND_CANCELED identifies a remote CallCancel packet.
|
|
35
|
-
*
|
|
36
|
-
* @generated from enum value: TERMINAL_KIND_CANCELED = 2;
|
|
37
|
-
*/
|
|
38
|
-
CANCELED = 2,
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* TERMINAL_KIND_TRANSPORT_LOST identifies a remote error or transport failure.
|
|
42
|
-
*
|
|
43
|
-
* @generated from enum value: TERMINAL_KIND_TRANSPORT_LOST = 3;
|
|
44
|
-
*/
|
|
45
|
-
TRANSPORT_LOST = 3,
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* TERMINAL_KIND_CLOSED identifies a bare remote close without completion.
|
|
49
|
-
*
|
|
50
|
-
* @generated from enum value: TERMINAL_KIND_CLOSED = 4;
|
|
51
|
-
*/
|
|
52
|
-
CLOSED = 4,
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* TERMINAL_KIND_ABANDONED identifies owner-context expiry without a remote terminal.
|
|
56
|
-
*
|
|
57
|
-
* @generated from enum value: TERMINAL_KIND_ABANDONED = 5;
|
|
58
|
-
*/
|
|
59
|
-
ABANDONED = 5,
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
export const TerminalKind_Enum = /* @__PURE__ */ createEnumType(
|
|
63
|
-
'srpc.TerminalKind',
|
|
64
|
-
[
|
|
65
|
-
[0, 'TERMINAL_KIND_UNSPECIFIED'],
|
|
66
|
-
[1, 'TERMINAL_KIND_COMMITTED'],
|
|
67
|
-
[2, 'TERMINAL_KIND_CANCELED'],
|
|
68
|
-
[3, 'TERMINAL_KIND_TRANSPORT_LOST'],
|
|
69
|
-
[4, 'TERMINAL_KIND_CLOSED'],
|
|
70
|
-
[5, 'TERMINAL_KIND_ABANDONED'],
|
|
71
|
-
],
|
|
72
|
-
)
|
|
73
|
-
|
|
74
12
|
/**
|
|
75
13
|
* CallStart requests starting a new RPC call.
|
|
76
14
|
*
|
package/srpc/rpcproto.proto
CHANGED
|
@@ -1,22 +1,6 @@
|
|
|
1
1
|
syntax = "proto3";
|
|
2
2
|
package srpc;
|
|
3
3
|
|
|
4
|
-
// TerminalKind is the terminal state of a held unary invocation.
|
|
5
|
-
enum TerminalKind {
|
|
6
|
-
// TERMINAL_KIND_UNSPECIFIED is not a valid terminal state.
|
|
7
|
-
TERMINAL_KIND_UNSPECIFIED = 0;
|
|
8
|
-
// TERMINAL_KIND_COMMITTED identifies an explicit remote CallData completion.
|
|
9
|
-
TERMINAL_KIND_COMMITTED = 1;
|
|
10
|
-
// TERMINAL_KIND_CANCELED identifies a remote CallCancel packet.
|
|
11
|
-
TERMINAL_KIND_CANCELED = 2;
|
|
12
|
-
// TERMINAL_KIND_TRANSPORT_LOST identifies a remote error or transport failure.
|
|
13
|
-
TERMINAL_KIND_TRANSPORT_LOST = 3;
|
|
14
|
-
// TERMINAL_KIND_CLOSED identifies a bare remote close without completion.
|
|
15
|
-
TERMINAL_KIND_CLOSED = 4;
|
|
16
|
-
// TERMINAL_KIND_ABANDONED identifies owner-context expiry without a remote terminal.
|
|
17
|
-
TERMINAL_KIND_ABANDONED = 5;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
4
|
// Packet is a message sent over a srpc packet connection.
|
|
21
5
|
message Packet {
|
|
22
6
|
// Body is the packet body.
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
declare const contextKeyValue: unique symbol
|
|
2
|
+
const contextParent = Symbol('server context parent')
|
|
3
|
+
const contextKey = Symbol('server context key')
|
|
4
|
+
const contextStoredValue = Symbol('server context value')
|
|
5
|
+
|
|
6
|
+
// ContextKey identifies one typed server-context value.
|
|
7
|
+
export interface ContextKey<T> {
|
|
8
|
+
readonly [contextKeyValue]?: T
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// ServerContext carries cancellation for one server invocation.
|
|
12
|
+
export interface ServerContext {
|
|
13
|
+
readonly signal: AbortSignal
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
type StoredServerContext = ServerContext & {
|
|
17
|
+
readonly [contextParent]?: ServerContext
|
|
18
|
+
readonly [contextKey]?: ContextKey<unknown>
|
|
19
|
+
readonly [contextStoredValue]?: unknown
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// createContextKey constructs an identity key for one server-context value.
|
|
23
|
+
export function createContextKey<T>(): ContextKey<T> {
|
|
24
|
+
return {}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// withServerContextValue derives a context with one immutable typed value.
|
|
28
|
+
export function withServerContextValue<T>(
|
|
29
|
+
context: ServerContext,
|
|
30
|
+
key: ContextKey<T>,
|
|
31
|
+
value: T,
|
|
32
|
+
): ServerContext {
|
|
33
|
+
const derived: StoredServerContext = {
|
|
34
|
+
signal: context.signal,
|
|
35
|
+
[contextParent]: context,
|
|
36
|
+
[contextKey]: key as ContextKey<unknown>,
|
|
37
|
+
[contextStoredValue]: value,
|
|
38
|
+
}
|
|
39
|
+
return derived
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// serverContextValue retrieves the nearest value for a typed identity key.
|
|
43
|
+
export function serverContextValue<T>(
|
|
44
|
+
context: ServerContext,
|
|
45
|
+
key: ContextKey<T>,
|
|
46
|
+
): T | undefined {
|
|
47
|
+
let current: StoredServerContext | undefined = context as StoredServerContext
|
|
48
|
+
while (current) {
|
|
49
|
+
if (current[contextKey] === key) {
|
|
50
|
+
return current[contextStoredValue] as T
|
|
51
|
+
}
|
|
52
|
+
current = current[contextParent] as StoredServerContext | undefined
|
|
53
|
+
}
|
|
54
|
+
return undefined
|
|
55
|
+
}
|
package/srpc/server-rpc.go
CHANGED
|
@@ -98,7 +98,7 @@ func (r *ServerRPC) HandleCallStart(pkt *CallStart) error {
|
|
|
98
98
|
// invokeRPC invokes the RPC after CallStart is received.
|
|
99
99
|
func (r *ServerRPC) invokeRPC(serviceID, methodID string) {
|
|
100
100
|
// on the server side, the writer is closed by invokeRPC.
|
|
101
|
-
strm := NewMsgStream(
|
|
101
|
+
strm := NewMsgStream(r.ctx, r, r.cancelContext)
|
|
102
102
|
ok, err := r.invoker.InvokeMethod(serviceID, methodID, strm)
|
|
103
103
|
if err == nil && !ok {
|
|
104
104
|
err = ErrUnimplemented
|
package/srpc/server-rpc.ts
CHANGED
|
@@ -3,8 +3,8 @@ import type { Sink, Source } from 'it-stream-types'
|
|
|
3
3
|
import type { CallData, CallStart } from './rpcproto.pb.js'
|
|
4
4
|
import { CommonRPC } from './common-rpc.js'
|
|
5
5
|
import { InvokeFn } from './handler.js'
|
|
6
|
-
import { ServerInvocation } from './server-invocation.js'
|
|
7
6
|
import { LookupMethod } from './mux.js'
|
|
7
|
+
import type { ServerContext } from './server-context.js'
|
|
8
8
|
|
|
9
9
|
// ServerRPC is an ongoing RPC from the server side.
|
|
10
10
|
export class ServerRPC extends CommonRPC {
|
|
@@ -47,12 +47,10 @@ export class ServerRPC extends CommonRPC {
|
|
|
47
47
|
// invokeRPC starts invoking the RPC handler.
|
|
48
48
|
private async invokeRPC(invokeFn: InvokeFn) {
|
|
49
49
|
const dataSink = this._createDataSink()
|
|
50
|
-
const invocation = new ServerInvocation(
|
|
51
|
-
this.invocationSignal,
|
|
52
|
-
(ownerSignal) => this.waitTerminal(ownerSignal),
|
|
53
|
-
)
|
|
54
50
|
try {
|
|
55
|
-
await invokeFn(this.rpcDataSource, dataSink,
|
|
51
|
+
await invokeFn(this.rpcDataSource, dataSink, {
|
|
52
|
+
signal: this.invocationSignal,
|
|
53
|
+
} satisfies ServerContext)
|
|
56
54
|
} catch (err) {
|
|
57
55
|
this.close(err as Error)
|
|
58
56
|
}
|
package/srpc/server.test.ts
CHANGED
|
@@ -10,18 +10,23 @@ import {
|
|
|
10
10
|
combineUint8ArrayListTransform,
|
|
11
11
|
ChannelStreamOpts,
|
|
12
12
|
Packet,
|
|
13
|
+
createContextKey,
|
|
14
|
+
serverContextValue,
|
|
15
|
+
withServerContextValue,
|
|
16
|
+
type ServerContext,
|
|
13
17
|
} from '../srpc/index.js'
|
|
14
18
|
import {
|
|
15
19
|
EchoerDefinition,
|
|
16
20
|
EchoerServer,
|
|
17
21
|
EchoerServiceName,
|
|
22
|
+
EchoMsg,
|
|
18
23
|
runClientTest,
|
|
19
24
|
} from '../echo/index.js'
|
|
20
25
|
import {
|
|
21
26
|
runAbortControllerTest,
|
|
22
27
|
runRpcStreamTest,
|
|
23
28
|
} from '../echo/client-test.js'
|
|
24
|
-
|
|
29
|
+
import type { EchoMsg as EchoMsgType } from '../echo/echo.pb.js'
|
|
25
30
|
describe('srpc server', () => {
|
|
26
31
|
let client: Client
|
|
27
32
|
|
|
@@ -77,6 +82,78 @@ describe('srpc server', () => {
|
|
|
77
82
|
it('should pass rpc stream tests', async () => {
|
|
78
83
|
await runRpcStreamTest(client)
|
|
79
84
|
})
|
|
85
|
+
it('passes the exact invocation context after async request decode', async () => {
|
|
86
|
+
const controller = new AbortController()
|
|
87
|
+
const callerKey = createContextKey<string>()
|
|
88
|
+
let observedAbortSignal: AbortSignal | undefined
|
|
89
|
+
let observedContextSignal: AbortSignal | undefined
|
|
90
|
+
let observedCaller: string | undefined
|
|
91
|
+
const handler = createHandler(EchoerDefinition, {
|
|
92
|
+
Echo: async (
|
|
93
|
+
request: EchoMsgType,
|
|
94
|
+
abortSignal: AbortSignal,
|
|
95
|
+
context: ServerContext,
|
|
96
|
+
) => {
|
|
97
|
+
observedAbortSignal = abortSignal
|
|
98
|
+
observedContextSignal = context.signal
|
|
99
|
+
observedCaller = serverContextValue(context, callerKey)
|
|
100
|
+
return request
|
|
101
|
+
},
|
|
102
|
+
})
|
|
103
|
+
const invokeFn = await handler.lookupMethod(EchoerServiceName, 'Echo')
|
|
104
|
+
if (!invokeFn) {
|
|
105
|
+
throw new Error('Echo method was not found')
|
|
106
|
+
}
|
|
107
|
+
const request = EchoMsg.create({ body: 'signal identity' })
|
|
108
|
+
const drained = Promise.withResolvers<void>()
|
|
109
|
+
await invokeFn(
|
|
110
|
+
(async function* () {
|
|
111
|
+
await Promise.resolve()
|
|
112
|
+
yield EchoMsg.toBinary(request)
|
|
113
|
+
})(),
|
|
114
|
+
async (source) => {
|
|
115
|
+
for await (const _data of source) {
|
|
116
|
+
// Drain the encoded response so the invocation pipeline completes.
|
|
117
|
+
}
|
|
118
|
+
drained.resolve()
|
|
119
|
+
},
|
|
120
|
+
withServerContextValue(
|
|
121
|
+
{ signal: controller.signal },
|
|
122
|
+
callerKey,
|
|
123
|
+
'caller-1',
|
|
124
|
+
),
|
|
125
|
+
)
|
|
126
|
+
await drained.promise
|
|
127
|
+
expect(observedAbortSignal).toBe(controller.signal)
|
|
128
|
+
expect(observedContextSignal).toBe(controller.signal)
|
|
129
|
+
expect(observedCaller).toBe('caller-1')
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
it('keeps two-argument server handlers compatible', async () => {
|
|
133
|
+
const controller = new AbortController()
|
|
134
|
+
let observedSignal: AbortSignal | undefined
|
|
135
|
+
const handler = createHandler(EchoerDefinition, {
|
|
136
|
+
Echo: async (request: EchoMsgType, abortSignal?: AbortSignal) => {
|
|
137
|
+
observedSignal = abortSignal
|
|
138
|
+
return request
|
|
139
|
+
},
|
|
140
|
+
})
|
|
141
|
+
const invokeFn = await handler.lookupMethod(EchoerServiceName, 'Echo')
|
|
142
|
+
if (!invokeFn) throw new Error('Echo method was not found')
|
|
143
|
+
const request = EchoMsg.create({ body: 'legacy handler' })
|
|
144
|
+
await invokeFn(
|
|
145
|
+
(async function* () {
|
|
146
|
+
yield EchoMsg.toBinary(request)
|
|
147
|
+
})(),
|
|
148
|
+
async (source) => {
|
|
149
|
+
for await (const _data of source) {
|
|
150
|
+
// Drain the response.
|
|
151
|
+
}
|
|
152
|
+
},
|
|
153
|
+
{ signal: controller.signal },
|
|
154
|
+
)
|
|
155
|
+
expect(observedSignal).toBe(controller.signal)
|
|
156
|
+
})
|
|
80
157
|
|
|
81
158
|
it('keeps detached server-streaming responses open after request source completes', async () => {
|
|
82
159
|
const mux = createMux()
|
|
@@ -249,6 +326,22 @@ describe('srpc server', () => {
|
|
|
249
326
|
)
|
|
250
327
|
})
|
|
251
328
|
|
|
329
|
+
it('settles a late abort after the call has already closed', async () => {
|
|
330
|
+
const controller = new AbortController()
|
|
331
|
+
vi.spyOn(controller.signal, 'removeEventListener').mockImplementation(
|
|
332
|
+
() => undefined,
|
|
333
|
+
)
|
|
334
|
+
|
|
335
|
+
await client.request(
|
|
336
|
+
EchoerServiceName,
|
|
337
|
+
'Echo',
|
|
338
|
+
new TextEncoder().encode('late abort'),
|
|
339
|
+
controller.signal,
|
|
340
|
+
)
|
|
341
|
+
controller.abort()
|
|
342
|
+
await Promise.resolve()
|
|
343
|
+
})
|
|
344
|
+
|
|
252
345
|
it('tears down passive channel close state', async () => {
|
|
253
346
|
const { port1, port2 } = new MessageChannel()
|
|
254
347
|
const opts: ChannelStreamOpts = { idleTimeoutMs: 1000, keepAliveMs: 1000 }
|
package/srpc/stream-pipe.go
CHANGED
package/srpc/stream-rwc.go
CHANGED
package/srpc/stream.go
CHANGED
|
@@ -23,10 +23,6 @@ type Stream interface {
|
|
|
23
23
|
Close() error
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
type receiptTerminalStream interface {
|
|
27
|
-
receiptTerminalKind() (TerminalKind, bool)
|
|
28
|
-
}
|
|
29
|
-
|
|
30
26
|
// StreamRecv is a stream that can receive typed messages.
|
|
31
27
|
//
|
|
32
28
|
// T is the response type.
|
|
@@ -73,14 +69,6 @@ func (s *streamWithClose) Close() error {
|
|
|
73
69
|
return err2
|
|
74
70
|
}
|
|
75
71
|
|
|
76
|
-
func (s *streamWithClose) receiptTerminalKind() (TerminalKind, bool) {
|
|
77
|
-
receipt, ok := s.Stream.(receiptTerminalStream)
|
|
78
|
-
if !ok {
|
|
79
|
-
return 0, false
|
|
80
|
-
}
|
|
81
|
-
return receipt.receiptTerminalKind()
|
|
82
|
-
}
|
|
83
|
-
|
|
84
72
|
// streamWithContext is a Stream with a wrapped Context function.
|
|
85
73
|
type streamWithContext struct {
|
|
86
74
|
Stream
|
|
@@ -97,14 +85,6 @@ func (s *streamWithContext) Context() context.Context {
|
|
|
97
85
|
return s.ctx
|
|
98
86
|
}
|
|
99
87
|
|
|
100
|
-
func (s *streamWithContext) receiptTerminalKind() (TerminalKind, bool) {
|
|
101
|
-
receipt, ok := s.Stream.(receiptTerminalStream)
|
|
102
|
-
if !ok {
|
|
103
|
-
return 0, false
|
|
104
|
-
}
|
|
105
|
-
return receipt.receiptTerminalKind()
|
|
106
|
-
}
|
|
107
|
-
|
|
108
88
|
// _ is a type assertion
|
|
109
89
|
var (
|
|
110
90
|
_ Stream = (*streamWithClose)(nil)
|
package/srpc/watchdog.test.ts
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
/// <reference lib="es2024.promise" />
|
|
2
|
-
import { describe, expect, it } from 'vitest';
|
|
3
|
-
import { Client } from '../srpc/client.js';
|
|
4
|
-
import { Server } from '../srpc/server.js';
|
|
5
|
-
import { TerminalKind } from '../srpc/rpcproto.pb.js';
|
|
6
|
-
import { buildRpcStreamOpenStream, handleRpcStream } from './rpcstream.js';
|
|
7
|
-
const response = new Uint8Array([4, 5, 6]);
|
|
8
|
-
// This guard mirrors the Go rpcstream/receipt_test.go nested tunnel: an
|
|
9
|
-
// ExecCallReceipt-equivalent held unary call runs through a real handleRpcStream
|
|
10
|
-
// tunnel. The inner generated handler sends its single response, then the
|
|
11
|
-
// wrapping invocation synchronously holds on waitTerminal. The first response
|
|
12
|
-
// must be client-visible while the exact invocation stays open for commit.
|
|
13
|
-
describe('rpcstream held receipt tunnel', () => {
|
|
14
|
-
it('delivers the first inner response while the invocation stays held', async () => {
|
|
15
|
-
const terminal = Promise.withResolvers();
|
|
16
|
-
const innerServer = new Server(async () => {
|
|
17
|
-
return async (_source, dataSink, invocation) => {
|
|
18
|
-
await dataSink((async function* () {
|
|
19
|
-
yield response;
|
|
20
|
-
if (!invocation) {
|
|
21
|
-
throw new Error('missing invocation');
|
|
22
|
-
}
|
|
23
|
-
terminal.resolve(await invocation.waitTerminal(new AbortController().signal));
|
|
24
|
-
})());
|
|
25
|
-
};
|
|
26
|
-
});
|
|
27
|
-
const caller = (packetTx) => handleRpcStream(packetTx[Symbol.asyncIterator](), async () => innerServer.rpcStreamHandler);
|
|
28
|
-
const client = new Client(buildRpcStreamOpenStream('component-a', caller));
|
|
29
|
-
const held = await client.requestWithReceipt('test.Service', 'Unary', response);
|
|
30
|
-
// The first inner response is client-visible while the invocation is held.
|
|
31
|
-
expect(held.response).toEqual(response);
|
|
32
|
-
// The inner invocation must still be open: no terminal observed yet.
|
|
33
|
-
const raced = await Promise.race([
|
|
34
|
-
terminal.promise.then(() => 'terminal'),
|
|
35
|
-
Promise.resolve('pending'),
|
|
36
|
-
]);
|
|
37
|
-
expect(raced).toBe('pending');
|
|
38
|
-
await expect(held.receipt.commit()).resolves.toBeUndefined();
|
|
39
|
-
await expect(terminal.promise).resolves.toBe(TerminalKind.COMMITTED);
|
|
40
|
-
});
|
|
41
|
-
});
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { ClientRPC } from './client-rpc.js';
|
|
2
|
-
export interface ReceiptRpc {
|
|
3
|
-
requestWithReceipt(service: string, method: string, data: Uint8Array, abortSignal?: AbortSignal): Promise<HeldCall>;
|
|
4
|
-
}
|
|
5
|
-
export interface HeldCall {
|
|
6
|
-
readonly response: Uint8Array;
|
|
7
|
-
readonly receipt: CallReceipt;
|
|
8
|
-
}
|
|
9
|
-
export declare class CallReceipt {
|
|
10
|
-
#private;
|
|
11
|
-
constructor(call: ClientRPC, iterator: AsyncIterator<Uint8Array>);
|
|
12
|
-
get done(): Promise<void>;
|
|
13
|
-
get settled(): boolean;
|
|
14
|
-
commit(): Promise<void>;
|
|
15
|
-
abort(reason?: Error): Promise<void>;
|
|
16
|
-
[Symbol.asyncDispose](): Promise<void>;
|
|
17
|
-
}
|