Skip to content

Commit

Permalink
fix: pinging the server at 127.0.0.1 when using just the port number (#…
Browse files Browse the repository at this point in the history
…357)

* add 0.0.0.0 server ci example

* add explicit zero test

* add timeout to the waiting failing demo

* add test and explanation

* run more tests in Node v16

* add debugging

* update what we run on node v16

* switch from localhost to 127.0.0.1 for default host

* prepend localhost and common host names with http

* update readme

* ipv6 test

* update localhost in README snippets
  • Loading branch information
bahmutov committed Feb 26, 2023
1 parent 0e9984e commit 9df0423
Show file tree
Hide file tree
Showing 7 changed files with 136 additions and 32 deletions.
39 changes: 38 additions & 1 deletion .github/workflows/ci.yml
Expand Up @@ -71,6 +71,34 @@ jobs:
- name: Run demo expect 403 code 📊
run: npm run demo-expect-403

tests-node-v16:
runs-on: ubuntu-latest
steps:
- name: Checkout 🛎
uses: actions/checkout@v3

- uses: actions/setup-node@v3
with:
node-version: 16

- name: Node version 🖨️
run: node -v

- name: NPM install
uses: bahmutov/npm-install@v1

- name: Run json-server test
run: npm run demo-json-server

- name: Run explicit 0.0.0.0 host example
run: npm run demo-zero-explicit

- name: Run 0.0.0.0 host with 127.0.0.1 url
run: npm run demo-zero-127

- name: Run 0.0.0.0 host example
run: npm run demo-zero

tests-node-v18:
runs-on: ubuntu-latest
steps:
Expand All @@ -93,8 +121,17 @@ jobs:
- name: Run ::1 host example
run: npm run demo-ip6

- name: Run explicit 0.0.0.0 host example
run: npm run demo-zero-explicit

- name: Run 0.0.0.0 host with 127.0.0.1 url
run: npm run demo-zero-127

- name: Run 0.0.0.0 host example
run: npm run demo-zero

release:
needs: ['tests1', 'tests2', 'tests-node-v18']
needs: ['tests1', 'tests2', 'tests-node-v16', 'tests-node-v18']
if: github.ref == 'refs/heads/master'
runs-on: ubuntu-latest
steps:
Expand Down
37 changes: 28 additions & 9 deletions README.md
Expand Up @@ -80,14 +80,17 @@ You can use either `start-server-and-test`, `server-test` or `start-test` comman
You can use `:` in front of port number like `server-test :8080`, so all these are equivalent

```
start-server-and-test start http://localhost:8080 test
server-test start http://localhost:8080 test
server-test http://localhost:8080 test
start-server-and-test start http://127.0.0.1:8080 test
server-test start http://127.0.0.1:8080 test
server-test http://127.0.0.1:8080 test
server-test 127.0.0.1:8080 test
start-test :8080 test
start-test 8080 test
start-test 8080
```

**Tip:** I highly recommend you specify the full url instead of the port, see the `localhost vs 0.0.0.0 vs 127.0.0.1` section later in this README.

### Options

If you use convention and name your scripts "start" and "test" you can simply provide URL
Expand All @@ -102,7 +105,7 @@ If you use convention and name your scripts "start" and "test" you can simply pr
}
```

You can also shorten local url to just port, the code below is equivalent to checking `http://localhost:8080`.
You can also shorten local url to just port, the code below is equivalent to checking `http://127.0.0.1:8080`.

```json
{
Expand Down Expand Up @@ -165,7 +168,7 @@ If you want to start the server, wait for it to respond, and then run multiple t
}
```

The above script `ci` after the `localhost:9000` responds executes the `npm run test:unit` command. Then when it finishes it runs `npm run test:e2e`. If the first or second command fails, the `ci` script fails. Of course, your mileage on Windows might vary.
The above script `ci` after the `127.0.0.1:9000` responds executes the `npm run test:unit` command. Then when it finishes it runs `npm run test:e2e`. If the first or second command fails, the `ci` script fails. Of course, your mileage on Windows might vary.

#### expected

Expand Down Expand Up @@ -198,7 +201,7 @@ Then you can execute tests simply:
```text
$ npx start-test 'http-server -c-1 .' 8080 'cypress run'
starting server using command "http-server -c-1 ."
and when url "http://localhost:8080" is responding
and when url "http://127.0.0.1:8080" is responding
running tests using command "cypress run"
Starting up http-server, serving .
...
Expand All @@ -211,12 +214,28 @@ $ yarn start-test 'http-server -c-1 .' 8080 'cypress run'
yarn run v1.13.0
$ /private/tmp/test-t/node_modules/.bin/start-test 'http-server -c-1 .' 8080 'cypress run'
starting server using command "http-server -c-1 ."
and when url "http://localhost:8080" is responding
and when url "http://127.0.0.1:8080" is responding
running tests using command "cypress run"
Starting up http-server, serving .
...
```

