Compare commits

...

1 Commits

Author SHA1 Message Date
Jarred Sumner
ac96742dac Add readable/writable pipe flags 2025-05-28 19:12:56 -07:00
6 changed files with 129 additions and 0 deletions

View File

@@ -1131,6 +1131,19 @@ LIBUS_SOCKET_DESCRIPTOR bsd_create_listen_socket_unix(const char *path, size_t l
}
#endif
#ifndef _WIN32
if (listenFd != LIBUS_SOCKET_ERROR && (options & (LIBUS_LISTEN_READABLE_ALL | LIBUS_LISTEN_WRITABLE_ALL))) {
mode_t mode = 0;
if (options & LIBUS_LISTEN_READABLE_ALL) {
mode |= S_IRUSR | S_IRGRP | S_IROTH;
}
if (options & LIBUS_LISTEN_WRITABLE_ALL) {
mode |= S_IWUSR | S_IWGRP | S_IWOTH;
}
chmod(path, mode);
}
#endif
return listenFd;
}

View File

@@ -102,6 +102,8 @@ enum {
LIBUS_SOCKET_IPV6_ONLY = 8,
LIBUS_LISTEN_REUSE_ADDR = 16,
LIBUS_LISTEN_DISALLOW_REUSE_PORT_FAILURE = 32,
LIBUS_LISTEN_READABLE_ALL = 64,
LIBUS_LISTEN_WRITABLE_ALL = 128,
};
/* Library types publicly available */

View File

@@ -313,6 +313,8 @@ pub const SocketConfig = struct {
allowHalfOpen: bool = false,
reusePort: bool = false,
ipv6Only: bool = false,
readableAll: bool = false,
writableAll: bool = false,
pub fn socketFlags(this: *const SocketConfig) i32 {
var flags: i32 = if (this.exclusive)
@@ -328,6 +330,12 @@ pub const SocketConfig = struct {
if (this.ipv6Only) {
flags |= uws.LIBUS_SOCKET_IPV6_ONLY;
}
if (this.readableAll) {
flags |= uws.LIBUS_LISTEN_READABLE_ALL;
}
if (this.writableAll) {
flags |= uws.LIBUS_LISTEN_WRITABLE_ALL;
}
return flags;
}
@@ -341,6 +349,8 @@ pub const SocketConfig = struct {
var allowHalfOpen = false;
var reusePort = false;
var ipv6Only = false;
var readableAll = false;
var writableAll = false;
var ssl: ?JSC.API.ServerConfig.SSLConfig = null;
var default_data = JSValue.zero;
@@ -404,6 +414,12 @@ pub const SocketConfig = struct {
if (try opts.getBooleanLoose(globalObject, "ipv6Only")) |ipv6_only| {
ipv6Only = ipv6_only;
}
if (try opts.getBooleanLoose(globalObject, "readableAll")) |readable_all| {
readableAll = readable_all;
}
if (try opts.getBooleanLoose(globalObject, "writableAll")) |writable_all| {
writableAll = writable_all;
}
if (try opts.getStringish(globalObject, "hostname") orelse try opts.getStringish(globalObject, "host")) |hostname| {
defer hostname.deref();
@@ -473,6 +489,8 @@ pub const SocketConfig = struct {
.allowHalfOpen = allowHalfOpen,
.reusePort = reusePort,
.ipv6Only = ipv6Only,
.readableAll = readableAll,
.writableAll = writableAll,
};
}
};

View File

@@ -8,6 +8,8 @@ pub const LIBUS_LISTEN_REUSE_PORT: i32 = 4;
pub const LIBUS_SOCKET_IPV6_ONLY: i32 = 8;
pub const LIBUS_LISTEN_REUSE_ADDR: i32 = 16;
pub const LIBUS_LISTEN_DISALLOW_REUSE_PORT_FAILURE: i32 = 32;
pub const LIBUS_LISTEN_READABLE_ALL: i32 = 64;
pub const LIBUS_LISTEN_WRITABLE_ALL: i32 = 128;
pub const Socket = @import("uws/socket.zig").Socket;
pub const ConnectingSocket = opaque {};

View File

@@ -2359,6 +2359,8 @@ Server.prototype[kRealListen] = function (
reusePort: reusePort || this[bunSocketServerOptions]?.reusePort || false,
ipv6Only: ipv6Only || this[bunSocketServerOptions]?.ipv6Only || false,
exclusive: exclusive || this[bunSocketServerOptions]?.exclusive || false,
readableAll: this[bunSocketServerOptions]?.readableAll || false,
writableAll: this[bunSocketServerOptions]?.writableAll || false,
socket: ServerHandlers,
});
} else if (fd != null) {

View File

@@ -0,0 +1,92 @@
const common = require('../common');
const net = require('net');
const assert = require('assert');
const fs = require('fs');
const tmpdir = require('../common/tmpdir');
tmpdir.refresh();
function closeServer() {
return common.mustCall(function () {
this.close();
});
}
let counter = 0;
// Avoid conflict with listen-handle
function randomPipePath() {
return `${common.PIPE}-listen-path-${counter++}`;
}
// Test listen(path)
{
const handlePath = randomPipePath();
net.createServer().listen(handlePath).on('listening', closeServer());
}
// Test listen({path})
{
const handlePath = randomPipePath();
net.createServer().listen({ path: handlePath }).on('listening', closeServer());
}
// Test listen(path, cb)
{
const handlePath = randomPipePath();
net.createServer().listen(handlePath, closeServer());
}
// Test listen({path}, cb)
{
const handlePath = randomPipePath();
net.createServer().listen({ path: handlePath }, closeServer());
}
// Test pipe chmod
{
const handlePath = randomPipePath();
const server = net.createServer().listen(
{
path: handlePath,
readableAll: true,
writableAll: true,
},
common.mustCall(() => {
if (process.platform !== 'win32') {
const mode = fs.statSync(handlePath).mode;
assert.notStrictEqual(mode & fs.constants.S_IROTH, 0);
assert.notStrictEqual(mode & fs.constants.S_IWOTH, 0);
}
server.close();
}),
);
}
// Test should emit "error" events when listening fails.
{
const handlePath = randomPipePath();
const server1 = net.createServer().listen({ path: handlePath }, () => {
// As the handlePath is in use, binding to the same address again should
// make the server emit an 'EADDRINUSE' error.
const server2 = net
.createServer()
.listen(
{
path: handlePath,
writableAll: true,
},
common.mustNotCall(),
);
server2.on(
'error',
common.mustCall((err) => {
server1.close();
assert.strictEqual(err.code, 'EADDRINUSE');
assert.match(err.message, /^listen EADDRINUSE: address already in use/);
}),
);
});
}