@yuneta/gobj-js 7.26.24 → 7.26.25
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/README.md +34 -6
- package/dist/gobj-js.cjs.js +190 -18
- package/dist/gobj-js.cjs.js.map +1 -1
- package/dist/gobj-js.cjs.min.js +1952 -1847
- package/dist/gobj-js.cjs.min.js.map +1 -1
- package/dist/gobj-js.es.js +190 -18
- package/dist/gobj-js.es.js.map +1 -1
- package/dist/gobj-js.es.min.js +1731 -1626
- package/dist/gobj-js.es.min.js.map +1 -1
- package/dist/gobj-js.iife.js +190 -18
- package/dist/gobj-js.iife.js.map +1 -1
- package/dist/gobj-js.iife.min.js +1683 -1578
- package/dist/gobj-js.iife.min.js.map +1 -1
- package/dist/gobj-js.umd.js +190 -18
- package/dist/gobj-js.umd.js.map +1 -1
- package/dist/gobj-js.umd.min.js +1683 -1578
- package/dist/gobj-js.umd.min.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -302,12 +302,31 @@ gobj_write_bool_attr(gobj, "selectable", kw.selectable === true); // false
|
|
|
302
302
|
|
|
303
303
|
A value with no json text (a circular object such as a gobj or a DOM node, a
|
|
304
304
|
`BigInt`) given to a string attribute is refused the same way, and is never
|
|
305
|
-
thrown (7.26.24).
|
|
305
|
+
thrown (7.26.24). So is a value that is not json ALL THE WAY DOWN given to a
|
|
306
|
+
string, a `DTP_DICT` or a `DTP_LIST` (7.26.25): a cycle anywhere inside, a
|
|
307
|
+
`BigInt` inside (`[1n]`), a `Map`, a `Set`, a `Date`, a typed array, an instance
|
|
308
|
+
of a class. They were written, and burst (or turned into `"{}"`) when the value
|
|
309
|
+
was serialized, far from the write. A key whose value is `undefined` or a
|
|
310
|
+
function is dropped, as `JSON.stringify()` drops it:
|
|
311
|
+
|
|
312
|
+
```javascript
|
|
313
|
+
gobj_write_attr(gobj, "settings", {a: 1, b: undefined}); // 0: b goes when serialized
|
|
314
|
+
gobj_write_attr(gobj, "settings", new Map([["a", 1]])); // -1, ERROR "a value with no json text"
|
|
315
|
+
gobj_write_str_attr(gobj, "name", new Map()); // -1: it was the text "{}"
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
A `DTP_JSON` attribute is not checked: it takes any value, as before.
|
|
319
|
+
|
|
320
|
+
A json TEXT is read as the C parser reads it: one that C
|
|
306
321
|
refuses is refused in JS too -- an escaped NUL (`"\u0000"`), a lone surrogate,
|
|
307
322
|
an integer out of int64, a real that overflows (`1e309`; to a `DTP_JSON` it is
|
|
308
|
-
the text `"1e309"`, not `Infinity`)
|
|
309
|
-
|
|
310
|
-
|
|
323
|
+
the text `"1e309"`, not `Infinity`), a text nested deeper than 2048 levels (a
|
|
324
|
+
scalar is a level too; 7.26.25). A number beyond int64 is "a real" to a dict,
|
|
325
|
+
as in C (`1e20`), and `9223372036854775807` is taken by an integer (JSON.parse()
|
|
326
|
+
makes it 2^63, kept as such). Two differences remain, because a JS number
|
|
327
|
+
cannot carry them: an integer beyond 2^53 is rounded, and `2.0` (and `-0.0`) is
|
|
328
|
+
the integer (see `tests/fixtures/attr_types_parity.json`, which the C test
|
|
329
|
+
`tests/c/attr_types` of yunetas reads too).
|
|
311
330
|
|
|
312
331
|
### Events & Pub-Sub
|
|
313
332
|
|
|
@@ -1014,7 +1033,14 @@ Key attributes: `url`, `jwt`, `wanted_yuno_role`, `wanted_yuno_name`, `wanted_yu
|
|
|
1014
1033
|
|
|
1015
1034
|
Its events (`EV_ON_OPEN`, `EV_ON_CLOSE`, `EV_ON_OPEN_ERROR`, `EV_ON_ID_NAK` and every message that names no service) are PUBLISHED: to the `subscriber` attr, else, for a pure child, to its parent, which it subscribes in `mt_create` — one subscription, as the C transport. Up to 7.26.21 a pure child SENT three of them to its parent and subscribed nobody, so its parent never heard `EV_ON_CLOSE` / `EV_ON_OPEN_ERROR`: a host of a pure child must declare both in its FSM (BREAKING in 7.26.22).
|
|
1016
1035
|
|
|
1017
|
-
A message that names no service is published only when its event is declared by some registered gclass (`gobj_find_event_type()`); any other is dropped with a WARNING, its name masked
|
|
1036
|
+
A message that names no service is published only when its event is declared PUBLIC by some registered gclass (`gobj_find_event_type(name, EVF_PUBLIC_EVENT)`, as C's `gclass_find_public_event()`; 7.26.24 took any declaration, 7.26.25 requires it public); any other is dropped with a WARNING, its name masked. The events the transport says itself (`EV_ON_OPEN`, `EV_ON_CLOSE`, `EV_ON_OPEN_ERROR`, `EV_ON_ID_NAK`, `EV_TIMEOUT`, `EV_ON_MESSAGE`, and `EV_IDENTITY_CARD_ACK` in session) are never taken from the server, in any case of letters and whatever service the message names: a WARNING, dropped (7.26.25). A backend event an app receives must therefore be declared `EVF_PUBLIC_EVENT` in the gclass that receives it:
|
|
1037
|
+
|
|
1038
|
+
```javascript
|
|
1039
|
+
const event_types = [
|
|
1040
|
+
["EV_MT_COMMAND_ANSWER", event_flag_t.EVF_PUBLIC_EVENT],
|
|
1041
|
+
["EV_REALTIME_ALARM", event_flag_t.EVF_PUBLIC_EVENT],
|
|
1042
|
+
];
|
|
1043
|
+
``` What the server gets wrong is a WARNING, never an ERROR. Before the session only `EV_IDENTITY_CARD_ACK` is taken; a `dst_role` or `dst_yuno` that is not this yuno's closes the channel too (an empty one is legal: anybody's), as the C transport does; an event that the named service does not take as public is dropped (7.26.24). A channel closed for a fault of the server is closed with **4002**, not 1000 (a browser may send only 1000 or 3000-4999, so 1002 cannot be used), so `EV_ON_CLOSE` says the session was cut (7.26.25).
|
|
1018
1044
|
|
|
1019
1045
|
`EV_ON_CLOSE` carries `code`, `reason` (masked) and `was_clean`, and so does `EV_ON_OPEN_ERROR` (7.26.24). A session that ends cleanly (a stop, a logout, the server closing with 1000 or 1001) is logged by nobody; a session CUT -- not clean, or any other code (1006, 1008, 1011) -- is one WARNING with the code and the reason masked:
|
|
1020
1046
|
|
|
@@ -1038,7 +1064,9 @@ naming services it does not host.
|
|
|
1038
1064
|
what breaks lockstep, so N links that dropped together do not stampede a backend
|
|
1039
1065
|
that is just coming back. The backoff resets when a session is actually reached,
|
|
1040
1066
|
and on `mt_start` — a deliberate reconnect never inherits a previous run's
|
|
1041
|
-
penalty.
|
|
1067
|
+
penalty. A session the transport closes for a fault of the server (4002) does not
|
|
1068
|
+
count as reached: the backoff goes on from where it was (7.26.25), so a server
|
|
1069
|
+
that breaks every session is not reconnected at the first delay for ever. For the old fixed-interval behaviour set `timeout_retry_max` equal to
|
|
1042
1070
|
`timeout_retry`.
|
|
1043
1071
|
|
|
1044
1072
|
---
|
package/dist/gobj-js.cjs.js
CHANGED
|
@@ -870,12 +870,48 @@ var JSON_NUMBER_TOKEN = /-?(0|[1-9][0-9]*)(\.[0-9]+)?([eE][+-]?[0-9]+)?/y;
|
|
|
870
870
|
function is_surrogate_pair(high, low) {
|
|
871
871
|
return high >= 55296 && high <= 56319 && low >= 56320 && low <= 57343;
|
|
872
872
|
}
|
|
873
|
+
var JSON_MAX_DEPTH = 2048;
|
|
873
874
|
function json_text_refused_by_c(s) {
|
|
874
875
|
const n = s.length;
|
|
875
876
|
let i = 0;
|
|
877
|
+
let depth = 0;
|
|
878
|
+
const in_object = [];
|
|
879
|
+
let key_next = false;
|
|
876
880
|
while (i < n) {
|
|
877
881
|
const c = s.charCodeAt(i);
|
|
882
|
+
if (c === 123 || c === 91) {
|
|
883
|
+
depth++;
|
|
884
|
+
if (depth > JSON_MAX_DEPTH) return true;
|
|
885
|
+
in_object.push(c === 123);
|
|
886
|
+
key_next = c === 123;
|
|
887
|
+
i++;
|
|
888
|
+
continue;
|
|
889
|
+
}
|
|
890
|
+
if (c === 125 || c === 93) {
|
|
891
|
+
depth--;
|
|
892
|
+
in_object.pop();
|
|
893
|
+
key_next = false;
|
|
894
|
+
i++;
|
|
895
|
+
continue;
|
|
896
|
+
}
|
|
897
|
+
if (c === 44) {
|
|
898
|
+
key_next = in_object.length > 0 && in_object[in_object.length - 1];
|
|
899
|
+
i++;
|
|
900
|
+
continue;
|
|
901
|
+
}
|
|
902
|
+
if (c === 58) {
|
|
903
|
+
key_next = false;
|
|
904
|
+
i++;
|
|
905
|
+
continue;
|
|
906
|
+
}
|
|
907
|
+
if (c === 116 || c === 102 || c === 110) {
|
|
908
|
+
if (depth + 1 > JSON_MAX_DEPTH) return true;
|
|
909
|
+
i += c === 102 ? 5 : 4;
|
|
910
|
+
continue;
|
|
911
|
+
}
|
|
878
912
|
if (c === 34) {
|
|
913
|
+
if (!key_next && depth + 1 > JSON_MAX_DEPTH) return true;
|
|
914
|
+
key_next = false;
|
|
879
915
|
i++;
|
|
880
916
|
while (i < n) {
|
|
881
917
|
const d = s.charCodeAt(i);
|
|
@@ -908,6 +944,7 @@ function json_text_refused_by_c(s) {
|
|
|
908
944
|
continue;
|
|
909
945
|
}
|
|
910
946
|
if (c === 45 || c >= 48 && c <= 57) {
|
|
947
|
+
if (depth + 1 > JSON_MAX_DEPTH) return true;
|
|
911
948
|
JSON_NUMBER_TOKEN.lastIndex = i;
|
|
912
949
|
const m = JSON_NUMBER_TOKEN.exec(s);
|
|
913
950
|
if (!m) return true;
|
|
@@ -946,7 +983,7 @@ function shape_of_value(value) {
|
|
|
946
983
|
if (is_array(value)) return "an array";
|
|
947
984
|
if (is_object(value)) return "an object";
|
|
948
985
|
if (is_string(value)) return REFUSED_TEXT;
|
|
949
|
-
if (typeof value === "number") return Number.isInteger(value) ? "an integer" : "a real";
|
|
986
|
+
if (typeof value === "number") return Number.isInteger(value) && Math.abs(value) <= 0x8000000000000000 ? "an integer" : "a real";
|
|
950
987
|
if (typeof value === "boolean") return "a boolean";
|
|
951
988
|
return "null";
|
|
952
989
|
}
|
|
@@ -967,7 +1004,7 @@ function number_as_type(type, value) {
|
|
|
967
1004
|
value: value ? 1 : 0,
|
|
968
1005
|
refused: null
|
|
969
1006
|
};
|
|
970
|
-
if (!(value >= -0x8000000000000000 && value
|
|
1007
|
+
if (!(value >= -0x8000000000000000 && value <= 0x8000000000000000)) return {
|
|
971
1008
|
value: void 0,
|
|
972
1009
|
refused: "a number out of its range"
|
|
973
1010
|
};
|
|
@@ -1128,6 +1165,10 @@ function command_parameter_from_json(type, value) {
|
|
|
1128
1165
|
refused: shape_of_value(value)
|
|
1129
1166
|
};
|
|
1130
1167
|
if (DTP_IS_NUMBER(type)) return number_as_type(type, value);
|
|
1168
|
+
if (!is_json_value(value)) return {
|
|
1169
|
+
value: void 0,
|
|
1170
|
+
refused: NO_JSON_TEXT
|
|
1171
|
+
};
|
|
1131
1172
|
return {
|
|
1132
1173
|
value,
|
|
1133
1174
|
refused: null
|
|
@@ -1146,8 +1187,34 @@ var TYPES_READ = [
|
|
|
1146
1187
|
data_type_t.DTP_LIST,
|
|
1147
1188
|
data_type_t.DTP_DICT
|
|
1148
1189
|
];
|
|
1190
|
+
var LEAVE_NODE = Symbol("leave");
|
|
1191
|
+
function is_json_value(value) {
|
|
1192
|
+
const on_path = /* @__PURE__ */ new Set();
|
|
1193
|
+
const stack = [value];
|
|
1194
|
+
while (stack.length) {
|
|
1195
|
+
const v = stack.pop();
|
|
1196
|
+
if (v === LEAVE_NODE) {
|
|
1197
|
+
on_path.delete(stack.pop());
|
|
1198
|
+
continue;
|
|
1199
|
+
}
|
|
1200
|
+
if (typeof v === "bigint") return false;
|
|
1201
|
+
if (v === null || typeof v !== "object") continue;
|
|
1202
|
+
if (on_path.has(v)) return false;
|
|
1203
|
+
const array = Array.isArray(v);
|
|
1204
|
+
if (!array) {
|
|
1205
|
+
const proto = Object.getPrototypeOf(v);
|
|
1206
|
+
if (proto !== Object.prototype && proto !== null) return false;
|
|
1207
|
+
}
|
|
1208
|
+
on_path.add(v);
|
|
1209
|
+
stack.push(v, LEAVE_NODE);
|
|
1210
|
+
if (array) for (let i = 0; i < v.length; i++) stack.push(v[i]);
|
|
1211
|
+
else for (const key of Object.keys(v)) stack.push(v[key]);
|
|
1212
|
+
}
|
|
1213
|
+
return true;
|
|
1214
|
+
}
|
|
1149
1215
|
var NO_JSON_TEXT = "a value with no json text";
|
|
1150
1216
|
function json_text_of(value) {
|
|
1217
|
+
if (!is_json_value(value)) return;
|
|
1151
1218
|
try {
|
|
1152
1219
|
const s = JSON.stringify(value);
|
|
1153
1220
|
return is_string(s) ? s : void 0;
|
|
@@ -4106,6 +4173,35 @@ function is_secret_name_tail(str, from, to, names) {
|
|
|
4106
4173
|
}
|
|
4107
4174
|
return secret_name_check(str, i, to, true, by_segments);
|
|
4108
4175
|
}
|
|
4176
|
+
function param_name_tail_from(str, from, to) {
|
|
4177
|
+
let bytes = 0;
|
|
4178
|
+
let i = to;
|
|
4179
|
+
while (i > from) {
|
|
4180
|
+
const code = str.charCodeAt(i - 1);
|
|
4181
|
+
let n = 3;
|
|
4182
|
+
let units = 1;
|
|
4183
|
+
if (code < 128) n = 1;
|
|
4184
|
+
else if (code < 2048) n = 2;
|
|
4185
|
+
else if (code >= 56320 && code <= 57343 && i - 2 >= from) {
|
|
4186
|
+
const high = str.charCodeAt(i - 2);
|
|
4187
|
+
if (high >= 55296 && high <= 56319) {
|
|
4188
|
+
n = 4;
|
|
4189
|
+
units = 2;
|
|
4190
|
+
}
|
|
4191
|
+
}
|
|
4192
|
+
if (bytes + n > MASK_MAX_NAME) return -1;
|
|
4193
|
+
if (code === 38 || code === 63 || code === 59) return i;
|
|
4194
|
+
bytes += n;
|
|
4195
|
+
i -= units;
|
|
4196
|
+
}
|
|
4197
|
+
return -1;
|
|
4198
|
+
}
|
|
4199
|
+
function is_param_name_secret(str, from, to, names) {
|
|
4200
|
+
const tail = names === NAMES_NOW ? param_name_tail_from(str, from, to) : -1;
|
|
4201
|
+
if (tail < 0) return is_secret_name_tail(str, from, to, names);
|
|
4202
|
+
if (is_secret_name_tail(str, from, to, NAMES_7_26_9)) return true;
|
|
4203
|
+
return is_secret_name_tail(str, from, to, NAMES_NOW) && secret_name_check(str, tail, to, false, true);
|
|
4204
|
+
}
|
|
4109
4205
|
/************************************************************
|
|
4110
4206
|
* Where an unquoted secret value that begins at `p` ends: at
|
|
4111
4207
|
* the end, at the quote that closes an outer quoted value, or
|
|
@@ -4403,7 +4499,7 @@ function mark_params(str, value_is_secret, reading, marks, judged) {
|
|
|
4403
4499
|
}
|
|
4404
4500
|
const name_from = word;
|
|
4405
4501
|
word = p + 1;
|
|
4406
|
-
const secret =
|
|
4502
|
+
const secret = is_param_name_secret(str, name_from, p, names);
|
|
4407
4503
|
if (secret && names === NAMES_NOW && !not_7_26_9 && !is_secret_name_tail(str, name_from, p, NAMES_7_26_9)) not_7_26_9 = true;
|
|
4408
4504
|
if (!(secret || value_is_secret && p - name_from === 5 && ascii_case_equal(str, name_from, "value"))) {
|
|
4409
4505
|
if (judged && query_name_from(str, p) === name_from) judged[p] = JUDGED_NOT_SECRET;
|
|
@@ -4590,7 +4686,7 @@ function mark_labels(str, marks) {
|
|
|
4590
4686
|
p++;
|
|
4591
4687
|
continue;
|
|
4592
4688
|
}
|
|
4593
|
-
if (!
|
|
4689
|
+
if (!is_param_name_secret(str, word, p, NAMES_NOW)) {
|
|
4594
4690
|
p++;
|
|
4595
4691
|
word = p;
|
|
4596
4692
|
continue;
|
|
@@ -4619,6 +4715,50 @@ function inline_value_is_secret(str, names) {
|
|
|
4619
4715
|
}
|
|
4620
4716
|
return false;
|
|
4621
4717
|
}
|
|
4718
|
+
function mark_inline_rules_of_now(str, marks) {
|
|
4719
|
+
const value_is_secret = inline_value_is_secret(str, NAMES_NOW);
|
|
4720
|
+
const judged = new Uint8Array(str.length);
|
|
4721
|
+
mark_params(str, value_is_secret, READ_NOW, marks, judged);
|
|
4722
|
+
mark_query_params(str, marks, judged);
|
|
4723
|
+
mark_url_passwords(str, marks);
|
|
4724
|
+
mark_shapes(str, marks);
|
|
4725
|
+
mark_labels(str, marks);
|
|
4726
|
+
}
|
|
4727
|
+
var MASK_MAX_DECODINGS = 2;
|
|
4728
|
+
function encoded_ascii_at(str, p) {
|
|
4729
|
+
if (p + 3 > str.length || str.charCodeAt(p) !== 37) return -1;
|
|
4730
|
+
const hi = hex_digit_value(str.charCodeAt(p + 1));
|
|
4731
|
+
const lo = hi >= 0 ? hex_digit_value(str.charCodeAt(p + 2)) : -1;
|
|
4732
|
+
if (lo < 0) return -1;
|
|
4733
|
+
const c = hi * 16 + lo;
|
|
4734
|
+
return c > 0 && c < 128 ? c : -1;
|
|
4735
|
+
}
|
|
4736
|
+
function mark_decoded_view(str, marks, decodings) {
|
|
4737
|
+
if (decodings >= MASK_MAX_DECODINGS) return;
|
|
4738
|
+
const pieces = [];
|
|
4739
|
+
let last = 0;
|
|
4740
|
+
let p = str.indexOf("%");
|
|
4741
|
+
while (p >= 0) {
|
|
4742
|
+
const c = encoded_ascii_at(str, p);
|
|
4743
|
+
if (c >= 0) {
|
|
4744
|
+
pieces.push(str.substring(last, p), String.fromCharCode(c));
|
|
4745
|
+
last = p + 3;
|
|
4746
|
+
p = str.indexOf("%", last);
|
|
4747
|
+
} else p = str.indexOf("%", p + 1);
|
|
4748
|
+
}
|
|
4749
|
+
if (pieces.length === 0) return;
|
|
4750
|
+
pieces.push(str.substring(last));
|
|
4751
|
+
const view = pieces.join("");
|
|
4752
|
+
const view_marks = new Uint8Array(view.length);
|
|
4753
|
+
mark_inline_rules_of_now(view, view_marks);
|
|
4754
|
+
mark_decoded_view(view, view_marks, decodings + 1);
|
|
4755
|
+
let q = 0;
|
|
4756
|
+
for (let i = 0; i < view.length; i++) {
|
|
4757
|
+
const n = encoded_ascii_at(str, q) >= 0 ? 3 : 1;
|
|
4758
|
+
if (view_marks[i]) mark_range(marks, q, q + n);
|
|
4759
|
+
q += n;
|
|
4760
|
+
}
|
|
4761
|
+
}
|
|
4622
4762
|
function mark_inline_secrets(str, marks) {
|
|
4623
4763
|
const value_is_secret = inline_value_is_secret(str, NAMES_NOW);
|
|
4624
4764
|
const value_is_secret_7_26_9 = inline_value_is_secret(str, NAMES_7_26_9);
|
|
@@ -4629,6 +4769,7 @@ function mark_inline_secrets(str, marks) {
|
|
|
4629
4769
|
mark_url_passwords(str, marks);
|
|
4630
4770
|
mark_shapes(str, marks);
|
|
4631
4771
|
mark_labels(str, marks);
|
|
4772
|
+
mark_decoded_view(str, marks, 0);
|
|
4632
4773
|
}
|
|
4633
4774
|
/************************************************************
|
|
4634
4775
|
* A text (a command line) with the secrets that can be told
|
|
@@ -4640,13 +4781,14 @@ function mark_inline_secrets(str, marks) {
|
|
|
4640
4781
|
* same for a "name: value", its name in quotes or not (a json, a
|
|
4641
4782
|
* header, a label: "password": x, Authorization: Basic x, bearer:
|
|
4642
4783
|
* tok); and a JWT or the credential after "Bearer " or "Basic ",
|
|
4643
|
-
* wherever they are, whatever names them
|
|
4644
|
-
*
|
|
4645
|
-
*
|
|
4784
|
+
* wherever they are, whatever names them; in a URL or a query
|
|
4785
|
+
* %-encoded inside a value, the same read decoded (mark_decoded_view()).
|
|
4786
|
+
* Each rule judges the text as it came. Return null when there was
|
|
4787
|
+
* nothing to mask. As the C kernel's mask_secrets_inline().
|
|
4646
4788
|
************************************************************/
|
|
4647
4789
|
function mask_secrets_inline(str) {
|
|
4648
4790
|
if (typeof str !== "string") return null;
|
|
4649
|
-
if (str.indexOf("=") < 0 && str.indexOf(":") < 0 && str.indexOf("eyJ") < 0 && !/bearer|basic/i.test(str)) return null;
|
|
4791
|
+
if (str.indexOf("=") < 0 && str.indexOf(":") < 0 && str.indexOf("%") < 0 && str.indexOf("eyJ") < 0 && !/bearer|basic/i.test(str)) return null;
|
|
4650
4792
|
if (str.length > MASK_MAX_BYTES) return MASK_TOO_LARGE;
|
|
4651
4793
|
const marks = new Uint8Array(str.length);
|
|
4652
4794
|
mark_inline_secrets(str, marks);
|
|
@@ -8409,7 +8551,8 @@ var PRIVATE_DATA = {
|
|
|
8409
8551
|
inform_on_close: false,
|
|
8410
8552
|
websocket: null,
|
|
8411
8553
|
inside_on_open: false,
|
|
8412
|
-
retry_ms: 0
|
|
8554
|
+
retry_ms: 0,
|
|
8555
|
+
retry_ms_before_session: 0
|
|
8413
8556
|
};
|
|
8414
8557
|
var __gclass__ = null;
|
|
8415
8558
|
/******************************
|
|
@@ -8460,6 +8603,7 @@ function mt_writing(gobj, path) {
|
|
|
8460
8603
|
function mt_start(gobj) {
|
|
8461
8604
|
let priv = gobj.priv;
|
|
8462
8605
|
priv.retry_ms = 0;
|
|
8606
|
+
priv.retry_ms_before_session = 0;
|
|
8463
8607
|
if (gobj_current_state(gobj) !== "ST_DISCONNECTED") {
|
|
8464
8608
|
gobj_send_event(gobj, "EV_ON_CLOSE", {
|
|
8465
8609
|
url: priv.url,
|
|
@@ -8659,6 +8803,26 @@ function close_websocket(gobj, code = 1e3, reason = "") {
|
|
|
8659
8803
|
}
|
|
8660
8804
|
}
|
|
8661
8805
|
/***************************************************************
|
|
8806
|
+
* Close the channel for a fault of the server: a message for a role
|
|
8807
|
+
* or a yuno that is not this one, an event out of its place.
|
|
8808
|
+
*
|
|
8809
|
+
* Not with 1000: EV_ON_CLOSE would say the session ended well. A
|
|
8810
|
+
* browser closes a WebSocket only with 1000 or 3000-4999 (1002, the
|
|
8811
|
+
* protocol error, throws there), so it is 4002 -- and the server
|
|
8812
|
+
* echoes it, so EV_ON_CLOSE carries it.
|
|
8813
|
+
*
|
|
8814
|
+
* And a session the server broke was not a session reached: the
|
|
8815
|
+
* backoff the ack reset is put back, or a server that breaks every
|
|
8816
|
+
* session is reconnected at the first delay for ever, with an
|
|
8817
|
+
* EV_ON_OPEN and an EV_ON_CLOSE in the app per round.
|
|
8818
|
+
***************************************************************/
|
|
8819
|
+
var CLOSE_PEER_FAULT = 4002;
|
|
8820
|
+
function close_for_peer_fault(gobj, reason) {
|
|
8821
|
+
let priv = gobj.priv;
|
|
8822
|
+
if (gobj_current_state(gobj) === "ST_SESSION") priv.retry_ms = priv.retry_ms_before_session;
|
|
8823
|
+
close_websocket(gobj, CLOSE_PEER_FAULT, reason);
|
|
8824
|
+
}
|
|
8825
|
+
/***************************************************************
|
|
8662
8826
|
* A text of the server -- a frame, the name of an event, of a
|
|
8663
8827
|
* service, of a role -- as a log shows it: masked, then capped on a
|
|
8664
8828
|
* whole character (capped first, a credential the cap cut would
|
|
@@ -8940,7 +9104,8 @@ function ac_on_close(gobj, event, kw, src) {
|
|
|
8940
9104
|
*
|
|
8941
9105
|
* Reset to zero when a session is actually reached
|
|
8942
9106
|
* (ac_identity_card_ack), so a link that flaps once does not carry a
|
|
8943
|
-
* minute-long penalty into its next real drop
|
|
9107
|
+
* minute-long penalty into its next real drop -- and put back when the
|
|
9108
|
+
* server breaks that session (close_for_peer_fault()).
|
|
8944
9109
|
***************************************************************/
|
|
8945
9110
|
function next_retry(gobj) {
|
|
8946
9111
|
let priv = gobj.priv;
|
|
@@ -8990,6 +9155,7 @@ function ac_identity_card_ack(gobj, event, kw, src) {
|
|
|
8990
9155
|
} else {
|
|
8991
9156
|
gobj_change_state(gobj, "ST_SESSION");
|
|
8992
9157
|
priv.inside_on_open = true;
|
|
9158
|
+
priv.retry_ms_before_session = priv.retry_ms;
|
|
8993
9159
|
priv.retry_ms = 0;
|
|
8994
9160
|
if (!priv.inform_on_close) {
|
|
8995
9161
|
priv.inform_on_close = true;
|
|
@@ -9029,7 +9195,7 @@ function ac_on_message(gobj, event, kw, src) {
|
|
|
9029
9195
|
if (String(iev_event).toLowerCase() === "ev_identity_card_ack") {
|
|
9030
9196
|
if (!(gobj_send_event(gobj, "EV_IDENTITY_CARD_ACK", iev_kw, gobj) < 0)) return 0;
|
|
9031
9197
|
} else log_warning(`${gobj_short_name(gobj)}: event UNKNOWN in not-session state ${peer_text_shown(iev_event, PEER_NAME_SHOWN)}`);
|
|
9032
|
-
|
|
9198
|
+
close_for_peer_fault(gobj, "event out of session");
|
|
9033
9199
|
return -1;
|
|
9034
9200
|
}
|
|
9035
9201
|
iev_msg = null;
|
|
@@ -9038,13 +9204,13 @@ function ac_on_message(gobj, event, kw, src) {
|
|
|
9038
9204
|
let iev_dst_role = kw_get_str(gobj, event_id, "dst_role", "");
|
|
9039
9205
|
if (!empty_string(iev_dst_role) && iev_dst_role.toLowerCase() !== String(gobj_yuno_role()).toLowerCase()) {
|
|
9040
9206
|
log_warning(`${gobj_short_name(gobj)}: It's not my role, dst_role: ${peer_text_shown(iev_dst_role, PEER_NAME_SHOWN)}, my_role: ${gobj_yuno_role()}, event: ${peer_text_shown(iev_event, PEER_NAME_SHOWN)}`);
|
|
9041
|
-
|
|
9207
|
+
close_for_peer_fault(gobj, "not my role");
|
|
9042
9208
|
return -1;
|
|
9043
9209
|
}
|
|
9044
9210
|
let iev_dst_yuno = kw_get_str(gobj, event_id, "dst_yuno", "");
|
|
9045
9211
|
if (!empty_string(iev_dst_yuno) && iev_dst_yuno.toLowerCase() !== String(gobj_yuno_name()).toLowerCase()) {
|
|
9046
9212
|
log_warning(`${gobj_short_name(gobj)}: It's not my name, dst_yuno: ${peer_text_shown(iev_dst_yuno, PEER_NAME_SHOWN)}, my_name: ${gobj_yuno_name()}, event: ${peer_text_shown(iev_event, PEER_NAME_SHOWN)}`);
|
|
9047
|
-
|
|
9213
|
+
close_for_peer_fault(gobj, "not my name");
|
|
9048
9214
|
return -1;
|
|
9049
9215
|
}
|
|
9050
9216
|
let iev_dst_service = kw_get_str(gobj, event_id, "dst_service", "");
|
|
@@ -9056,8 +9222,14 @@ function ac_on_message(gobj, event, kw, src) {
|
|
|
9056
9222
|
log_warning(`${gobj_short_name(gobj)}: unsubscription event ignored, I'm client (service: ${peer_text_shown(iev_dst_service, PEER_NAME_SHOWN)}, event: ${peer_text_shown(iev_event, PEER_NAME_SHOWN)})`);
|
|
9057
9223
|
return -1;
|
|
9058
9224
|
}
|
|
9059
|
-
|
|
9060
|
-
|
|
9225
|
+
const declared = gobj_find_event_type(iev_event, 0, false);
|
|
9226
|
+
if (declared && gobj_has_event(gobj, declared.event_name, 0)) {
|
|
9227
|
+
const own_event = declared.event_name;
|
|
9228
|
+
if (!gobj_has_event(gobj, own_event, event_flag_t.EVF_PUBLIC_EVENT) || own_event === "EV_IDENTITY_CARD_ACK") {
|
|
9229
|
+
log_warning(`${gobj_short_name(gobj)}: event of the transport itself, or out of its place, DROPPED: ev '${peer_text_shown(iev_event, PEER_NAME_SHOWN)}'`);
|
|
9230
|
+
return -1;
|
|
9231
|
+
}
|
|
9232
|
+
gobj_send_event(gobj, own_event, iev_kw, gobj);
|
|
9061
9233
|
return 0;
|
|
9062
9234
|
}
|
|
9063
9235
|
if (!empty_string(iev_dst_service)) {
|
|
@@ -9073,9 +9245,9 @@ function ac_on_message(gobj, event, kw, src) {
|
|
|
9073
9245
|
gobj_send_event(gobj_service, iev_event, iev_kw, gobj);
|
|
9074
9246
|
return 0;
|
|
9075
9247
|
}
|
|
9076
|
-
let event_type = gobj_find_event_type(iev_event,
|
|
9248
|
+
let event_type = gobj_find_event_type(iev_event, event_flag_t.EVF_PUBLIC_EVENT, false);
|
|
9077
9249
|
if (!event_type) {
|
|
9078
|
-
log_warning(`${gobj_short_name(gobj)}: event that no gclass declares, DROPPED: ev '${peer_text_shown(iev_event, PEER_NAME_SHOWN)}'`);
|
|
9250
|
+
log_warning(`${gobj_short_name(gobj)}: event that no gclass declares public, DROPPED: ev '${peer_text_shown(iev_event, PEER_NAME_SHOWN)}'`);
|
|
9079
9251
|
return -1;
|
|
9080
9252
|
}
|
|
9081
9253
|
gobj_publish_event(gobj, event_type.event_name, iev_kw);
|
|
@@ -9198,7 +9370,7 @@ function create_gclass(gclass_name) {
|
|
|
9198
9370
|
__gclass__ = gclass_create(gclass_name, [
|
|
9199
9371
|
["EV_ON_MESSAGE", event_flag_t.EVF_OUTPUT_EVENT],
|
|
9200
9372
|
["EV_IDENTITY_CARD_ACK", event_flag_t.EVF_PUBLIC_EVENT],
|
|
9201
|
-
["EV_ON_ID_NAK", event_flag_t.
|
|
9373
|
+
["EV_ON_ID_NAK", event_flag_t.EVF_OUTPUT_EVENT],
|
|
9202
9374
|
["EV_MT_STATS", event_flag_t.EVF_PUBLIC_EVENT],
|
|
9203
9375
|
["EV_MT_COMMAND", event_flag_t.EVF_PUBLIC_EVENT],
|
|
9204
9376
|
["EV_ON_OPEN", event_flag_t.EVF_OUTPUT_EVENT | event_flag_t.EVF_NO_WARN_SUBS],
|