Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

http: add http.createStaticServer #45096

Open
wants to merge 34 commits into
base: main
Choose a base branch
from
Open

Conversation

aduh95
Copy link
Contributor

@aduh95 aduh95 commented Oct 20, 2022

This PR adds a small utility to spawns a really simple HTTP 1.1 server to serve a local directory. The server is not meant to be secure or extremely performant, it's meant to be minimal and a useful replacement for python -m http.server. This should never be used in production of course.

Usage:

node -e 'http.createStaticServer()' # starts serving the cwd on a random port

# To start serving on the port 8080 using /path/to/dir as the root:
node -e 'http.createStaticServer({directory: "/path/to/dir", port: 8080})'

# Same as above, but exposing your local file system to the whole IPv4 network:
node -e 'http.createStaticServer({directory: "/path/to/dir", port: 8080, host: "0.0.0.0"})'

Semver-major if we want to release it without requiring the node: prefix.
EDIT: I've removed the node:http/static module from this PR, it can be discussed in a separate PR.

Fixes: #45079

@aduh95 aduh95 added the semver-major PRs that contain breaking changes and should be released in the next major version. label Oct 20, 2022
@nodejs-github-bot nodejs-github-bot added http Issues or PRs related to the http subsystem. needs-ci PRs that need a full CI run. labels Oct 20, 2022
doc/api/http.md Outdated Show resolved Hide resolved
doc/api/http.md Outdated Show resolved Hide resolved
doc/api/http.md Outdated Show resolved Hide resolved
doc/api/http.md Outdated Show resolved Hide resolved
doc/api/http.md Outdated Show resolved Hide resolved
doc/api/http.md Outdated Show resolved Hide resolved
doc/api/http.md Outdated Show resolved Hide resolved
doc/api/http.md Outdated
node -r node:http/server # starts serving the cwd on a random port

# To start serving on the port 8080 using /path/to/dir as the root:
node -r node:http/server /path/to/dir --port 8080
Copy link
Member

Choose a reason for hiding this comment

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

Not really a fan of building in a preload like this. Someone can easily use the -pe syntax to achieve this if they wanted, e.g. node -pe "require('http').createStaticServer(...)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

One reason to use -r is you can get autocomplete when selecting the root directory, and it's easier to offer shorter versions of flags (i.e. I'd rather type node -r http/server . -p 8080 -h 0.0.0.0 than node -e 'http.createStaticServer({port: 8080, host: '0.0.0.0'})'). Currently this PR offers both syntax, so I guess I should also document that, and folks can chose the syntax they prefer, wdyt?

lib/http/server.js Outdated Show resolved Hide resolved

emitExperimentalWarning('http/server');

if (module.isPreloading) {
Copy link
Member

Choose a reason for hiding this comment

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

This is dangerous. If someone has their own preload module and happens to require('http/server') within it, this part will be run while they may not expect it.

lib/http/server.js Outdated Show resolved Hide resolved
lib/http/server.js Outdated Show resolved Hide resolved
lib/http/server.js Outdated Show resolved Hide resolved
lib/http/server.js Outdated Show resolved Hide resolved
Copy link
Member

@mcollina mcollina left a comment

Choose a reason for hiding this comment

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

This needs tests.

I would also recommend we implement:

  • partial responses (Ranges)
  • conditional-GET negotiation (If-Match, If-Unmodified-Since, If-None-Match, If-Modified-Since) - this includes etag generation.

It's not necessary that those are implemented now, however we should note those are not implemented in the docs.

stream.on('error', reject);
stream.on('close', resolve);
stream.pipe(res);
});
Copy link
Member

Choose a reason for hiding this comment

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

You should use stream.pipeline instead if this.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

pipeline implementation seems overly complicated in comparison of what's needed here (we don't need any AbortSignal support, etc.), and tries to do more that we want (i.e. it closes the request in case of error before we can send a 404 status).

doc/api/http.md Outdated Show resolved Hide resolved
@aduh95 aduh95 marked this pull request as ready for review October 23, 2022 02:49
@aduh95
Copy link
Contributor Author

aduh95 commented Oct 23, 2022

Tests have been added, this is ready for reviews. There's a discussion regarding the name of the module (http/server, or http/static, or?) in #45096 (comment), if you have thoughts please chime in.

@aduh95
Copy link
Contributor Author

aduh95 commented Oct 23, 2022

There are a few CI failures that need to be taken care of in separate PRs, converting this one back to draft.

@aduh95 aduh95 marked this pull request as draft October 23, 2022 16:00
@aduh95 aduh95 changed the title http: add node:http/server http: add node:http/static Oct 26, 2022
@aduh95 aduh95 marked this pull request as ready for review October 26, 2022 01:11
const { validateFunction, validatePort } = require('internal/validators');

if (module.isPreloading) {
const requiredModules = getOptionValue('--require');
Copy link
Member

Choose a reason for hiding this comment

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

Still very much not a fan of building preload into this.

Copy link
Member

Choose a reason for hiding this comment

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

Keep in mind, for instance, that -r is permitted in the NODE_OPTIONS

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Do you have in mind what changes we would want to bring before we're happy with this pattern? Or do you think the pattern itself is undesirable? Personally I quite like the UX of it, and I think it's better than coming up with another built-in flag, but I'm happy to withdraw it if I'm in the minority.

lib/http/static.js Outdated Show resolved Hide resolved
@aduh95
Copy link
Contributor Author

aduh95 commented Nov 23, 2022

@mcollina are you still blocking this?

Copy link
Member

@mcollina mcollina left a comment

Choose a reason for hiding this comment

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

lgtm

Copy link

@vlourenco-stf vlourenco-stf left a comment

Choose a reason for hiding this comment

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

Non-blocking comments, just things to make it a little bit faster.

lib/internal/http/static.js Outdated Show resolved Hide resolved
lib/internal/http/static.js Show resolved Hide resolved

try {
try {
const stream = createReadStream(url, { emitClose: false });

Choose a reason for hiding this comment

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

I know is a nitpick but I think you can cache this object to avoid creating every time.

Also, maybe expose the highWaterMark to the user configure is a good idea.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm not sure I understand what you mean, do you mean cache the stream? We're going to need a different stream for each request, I think, so I don't see how it'd be useful 🤔 could you send an example maybe?

Copy link
Member

Choose a reason for hiding this comment

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

Caching { emitClose: false }

Copy link
Member

@ruyadorno ruyadorno left a comment

Choose a reason for hiding this comment

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

LGTM 👍

@ruyadorno ruyadorno added the notable-change PRs with changes that should be highlighted in changelogs. label Apr 4, 2024
Copy link
Contributor

github-actions bot commented Apr 4, 2024

The notable-change PRs with changes that should be highlighted in changelogs. label has been added by @ruyadorno.

Please suggest a text for the release notes if you'd like to include a more detailed summary, then proceed to update the PR description with the text or a link to the notable change suggested text comment. Otherwise, the commit will be placed in the Other Notable Changes section.

@tniessen tniessen added the semver-minor PRs that contain new features and should be released in the next minor version. label Apr 4, 2024
@tniessen
Copy link
Member

tniessen commented Apr 4, 2024

Should there be a note about security in the docs? Specifically, I am wondering what would constitute a vulnerability here.

@ruyadorno
Copy link
Member

@aduh95 heads up, please update the usage example in the body so that releasers & other collaborators can potentially refer to it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
http Issues or PRs related to the http subsystem. needs-ci PRs that need a full CI run. notable-change PRs with changes that should be highlighted in changelogs. review wanted PRs that need reviews. semver-minor PRs that contain new features and should be released in the next minor version.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

feat: Implement a way to expose static files from a directory (similar to Python SimpleHttpServer)