Skip to content

Commit 2827aaa

Browse files
committed
os: expose guessFileDescriptorType
Exposes the internal `guessHandleType` function as `guessFileDescriptorType`, which can be used to see if a handle has a specific type, regardless of the OS it is on. This helps out with detecting, for example, if standard input is piped into the process, instead of relying on file system calls. Refs: #57603
1 parent e6ef477 commit 2827aaa

4 files changed

Lines changed: 71 additions & 25 deletions

File tree

doc/api/os.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -508,6 +508,48 @@ On POSIX systems, the operating system release is determined by calling
508508
available, `GetVersionExW()` will be used. See
509509
<https://en.wikipedia.org/wiki/Uname#Examples> for more information.
510510

511+
## `os.guessHandleType(handle)`
512+
513+
<!-- YAML
514+
added: REPLACEME
515+
-->
516+
517+
* `handle` {integer} The handle number to try and guess the type of.
518+
519+
* Returns: {string}
520+
521+
Returns the type of the handle passed in, or `'INVALID'` if the provided handle
522+
is invalid.
523+
524+
Currently, the following types for a handle can be returned:
525+
526+
<table>
527+
<tr>
528+
<th>Constant</th>
529+
</tr>
530+
<tr>
531+
<td><code>TCP</code></td>
532+
</tr>
533+
<tr>
534+
<td><code>TTY</code></td>
535+
</tr>
536+
<tr>
537+
<td><code>UDP</code></td>
538+
</tr>
539+
<tr>
540+
<td><code>FILE</code></td>
541+
</tr>
542+
<tr>
543+
<td><code>PIPE</code></td>
544+
</tr>
545+
<tr>
546+
<td><code>UNKNOWN</code></td>
547+
</tr>
548+
<tr>
549+
<td><code>INVALID</code></td>
550+
</tr>
551+
</table>
552+
511553
## OS constants
512554

513555
The following constants are exported by `os.constants`.

lib/internal/util.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -898,6 +898,8 @@ function getCIDR(address, netmask, family) {
898898
}
899899

900900
const handleTypes = ['TCP', 'TTY', 'UDP', 'FILE', 'PIPE', 'UNKNOWN'];
901+
handleTypes[-1] = 'INVALID';
902+
901903
function guessHandleType(fd) {
902904
const type = _guessHandleType(fd);
903905
return handleTypes[type];

lib/os.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ const {
4040
},
4141
hideStackFrames,
4242
} = require('internal/errors');
43-
const { getCIDR } = require('internal/util');
43+
const { getCIDR, guessHandleType: _guessHandleType } = require('internal/util');
4444
const { validateInt32 } = require('internal/validators');
4545

