README
node-socket-activation
node-socket-activation is a socket activation library for Node.js.
Supported systems
Service management framework | OS | Supported? |
---|---|---|
launchd | macOS 10.9 or later | :thumbsup: :bowtie: |
systemd | Compatible Linux distro | :thumbsup: :bowtie: |
Installation
Using yarn:
yarn add socket-activation
Using npm:
npm install --save socket-activation
Usage
How your app should look like
app.js
const net = require('net')
const sockets = require('socket-activation')
// There may be more than one fd, e.g. if you select the "IPv4v6"
// SockFamily which listens to both IPv4 and IPv6. Up to you to
// decide if you want to error out or setup multiple servers as
// follows.
for (const fd of sockets.collect('app')) {
const server = net.createServer(conn => {
conn.end('hello!\n')
})
server.on('error', err => {
throw err
})
server.listen({ fd }, () => {
const addr = server.address()
console.log('Listening on', addr)
})
}
See the examples directory for more examples incl. supporting both socket activation and direct execution.
Launchd configuration
Normally you'd put this in ~/Library/LaunchAgents
.
org.example.app.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>org.example.app</string>
<key>ProgramArguments</key>
<array>
<string>/usr/local/bin/node</string>
<string>ABSOLUTE_PATH_TO_YOUR_JS_FILE</string>
</array>
<key>Sockets</key>
<dict>
<key>app</key>
<array>
<dict>
<key>SockFamily</key>
<string>IPv4</string>
<key>SockServiceName</key>
<string>3000</string>
</dict>
</array>
</dict>
<key>StandardErrorPath</key>
<string>/tmp/org.example.app.err</string>
<key>StandardOutPath</key>
<string>/tmp/org.example.app.out</string>
</dict>
</plist>
There's no need to set RunAtLoad
, launchd will start your app on the first request.
Enable and start listening by running:
launchctl load ~/Library/LaunchAgents/org.example.app.plist
Systemd configuration
Normally you'd put these in /etc/systemd/system
.
app.service
[Unit]
RefuseManualStart=true
[Service]
Restart=always
ExecStart=/usr/local/bin/node ABSOLUTE_PATH_TO_YOUR_JS_FILE
app.socket
[Socket]
ListenStream=127.0.0.1:3000
NoDelay=true
FileDescriptorName=app
[Install]
WantedBy=sockets.target
Enable and start listening by running:
systemctl enable app.socket
systemctl start app.socket
There's no need to enable the service, systemd will start it on the first request.
API
.collect(String name) → [Number]
Returns the list of fds the system has managed for us. This operation consumes the list and can therefore only be performed once per launch. Subsequent calls with the same name
throw an Error
. It's your responsibility not to do that.
The name
is case sensitive and must match the name of the socket. Under systemd, the setting is FileDescriptorName=. Under launchd, the <key>...</key>
of the socket definition defines the name.
Note that the method returns an Array
of fds. For example, there may be more than one fd if you select the "IPv4v6" SockFamily in launchd.plist which listens to both IPv4 and IPv6, or if several ListenStream are given in systemd.socket. Up to you to decide if you want to error out when given multiple fds or setup multiple listeners.
The API is the same for all supported service managers.
This method throws an Error
if:
- The process was not launched by the service management framework (or with
launchd
, if there's no matching socket).- Error
.code
is'ESRCH'
- Error
- There is no matching socket.
- Error
.code
is'ENOENT'
- Error
- The fd list has already been collected previously.
- Error
.code
is'EALREADY'
- Error
- An internal system error occurred.
License
See LICENSE.