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

Fix pinging the server when it listens on 0.0.0.0 and the user provided the port number #357

Merged
merged 12 commits into from Feb 26, 2023
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')
});