@zlink-systems/framework 0.31.0 → 0.32.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/dist/.tsbuildinfo +1 -1
- package/dist/contracts/Actors/ZLinkActor.d.ts +2 -0
- package/dist/contracts/Actors/ZLinkSessionBindingIdentity.d.ts +6 -0
- package/dist/contracts/Actors/ZLinkSessionBindingIdentity.js +2 -0
- package/dist/contracts/Actors/index.d.ts +1 -0
- package/dist/contracts/Actors/index.js +1 -0
- package/dist/contracts/Common/payload-slab.d.ts +4 -0
- package/dist/contracts/Common/payload-slab.js +30 -0
- package/dist/contracts/Handlers/Contexts.d.ts +1 -0
- package/dist/internal.d.ts +9 -0
- package/dist/internal.js +23 -1
- package/dist/runtime/actors/actor-client.d.ts +1 -1
- package/dist/runtime/actors/actor-client.js +32 -23
- package/dist/runtime/actors/actor-mailbox.d.ts +1 -1
- package/dist/runtime/actors/actor-runtime-state.d.ts +1 -1
- package/dist/runtime/actors/spot-actor-dispatch.d.ts +4 -2
- package/dist/runtime/actors/spot-actor-dispatch.js +2 -1
- package/dist/runtime/application-jobs/application-ingress-record-owner.d.ts +1 -0
- package/dist/runtime/application-jobs/application-ingress-record-owner.js +3 -0
- package/dist/runtime/backend/contracts/index.d.ts +2 -0
- package/dist/runtime/backend/mesh-completion-table.d.ts +1 -0
- package/dist/runtime/backend/mesh-completion-table.js +1 -0
- package/dist/runtime/backend/node/node-mesh-backend-adapter.js +5 -1
- package/dist/runtime/backend/node/node-raw-binding-port.d.ts +7 -2
- package/dist/runtime/backend/node/node-raw-binding-port.js +17 -11
- package/dist/runtime/backend/node/node-raw-mesh-backend.d.ts +2 -2
- package/dist/runtime/backend/node/node-raw-mesh-backend.js +15 -8
- package/dist/runtime/channels/channel-dispatch-pipeline.d.ts +7 -6
- package/dist/runtime/channels/channel-dispatch-pipeline.js +1 -0
- package/dist/runtime/channels/channel-dispatchers.d.ts +7 -3
- package/dist/runtime/channels/channel-dispatchers.js +27 -19
- package/dist/runtime/channels/channel-envelope.js +543 -23
- package/dist/runtime/channels/channel-receive-loops.d.ts +9 -6
- package/dist/runtime/channels/channel-receive-loops.js +59 -18
- package/dist/runtime/channels/channel-runtime-lifecycle.d.ts +22 -3
- package/dist/runtime/channels/channel-socket-registry.d.ts +22 -4
- package/dist/runtime/diagnostics/runtime-metrics.d.ts +1 -1
- package/dist/runtime/diagnostics/runtime-metrics.js +2 -1
- package/dist/runtime/execution/serial-execution-queue.d.ts +26 -13
- package/dist/runtime/foundation/raw-service-mesh-runtime.d.ts +27 -11
- package/dist/runtime/foundation/raw-service-mesh-runtime.js +29 -25
- package/dist/runtime/foundation/service-liveness-registry.d.ts +1 -1
- package/dist/runtime/foundation/service-mailbox.d.ts +3 -1
- package/dist/runtime/foundation/service-mailbox.js +4 -1
- package/dist/runtime/foundation/service-runtime-contracts.d.ts +3 -1
- package/dist/runtime/foundation/service-stateful-registry.d.ts +2 -0
- package/dist/runtime/foundation/service-stateful-registry.js +28 -12
- package/dist/runtime/foundation/service-stateful-runtime.d.ts +51 -19
- package/dist/runtime/foundation/service-stateful-runtime.js +54 -5
- package/dist/runtime/foundation/service-stateful-wire-codec.d.ts +3 -0
- package/dist/runtime/foundation/service-stateful-wire-codec.js +15 -0
- package/dist/runtime/foundation/service-wire-constants.generated.d.ts +1 -0
- package/dist/runtime/foundation/service-wire-constants.generated.js +12 -0
- package/dist/runtime/foundation/service-wire-m6a-codec.d.ts +2 -0
- package/dist/runtime/foundation/service-wire-m6a-codec.js +17 -7
- package/dist/runtime/host/actor-placement-coordinator.d.ts +1 -1
- package/dist/runtime/host/actor-transfer-runtime.d.ts +3 -3
- package/dist/runtime/host/application-job-queue.d.ts +1 -1
- package/dist/runtime/host/index.d.ts +89 -36
- package/dist/runtime/host/index.js +101 -7
- package/dist/runtime/host/location-runtime-owner.d.ts +2 -2
- package/dist/runtime/host/service-relocation-host-runtime.d.ts +181 -44
- package/dist/runtime/host/service-relocation-host-runtime.js +47 -13
- package/dist/runtime/host/spot-address-transport.js +50 -42
- package/dist/runtime/locations/auto-connect-loop.d.ts +0 -1
- package/dist/runtime/locations/auto-connect-loop.js +25 -19
- package/dist/runtime/locations/auto-connect-planner.js +2 -5
- package/dist/runtime/locations/auto-connect-reconciler.d.ts +1 -0
- package/dist/runtime/locations/auto-connect-reconciler.js +35 -23
- package/dist/runtime/locations/in-memory-location-store.d.ts +0 -2
- package/dist/runtime/locations/in-memory-location-store.js +1 -38
- package/dist/runtime/locations/location-store-repository.d.ts +0 -1
- package/dist/runtime/locations/location-store-repository.js +13 -115
- package/dist/runtime/locations/runtime.d.ts +3 -3
- package/dist/runtime/locations/runtime.js +12 -16
- package/dist/runtime/messaging/creation-payload-codec.js +2 -2
- package/dist/runtime/protocol/service_wire_codec.generated.d.ts +4 -0
- package/dist/runtime/protocol/service_wire_codec.generated.js +17 -7
- package/dist/runtime/spots/index.d.ts +20 -5
- package/dist/runtime/spots/index.js +14 -0
- package/dist/runtime/spots/spot-actor-admission-coordinator.d.ts +3 -3
- package/dist/runtime/spots/spot-actor-packet-dispatch.d.ts +1 -0
- package/dist/runtime/spots/spot-actor-packet-dispatch.js +13 -7
- package/dist/runtime/spots/spot-actor-packet-drain.d.ts +1 -0
- package/dist/runtime/spots/spot-actor-packet-drain.js +12 -5
- package/dist/runtime/spots/spot-entry-activation.d.ts +2 -1
- package/dist/runtime/spots/spot-node-runtime-manager.d.ts +8 -6
- package/dist/runtime/spots/spot-node-runtime-manager.js +2 -0
- package/dist/runtime/spots/spot-route-packet-dispatch.js +7 -4
- package/dist/runtime/spots/spot-routed-spot-packet-dispatch.js +3 -1
- package/dist/runtime/spots/spot-serial-executor.d.ts +2 -1
- package/dist/runtime/spots/spot-subscription-dispatch.js +3 -1
- package/dist/runtime/spots/spot-timer.d.ts +1 -1
- package/dist/runtime/streams/actor-session-binding-registry.d.ts +9 -3
- package/dist/runtime/streams/actor-session-binding-registry.js +17 -0
- package/dist/runtime/streams/index.d.ts +3 -3
- package/dist/runtime/streams/index.js +22 -2
- package/dist/runtime/streams/managed-stream.js +2 -1
- package/dist/runtime/streams/session-calls.d.ts +1 -1
- package/dist/runtime/streams/session-calls.js +3 -9
- package/dist/runtime/streams/session-context.d.ts +2 -2
- package/dist/runtime/streams/session-context.js +2 -2
- package/dist/runtime/streams/stream-session-runtime.d.ts +1 -0
- package/dist/runtime/streams/stream-session-runtime.js +37 -12
- package/package.json +3 -3
- package/src/contracts/Actors/ZLinkActor.ts +6 -0
- package/src/contracts/Actors/ZLinkSessionBindingIdentity.ts +7 -0
- package/src/contracts/Actors/index.ts +1 -0
- package/src/contracts/Common/payload-slab.ts +29 -0
- package/src/contracts/Handlers/Contexts.ts +1 -0
- package/src/internal.ts +17 -0
- package/src/runtime/actors/actor-client.ts +48 -23
- package/src/runtime/actors/actor-mailbox.ts +1 -1
- package/src/runtime/actors/actor-runtime-state.ts +1 -1
- package/src/runtime/actors/spot-actor-dispatch.ts +4 -3
- package/src/runtime/application-jobs/application-ingress-record-owner.ts +4 -0
- package/src/runtime/backend/contracts/index.ts +2 -0
- package/src/runtime/backend/mesh-completion-table.ts +2 -0
- package/src/runtime/backend/node/node-mesh-backend-adapter.ts +5 -1
- package/src/runtime/backend/node/node-raw-binding-port.ts +20 -7
- package/src/runtime/backend/node/node-raw-mesh-backend.ts +18 -10
- package/src/runtime/channels/channel-dispatch-pipeline.ts +6 -5
- package/src/runtime/channels/channel-dispatchers.ts +39 -19
- package/src/runtime/channels/channel-envelope.ts +547 -20
- package/src/runtime/channels/channel-receive-loops.ts +93 -36
- package/src/runtime/channels/channel-runtime-lifecycle.ts +2 -2
- package/src/runtime/channels/channel-socket-registry.ts +3 -3
- package/src/runtime/diagnostics/runtime-metrics.ts +3 -1
- package/src/runtime/execution/serial-execution-queue.ts +13 -13
- package/src/runtime/foundation/raw-service-mesh-runtime.ts +40 -39
- package/src/runtime/foundation/service-liveness-registry.ts +1 -1
- package/src/runtime/foundation/service-mailbox.ts +6 -1
- package/src/runtime/foundation/service-runtime-contracts.ts +7 -1
- package/src/runtime/foundation/service-stateful-registry.ts +39 -14
- package/src/runtime/foundation/service-stateful-runtime.ts +87 -21
- package/src/runtime/foundation/service-stateful-wire-codec.ts +18 -0
- package/src/runtime/foundation/service-wire-constants.generated.ts +10 -0
- package/src/runtime/foundation/service-wire-m6a-codec.ts +29 -7
- package/src/runtime/host/actor-placement-coordinator.ts +1 -1
- package/src/runtime/host/actor-transfer-runtime.ts +3 -3
- package/src/runtime/host/application-job-queue.ts +1 -1
- package/src/runtime/host/index.ts +183 -44
- package/src/runtime/host/location-runtime-owner.ts +2 -2
- package/src/runtime/host/service-relocation-host-runtime.ts +101 -65
- package/src/runtime/host/spot-address-transport.ts +67 -61
- package/src/runtime/locations/auto-connect-loop.ts +24 -16
- package/src/runtime/locations/auto-connect-planner.ts +2 -5
- package/src/runtime/locations/auto-connect-reconciler.ts +37 -24
- package/src/runtime/locations/in-memory-location-store.ts +1 -74
- package/src/runtime/locations/location-store-repository.ts +32 -153
- package/src/runtime/locations/runtime.ts +16 -28
- package/src/runtime/messaging/creation-payload-codec.ts +3 -3
- package/src/runtime/protocol/service_wire_codec.generated.ts +18 -8
- package/src/runtime/protocol/service_wire_pilot_codec.generated.ts +1 -1
- package/src/runtime/spots/index.ts +28 -4
- package/src/runtime/spots/spot-actor-admission-coordinator.ts +1 -1
- package/src/runtime/spots/spot-actor-packet-dispatch.ts +16 -6
- package/src/runtime/spots/spot-actor-packet-drain.ts +21 -6
- package/src/runtime/spots/spot-entry-activation.ts +1 -1
- package/src/runtime/spots/spot-node-runtime-manager.ts +8 -6
- package/src/runtime/spots/spot-route-packet-dispatch.ts +8 -4
- package/src/runtime/spots/spot-routed-spot-packet-dispatch.ts +3 -1
- package/src/runtime/spots/spot-serial-executor.ts +1 -1
- package/src/runtime/spots/spot-subscription-dispatch.ts +3 -1
- package/src/runtime/spots/spot-timer.ts +1 -1
- package/src/runtime/streams/actor-session-binding-registry.ts +30 -3
- package/src/runtime/streams/index.ts +30 -4
- package/src/runtime/streams/managed-stream.ts +2 -1
- package/src/runtime/streams/session-calls.ts +9 -14
- package/src/runtime/streams/session-context.ts +6 -11
- package/src/runtime/streams/stream-session-runtime.ts +49 -20
|
@@ -39,13 +39,13 @@ export interface ZLinkSessionCallContext {
|
|
|
39
39
|
payload: unknown,
|
|
40
40
|
correlationId?: string
|
|
41
41
|
): Message;
|
|
42
|
-
|
|
42
|
+
submitReply(
|
|
43
43
|
requestHeader: ZLinkStreamFrameHeader,
|
|
44
44
|
kind: ZLinkStreamReplyMessageKind,
|
|
45
45
|
metadata: ReadonlyMap<string, string>,
|
|
46
46
|
compressed: boolean,
|
|
47
47
|
payload: unknown
|
|
48
|
-
):
|
|
48
|
+
): Promise<ZLinkSubmitResult>;
|
|
49
49
|
claimReply(requestHeader: ZLinkStreamFrameHeader): void;
|
|
50
50
|
/**
|
|
51
51
|
* Spec 26 §2.2: the STREAM surface records session-connected sends and the
|
|
@@ -168,23 +168,18 @@ export class DefaultZLinkSessionReplyCall implements ZLinkSessionReplyCall {
|
|
|
168
168
|
throw new Error('Reply is only available while handling a request packet.');
|
|
169
169
|
}
|
|
170
170
|
this.context.claimReply(requestHeader);
|
|
171
|
-
const
|
|
171
|
+
const result = await this.context.submitReply(
|
|
172
172
|
requestHeader,
|
|
173
173
|
ZLinkStreamMessageKind.Response,
|
|
174
174
|
new Map<string, string>(),
|
|
175
175
|
this.compressionEnabled,
|
|
176
176
|
this.message
|
|
177
177
|
);
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
requestHeader.correlationId
|
|
185
|
-
);
|
|
186
|
-
} finally {
|
|
187
|
-
message.close();
|
|
188
|
-
}
|
|
178
|
+
requireOneWayCompletion(result, 'STREAM session reply');
|
|
179
|
+
this.context.traceFrameWritten?.(
|
|
180
|
+
ZLinkStreamMessageKind.Response,
|
|
181
|
+
requestHeader.name,
|
|
182
|
+
requestHeader.correlationId
|
|
183
|
+
);
|
|
189
184
|
}
|
|
190
185
|
}
|
|
@@ -65,13 +65,14 @@ interface ZLinkSessionContextRuntime {
|
|
|
65
65
|
payload: unknown,
|
|
66
66
|
correlationId?: string
|
|
67
67
|
): Message;
|
|
68
|
-
|
|
68
|
+
submitReply(
|
|
69
|
+
context: DefaultZLinkSessionContext,
|
|
69
70
|
requestHeader: ZLinkStreamFrameHeader,
|
|
70
71
|
kind: ZLinkStreamReplyMessageKind,
|
|
71
72
|
metadata: ReadonlyMap<string, string>,
|
|
72
73
|
compressed: boolean,
|
|
73
74
|
payload: unknown
|
|
74
|
-
):
|
|
75
|
+
): Promise<ZLinkSubmitResult>;
|
|
75
76
|
decompressPayload(payload: Message): Message;
|
|
76
77
|
bind(
|
|
77
78
|
context: DefaultZLinkSessionContext,
|
|
@@ -267,20 +268,14 @@ export class DefaultZLinkSessionContext implements ZLinkSessionContext {
|
|
|
267
268
|
);
|
|
268
269
|
}
|
|
269
270
|
|
|
270
|
-
|
|
271
|
+
submitReply(
|
|
271
272
|
requestHeader: ZLinkStreamFrameHeader,
|
|
272
273
|
kind: ZLinkStreamReplyMessageKind,
|
|
273
274
|
metadata: ReadonlyMap<string, string>,
|
|
274
275
|
compressed: boolean,
|
|
275
276
|
payload: unknown
|
|
276
|
-
):
|
|
277
|
-
return this.runtime.
|
|
278
|
-
requestHeader,
|
|
279
|
-
kind,
|
|
280
|
-
metadata,
|
|
281
|
-
compressed,
|
|
282
|
-
payload
|
|
283
|
-
);
|
|
277
|
+
): Promise<ZLinkSubmitResult> {
|
|
278
|
+
return this.runtime.submitReply(this, requestHeader, kind, metadata, compressed, payload);
|
|
284
279
|
}
|
|
285
280
|
|
|
286
281
|
enterDispatch(header: ZLinkStreamFrameHeader): void {
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { requireOneWayCompletion } from '../messaging/submission-result';
|
|
2
|
+
import { logs, SeverityNumber } from '@opentelemetry/api-logs';
|
|
3
|
+
import { ZlinkStreamControlPacket } from '@zlink-systems/stream-wire';
|
|
1
4
|
import type { RoutingId, ZLinkMessageSerializer, ZLinkSession } from '../../contracts';
|
|
2
5
|
import type { Message } from '../../contracts/Common/Message';
|
|
3
6
|
import type { ZLinkProviderResolver } from '../../contracts/Common/ZLinkProviderResolver';
|
|
@@ -30,6 +33,7 @@ import type {
|
|
|
30
33
|
import type { ZLinkMeshCompletionTable } from '../backend/mesh-completion-table';
|
|
31
34
|
import { ZLinkDispatchErrorReporter, ZLinkRouteDisconnectedError } from '../channels';
|
|
32
35
|
import { type ZLinkRuntimeMetrics, flowIfEnabled } from '../diagnostics';
|
|
36
|
+
import { TELEMETRY_INSTRUMENTATION_NAME } from '../diagnostics/message-flow-record';
|
|
33
37
|
|
|
34
38
|
import { createInboundFlow, runWithFlow } from '../diagnostics/flow-context';
|
|
35
39
|
import { ZLinkSocketNativeEventType } from '../diagnostics/internal-event-contracts';
|
|
@@ -63,7 +67,6 @@ import { ZLinkSessionSerialExecutor } from './session-serial-executor';
|
|
|
63
67
|
import { ownedMessage } from './stream-message-utils';
|
|
64
68
|
import { normalizeStreamLiveness } from '../../contracts/Configuration/RegistrationNormalizers';
|
|
65
69
|
|
|
66
|
-
const ZLINK_SEND_DONT_WAIT = 1;
|
|
67
70
|
const ZLINK_RECV_DONT_WAIT = 1;
|
|
68
71
|
const ZLINK_STREAM_RECEIVE_FRAME_BATCH_LIMIT = 64;
|
|
69
72
|
const ZLINK_STREAM_ACTOR_BINDING_REPLACEMENT_CLOSE_DELAY_MS = 100;
|
|
@@ -88,6 +91,7 @@ interface ZLinkStreamLivenessOptions {
|
|
|
88
91
|
}
|
|
89
92
|
|
|
90
93
|
const MAX_TIMER_DELAY_MS = 2_147_483_647;
|
|
94
|
+
const STREAM_RUNTIME_LOGGER = logs.getLogger(TELEMETRY_INSTRUMENTATION_NAME);
|
|
91
95
|
|
|
92
96
|
const systemLivenessClock: ZLinkStreamLivenessClock = {
|
|
93
97
|
now: () => performance.now(),
|
|
@@ -583,19 +587,33 @@ export class ZLinkStreamSessionRuntime {
|
|
|
583
587
|
}
|
|
584
588
|
|
|
585
589
|
private async handleControl(header: ZLinkStreamFrameHeader, payload: Message): Promise<void> {
|
|
586
|
-
|
|
590
|
+
const payloadBytes = messageToBytes(payload);
|
|
591
|
+
if (header.name === ZLINK_STREAM_HEARTBEAT_PING) {
|
|
592
|
+
if (payloadBytes.length !== 0) {
|
|
593
|
+
this.logIgnoredControl(header.name, payloadBytes.length);
|
|
594
|
+
}
|
|
595
|
+
await this.stream.writeControl(ZLINK_STREAM_HEARTBEAT_PONG);
|
|
596
|
+
return;
|
|
597
|
+
}
|
|
598
|
+
if (header.name === ZLINK_STREAM_HEARTBEAT_PONG) {
|
|
599
|
+
if (payloadBytes.length !== 0) {
|
|
600
|
+
this.logIgnoredControl(header.name, payloadBytes.length);
|
|
601
|
+
}
|
|
602
|
+
return;
|
|
603
|
+
}
|
|
604
|
+
if (
|
|
605
|
+
header.name === ZlinkStreamControlPacket.ActorBound ||
|
|
606
|
+
header.name === ZlinkStreamControlPacket.ActorUnbound
|
|
607
|
+
) {
|
|
587
608
|
await this.closeForLiveness(
|
|
588
609
|
ZLinkStreamCloseReasonCode.ProtocolError,
|
|
589
610
|
'protocol_error',
|
|
590
|
-
|
|
611
|
+
`Unexpected stream actor control frame: ${header.name}`
|
|
591
612
|
);
|
|
592
613
|
return;
|
|
593
614
|
}
|
|
594
|
-
if (header.name
|
|
595
|
-
|
|
596
|
-
}
|
|
597
|
-
if (header.name === ZLINK_STREAM_HEARTBEAT_PING) {
|
|
598
|
-
await this.stream.writeControl(ZLINK_STREAM_HEARTBEAT_PONG);
|
|
615
|
+
if (header.name.startsWith('$zlink.')) {
|
|
616
|
+
this.logIgnoredControl(header.name, payloadBytes.length);
|
|
599
617
|
return;
|
|
600
618
|
}
|
|
601
619
|
await this.closeForLiveness(
|
|
@@ -605,6 +623,21 @@ export class ZLinkStreamSessionRuntime {
|
|
|
605
623
|
);
|
|
606
624
|
}
|
|
607
625
|
|
|
626
|
+
private logIgnoredControl(name: string, payloadLength: number): void {
|
|
627
|
+
STREAM_RUNTIME_LOGGER.emit({
|
|
628
|
+
eventName: 'zlink.stream.control_ignored',
|
|
629
|
+
timestamp: new Date(),
|
|
630
|
+
severityNumber: SeverityNumber.DEBUG,
|
|
631
|
+
severityText: 'DEBUG',
|
|
632
|
+
body: `Ignored STREAM control name=${name} payloadBytes=${payloadLength} peerSessionRid=${this.stream.sessionId}`,
|
|
633
|
+
attributes: {
|
|
634
|
+
control_name: name,
|
|
635
|
+
payload_bytes: payloadLength,
|
|
636
|
+
peer_session_rid: this.stream.sessionId
|
|
637
|
+
}
|
|
638
|
+
});
|
|
639
|
+
}
|
|
640
|
+
|
|
608
641
|
private scheduleLivenessCheck(): void {
|
|
609
642
|
if (this.disposed || this.disconnected || this.livenessTimer !== undefined) {
|
|
610
643
|
return;
|
|
@@ -694,22 +727,18 @@ export class ZLinkStreamSessionRuntime {
|
|
|
694
727
|
// record (spec 26 §2.2) and confuse the peer's reply matching.
|
|
695
728
|
return;
|
|
696
729
|
}
|
|
697
|
-
const message = this.context.createJsonReplyFrameMessage(
|
|
698
|
-
header,
|
|
699
|
-
ZLinkStreamMessageKind.Error,
|
|
700
|
-
new Map(),
|
|
701
|
-
false,
|
|
702
|
-
boundSessionErrorPayload(error)
|
|
703
|
-
);
|
|
704
730
|
try {
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
731
|
+
const result = await this.context.submitReply(
|
|
732
|
+
header,
|
|
733
|
+
ZLinkStreamMessageKind.Error,
|
|
734
|
+
new Map(),
|
|
735
|
+
false,
|
|
736
|
+
boundSessionErrorPayload(error)
|
|
737
|
+
);
|
|
738
|
+
requireOneWayCompletion(result, 'Client stream error reply');
|
|
708
739
|
this.traceFrameWritten(ZLinkStreamMessageKind.Error, header.name, header.correlationId);
|
|
709
740
|
} catch (replyError) {
|
|
710
741
|
this.options.onError?.(replyError);
|
|
711
|
-
} finally {
|
|
712
|
-
message.close();
|
|
713
742
|
}
|
|
714
743
|
}
|
|
715
744
|
|