mirror of
https://github.com/zebrajr/node.git
synced 2026-01-15 12:15:26 +00:00
inspector: fix StringUtil::CharacterCount for unicodes
`StringUtil::CharacterCount` should return the length of underlying representation storage of a protocol string. `StringUtil::CharacterCount` is only used in DictionaryValue serialization. Only `Network.Headers` is an object type, represented with DictionaryValue. PR-URL: https://github.com/nodejs/node/pull/56788 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com> Reviewed-By: Daniel Lemire <daniel@lemire.me> Reviewed-By: Kohei Ueno <kohei.ueno119@gmail.com>
This commit is contained in:
2
Makefile
2
Makefile
@@ -1474,6 +1474,8 @@ LINT_CPP_FILES = $(filter-out $(LINT_CPP_EXCLUDE), $(wildcard \
|
|||||||
src/*/*.h \
|
src/*/*.h \
|
||||||
test/addons/*/*.cc \
|
test/addons/*/*.cc \
|
||||||
test/addons/*/*.h \
|
test/addons/*/*.h \
|
||||||
|
test/cctest/*/*.cc \
|
||||||
|
test/cctest/*/*.h \
|
||||||
test/cctest/*.cc \
|
test/cctest/*.cc \
|
||||||
test/cctest/*.h \
|
test/cctest/*.h \
|
||||||
test/embedding/*.cc \
|
test/embedding/*.cc \
|
||||||
|
|||||||
9
node.gyp
9
node.gyp
@@ -432,6 +432,7 @@
|
|||||||
'test/cctest/test_quic_tokens.cc',
|
'test/cctest/test_quic_tokens.cc',
|
||||||
],
|
],
|
||||||
'node_cctest_inspector_sources': [
|
'node_cctest_inspector_sources': [
|
||||||
|
'test/cctest/inspector/test_node_protocol.cc',
|
||||||
'test/cctest/test_inspector_socket.cc',
|
'test/cctest/test_inspector_socket.cc',
|
||||||
'test/cctest/test_inspector_socket_server.cc',
|
'test/cctest/test_inspector_socket_server.cc',
|
||||||
],
|
],
|
||||||
@@ -1210,6 +1211,14 @@
|
|||||||
'HAVE_INSPECTOR=1',
|
'HAVE_INSPECTOR=1',
|
||||||
],
|
],
|
||||||
'sources': [ '<@(node_cctest_inspector_sources)' ],
|
'sources': [ '<@(node_cctest_inspector_sources)' ],
|
||||||
|
'include_dirs': [
|
||||||
|
# TODO(legendecas): make node_inspector.gypi a dependable target.
|
||||||
|
'<(SHARED_INTERMEDIATE_DIR)', # for inspector
|
||||||
|
'<(SHARED_INTERMEDIATE_DIR)/src', # for inspector
|
||||||
|
],
|
||||||
|
'dependencies': [
|
||||||
|
'deps/inspector_protocol/inspector_protocol.gyp:crdtp',
|
||||||
|
],
|
||||||
}, {
|
}, {
|
||||||
'defines': [
|
'defines': [
|
||||||
'HAVE_INSPECTOR=0',
|
'HAVE_INSPECTOR=0',
|
||||||
|
|||||||
@@ -85,11 +85,12 @@ const uint8_t* StringUtil::CharactersUTF8(const std::string_view s) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
size_t StringUtil::CharacterCount(const std::string_view s) {
|
size_t StringUtil::CharacterCount(const std::string_view s) {
|
||||||
// The utf32_length_from_utf8 function calls count_utf8.
|
// Return the length of underlying representation storage.
|
||||||
// The count_utf8 function counts the number of code points
|
// E.g. for std::basic_string_view<char>, return its byte length.
|
||||||
// (characters) in the string, assuming that the string is valid Unicode.
|
// If we adopt a variant underlying store string type, like
|
||||||
// TODO(@anonrig): Test to make sure CharacterCount returns correctly.
|
// `v8_inspector::StringView`, for UTF16, return the length of the
|
||||||
return simdutf::utf32_length_from_utf8(s.data(), s.length());
|
// underlying uint16_t store.
|
||||||
|
return s.length();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace protocol
|
} // namespace protocol
|
||||||
|
|||||||
@@ -32,6 +32,9 @@ using StringBuilder = std::ostringstream;
|
|||||||
using ProtocolMessage = std::string;
|
using ProtocolMessage = std::string;
|
||||||
|
|
||||||
// Implements StringUtil methods used in `inspector_protocol/lib/`.
|
// Implements StringUtil methods used in `inspector_protocol/lib/`.
|
||||||
|
// Refer to
|
||||||
|
// https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/core/inspector/v8_inspector_string.h;l=40;drc=2b15d6974a49d3a14d3d67ae099a649d523a828d
|
||||||
|
// for more details about the interface.
|
||||||
struct StringUtil {
|
struct StringUtil {
|
||||||
// Convert Utf16 in local endianness to Utf8 if needed.
|
// Convert Utf16 in local endianness to Utf8 if needed.
|
||||||
static String StringViewToUtf8(v8_inspector::StringView view);
|
static String StringViewToUtf8(v8_inspector::StringView view);
|
||||||
|
|||||||
33
test/cctest/inspector/test_node_protocol.cc
Normal file
33
test/cctest/inspector/test_node_protocol.cc
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
#include "crdtp/json.h"
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
#include "inspector/node_json.h"
|
||||||
|
#include "node/inspector/protocol/Protocol.h"
|
||||||
|
|
||||||
|
namespace node {
|
||||||
|
namespace inspector {
|
||||||
|
namespace protocol {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
TEST(InspectorProtocol, Utf8StringSerDes) {
|
||||||
|
constexpr const char* kKey = "unicode_key";
|
||||||
|
constexpr const char* kValue = "CJK 汉字 🍱 🧑🧑🧒🧒";
|
||||||
|
std::unique_ptr<DictionaryValue> val = DictionaryValue::create();
|
||||||
|
val->setString(kKey, kValue);
|
||||||
|
|
||||||
|
std::vector<uint8_t> cbor = val->Serialize();
|
||||||
|
std::string json;
|
||||||
|
crdtp::Status status =
|
||||||
|
crdtp::json::ConvertCBORToJSON(crdtp::SpanFrom(cbor), &json);
|
||||||
|
CHECK(status.ok());
|
||||||
|
|
||||||
|
std::unique_ptr<DictionaryValue> parsed =
|
||||||
|
DictionaryValue::cast(JsonUtil::parseJSON(json));
|
||||||
|
std::string parsed_value;
|
||||||
|
CHECK(parsed->getString(kKey, &parsed_value));
|
||||||
|
CHECK_EQ(parsed_value, std::string(kValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
} // namespace protocol
|
||||||
|
} // namespace inspector
|
||||||
|
} // namespace node
|
||||||
@@ -6,7 +6,7 @@ const http = require('http');
|
|||||||
const fixtures = require('../common/fixtures');
|
const fixtures = require('../common/fixtures');
|
||||||
const { spawn } = require('child_process');
|
const { spawn } = require('child_process');
|
||||||
const { URL, pathToFileURL } = require('url');
|
const { URL, pathToFileURL } = require('url');
|
||||||
const { EventEmitter } = require('events');
|
const { EventEmitter, once } = require('events');
|
||||||
|
|
||||||
const _MAINSCRIPT = fixtures.path('loop.js');
|
const _MAINSCRIPT = fixtures.path('loop.js');
|
||||||
const DEBUG = false;
|
const DEBUG = false;
|
||||||
@@ -544,6 +544,33 @@ function fires(promise, error, timeoutMs) {
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When waiting for inspector events, there might be no handles on the event
|
||||||
|
* loop, and leads to process exits.
|
||||||
|
*
|
||||||
|
* This function provides a utility to wait until a inspector event for a certain
|
||||||
|
* time.
|
||||||
|
*/
|
||||||
|
function waitUntil(session, eventName, timeout = 1000) {
|
||||||
|
const resolvers = Promise.withResolvers();
|
||||||
|
const timer = setTimeout(() => {
|
||||||
|
resolvers.reject(new Error(`Wait for inspector event ${eventName} timed out`));
|
||||||
|
}, timeout);
|
||||||
|
|
||||||
|
once(session, eventName)
|
||||||
|
.then((res) => {
|
||||||
|
resolvers.resolve(res);
|
||||||
|
clearTimeout(timer);
|
||||||
|
}, (error) => {
|
||||||
|
// This should never happen.
|
||||||
|
resolvers.reject(error);
|
||||||
|
clearTimeout(timer);
|
||||||
|
});
|
||||||
|
|
||||||
|
return resolvers.promise;
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
NodeInstance,
|
NodeInstance,
|
||||||
|
waitUntil,
|
||||||
};
|
};
|
||||||
|
|||||||
41
test/parallel/test-inspector-network-arbitrary-data.js
Normal file
41
test/parallel/test-inspector-network-arbitrary-data.js
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
// Flags: --inspect=0 --experimental-network-inspection
|
||||||
|
'use strict';
|
||||||
|
const common = require('../common');
|
||||||
|
|
||||||
|
common.skipIfInspectorDisabled();
|
||||||
|
|
||||||
|
const inspector = require('node:inspector/promises');
|
||||||
|
const { Network } = require('node:inspector');
|
||||||
|
const test = require('node:test');
|
||||||
|
const assert = require('node:assert');
|
||||||
|
const { waitUntil } = require('../common/inspector-helper');
|
||||||
|
|
||||||
|
const session = new inspector.Session();
|
||||||
|
session.connect();
|
||||||
|
|
||||||
|
test('should emit Network.requestWillBeSent with unicode', async () => {
|
||||||
|
await session.post('Network.enable');
|
||||||
|
const expectedValue = 'CJK 汉字 🍱 🧑🧑🧒🧒';
|
||||||
|
|
||||||
|
const requestWillBeSentFuture = waitUntil(session, 'Network.requestWillBeSent')
|
||||||
|
.then(([event]) => {
|
||||||
|
assert.strictEqual(event.params.request.url, expectedValue);
|
||||||
|
assert.strictEqual(event.params.request.method, expectedValue);
|
||||||
|
assert.strictEqual(event.params.request.headers.mKey, expectedValue);
|
||||||
|
});
|
||||||
|
|
||||||
|
Network.requestWillBeSent({
|
||||||
|
requestId: '1',
|
||||||
|
timestamp: 1,
|
||||||
|
wallTime: 1,
|
||||||
|
request: {
|
||||||
|
url: expectedValue,
|
||||||
|
method: expectedValue,
|
||||||
|
headers: {
|
||||||
|
mKey: expectedValue,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await requestWillBeSentFuture;
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user