4646
const {
@@ -329,6 +329,7 @@ module.exports = {
329329
uptime: getUptime,
330330
version: getOSVersion,
331331
machine: getMachine,
332+
guessHandleType: _guessHandleType,
332333
};
333334

334335
ObjectFreeze(constants.signals);

src/node_util.cc

Lines changed: 25 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -67,18 +67,18 @@ static void GetOwnNonIndexProperties(
6767

6868
PropertyFilter filter = FromV8Value<PropertyFilter>(args[1]);
6969

70-
if (!object->GetPropertyNames(
71-
context, KeyCollectionMode::kOwnOnly,
72-
filter,
73-
IndexFilter::kSkipIndices)
74-
.ToLocal(&properties)) {
70+
if (!object
71+
->GetPropertyNames(context,
72+
KeyCollectionMode::kOwnOnly,
73+
filter,
74+
IndexFilter::kSkipIndices)
75+
.ToLocal(&properties)) {
7576
return;
7677
}
7778
args.GetReturnValue().Set(properties);
7879
}
7980

80-
static void GetConstructorName(
81-
const FunctionCallbackInfo<Value>& args) {
81+
static void GetConstructorName(const FunctionCallbackInfo<Value>& args) {
8282
CHECK(args[0]->IsObject());
8383

8484
Local<Object> object = args[0].As<Object>();
@@ -87,8 +87,7 @@ static void GetConstructorName(
8787
args.GetReturnValue().Set(name);
8888
}
8989

90-
static void GetExternalValue(
91-
const FunctionCallbackInfo<Value>& args) {
90+
static void GetExternalValue(const FunctionCallbackInfo<Value>& args) {
9291
CHECK(args[0]->IsExternal());
9392
Isolate* isolate = args.GetIsolate();
9493
Local<External> external = args[0].As<External>();
@@ -101,15 +100,14 @@ static void GetExternalValue(
101100

102101
static void GetPromiseDetails(const FunctionCallbackInfo<Value>& args) {
103102
// Return undefined if it's not a Promise.
104-
if (!args[0]->IsPromise())
105-
return;
103+
if (!args[0]->IsPromise()) return;
106104

107105
auto isolate = args.GetIsolate();
108106

109107
Local<Promise> promise = args[0].As<Promise>();
110108

111109
int state = promise->State();
112-
Local<Value> values[2] = { Integer::New(isolate, state) };
110+
Local<Value> values[2] = {Integer::New(isolate, state)};
113111
size_t number_of_values = 1;
114112
if (state != Promise::PromiseState::kPending)
115113
values[number_of_values++] = promise->Result();
@@ -119,19 +117,15 @@ static void GetPromiseDetails(const FunctionCallbackInfo<Value>& args) {
119117

120118
static void GetProxyDetails(const FunctionCallbackInfo<Value>& args) {
121119
// Return undefined if it's not a proxy.
122-
if (!args[0]->IsProxy())
123-
return;
120+
if (!args[0]->IsProxy()) return;
124121

125122
Local<Proxy> proxy = args[0].As<Proxy>();
126123

127124
// TODO(BridgeAR): Remove the length check as soon as we prohibit access to
128125
// the util binding layer. It's accessed in the wild and `esm` would break in
129126
// case the check is removed.
130127
if (args.Length() == 1 || args[1]->IsTrue()) {
131-
Local<Value> ret[] = {
132-
proxy->GetTarget(),
133-
proxy->GetHandler()
134-
};
128+
Local<Value> ret[] = {proxy->GetTarget(), proxy->GetHandler()};
135129

136130
args.GetReturnValue().Set(
137131
Array::New(args.GetIsolate(), ret, arraysize(ret)));
@@ -167,17 +161,15 @@ static void GetCallerLocation(const FunctionCallbackInfo<Value>& args) {
167161
}
168162

169163
static void PreviewEntries(const FunctionCallbackInfo<Value>& args) {
170-
if (!args[0]->IsObject())
171-
return;
164+
if (!args[0]->IsObject()) return;
172165

173166
Isolate* isolate = args.GetIsolate();
174167
bool is_key_value;
175168
Local<Array> entries;
176169
if (!args[0].As<Object>()->PreviewEntries(&is_key_value).ToLocal(&entries))
177170
return;
178171
// Fast path for WeakMap and WeakSet.
179-
if (args.Length() == 1)
180-
return args.GetReturnValue().Set(entries);
172+
if (args.Length() == 1) return args.GetReturnValue().Set(entries);
181173

182174
Local<Value> ret[] = {entries, Boolean::New(isolate, is_key_value)};
183175
return args.GetReturnValue().Set(Array::New(isolate, ret, arraysize(ret)));
@@ -215,7 +207,10 @@ static uint32_t GetUVHandleTypeCode(const uv_handle_type type) {
215207
case UV_UNKNOWN_HANDLE:
216208
return 5;
217209
default:
218-
ABORT();
210+
// For an unhandled handle type, we want to return `UNKNOWN` instead of
211+
// `INVALID` since the type is "known" by UV, just not exposed further to
212+
// JS land
213+
return 5;
219214
}
220215
}
221216

@@ -224,7 +219,13 @@ static void GuessHandleType(const FunctionCallbackInfo<Value>& args) {
224219
Local<Context> context = isolate->GetCurrentContext();
225220
int fd;
226221
if (!args[0]->Int32Value(context).To(&fd)) return;
227-
CHECK_GE(fd, 0);
222+
223+
// If the provided file descriptor is not valid, we return `-1`, which in JS
224+
// land will be marked as "INVALID"
225+
if (fd < 0) [[unlikely]] {
226+
args.GetReturnValue().Set(-1);
227+
return;
228+
}
228229

229230
uv_handle_type t = uv_guess_handle(fd);
230231
args.GetReturnValue().Set(GetUVHandleTypeCode(t));

0 commit comments

Comments
 (0)