Skip to content

feat: support random port - #394

Open
revington wants to merge 1 commit into
QuorumDMS:mainfrom
revington:support-random-port
Open

feat: support random port#394
revington wants to merge 1 commit into
QuorumDMS:mainfrom
revington:support-random-port

Conversation

@revington

@revington revington commented May 12, 2026

Copy link
Copy Markdown

You can tell node to listen on "next available port" by setting port to 0.
If I tell ftp-srv to listen on port 0, it picks next available port as expected. But there is no way to tell which port was that because the event #listen emits the "port I setup" instead of the port the server is listening to.

On this merge I make the listen event to report the right port.

Acceptance Checklist

  • Story: Code is focused on the linked stories and solves a problem
  • One of:
    • For Bugs: A unit test is added or an existing one modified
    • [ x] For Features: New unit tests are added covering the new functions or modifications
  • Code Documentation changes are included for public interfaces and important / complex additions
  • External Documentation is included for API changes, or other external facing interfaces

Review Checklist

  • [ x] The code does not duplicate existing functionality that exists elsewhere
  • [x ] The code has been linted and follows team practices and style guidelines
  • [ x] The changes in the PR are relevant to the title
    • changes not related should be moved to a different PR
  • All errors or error handling is actionable, and informs the viewer on how to correct it

Summary by CodeRabbit

  • Bug Fixes

    • Server now correctly reports the actual bound port instead of the configured port value.
  • Tests

    • Added test to verify the FTP server accurately reports dynamically assigned listening ports when using port allocation features.

Review Change Stack

@revington
revington requested a review from a team as a code owner May 12, 2026 10:15
@coderabbitai

coderabbitai Bot commented May 12, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

The server's listen() method was logging the pre-configured port from this.url.port. It now logs the actual bound port from this.server.address().port, enabling correct reporting when port 0 triggers dynamic OS assignment. A new test validates this behavior end-to-end.

Changes

Random Port Support

Layer / File(s) Summary
Actual port logging in listen()
src/index.js
The listen() method reads and logs the server's actual bound port (this.server.address().port) instead of the configured this.url.port, enabling dynamic port allocation. Whitespace is adjusted around socket close handling, server error events, and client disconnect cleanup.
Test for dynamic port assignment
test/random-port-support.js
New test suite starts an FtpServer with port 0, captures the assigned port from the log callback, and asserts it is > 0, validating dynamic port allocation works end-to-end.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

Poem

🐰 When ports are wild and zero'd out,
The server finds what it's about.
No longer guess, just ask and see—
The OS picks the port, so free!
A test ensures this magic's right,
Dynamic binding shines so bright.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the main change: updating the listen event to report the actual dynamically-assigned port when listening on port 0.
Description check ✅ Passed The description explains the problem and solution well, and a new unit test was added to verify the feature. However, the checklist shows incomplete items for documentation and error handling.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@test/random-port-support.js`:
- Around line 13-15: The test currently verifies the logger side-effect by
checking that log.info received a non-zero port (via the info({port}) hook and
listeningPort), but it should assert the actual listen event payload/contract
instead; modify the test to attach to the server/emitter's 'listening' event (or
the function that emits the event) and assert the emitted payload contains the
expected port value (e.g., payload.port === expectedPort) rather than only
relying on the info hook, and update the other occurrence at the block around
lines 34-35 likewise to assert the event payload directly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: fb6ef21d-226e-465f-9e42-0537c9b592b0

📥 Commits

Reviewing files that changed from the base of the PR and between 6a3b227 and bbfa962.

📒 Files selected for processing (2)
  • src/index.js
  • test/random-port-support.js

Comment on lines +13 to +15
info({port}) {
listeningPort = port;
},

@coderabbitai coderabbitai Bot May 12, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Test validates logger side-effect, not the listen-event contract.

This assertion only proves log.info received a non-zero port. It does not verify the listen event payload/contract described in the PR objective, so the feature can regress while this test still passes. Please assert the event payload directly (or explicitly align the objective/docs to “log output” instead).

Suggested adjustment
- let listeningPort = 0;
+ let listeningPort = 0;
+ let eventPort = 0;

  before( (done) => {
    server = new FtpServer({
      log: {
        info({port}) {
          listeningPort = port;
        },
        debug() {}
      },
      ...
    });
+   // Replace 'listen' with the actual public event name if different.
+   server.once('listen', ({port}) => {
+     eventPort = port;
+   });
    server.listen().then(done).catch(done);
  });

  it('should emmit the assigned port on listen event', () => {
    expect(listeningPort).to.be.above(0);
+   expect(eventPort).to.equal(listeningPort);
  });

Also applies to: 34-35

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/random-port-support.js` around lines 13 - 15, The test currently
verifies the logger side-effect by checking that log.info received a non-zero
port (via the info({port}) hook and listeningPort), but it should assert the
actual listen event payload/contract instead; modify the test to attach to the
server/emitter's 'listening' event (or the function that emits the event) and
assert the emitted payload contains the expected port value (e.g., payload.port
=== expectedPort) rather than only relying on the info hook, and update the
other occurrence at the block around lines 34-35 likewise to assert the event
payload directly.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good point

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check what event(s) FtpServer emits in src/index.js on listen
rg -n "emit" src/index.js -A2 -B2

Length of output: 1169


🏁 Script executed:

#!/bin/bash
# Check the full listen method in src/index.js
rg -n "listen" src/index.js -A5 -B2

Length of output: 939


@iamart, I need to correct my earlier suggestion — after checking the source, FtpServer.listen() does not emit a 'listen' event at all. It only calls this.log.info(...) and resolves the promise with the string 'Listening'. So server.once('listen', ...) would never fire, making my diff invalid.

To properly establish a testable public contract, the fix needs a small change in both src/index.js and the test. Two options:


Option A — Resolve the promise with the port (minimal change)

src/index.js:

-        resolve('Listening');
+        resolve(listeningPort);

test/random-port-support.js:

- let listeningPort = 0;
+ let listeningPort = 0;
  ...
-   server.listen().then(done).catch(done);
+   server.listen().then((port) => {
+     listeningPort = port;
+     done();
+   }).catch(done);
  ...
  it('should assign a port greater than 0', () => {
    expect(listeningPort).to.be.above(0);
  });

Option B — Emit a named event (more expressive public contract)

src/index.js:

         this.log.info({ protocol: ..., ip: ..., port: listeningPort }, 'Listening');
+        this.emit('listen', { port: listeningPort });
         resolve('Listening');

test/random-port-support.js:

- let listeningPort = 0;
+ let listeningPort = 0;
+ let eventPort = 0;
  ...
+   server.once('listen', ({ port }) => { eventPort = port; });
    server.listen().then(done).catch(done);
  ...
  it('should assign a port greater than 0', () => {
    expect(listeningPort).to.be.above(0);
+   expect(eventPort).to.equal(listeningPort);
  });

Option B aligns most closely with the PR description's language ("the #listen event") and makes the contract explicit and observable by consumers of the library. Option A is simpler if no public event was ever intended.

ᓚᘏᗢ ฅ^•ﻌ•^ฅ

iamart
iamart previously approved these changes May 12, 2026
@iamart
iamart dismissed their stale review May 12, 2026 15:57

see rabbit's review on test file

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants