fix AMF3 JSON value encoding
This commit is contained in:
parent
7a28e0534b
commit
1c73e03f5d
@ -5,8 +5,97 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import Operation from "../Operation.mjs";
|
import Operation from "../Operation.mjs";
|
||||||
|
import Utils from "../Utils.mjs";
|
||||||
import "reflect-metadata"; // Required as a shim for the amf library
|
import "reflect-metadata"; // Required as a shim for the amf library
|
||||||
import { AMF0, AMF3 } from "@astronautlabs/amf";
|
import { AMF0 } from "@astronautlabs/amf";
|
||||||
|
|
||||||
|
const AMF3_MARKER = {
|
||||||
|
Null: 0x01,
|
||||||
|
False: 0x02,
|
||||||
|
True: 0x03,
|
||||||
|
Double: 0x05,
|
||||||
|
String: 0x06,
|
||||||
|
Array: 0x09,
|
||||||
|
Object: 0x0a
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {number} value
|
||||||
|
* @returns {number[]}
|
||||||
|
*/
|
||||||
|
function encodeU29(value) {
|
||||||
|
if (value < 0x80) {
|
||||||
|
return [value];
|
||||||
|
}
|
||||||
|
if (value < 0x4000) {
|
||||||
|
return [
|
||||||
|
((value >> 7) & 0x7f) | 0x80,
|
||||||
|
value & 0x7f
|
||||||
|
];
|
||||||
|
}
|
||||||
|
if (value < 0x200000) {
|
||||||
|
return [
|
||||||
|
((value >> 14) & 0x7f) | 0x80,
|
||||||
|
((value >> 7) & 0x7f) | 0x80,
|
||||||
|
value & 0x7f
|
||||||
|
];
|
||||||
|
}
|
||||||
|
return [
|
||||||
|
((value >> 22) & 0x7f) | 0x80,
|
||||||
|
((value >> 15) & 0x7f) | 0x80,
|
||||||
|
((value >> 8) & 0x7f) | 0x80,
|
||||||
|
value & 0xff
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} value
|
||||||
|
* @returns {number[]}
|
||||||
|
*/
|
||||||
|
function encodeAMF3Utf8(value) {
|
||||||
|
const bytes = Utils.strToUtf8ByteArray(value);
|
||||||
|
return encodeU29((bytes.length << 1) | 1).concat(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {number} value
|
||||||
|
* @returns {number[]}
|
||||||
|
*/
|
||||||
|
function encodeAMF3Double(value) {
|
||||||
|
const bytes = new Uint8Array(8);
|
||||||
|
new DataView(bytes.buffer).setFloat64(0, value, false);
|
||||||
|
return [AMF3_MARKER.Double].concat(Array.from(bytes));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {JSON} value
|
||||||
|
* @returns {number[]}
|
||||||
|
*/
|
||||||
|
function encodeAMF3Value(value) {
|
||||||
|
if (value === null) return [AMF3_MARKER.Null];
|
||||||
|
if (value === false) return [AMF3_MARKER.False];
|
||||||
|
if (value === true) return [AMF3_MARKER.True];
|
||||||
|
if (typeof value === "number") return encodeAMF3Double(value);
|
||||||
|
if (typeof value === "string") return [AMF3_MARKER.String].concat(encodeAMF3Utf8(value));
|
||||||
|
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
return [
|
||||||
|
AMF3_MARKER.Array,
|
||||||
|
...encodeU29((value.length << 1) | 1),
|
||||||
|
0x01,
|
||||||
|
...value.flatMap(encodeAMF3Value)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
const keys = Object.keys(value);
|
||||||
|
return [
|
||||||
|
AMF3_MARKER.Object,
|
||||||
|
...encodeU29((keys.length << 4) | 0x03),
|
||||||
|
0x01,
|
||||||
|
...keys.flatMap(encodeAMF3Utf8),
|
||||||
|
...keys.flatMap(key => encodeAMF3Value(value[key]))
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AMF Encode operation
|
* AMF Encode operation
|
||||||
@ -42,8 +131,9 @@ class AMFEncode extends Operation {
|
|||||||
*/
|
*/
|
||||||
run(input, args) {
|
run(input, args) {
|
||||||
const [format] = args;
|
const [format] = args;
|
||||||
const handler = format === "AMF0" ? AMF0 : AMF3;
|
const output = format === "AMF0" ?
|
||||||
const output = handler.Value.any(input).serialize();
|
AMF0.Value.any(input).serialize() :
|
||||||
|
Uint8Array.from(encodeAMF3Value(input));
|
||||||
return output.buffer;
|
return output.buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
100
tests/operations/tests/AMF.mjs
Normal file
100
tests/operations/tests/AMF.mjs
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
/**
|
||||||
|
* AMF tests.
|
||||||
|
*
|
||||||
|
* @copyright Crown Copyright 2026
|
||||||
|
* @license Apache-2.0
|
||||||
|
*/
|
||||||
|
import TestRegister from "../../lib/TestRegister.mjs";
|
||||||
|
|
||||||
|
TestRegister.addTests([
|
||||||
|
{
|
||||||
|
name: "AMF3 Encode: object string",
|
||||||
|
input: "{\"a\": \"test\"}",
|
||||||
|
expectedOutput: "0a13010361060974657374",
|
||||||
|
recipeConfig: [
|
||||||
|
{
|
||||||
|
op: "AMF Encode",
|
||||||
|
args: ["AMF3"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
op: "To Hex",
|
||||||
|
args: ["None", 0]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "AMF3 Encode: object boolean true",
|
||||||
|
input: "{\"a\": true}",
|
||||||
|
expectedOutput: "0a1301036103",
|
||||||
|
recipeConfig: [
|
||||||
|
{
|
||||||
|
op: "AMF Encode",
|
||||||
|
args: ["AMF3"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
op: "To Hex",
|
||||||
|
args: ["None", 0]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "AMF3 Encode: object boolean false",
|
||||||
|
input: "{\"a\": false}",
|
||||||
|
expectedOutput: "0a1301036102",
|
||||||
|
recipeConfig: [
|
||||||
|
{
|
||||||
|
op: "AMF Encode",
|
||||||
|
args: ["AMF3"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
op: "To Hex",
|
||||||
|
args: ["None", 0]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "AMF3 Encode: object null",
|
||||||
|
input: "{\"a\": null}",
|
||||||
|
expectedOutput: "0a1301036101",
|
||||||
|
recipeConfig: [
|
||||||
|
{
|
||||||
|
op: "AMF Encode",
|
||||||
|
args: ["AMF3"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
op: "To Hex",
|
||||||
|
args: ["None", 0]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "AMF3 Encode: object array",
|
||||||
|
input: "{\"a\": []}",
|
||||||
|
expectedOutput: "0a13010361090101",
|
||||||
|
recipeConfig: [
|
||||||
|
{
|
||||||
|
op: "AMF Encode",
|
||||||
|
args: ["AMF3"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
op: "To Hex",
|
||||||
|
args: ["None", 0]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "AMF3 Encode: object nested JSON values",
|
||||||
|
input: "{\"a\": [true, false, null, \"x\", 1]}",
|
||||||
|
expectedOutput: "0a13010361090b01030201060378053ff0000000000000",
|
||||||
|
recipeConfig: [
|
||||||
|
{
|
||||||
|
op: "AMF Encode",
|
||||||
|
args: ["AMF3"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
op: "To Hex",
|
||||||
|
args: ["None", 0]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]);
|
||||||
Loading…
x
Reference in New Issue
Block a user