-
-
Notifications
You must be signed in to change notification settings - Fork 22
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: alternative ips support (#243)
Co-authored-by: Martin Kolárik <martin@kolarik.sk>
- Loading branch information
1 parent
35635a3
commit 02f57ff
Showing
10 changed files
with
189 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
import os from 'node:os'; | ||
import config from 'config'; | ||
import _ from 'lodash'; | ||
import { scopedLogger } from '../lib/logger.js'; | ||
import got, { RequestError } from 'got'; | ||
|
||
const logger = scopedLogger('api:connect:alt-ips-handler'); | ||
|
||
export const apiConnectAltIpsHandler = async ({ token, socketId, ip }: { token: string, socketId: string, ip: string }): Promise<void> => { | ||
const allIps = [ ip ]; | ||
const addresses = _(os.networkInterfaces()) | ||
.values() | ||
.filter((int): int is os.NetworkInterfaceInfo[] => !!int) | ||
.flatten() | ||
.uniqBy('address') | ||
.filter(address => !address.internal) | ||
.filter(address => !address.address.startsWith('fe80:')) // filter out link-local addresses | ||
.filter(address => !address.address.startsWith('169.254.')) // filter out link-local addresses | ||
.value(); | ||
|
||
const results = await Promise.allSettled(addresses.map(({ address, family }) => sendToken(address, family === 'IPv6' ? 6 : 4, token, socketId))); | ||
|
||
results.forEach((result) => { | ||
if (result.status === 'fulfilled') { | ||
allIps.push(result.value); | ||
} else { | ||
if (!(result.reason instanceof RequestError)) { | ||
logger.error(result.reason); | ||
} else if (result.reason.response?.statusCode !== 400) { | ||
logger.error(result.reason.message); | ||
} | ||
} | ||
}); | ||
|
||
const uniqIps = _(allIps).uniq().value(); | ||
|
||
if (uniqIps.length === 1) { | ||
logger.info(`IP address of the probe: ${uniqIps[0]}`); | ||
} else { | ||
logger.info(`IP addresses of the probe: ${uniqIps.join(', ')}`); | ||
} | ||
}; | ||
|
||
const sendToken = async (ip: string, dnsLookupIpVersion: 4 | 6, token: string, socketId: string) => { | ||
const httpHost = config.get<string>('api.httpHost'); | ||
const response = await got.post<{ ip: string }>(`${httpHost}/alternative-ip`, { | ||
localAddress: ip, | ||
dnsLookupIpVersion, | ||
json: { | ||
token, | ||
socketId, | ||
}, | ||
retry: { | ||
limit: 1, | ||
methods: [ 'POST' ], | ||
statusCodes: [ 504 ], | ||
}, | ||
responseType: 'json', | ||
}); | ||
|
||
return response.body.ip; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
import * as td from 'testdouble'; | ||
import sinon from 'sinon'; | ||
import nock from 'nock'; | ||
import { expect } from 'chai'; | ||
|
||
import type { apiConnectAltIpsHandler as apiConnectAltIpsHandlerSrc } from '../../../src/helper/alt-ips-handler.js'; | ||
|
||
describe('apiConnectAltIpsHandler', async () => { | ||
const networkInterfaces = sinon.stub(); | ||
let apiConnectAltIpsHandler: typeof apiConnectAltIpsHandlerSrc; | ||
|
||
before(async () => { | ||
await td.replaceEsm('node:os', {}, { | ||
networkInterfaces, | ||
}); | ||
|
||
({ apiConnectAltIpsHandler } = await import('../../../src/helper/alt-ips-handler.js')); | ||
}); | ||
|
||
beforeEach(() => { | ||
networkInterfaces.returns({ | ||
lo: [ | ||
{ | ||
address: '127.0.0.1', | ||
netmask: '255.0.0.0', | ||
family: 'IPv4', | ||
mac: '00:00:00:00:00:00', | ||
internal: true, | ||
cidr: '127.0.0.1/8', | ||
}, | ||
{ | ||
address: '::1', | ||
netmask: 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', | ||
family: 'IPv6', | ||
mac: '00:00:00:00:00:00', | ||
internal: true, | ||
cidr: '::1/128', | ||
scopeid: 0, | ||
}, | ||
], | ||
ens5: [ | ||
{ | ||
address: '172.31.43.80', | ||
netmask: '255.255.240.0', | ||
family: 'IPv4', | ||
mac: '0a:ab:82:5a:50:d1', | ||
internal: false, | ||
cidr: '172.31.43.80/20', | ||
}, | ||
{ | ||
address: '172.31.43.80', | ||
netmask: '255.255.240.0', | ||
family: 'IPv4', | ||
mac: '0a:ab:82:5a:50:d1', | ||
internal: false, | ||
cidr: '172.31.43.80/20', | ||
}, | ||
{ | ||
address: '2a05:d016:174:7b28:f47b:e6:3307:fab6', | ||
netmask: 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', | ||
family: 'IPv6', | ||
mac: '0a:ab:82:5a:50:d1', | ||
internal: false, | ||
cidr: '2a05:d016:174:7b28:f47b:e6:3307:fab6/128', | ||
scopeid: 0, | ||
}, | ||
{ | ||
address: 'fe80::8ab:82ff:fe5a:50d1', | ||
netmask: 'ffff:ffff:ffff:ffff::', | ||
family: 'IPv6', | ||
mac: '0a:ab:82:5a:50:d1', | ||
internal: false, | ||
cidr: 'fe80::8ab:82ff:fe5a:50d1/64', | ||
scopeid: 2, | ||
}, | ||
], | ||
}); | ||
}); | ||
|
||
afterEach(() => { | ||
nock.cleanAll(); | ||
}); | ||
|
||
after(() => { | ||
td.reset(); | ||
}); | ||
|
||
it('should send alt ip request through valid addresses', async () => { | ||
const reqs = []; | ||
const nockRequest = nock('https://api.globalping.io/v1').persist() | ||
.post('/alternative-ip', (body) => { | ||
expect(body).to.deep.equal({ token: 'token', socketId: 'socketId' }); | ||
return true; | ||
}).reply(200, function () { | ||
reqs.push(this.req); | ||
}); | ||
|
||
await apiConnectAltIpsHandler({ | ||
token: 'token', | ||
socketId: 'socketId', | ||
ip: '3.3.3.3', | ||
}); | ||
|
||
expect(reqs.length).to.equal(2); | ||
expect(reqs[0].options.localAddress).to.equal('172.31.43.80'); | ||
expect(reqs[1].options.localAddress).to.equal('2a05:d016:174:7b28:f47b:e6:3307:fab6'); | ||
expect(nockRequest.isDone()).to.equal(true); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters