mirror of
https://github.com/zebrajr/node.git
synced 2025-12-06 00:20:08 +01:00
net: add SocketAddress class
Signed-off-by: James M Snell <jasnell@gmail.com> PR-URL: https://github.com/nodejs/node/pull/37917 Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
This commit is contained in:
parent
a4169ce519
commit
daa8a7bbcf
|
|
@ -135,6 +135,51 @@ added: v15.0.0
|
|||
|
||||
The list of rules added to the blocklist.
|
||||
|
||||
## Class: `net.SocketAddress`
|
||||
<!-- YAML
|
||||
added: REPLACEME
|
||||
-->
|
||||
### `new net.SocketAddress([options])`
|
||||
<!-- YAML
|
||||
added: REPLACEME
|
||||
-->
|
||||
|
||||
* `options` {Object}
|
||||
* `address` {string} The network address as either an IPv4 or IPv6 string.
|
||||
**Default**: `'127.0.0.1'` if `family` is `'ipv4'`; `'::'` if `family` is
|
||||
`'ipv6'`.
|
||||
* `family` {string} One of either `'ipv4'` or 'ipv6'`. **Default**: `'ipv4'`.
|
||||
* `flowlabel` {number} An IPv6 flow-label used only if `family` is `'ipv6'`.
|
||||
* `port` {number} An IP port.
|
||||
|
||||
### `socketaddress.address`
|
||||
<!-- YAML
|
||||
added: REPLACEME
|
||||
-->
|
||||
|
||||
* Type {string}
|
||||
|
||||
### `socketaddress.family`
|
||||
<!-- YAML
|
||||
added: REPLACEME
|
||||
-->
|
||||
|
||||
* Type {string} Either `'ipv4'` or `'ipv6'`.
|
||||
|
||||
### `socketaddress.flowlabel`
|
||||
<!-- YAML
|
||||
added: REPLACEME
|
||||
-->
|
||||
|
||||
* Type {number}
|
||||
|
||||
### `socketaddress.port`
|
||||
<!-- YAML
|
||||
added: REPLACEME
|
||||
-->
|
||||
|
||||
* Type {number}
|
||||
|
||||
## Class: `net.Server`
|
||||
<!-- YAML
|
||||
added: v0.1.90
|
||||
|
|
|
|||
|
|
@ -573,6 +573,7 @@ In particular, the significant differences to `JSON` are:
|
|||
* {KeyObject}s,
|
||||
* {MessagePort}s,
|
||||
* {net.BlockList}s,
|
||||
* {net.SocketAddress}es,
|
||||
* {X509Certificate}s.
|
||||
|
||||
```js
|
||||
|
|
|
|||
153
lib/internal/socketaddress.js
Normal file
153
lib/internal/socketaddress.js
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
'use strict';
|
||||
|
||||
const {
|
||||
ObjectSetPrototypeOf,
|
||||
Symbol,
|
||||
} = primordials;
|
||||
|
||||
const {
|
||||
SocketAddress: _SocketAddress,
|
||||
AF_INET,
|
||||
AF_INET6,
|
||||
} = internalBinding('block_list');
|
||||
|
||||
const {
|
||||
validateObject,
|
||||
validateString,
|
||||
validatePort,
|
||||
validateUint32,
|
||||
} = require('internal/validators');
|
||||
|
||||
const {
|
||||
codes: {
|
||||
ERR_INVALID_ARG_VALUE,
|
||||
},
|
||||
} = require('internal/errors');
|
||||
|
||||
const {
|
||||
customInspectSymbol: kInspect,
|
||||
} = require('internal/util');
|
||||
|
||||
const { inspect } = require('internal/util/inspect');
|
||||
|
||||
const {
|
||||
JSTransferable,
|
||||
kClone,
|
||||
kDeserialize,
|
||||
} = require('internal/worker/js_transferable');
|
||||
|
||||
const kHandle = Symbol('kHandle');
|
||||
const kDetail = Symbol('kDetail');
|
||||
|
||||
class SocketAddress extends JSTransferable {
|
||||
static isSocketAddress(value) {
|
||||
return value?.[kHandle] !== undefined;
|
||||
}
|
||||
|
||||
constructor(options = {}) {
|
||||
super();
|
||||
validateObject(options, 'options');
|
||||
const {
|
||||
family = 'ipv4',
|
||||
address = (family === 'ipv4' ? '127.0.0.1' : '::'),
|
||||
port = 0,
|
||||
flowlabel = 0,
|
||||
} = options;
|
||||
|
||||
let type;
|
||||
switch (family) {
|
||||
case 'ipv4':
|
||||
type = AF_INET;
|
||||
break;
|
||||
case 'ipv6':
|
||||
type = AF_INET6;
|
||||
break;
|
||||
default:
|
||||
throw new ERR_INVALID_ARG_VALUE('options.family', family);
|
||||
}
|
||||
|
||||
validateString(address, 'options.address');
|
||||
validatePort(port, 'options.port');
|
||||
validateUint32(flowlabel, 'options.flowlabel', false);
|
||||
|
||||
this[kHandle] = new _SocketAddress(address, port, type, flowlabel);
|
||||
this[kDetail] = this[kHandle].detail({
|
||||
address: undefined,
|
||||
port: undefined,
|
||||
family: undefined,
|
||||
flowlabel: undefined,
|
||||
});
|
||||
}
|
||||
|
||||
get address() {
|
||||
return this[kDetail].address;
|
||||
}
|
||||
|
||||
get port() {
|
||||
return this[kDetail].port;
|
||||
}
|
||||
|
||||
get family() {
|
||||
return this[kDetail].family === AF_INET ? 'ipv4' : 'ipv6';
|
||||
}
|
||||
|
||||
get flowlabel() {
|
||||
// The flow label can be changed internally.
|
||||
return this[kHandle].flowlabel();
|
||||
}
|
||||
|
||||
[kInspect](depth, options) {
|
||||
if (depth < 0)
|
||||
return this;
|
||||
|
||||
const opts = {
|
||||
...options,
|
||||
depth: options.depth == null ? null : options.depth - 1
|
||||
};
|
||||
|
||||
return `SocketAddress ${inspect(this.toJSON(), opts)}`;
|
||||
}
|
||||
|
||||
[kClone]() {
|
||||
const handle = this[kHandle];
|
||||
return {
|
||||
data: { handle },
|
||||
deserializeInfo: 'internal/socketaddress:InternalSocketAddress',
|
||||
};
|
||||
}
|
||||
|
||||
[kDeserialize]({ handle }) {
|
||||
this[kHandle] = handle;
|
||||
this[kDetail] = handle.detail({
|
||||
address: undefined,
|
||||
port: undefined,
|
||||
family: undefined,
|
||||
flowlabel: undefined,
|
||||
});
|
||||
}
|
||||
|
||||
toJSON() {
|
||||
return {
|
||||
address: this.address,
|
||||
port: this.port,
|
||||
family: this.family,
|
||||
flowlabel: this.flowlabel,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class InternalSocketAddress extends JSTransferable {
|
||||
constructor(handle) {
|
||||
super();
|
||||
this[kHandle] = handle;
|
||||
}
|
||||
}
|
||||
|
||||
InternalSocketAddress.prototype.constructor =
|
||||
SocketAddress.prototype.construtor;
|
||||
ObjectSetPrototypeOf(InternalSocketAddress.prototype, SocketAddress.prototype);
|
||||
|
||||
module.exports = {
|
||||
SocketAddress,
|
||||
InternalSocketAddress,
|
||||
};
|
||||
|
|
@ -122,6 +122,7 @@ const {
|
|||
let cluster;
|
||||
let dns;
|
||||
let BlockList;
|
||||
let SocketAddress;
|
||||
|
||||
const { clearTimeout } = require('timers');
|
||||
const { kTimeout } = require('internal/timers');
|
||||
|
|
@ -1751,6 +1752,10 @@ module.exports = {
|
|||
BlockList ??= require('internal/blocklist').BlockList;
|
||||
return BlockList;
|
||||
},
|
||||
get SocketAddress() {
|
||||
SocketAddress ??= require('internal/socketaddress').SocketAddress;
|
||||
return SocketAddress;
|
||||
},
|
||||
connect,
|
||||
createConnection: connect,
|
||||
createServer,
|
||||
|
|
|
|||
1
node.gyp
1
node.gyp
|
|
@ -212,6 +212,7 @@
|
|||
'lib/internal/repl/await.js',
|
||||
'lib/internal/repl/history.js',
|
||||
'lib/internal/repl/utils.js',
|
||||
'lib/internal/socketaddress.js',
|
||||
'lib/internal/socket_list.js',
|
||||
'lib/internal/source_map/prepare_stack_trace.js',
|
||||
'lib/internal/source_map/source_map.js',
|
||||
|
|
|
|||
|
|
@ -258,6 +258,7 @@ constexpr size_t kFsStatsBufferLength =
|
|||
V(fingerprint256_string, "fingerprint256") \
|
||||
V(fingerprint_string, "fingerprint") \
|
||||
V(flags_string, "flags") \
|
||||
V(flowlabel_string, "flowlabel") \
|
||||
V(fragment_string, "fragment") \
|
||||
V(function_string, "function") \
|
||||
V(get_data_clone_error_string, "_getDataCloneError") \
|
||||
|
|
@ -465,6 +466,7 @@ constexpr size_t kFsStatsBufferLength =
|
|||
V(script_context_constructor_template, v8::FunctionTemplate) \
|
||||
V(secure_context_constructor_template, v8::FunctionTemplate) \
|
||||
V(shutdown_wrap_template, v8::ObjectTemplate) \
|
||||
V(socketaddress_constructor_template, v8::FunctionTemplate) \
|
||||
V(streambaseoutputstream_constructor_template, v8::ObjectTemplate) \
|
||||
V(qlogoutputstream_constructor_template, v8::ObjectTemplate) \
|
||||
V(tcp_constructor_template, v8::FunctionTemplate) \
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ void OnFatalError(const char* location, const char* message);
|
|||
V(ERR_CRYPTO_JOB_INIT_FAILED, Error) \
|
||||
V(ERR_DLOPEN_FAILED, Error) \
|
||||
V(ERR_EXECUTION_ENVIRONMENT_NOT_AVAILABLE, Error) \
|
||||
V(ERR_INVALID_ADDRESS, Error) \
|
||||
V(ERR_INVALID_ARG_VALUE, TypeError) \
|
||||
V(ERR_OSSL_EVP_INVALID_DIGEST, Error) \
|
||||
V(ERR_INVALID_ARG_TYPE, TypeError) \
|
||||
|
|
@ -143,6 +144,7 @@ ERRORS_WITH_CODE(V)
|
|||
V(ERR_DLOPEN_FAILED, "DLOpen failed") \
|
||||
V(ERR_EXECUTION_ENVIRONMENT_NOT_AVAILABLE, \
|
||||
"Context not associated with Node.js environment") \
|
||||
V(ERR_INVALID_ADDRESS, "Invalid socket address") \
|
||||
V(ERR_INVALID_MODULE, "No such module") \
|
||||
V(ERR_INVALID_THIS, "Value of \"this\" is the wrong type") \
|
||||
V(ERR_INVALID_TRANSFER_OBJECT, "Found invalid object in transferList") \
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
#include "base64-inl.h"
|
||||
#include "base_object-inl.h"
|
||||
#include "memory_tracker-inl.h"
|
||||
#include "node_errors.h"
|
||||
#include "uv.h"
|
||||
|
||||
#include <memory>
|
||||
|
|
@ -15,9 +16,11 @@ using v8::Array;
|
|||
using v8::Context;
|
||||
using v8::FunctionCallbackInfo;
|
||||
using v8::FunctionTemplate;
|
||||
using v8::Int32;
|
||||
using v8::Local;
|
||||
using v8::MaybeLocal;
|
||||
using v8::Object;
|
||||
using v8::Uint32;
|
||||
using v8::Value;
|
||||
|
||||
namespace {
|
||||
|
|
@ -752,6 +755,8 @@ void SocketAddressBlockListWrap::Initialize(
|
|||
GetConstructorTemplate(env),
|
||||
Environment::SetConstructorFunctionFlag::NONE);
|
||||
|
||||
SocketAddressBase::Initialize(env, target);
|
||||
|
||||
NODE_DEFINE_CONSTANT(target, AF_INET);
|
||||
NODE_DEFINE_CONSTANT(target, AF_INET6);
|
||||
}
|
||||
|
|
@ -768,6 +773,144 @@ void SocketAddressBlockListWrap::TransferData::MemoryInfo(
|
|||
blocklist_->MemoryInfo(tracker);
|
||||
}
|
||||
|
||||
bool SocketAddressBase::HasInstance(Environment* env, Local<Value> value) {
|
||||
return GetConstructorTemplate(env)->HasInstance(value);
|
||||
}
|
||||
|
||||
Local<FunctionTemplate> SocketAddressBase::GetConstructorTemplate(
|
||||
Environment* env) {
|
||||
Local<FunctionTemplate> tmpl = env->socketaddress_constructor_template();
|
||||
if (tmpl.IsEmpty()) {
|
||||
tmpl = env->NewFunctionTemplate(New);
|
||||
tmpl->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "SocketAddress"));
|
||||
tmpl->InstanceTemplate()->SetInternalFieldCount(
|
||||
SocketAddressBase::kInternalFieldCount);
|
||||
tmpl->Inherit(BaseObject::GetConstructorTemplate(env));
|
||||
env->SetProtoMethod(tmpl, "detail", Detail);
|
||||
env->SetProtoMethod(tmpl, "legacyDetail", LegacyDetail);
|
||||
env->SetProtoMethodNoSideEffect(tmpl, "flowlabel", GetFlowLabel);
|
||||
env->set_socketaddress_constructor_template(tmpl);
|
||||
}
|
||||
return tmpl;
|
||||
}
|
||||
|
||||
void SocketAddressBase::Initialize(Environment* env, Local<Object> target) {
|
||||
env->SetConstructorFunction(
|
||||
target,
|
||||
"SocketAddress",
|
||||
GetConstructorTemplate(env),
|
||||
Environment::SetConstructorFunctionFlag::NONE);
|
||||
}
|
||||
|
||||
BaseObjectPtr<SocketAddressBase> SocketAddressBase::Create(
|
||||
Environment* env,
|
||||
std::shared_ptr<SocketAddress> address) {
|
||||
Local<Object> obj;
|
||||
if (!GetConstructorTemplate(env)
|
||||
->InstanceTemplate()
|
||||
->NewInstance(env->context()).ToLocal(&obj)) {
|
||||
return BaseObjectPtr<SocketAddressBase>();
|
||||
}
|
||||
|
||||
return MakeBaseObject<SocketAddressBase>(env, obj, std::move(address));
|
||||
}
|
||||
|
||||
void SocketAddressBase::New(const FunctionCallbackInfo<Value>& args) {
|
||||
Environment* env = Environment::GetCurrent(args);
|
||||
CHECK(args.IsConstructCall());
|
||||
CHECK(args[0]->IsString()); // address
|
||||
CHECK(args[1]->IsInt32()); // port
|
||||
CHECK(args[2]->IsInt32()); // family
|
||||
CHECK(args[3]->IsUint32()); // flow label
|
||||
|
||||
Utf8Value address(env->isolate(), args[0]);
|
||||
int32_t port = args[1].As<Int32>()->Value();
|
||||
int32_t family = args[2].As<Int32>()->Value();
|
||||
uint32_t flow_label = args[3].As<Uint32>()->Value();
|
||||
|
||||
std::shared_ptr<SocketAddress> addr = std::make_shared<SocketAddress>();
|
||||
|
||||
if (!SocketAddress::New(family, *address, port, addr.get()))
|
||||
return THROW_ERR_INVALID_ADDRESS(env);
|
||||
|
||||
addr->set_flow_label(flow_label);
|
||||
|
||||
new SocketAddressBase(env, args.This(), std::move(addr));
|
||||
}
|
||||
|
||||
void SocketAddressBase::Detail(const FunctionCallbackInfo<Value>& args) {
|
||||
Environment* env = Environment::GetCurrent(args);
|
||||
CHECK(args[0]->IsObject());
|
||||
Local<Object> detail = args[0].As<Object>();
|
||||
|
||||
SocketAddressBase* base;
|
||||
ASSIGN_OR_RETURN_UNWRAP(&base, args.Holder());
|
||||
|
||||
Local<Value> address;
|
||||
if (!ToV8Value(env->context(), base->address_->address()).ToLocal(&address))
|
||||
return;
|
||||
|
||||
if (detail->Set(env->context(), env->address_string(), address).IsJust() &&
|
||||
detail->Set(
|
||||
env->context(),
|
||||
env->port_string(),
|
||||
Int32::New(env->isolate(), base->address_->port())).IsJust() &&
|
||||
detail->Set(
|
||||
env->context(),
|
||||
env->family_string(),
|
||||
Int32::New(env->isolate(), base->address_->family())).IsJust() &&
|
||||
detail->Set(
|
||||
env->context(),
|
||||
env->flowlabel_string(),
|
||||
Uint32::New(env->isolate(), base->address_->flow_label()))
|
||||
.IsJust()) {
|
||||
args.GetReturnValue().Set(detail);
|
||||
}
|
||||
}
|
||||
|
||||
void SocketAddressBase::GetFlowLabel(const FunctionCallbackInfo<Value>& args) {
|
||||
Environment* env = Environment::GetCurrent(args);
|
||||
SocketAddressBase* base;
|
||||
ASSIGN_OR_RETURN_UNWRAP(&base, args.Holder());
|
||||
args.GetReturnValue().Set(base->address_->flow_label());
|
||||
}
|
||||
|
||||
void SocketAddressBase::LegacyDetail(const FunctionCallbackInfo<Value>& args) {
|
||||
Environment* env = Environment::GetCurrent(args);
|
||||
SocketAddressBase* base;
|
||||
ASSIGN_OR_RETURN_UNWRAP(&base, args.Holder());
|
||||
args.GetReturnValue().Set(base->address_->ToJS(env));
|
||||
}
|
||||
|
||||
SocketAddressBase::SocketAddressBase(
|
||||
Environment* env,
|
||||
Local<Object> wrap,
|
||||
std::shared_ptr<SocketAddress> address)
|
||||
: BaseObject(env, wrap),
|
||||
address_(std::move(address)) {
|
||||
MakeWeak();
|
||||
}
|
||||
|
||||
void SocketAddressBase::MemoryInfo(MemoryTracker* tracker) const {
|
||||
tracker->TrackField("address", address_);
|
||||
}
|
||||
|
||||
std::unique_ptr<worker::TransferData>
|
||||
SocketAddressBase::CloneForMessaging() const {
|
||||
return std::make_unique<TransferData>(this);
|
||||
}
|
||||
|
||||
void SocketAddressBase::TransferData::MemoryInfo(MemoryTracker* tracker) const {
|
||||
tracker->TrackField("address", address_);
|
||||
}
|
||||
|
||||
BaseObjectPtr<BaseObject> SocketAddressBase::TransferData::Deserialize(
|
||||
Environment* env,
|
||||
v8::Local<v8::Context> context,
|
||||
std::unique_ptr<worker::TransferData> self) {
|
||||
return SocketAddressBase::Create(env, std::move(address_));
|
||||
}
|
||||
|
||||
} // namespace node
|
||||
|
||||
NODE_MODULE_CONTEXT_AWARE_INTERNAL(
|
||||
|
|
|
|||
|
|
@ -148,6 +148,60 @@ class SocketAddress : public MemoryRetainer {
|
|||
sockaddr_storage address_;
|
||||
};
|
||||
|
||||
class SocketAddressBase : public BaseObject {
|
||||
public:
|
||||
static bool HasInstance(Environment* env, v8::Local<v8::Value> value);
|
||||
static v8::Local<v8::FunctionTemplate> GetConstructorTemplate(
|
||||
Environment* env);
|
||||
static void Initialize(Environment* env, v8::Local<v8::Object> target);
|
||||
static BaseObjectPtr<SocketAddressBase> Create(
|
||||
Environment* env,
|
||||
std::shared_ptr<SocketAddress> address);
|
||||
|
||||
static void New(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void Detail(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void LegacyDetail(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void GetFlowLabel(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
|
||||
SocketAddressBase(
|
||||
Environment* env,
|
||||
v8::Local<v8::Object> wrap,
|
||||
std::shared_ptr<SocketAddress> address);
|
||||
|
||||
void MemoryInfo(MemoryTracker* tracker) const override;
|
||||
SET_MEMORY_INFO_NAME(SocketAddressBase);
|
||||
SET_SELF_SIZE(SocketAddressBase);
|
||||
|
||||
TransferMode GetTransferMode() const override {
|
||||
return TransferMode::kCloneable;
|
||||
}
|
||||
std::unique_ptr<worker::TransferData> CloneForMessaging() const override;
|
||||
|
||||
class TransferData : public worker::TransferData {
|
||||
public:
|
||||
inline explicit TransferData(const SocketAddressBase* wrap)
|
||||
: address_(wrap->address_) {}
|
||||
|
||||
inline explicit TransferData(std::shared_ptr<SocketAddress> address)
|
||||
: address_(std::move(address)) {}
|
||||
|
||||
BaseObjectPtr<BaseObject> Deserialize(
|
||||
Environment* env,
|
||||
v8::Local<v8::Context> context,
|
||||
std::unique_ptr<worker::TransferData> self) override;
|
||||
|
||||
void MemoryInfo(MemoryTracker* tracker) const override;
|
||||
SET_MEMORY_INFO_NAME(SocketAddressBase::TransferData)
|
||||
SET_SELF_SIZE(TransferData)
|
||||
|
||||
private:
|
||||
std::shared_ptr<SocketAddress> address_;
|
||||
};
|
||||
|
||||
private:
|
||||
std::shared_ptr<SocketAddress> address_;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class SocketAddressLRU : public MemoryRetainer {
|
||||
public:
|
||||
|
|
|
|||
110
test/parallel/test-socketaddress.js
Normal file
110
test/parallel/test-socketaddress.js
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
'use strict';
|
||||
|
||||
const common = require('../common');
|
||||
const {
|
||||
ok,
|
||||
strictEqual,
|
||||
throws,
|
||||
} = require('assert');
|
||||
const {
|
||||
SocketAddress,
|
||||
} = require('net');
|
||||
|
||||
{
|
||||
const sa = new SocketAddress();
|
||||
strictEqual(sa.address, '127.0.0.1');
|
||||
strictEqual(sa.port, 0);
|
||||
strictEqual(sa.family, 'ipv4');
|
||||
strictEqual(sa.flowlabel, 0);
|
||||
|
||||
const mc = new MessageChannel();
|
||||
mc.port1.onmessage = common.mustCall(({ data }) => {
|
||||
ok(SocketAddress.isSocketAddress(data));
|
||||
|
||||
strictEqual(data.address, '127.0.0.1');
|
||||
strictEqual(data.port, 0);
|
||||
strictEqual(data.family, 'ipv4');
|
||||
strictEqual(data.flowlabel, 0);
|
||||
|
||||
mc.port1.close();
|
||||
});
|
||||
mc.port2.postMessage(sa);
|
||||
}
|
||||
|
||||
{
|
||||
const sa = new SocketAddress({});
|
||||
strictEqual(sa.address, '127.0.0.1');
|
||||
strictEqual(sa.port, 0);
|
||||
strictEqual(sa.family, 'ipv4');
|
||||
strictEqual(sa.flowlabel, 0);
|
||||
}
|
||||
|
||||
{
|
||||
const sa = new SocketAddress({
|
||||
address: '123.123.123.123',
|
||||
});
|
||||
strictEqual(sa.address, '123.123.123.123');
|
||||
strictEqual(sa.port, 0);
|
||||
strictEqual(sa.family, 'ipv4');
|
||||
strictEqual(sa.flowlabel, 0);
|
||||
}
|
||||
|
||||
{
|
||||
const sa = new SocketAddress({
|
||||
address: '123.123.123.123',
|
||||
port: 80
|
||||
});
|
||||
strictEqual(sa.address, '123.123.123.123');
|
||||
strictEqual(sa.port, 80);
|
||||
strictEqual(sa.family, 'ipv4');
|
||||
strictEqual(sa.flowlabel, 0);
|
||||
}
|
||||
|
||||
{
|
||||
const sa = new SocketAddress({
|
||||
family: 'ipv6'
|
||||
});
|
||||
strictEqual(sa.address, '::');
|
||||
strictEqual(sa.port, 0);
|
||||
strictEqual(sa.family, 'ipv6');
|
||||
strictEqual(sa.flowlabel, 0);
|
||||
}
|
||||
|
||||
{
|
||||
const sa = new SocketAddress({
|
||||
family: 'ipv6',
|
||||
flowlabel: 1,
|
||||
});
|
||||
strictEqual(sa.address, '::');
|
||||
strictEqual(sa.port, 0);
|
||||
strictEqual(sa.family, 'ipv6');
|
||||
strictEqual(sa.flowlabel, 1);
|
||||
}
|
||||
|
||||
[1, false, 'hello'].forEach((i) => {
|
||||
throws(() => new SocketAddress(i), {
|
||||
code: 'ERR_INVALID_ARG_TYPE'
|
||||
});
|
||||
});
|
||||
|
||||
[1, false, {}, [], 'test'].forEach((family) => {
|
||||
throws(() => new SocketAddress({ family }), {
|
||||
code: 'ERR_INVALID_ARG_VALUE'
|
||||
});
|
||||
});
|
||||
|
||||
[1, false, {}, []].forEach((address) => {
|
||||
throws(() => new SocketAddress({ address }), {
|
||||
code: 'ERR_INVALID_ARG_TYPE'
|
||||
});
|
||||
});
|
||||
|
||||
[-1, false, {}, []].forEach((port) => {
|
||||
throws(() => new SocketAddress({ port }), {
|
||||
code: 'ERR_SOCKET_BAD_PORT'
|
||||
});
|
||||
});
|
||||
|
||||
throws(() => new SocketAddress({ flowlabel: -1 }), {
|
||||
code: 'ERR_OUT_OF_RANGE'
|
||||
});
|
||||
Loading…
Reference in New Issue
Block a user