## localhost vs 0.0.0.0 vs 127.0.0.1

The latest versions of Node and some web servers listen on host `0.0.0.0` which _no longer means localhost_. Thus if you specify _just the port number_, like `:3000`, this package will try `http://127.0.0.1:3000` to ping the server. A good practice is to specify the full URL you would like to ping.

```
# same as "http://127.0.0.1:3000"
start-server start 3000 test
# better
start-server start http://127.0.0.1:3000 test
# or
start-server start http://0.0.0.0:3000 test
# of course, if your server is listening on localhost
# you can still set the URL
start-server start http://localhost:3000 test
```

## Note for yarn users

By default, npm is used to run scripts, however you can specify that yarn is used as follows:
Expand Down Expand Up @@ -279,8 +298,8 @@ $ DEBUG=start-server-and-test npm run test
start-server-and-test parsing CLI arguments: [ 'dev', '3000', 'subtask' ] +0ms
start-server-and-test parsed args: { services: [ { start: 'npm run dev', url: [Array] } ], test: 'npm run subtask' }
...
making HTTP(S) head request to url:http://localhost:3000 ...
HTTP(S) error for http://localhost:3000 Error: Request failed with status code 404
making HTTP(S) head request to url:http://127.0.0.1:3000 ...
HTTP(S) error for http://127.0.0.1:3000 Error: Request failed with status code 404
```

### Disable HTTPS certificate checks
Expand Down
26 changes: 13 additions & 13 deletions __snapshots__/utils-spec.js
Expand Up @@ -53,13 +53,13 @@ exports['utils getArguments allows 5 arguments 1'] = {
{
"start": "npm run start",
"url": [
"http://localhost:6000"
"http://127.0.0.1:6000"
]
},
{
"start": "start:web",
"url": [
"http://localhost:6010"
"http://127.0.0.1:6010"
]
}
],
Expand All @@ -80,13 +80,13 @@ exports['utils getArguments determines NPM script for each command 1'] = {
{
"start": "npm run startA",
"url": [
"http://localhost:6000"
"http://127.0.0.1:6000"
]
},
{
"start": "npm run startB",
"url": [
"http://localhost:6010"
"http://127.0.0.1:6010"
]
}
],
Expand Down Expand Up @@ -117,7 +117,7 @@ exports['utils getArguments returns 3 arguments 1'] = {
{
"start": "npm run start",
"url": [
"http://localhost:8080"
"http://127.0.0.1:8080"
]
}
],
Expand All @@ -142,7 +142,7 @@ exports['utils getArguments understands custom commands 1'] = {
{
"start": "custom-command --with argument",
"url": [
"http://localhost:3000"
"http://127.0.0.1:3000"
]
}
],
Expand All @@ -154,9 +154,9 @@ exports['utils getArguments understands several ports 1'] = {
{
"start": "npm run start",
"url": [
"http://localhost:3000",
"http://localhost:4000",
"http://localhost:5000"
"http://127.0.0.1:3000",
"http://127.0.0.1:4000",
"http://127.0.0.1:5000"
]
}
],
Expand All @@ -168,7 +168,7 @@ exports['utils getArguments understands single :port 1'] = {
{
"start": "npm run start",
"url": [
"http://localhost:3000"
"http://127.0.0.1:3000"
]
}
],
Expand All @@ -180,7 +180,7 @@ exports['utils getArguments understands single port 1'] = {
{
"start": "npm run start",
"url": [
"http://localhost:3000"
"http://127.0.0.1:3000"
]
}
],
Expand All @@ -192,7 +192,7 @@ exports['utils getArguments understands start plus url 1'] = {
{
"start": "start-server",
"url": [
"http://localhost:6000"
"http://127.0.0.1:6000"
]
}
],
Expand All @@ -204,7 +204,7 @@ exports['utils getArguments understands url plus test 1'] = {
{
"start": "npm run start",
"url": [
"http://localhost:6000"
"http://127.0.0.1:6000"
]
}
],
Expand Down
7 changes: 5 additions & 2 deletions package.json
Expand Up @@ -100,8 +100,11 @@
"demo-commands": "node src/bin/start.js 'node test/server.js --port 8800' 8800 'node test/client --port 8800'",
"demo-multiple": "node src/bin/start.js 'node test/server --port 6000' 6000 'node test/server --port 6010' 6010 'curl http://127.0.0.1:6000 && curl http://127.0.0.1:6010'",
"demo-multiple-test-commands": "node src/bin/start.js 9000 'npm run message && npm run message2'",
"demo-json-server": "node src/bin/start.js 'json-server test/data.json' 3000 'echo json-server working'",
"demo-ip6": "node src/bin/start.js 'node test/ip6.mjs' 8000 'echo server with ::1 working'"
"demo-json-server": "WAIT_ON_TIMEOUT=10000 DEBUG=start-server-and-test node src/bin/start.js 'json-server test/data.json' localhost:3000 'echo json-server working'",
"demo-ip6": "WAIT_ON_TIMEOUT=10000 DEBUG=start-server-and-test node src/bin/start.js 'node test/ip6.mjs' localhost:8000 'echo server with ::1 working'",
"demo-zero": "WAIT_ON_TIMEOUT=10000 node src/bin/start.js 'node test/zero.mjs' 8000 'echo server with 0.0.0.0 working'",
"demo-zero-127": "WAIT_ON_TIMEOUT=10000 node src/bin/start.js 'node test/zero.mjs' http://127.0.0.1:8000 'echo server with 0.0.0.0 working'",
"demo-zero-explicit": "node src/bin/start.js 'node test/zero.mjs' http://0.0.0.0:8000 'echo server with 0.0.0.0 working'"
},
"devDependencies": {
"@types/node": "^18.14.1",
Expand Down
23 changes: 19 additions & 4 deletions src/utils-spec.js
Expand Up @@ -201,12 +201,27 @@ describe('utils', () => {
})

it('changes port to localhost', () => {
la(arrayEq(normalizeUrl('6006'), ['http://localhost:6006']))
la(arrayEq(normalizeUrl(8080), ['http://localhost:8080']))
la(arrayEq(normalizeUrl('6006'), ['http://127.0.0.1:6006']))
la(arrayEq(normalizeUrl(8080), ['http://127.0.0.1:8080']))
})

it('changes :port to localhost', () => {
la(arrayEq(normalizeUrl(':6006'), ['http://localhost:6006']))
la(arrayEq(normalizeUrl(':6006'), ['http://127.0.0.1:6006']))
})

it('appends http to localhost', () => {
la(arrayEq(normalizeUrl('localhost'), ['http://localhost']))
la(arrayEq(normalizeUrl('localhost:3030'), ['http://localhost:3030']))
})

it('appends http to 127.0.0.1', () => {
la(arrayEq(normalizeUrl('127.0.0.1'), ['http://127.0.0.1']))
la(arrayEq(normalizeUrl('127.0.0.1:3030'), ['http://127.0.0.1:3030']))
})

it('appends http to 0.0.0.0', () => {
la(arrayEq(normalizeUrl('0.0.0.0'), ['http://0.0.0.0']))
la(arrayEq(normalizeUrl('0.0.0.0:3030'), ['http://0.0.0.0:3030']))
})

it('returns original argument if does not know what to do', () => {
Expand All @@ -217,7 +232,7 @@ describe('utils', () => {
it('parses multiple resources', () => {
la(
arrayEq(normalizeUrl(':6006|http://foo.com'), [
'http://localhost:6006',
'http://127.0.0.1:6006',
'http://foo.com'
])
)
Expand Down
19 changes: 16 additions & 3 deletions src/utils.js
Expand Up @@ -189,28 +189,41 @@ const isUrlOrPort = input => {
})
}

/**
* Returns the host to ping if the user specified just the port.
* For a long time, the safest bet was "localhost", but now modern
* web servers seem to bind to "0.0.0.0", which means
* the "127.0.0.1" works better
*/
const getHost = () => '127.0.0.1'

const normalizeUrl = input => {
const str = is.string(input) ? input.split('|') : [input]
const defaultHost = getHost()

return str.map(s => {
if (is.url(s)) {
return s
}

if (is.number(s) && is.port(s)) {
return `http://localhost:${s}`
return `http://${defaultHost}:${s}`
}

if (!is.string(s)) {
return s
}

if (s.startsWith('localhost') || s.startsWith('127.0.0.1') || s.startsWith('0.0.0.0')) {
return `http://${s}`
}

if (is.port(parseInt(s))) {
return `http://localhost:${s}`
return `http://${defaultHost}:${s}`
}

if (s[0] === ':') {
return `http://localhost${s}`
return `http://${defaultHost}${s}`
}
// for anything else, return original argument
return s
Expand Down
17 changes: 17 additions & 0 deletions test/zero.mjs
@@ -0,0 +1,17 @@
import http from 'node:http';

// Create a local server to receive data from
const server = http.createServer();

// Listen to the request event
server.on('request', (request, res) => {
console.log('server responding')
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({
data: 'Hello World!',
}));
});

server.listen(8000, '0.0.0.0', () => {
console.log('server is listening on 0.0.0.0:8000')
});

0 comments on commit 9df0423

Please sign in to comment.