@camera.ui/rpc 1.0.3 → 1.0.4
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/externals/nats.js/core/src/authenticator.ts +159 -0
- package/externals/nats.js/core/src/bench.ts +426 -0
- package/externals/nats.js/core/src/codec.ts +28 -0
- package/externals/nats.js/core/src/core.ts +1219 -0
- package/externals/nats.js/core/src/databuffer.ts +129 -0
- package/externals/nats.js/core/src/denobuffer.ts +248 -0
- package/externals/nats.js/core/src/encoders.ts +53 -0
- package/externals/nats.js/core/src/errors.ts +300 -0
- package/externals/nats.js/core/src/headers.ts +315 -0
- package/externals/nats.js/core/src/heartbeats.ts +114 -0
- package/externals/nats.js/core/src/idleheartbeat_monitor.ts +140 -0
- package/externals/nats.js/core/src/internal_mod.ts +167 -0
- package/externals/nats.js/core/src/ipparser.ts +215 -0
- package/externals/nats.js/core/src/mod.ts +113 -0
- package/externals/nats.js/core/src/msg.ts +120 -0
- package/externals/nats.js/core/src/muxsubscription.ts +111 -0
- package/externals/nats.js/core/src/nats.ts +650 -0
- package/externals/nats.js/core/src/nkeys.ts +1 -0
- package/externals/nats.js/core/src/nuid.ts +16 -0
- package/externals/nats.js/core/src/options.ts +202 -0
- package/externals/nats.js/core/src/parser.ts +756 -0
- package/externals/nats.js/core/src/protocol.ts +1304 -0
- package/externals/nats.js/core/src/queued_iterator.ts +171 -0
- package/externals/nats.js/core/src/request.ts +177 -0
- package/externals/nats.js/core/src/semver.ts +165 -0
- package/externals/nats.js/core/src/servers.ts +424 -0
- package/externals/nats.js/core/src/transport.ts +117 -0
- package/externals/nats.js/core/src/types.ts +17 -0
- package/externals/nats.js/core/src/util.ts +367 -0
- package/externals/nats.js/core/src/version.ts +2 -0
- package/externals/nats.js/core/src/ws_transport.ts +391 -0
- package/package.json +2 -1
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2024 Synadia Communications, Inc
|
|
3
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
* you may not use this file except in compliance with the License.
|
|
5
|
+
* You may obtain a copy of the License at
|
|
6
|
+
*
|
|
7
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
*
|
|
9
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
* See the License for the specific language governing permissions and
|
|
13
|
+
* limitations under the License.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Represents an error that is thrown when an invalid subject is encountered.
|
|
18
|
+
* This class extends the built-in Error object.
|
|
19
|
+
*
|
|
20
|
+
* @class
|
|
21
|
+
* @extends Error
|
|
22
|
+
*/
|
|
23
|
+
export class InvalidSubjectError extends Error {
|
|
24
|
+
constructor(subject: string, options?: ErrorOptions) {
|
|
25
|
+
super(`illegal subject: '${subject}'`, options);
|
|
26
|
+
this.name = "InvalidSubjectError";
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export class InvalidArgumentError extends Error {
|
|
31
|
+
constructor(message: string, options?: ErrorOptions) {
|
|
32
|
+
super(message, options);
|
|
33
|
+
this.name = "InvalidArgumentError";
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
static format(
|
|
37
|
+
property: string | string[],
|
|
38
|
+
message: string,
|
|
39
|
+
options?: ErrorOptions,
|
|
40
|
+
): InvalidArgumentError {
|
|
41
|
+
if (Array.isArray(message) && message.length > 1) {
|
|
42
|
+
message = message[0];
|
|
43
|
+
}
|
|
44
|
+
if (Array.isArray(property)) {
|
|
45
|
+
property = property.map((n) => `'${n}'`);
|
|
46
|
+
property = property.join(",");
|
|
47
|
+
} else {
|
|
48
|
+
property = `'${property}'`;
|
|
49
|
+
}
|
|
50
|
+
return new InvalidArgumentError(`${property} ${message}`, options);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* InvalidOperationError is a custom error class that extends the standard Error object.
|
|
56
|
+
* It represents an error that occurs when an invalid operation is attempted on one of
|
|
57
|
+
* objects returned by the API. For example, trying to iterate on an object that was
|
|
58
|
+
* configured with a callback.
|
|
59
|
+
*
|
|
60
|
+
* @class InvalidOperationError
|
|
61
|
+
* @extends {Error}
|
|
62
|
+
*
|
|
63
|
+
* @param {string} message - The error message that explains the reason for the error.
|
|
64
|
+
* @param {ErrorOptions} [options] - Optional parameter to provide additional error options.
|
|
65
|
+
*/
|
|
66
|
+
export class InvalidOperationError extends Error {
|
|
67
|
+
constructor(message: string, options?: ErrorOptions) {
|
|
68
|
+
super(message, options);
|
|
69
|
+
this.name = "InvalidOperationError";
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Represents an error indicating that user authentication has expired.
|
|
75
|
+
* This error is typically thrown when a user attempts to access a connection
|
|
76
|
+
* but their authentication credentials have expired.
|
|
77
|
+
*/
|
|
78
|
+
export class UserAuthenticationExpiredError extends Error {
|
|
79
|
+
constructor(message: string, options?: ErrorOptions) {
|
|
80
|
+
super(message, options);
|
|
81
|
+
this.name = "UserAuthenticationExpiredError";
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
static parse(s: string): UserAuthenticationExpiredError | null {
|
|
85
|
+
const ss = s.toLowerCase();
|
|
86
|
+
if (ss.indexOf("user authentication expired") !== -1) {
|
|
87
|
+
return new UserAuthenticationExpiredError(s);
|
|
88
|
+
}
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Represents an error related to authorization issues.
|
|
95
|
+
* Note that these could represent an authorization violation,
|
|
96
|
+
* or that the account authentication configuration has expired,
|
|
97
|
+
* or an authentication timeout.
|
|
98
|
+
*/
|
|
99
|
+
export class AuthorizationError extends Error {
|
|
100
|
+
constructor(message: string, options?: ErrorOptions) {
|
|
101
|
+
super(message, options);
|
|
102
|
+
this.name = "AuthorizationError";
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
static parse(s: string): AuthorizationError | null {
|
|
106
|
+
const messages = [
|
|
107
|
+
"authorization violation",
|
|
108
|
+
"account authentication expired",
|
|
109
|
+
"authentication timeout",
|
|
110
|
+
];
|
|
111
|
+
|
|
112
|
+
const ss = s.toLowerCase();
|
|
113
|
+
|
|
114
|
+
for (let i = 0; i < messages.length; i++) {
|
|
115
|
+
if (ss.indexOf(messages[i]) !== -1) {
|
|
116
|
+
return new AuthorizationError(s);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Class representing an error thrown when an operation is attempted on a closed connection.
|
|
126
|
+
*
|
|
127
|
+
* This error is intended to signal that a connection-related operation could not be completed
|
|
128
|
+
* because the connection is no longer open or has been terminated.
|
|
129
|
+
*
|
|
130
|
+
* @class
|
|
131
|
+
* @extends Error
|
|
132
|
+
*/
|
|
133
|
+
export class ClosedConnectionError extends Error {
|
|
134
|
+
constructor() {
|
|
135
|
+
super("closed connection");
|
|
136
|
+
this.name = "ClosedConnectionError";
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* The `ConnectionDrainingError` class represents a specific type of error
|
|
142
|
+
* that occurs when a connection is being drained.
|
|
143
|
+
*
|
|
144
|
+
* This error is typically used in scenarios where connections need to be
|
|
145
|
+
* gracefully closed or when they are transitioning to an inactive state.
|
|
146
|
+
*
|
|
147
|
+
* The error message is set to "connection draining" and the error name is
|
|
148
|
+
* overridden to "DrainingConnectionError".
|
|
149
|
+
*/
|
|
150
|
+
export class DrainingConnectionError extends Error {
|
|
151
|
+
constructor() {
|
|
152
|
+
super("connection draining");
|
|
153
|
+
this.name = "DrainingConnectionError";
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Represents an error that occurs when a network connection fails.
|
|
159
|
+
* Extends the built-in Error class to provide additional context for connection-related issues.
|
|
160
|
+
*
|
|
161
|
+
* @param {string} message - A human-readable description of the error.
|
|
162
|
+
* @param {ErrorOptions} [options] - Optional settings for customizing the error behavior.
|
|
163
|
+
*/
|
|
164
|
+
export class ConnectionError extends Error {
|
|
165
|
+
constructor(message: string, options?: ErrorOptions) {
|
|
166
|
+
super(message, options);
|
|
167
|
+
this.name = "ConnectionError";
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Represents an error encountered during protocol operations.
|
|
173
|
+
* This class extends the built-in `Error` class, providing a specific
|
|
174
|
+
* error type called `ProtocolError`.
|
|
175
|
+
*
|
|
176
|
+
* @param {string} message - A descriptive message describing the error.
|
|
177
|
+
* @param {ErrorOptions} [options] - Optional parameters to include additional details.
|
|
178
|
+
*/
|
|
179
|
+
export class ProtocolError extends Error {
|
|
180
|
+
constructor(message: string, options?: ErrorOptions) {
|
|
181
|
+
super(message, options);
|
|
182
|
+
this.name = "ProtocolError";
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Class representing an error that occurs during an request operation
|
|
188
|
+
* (such as TimeoutError, or NoRespondersError, or some other error).
|
|
189
|
+
*
|
|
190
|
+
* @extends Error
|
|
191
|
+
*/
|
|
192
|
+
export class RequestError extends Error {
|
|
193
|
+
constructor(message = "", options?: ErrorOptions) {
|
|
194
|
+
super(message, options);
|
|
195
|
+
this.name = "RequestError";
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
isNoResponders(): boolean {
|
|
199
|
+
return this.cause instanceof NoRespondersError;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* TimeoutError is a custom error class that extends the built-in Error class.
|
|
205
|
+
* It is used to represent an error that occurs when an operation exceeds a
|
|
206
|
+
* predefined time limit.
|
|
207
|
+
*
|
|
208
|
+
* @class
|
|
209
|
+
* @extends {Error}
|
|
210
|
+
*/
|
|
211
|
+
export class TimeoutError extends Error {
|
|
212
|
+
constructor(options?: ErrorOptions) {
|
|
213
|
+
super("timeout", options);
|
|
214
|
+
this.name = "TimeoutError";
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* NoRespondersError is an error thrown when no responders (no service is
|
|
220
|
+
* subscribing to the subject) are found for a given subject. This error
|
|
221
|
+
* is typically found as the cause for a RequestError
|
|
222
|
+
*
|
|
223
|
+
* @extends Error
|
|
224
|
+
*
|
|
225
|
+
* @param {string} subject - The subject for which no responders were found.
|
|
226
|
+
* @param {ErrorOptions} [options] - Optional error options.
|
|
227
|
+
*/
|
|
228
|
+
export class NoRespondersError extends Error {
|
|
229
|
+
subject: string;
|
|
230
|
+
constructor(subject: string, options?: ErrorOptions) {
|
|
231
|
+
super(`no responders: '${subject}'`, options);
|
|
232
|
+
this.subject = subject;
|
|
233
|
+
this.name = "NoResponders";
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Class representing a Permission Violation Error.
|
|
239
|
+
* It provides information about the operation (either "publish" or "subscription")
|
|
240
|
+
* and the subject used for the operation and the optional queue (if a subscription).
|
|
241
|
+
*
|
|
242
|
+
* This error is terminal for a subscription.
|
|
243
|
+
*/
|
|
244
|
+
export class PermissionViolationError extends Error {
|
|
245
|
+
operation: "publish" | "subscription";
|
|
246
|
+
subject: string;
|
|
247
|
+
queue?: string;
|
|
248
|
+
|
|
249
|
+
constructor(
|
|
250
|
+
message: string,
|
|
251
|
+
operation: "publish" | "subscription",
|
|
252
|
+
subject: string,
|
|
253
|
+
queue?: string,
|
|
254
|
+
options?: ErrorOptions,
|
|
255
|
+
) {
|
|
256
|
+
super(message, options);
|
|
257
|
+
this.name = "PermissionViolationError";
|
|
258
|
+
this.operation = operation;
|
|
259
|
+
this.subject = subject;
|
|
260
|
+
this.queue = queue;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
static parse(s: string): PermissionViolationError | null {
|
|
264
|
+
const t = s ? s.toLowerCase() : "";
|
|
265
|
+
if (t.indexOf("permissions violation") === -1) {
|
|
266
|
+
return null;
|
|
267
|
+
}
|
|
268
|
+
let operation: "publish" | "subscription" = "publish";
|
|
269
|
+
let subject = "";
|
|
270
|
+
let queue: string | undefined = undefined;
|
|
271
|
+
const m = s.match(/(Publish|Subscription) to "(\S+)"/);
|
|
272
|
+
if (m) {
|
|
273
|
+
operation = m[1].toLowerCase() as "publish" | "subscription";
|
|
274
|
+
subject = m[2];
|
|
275
|
+
if (operation === "subscription") {
|
|
276
|
+
const qm = s.match(/using queue "(\S+)"/);
|
|
277
|
+
if (qm) {
|
|
278
|
+
queue = qm[1];
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
return new PermissionViolationError(s, operation, subject, queue);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
export const errors = {
|
|
287
|
+
AuthorizationError,
|
|
288
|
+
ClosedConnectionError,
|
|
289
|
+
ConnectionError,
|
|
290
|
+
DrainingConnectionError,
|
|
291
|
+
InvalidArgumentError,
|
|
292
|
+
InvalidOperationError,
|
|
293
|
+
InvalidSubjectError,
|
|
294
|
+
NoRespondersError,
|
|
295
|
+
PermissionViolationError,
|
|
296
|
+
ProtocolError,
|
|
297
|
+
RequestError,
|
|
298
|
+
TimeoutError,
|
|
299
|
+
UserAuthenticationExpiredError,
|
|
300
|
+
};
|
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2020-2023 The NATS Authors
|
|
3
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
* you may not use this file except in compliance with the License.
|
|
5
|
+
* You may obtain a copy of the License at
|
|
6
|
+
*
|
|
7
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
*
|
|
9
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
* See the License for the specific language governing permissions and
|
|
13
|
+
* limitations under the License.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
// Heavily inspired by Golang's https://golang.org/src/net/http/header.go
|
|
17
|
+
|
|
18
|
+
import { TD, TE } from "./encoders.ts";
|
|
19
|
+
import type { MsgHdrs } from "./core.ts";
|
|
20
|
+
import { Match } from "./core.ts";
|
|
21
|
+
import { InvalidArgumentError } from "./errors.ts";
|
|
22
|
+
|
|
23
|
+
// https://www.ietf.org/rfc/rfc822.txt
|
|
24
|
+
// 3.1.2. STRUCTURE OF HEADER FIELDS
|
|
25
|
+
//
|
|
26
|
+
// Once a field has been unfolded, it may be viewed as being com-
|
|
27
|
+
// posed of a field-name followed by a colon (":"), followed by a
|
|
28
|
+
// field-body, and terminated by a carriage-return/line-feed.
|
|
29
|
+
// The field-name must be composed of printable ASCII characters
|
|
30
|
+
// (i.e., characters that have values between 33. and 126.,
|
|
31
|
+
// decimal, except colon). The field-body may be composed of any
|
|
32
|
+
// ASCII characters, except CR or LF. (While CR and/or LF may be
|
|
33
|
+
// present in the actual text, they are removed by the action of
|
|
34
|
+
// unfolding the field.)
|
|
35
|
+
export function canonicalMIMEHeaderKey(k: string): string {
|
|
36
|
+
const a = 97;
|
|
37
|
+
const A = 65;
|
|
38
|
+
const Z = 90;
|
|
39
|
+
const z = 122;
|
|
40
|
+
const dash = 45;
|
|
41
|
+
const colon = 58;
|
|
42
|
+
const start = 33;
|
|
43
|
+
const end = 126;
|
|
44
|
+
const toLower = a - A;
|
|
45
|
+
|
|
46
|
+
let upper = true;
|
|
47
|
+
const buf: number[] = new Array(k.length);
|
|
48
|
+
for (let i = 0; i < k.length; i++) {
|
|
49
|
+
let c = k.charCodeAt(i);
|
|
50
|
+
if (c === colon || c < start || c > end) {
|
|
51
|
+
throw InvalidArgumentError.format(
|
|
52
|
+
"header",
|
|
53
|
+
`'${k[i]}' is not a valid character in a header name`,
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
if (upper && a <= c && c <= z) {
|
|
57
|
+
c -= toLower;
|
|
58
|
+
} else if (!upper && A <= c && c <= Z) {
|
|
59
|
+
c += toLower;
|
|
60
|
+
}
|
|
61
|
+
buf[i] = c;
|
|
62
|
+
upper = c == dash;
|
|
63
|
+
}
|
|
64
|
+
return String.fromCharCode(...buf);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function headers(code = 0, description = ""): MsgHdrs {
|
|
68
|
+
if (code === 0 && description !== "") {
|
|
69
|
+
throw InvalidArgumentError.format("code", "is required");
|
|
70
|
+
} else if (code > 0 && description === "") {
|
|
71
|
+
throw InvalidArgumentError.format("description", "is required");
|
|
72
|
+
}
|
|
73
|
+
return new MsgHdrsImpl(code, description);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const HEADER = "NATS/1.0";
|
|
77
|
+
|
|
78
|
+
export class MsgHdrsImpl implements MsgHdrs {
|
|
79
|
+
_code: number;
|
|
80
|
+
headers: Map<string, string[]>;
|
|
81
|
+
_description: string;
|
|
82
|
+
|
|
83
|
+
constructor(code = 0, description = "") {
|
|
84
|
+
this._code = code;
|
|
85
|
+
this._description = description;
|
|
86
|
+
this.headers = new Map();
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
[Symbol.iterator](): IterableIterator<[string, string[]]> {
|
|
90
|
+
return this.headers.entries();
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
size(): number {
|
|
94
|
+
return this.headers.size;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
equals(mh: MsgHdrsImpl): boolean {
|
|
98
|
+
if (
|
|
99
|
+
mh && this.headers.size === mh.headers.size &&
|
|
100
|
+
this._code === mh._code
|
|
101
|
+
) {
|
|
102
|
+
for (const [k, v] of this.headers) {
|
|
103
|
+
const a = mh.values(k);
|
|
104
|
+
if (v.length !== a.length) {
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
const vv = [...v].sort();
|
|
108
|
+
const aa = [...a].sort();
|
|
109
|
+
for (let i = 0; i < vv.length; i++) {
|
|
110
|
+
if (vv[i] !== aa[i]) {
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
static decode(a: Uint8Array): MsgHdrsImpl {
|
|
121
|
+
const mh = new MsgHdrsImpl();
|
|
122
|
+
const s = TD.decode(a);
|
|
123
|
+
const lines = s.split("\r\n");
|
|
124
|
+
const h = lines[0];
|
|
125
|
+
if (h !== HEADER) {
|
|
126
|
+
// malformed headers could add extra space without adding a code or description
|
|
127
|
+
let str = h.replace(HEADER, "").trim();
|
|
128
|
+
if (str.length > 0) {
|
|
129
|
+
mh._code = parseInt(str, 10);
|
|
130
|
+
if (isNaN(mh._code)) {
|
|
131
|
+
mh._code = 0;
|
|
132
|
+
}
|
|
133
|
+
const scode = mh._code.toString();
|
|
134
|
+
str = str.replace(scode, "");
|
|
135
|
+
mh._description = str.trim();
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
if (lines.length >= 1) {
|
|
139
|
+
lines.slice(1).map((s) => {
|
|
140
|
+
if (s) {
|
|
141
|
+
const idx = s.indexOf(":");
|
|
142
|
+
if (idx > -1) {
|
|
143
|
+
const k = s.slice(0, idx);
|
|
144
|
+
const v = s.slice(idx + 1).trim();
|
|
145
|
+
mh.append(k, v);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
return mh;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
toString(): string {
|
|
154
|
+
if (this.headers.size === 0 && this._code === 0) {
|
|
155
|
+
return "";
|
|
156
|
+
}
|
|
157
|
+
let s = HEADER;
|
|
158
|
+
if (this._code > 0 && this._description !== "") {
|
|
159
|
+
s += ` ${this._code} ${this._description}`;
|
|
160
|
+
}
|
|
161
|
+
for (const [k, v] of this.headers) {
|
|
162
|
+
for (let i = 0; i < v.length; i++) {
|
|
163
|
+
s = `${s}\r\n${k}: ${v[i]}`;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return `${s}\r\n\r\n`;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
encode(): Uint8Array {
|
|
170
|
+
return TE.encode(this.toString());
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
static validHeaderValue(k: string): string {
|
|
174
|
+
const inv = /[\r\n]/;
|
|
175
|
+
if (inv.test(k)) {
|
|
176
|
+
throw InvalidArgumentError.format(
|
|
177
|
+
"header",
|
|
178
|
+
"values cannot contain \\r or \\n",
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
return k.trim();
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
keys(): string[] {
|
|
185
|
+
const keys = [];
|
|
186
|
+
for (const sk of this.headers.keys()) {
|
|
187
|
+
keys.push(sk);
|
|
188
|
+
}
|
|
189
|
+
return keys;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
findKeys(k: string, match: Match = Match.Exact): string[] {
|
|
193
|
+
const keys = this.keys();
|
|
194
|
+
switch (match) {
|
|
195
|
+
case Match.Exact:
|
|
196
|
+
return keys.filter((v) => {
|
|
197
|
+
return v === k;
|
|
198
|
+
});
|
|
199
|
+
case Match.CanonicalMIME:
|
|
200
|
+
k = canonicalMIMEHeaderKey(k);
|
|
201
|
+
return keys.filter((v) => {
|
|
202
|
+
return v === k;
|
|
203
|
+
});
|
|
204
|
+
default: {
|
|
205
|
+
const lci = k.toLowerCase();
|
|
206
|
+
return keys.filter((v) => {
|
|
207
|
+
return lci === v.toLowerCase();
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
get(k: string, match = Match.Exact): string {
|
|
214
|
+
const keys = this.findKeys(k, match);
|
|
215
|
+
if (keys.length) {
|
|
216
|
+
const v = this.headers.get(keys[0]);
|
|
217
|
+
if (v) {
|
|
218
|
+
return Array.isArray(v) ? v[0] : v;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
return "";
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
last(k: string, match = Match.Exact): string {
|
|
225
|
+
const keys = this.findKeys(k, match);
|
|
226
|
+
if (keys.length) {
|
|
227
|
+
const v = this.headers.get(keys[0]);
|
|
228
|
+
if (v) {
|
|
229
|
+
return Array.isArray(v) ? v[v.length - 1] : v;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
return "";
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
has(k: string, match: Match = Match.Exact): boolean {
|
|
236
|
+
return this.findKeys(k, match).length > 0;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
set(k: string, v: string, match: Match = Match.Exact): void {
|
|
240
|
+
this.delete(k, match);
|
|
241
|
+
this.append(k, v, match);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
append(k: string, v: string, match: Match = Match.Exact): void {
|
|
245
|
+
// validate the key
|
|
246
|
+
const ck = canonicalMIMEHeaderKey(k);
|
|
247
|
+
if (match === Match.CanonicalMIME) {
|
|
248
|
+
k = ck;
|
|
249
|
+
}
|
|
250
|
+
// if we get non-sensical ignores/etc, we should try
|
|
251
|
+
// to do the right thing and use the first key that matches
|
|
252
|
+
const keys = this.findKeys(k, match);
|
|
253
|
+
k = keys.length > 0 ? keys[0] : k;
|
|
254
|
+
|
|
255
|
+
const value = MsgHdrsImpl.validHeaderValue(v);
|
|
256
|
+
let a = this.headers.get(k);
|
|
257
|
+
if (!a) {
|
|
258
|
+
a = [];
|
|
259
|
+
this.headers.set(k, a);
|
|
260
|
+
}
|
|
261
|
+
a.push(value);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
values(k: string, match: Match = Match.Exact): string[] {
|
|
265
|
+
const buf: string[] = [];
|
|
266
|
+
const keys = this.findKeys(k, match);
|
|
267
|
+
keys.forEach((v) => {
|
|
268
|
+
const values = this.headers.get(v);
|
|
269
|
+
if (values) {
|
|
270
|
+
buf.push(...values);
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
return buf;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
delete(k: string, match: Match = Match.Exact): void {
|
|
277
|
+
const keys = this.findKeys(k, match);
|
|
278
|
+
keys.forEach((v) => {
|
|
279
|
+
this.headers.delete(v);
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
get hasError(): boolean {
|
|
284
|
+
return this._code >= 300;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
get status(): string {
|
|
288
|
+
return `${this._code} ${this._description}`.trim();
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
toRecord(): Record<string, string[]> {
|
|
292
|
+
const data = {} as Record<string, string[]>;
|
|
293
|
+
this.keys().forEach((v) => {
|
|
294
|
+
data[v] = this.values(v);
|
|
295
|
+
});
|
|
296
|
+
return data;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
get code(): number {
|
|
300
|
+
return this._code;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
get description(): string {
|
|
304
|
+
return this._description;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
static fromRecord(r: Record<string, string[]>): MsgHdrs {
|
|
308
|
+
const h = new MsgHdrsImpl();
|
|
309
|
+
for (const k in r) {
|
|
310
|
+
const v = r[k];
|
|
311
|
+
h.headers.set(k, Array.isArray(v) ? v : [`${v}`]);
|
|
312
|
+
}
|
|
313
|
+
return h;
|
|
314
|
+
}
|
|
315
|
+
}
|