Debt: Test findPorts (#112092)

Add a test for the port finding logic
This commit is contained in:
Alex Ross
2020-12-08 17:03:21 +01:00
committed by GitHub
parent 5e5ae15b22
commit b3a3dc9c0a
2 changed files with 353 additions and 86 deletions

View File

@@ -34,6 +34,92 @@ class ExtensionTunnel implements vscode.Tunnel {
}
}
export function getSockets(stdout: string): { pid: number, socket: number }[] {
const lines = stdout.trim().split('\n');
const mapped: { pid: number, socket: number }[] = [];
lines.forEach(line => {
const match = /\/proc\/(\d+)\/fd\/\d+ -> socket:\[(\d+)\]/.exec(line)!;
if (match && match.length >= 3) {
mapped.push({
pid: parseInt(match[1], 10),
socket: parseInt(match[2], 10)
});
}
});
return mapped;
}
export function loadListeningPorts(...stdouts: string[]): { socket: number, ip: string, port: number }[] {
const table = ([] as Record<string, string>[]).concat(...stdouts.map(loadConnectionTable));
return [
...new Map(
table.filter(row => row.st === '0A')
.map(row => {
const address = row.local_address.split(':');
return {
socket: parseInt(row.inode, 10),
ip: parseIpAddress(address[0]),
port: parseInt(address[1], 16)
};
}).map(port => [port.ip + ':' + port.port, port])
).values()
];
}
export function parseIpAddress(hex: string): string {
let result = '';
if (hex.length === 8) {
for (let i = hex.length - 2; i >= 0; i -= 2) {
result += parseInt(hex.substr(i, 2), 16);
if (i !== 0) {
result += '.';
}
}
} else {
for (let i = hex.length - 4; i >= 0; i -= 4) {
result += parseInt(hex.substr(i, 4), 16).toString(16);
if (i !== 0) {
result += ':';
}
}
}
return result;
}
export function loadConnectionTable(stdout: string): Record<string, string>[] {
const lines = stdout.trim().split('\n');
const names = lines.shift()!.trim().split(/\s+/)
.filter(name => name !== 'rx_queue' && name !== 'tm->when');
const table = lines.map(line => line.trim().split(/\s+/).reduce((obj, value, i) => {
obj[names[i] || i] = value;
return obj;
}, {} as Record<string, string>));
return table;
}
export async function findPorts(tcp: string, tcp6: string, procSockets: string, processes: { pid: number, cwd: string, cmd: string }[]) {
const connections: { socket: number, ip: string, port: number }[] = loadListeningPorts(tcp, tcp6);
const sockets = getSockets(procSockets);
const socketMap = sockets.reduce((m, socket) => {
m[socket.socket] = socket;
return m;
}, {} as Record<string, typeof sockets[0]>);
const processMap = processes.reduce((m, process) => {
m[process.pid] = process;
return m;
}, {} as Record<string, typeof processes[0]>);
const ports: { host: string, port: number, detail: string }[] = [];
connections.filter((connection => socketMap[connection.socket])).forEach(({ socket, ip, port }) => {
const command = processMap[socketMap[socket].pid].cmd;
if (!command.match(/.*\.vscode-server-[a-zA-Z]+\/bin.*/) && (command.indexOf('out/vs/server/main.js') === -1)) {
ports.push({ host: ip, port, detail: processMap[socketMap[socket].pid].cmd });
}
});
return ports;
}
export class ExtHostTunnelService extends Disposable implements IExtHostTunnelService {
readonly _serviceBrand: undefined;
private readonly _proxy: MainThreadTunnelServiceShape;
@@ -137,9 +223,7 @@ export class ExtHostTunnelService extends Disposable implements IExtHostTunnelSe
return undefined;
}
async findCandidatePorts(): Promise<{ host: string, port: number, detail: string }[]> {
const ports: { host: string, port: number, detail: string }[] = [];
let tcp: string = '';
let tcp6: string = '';
try {
@@ -170,89 +254,6 @@ export class ExtHostTunnelService extends Disposable implements IExtHostTunnelSe
//
}
}
const connections: { socket: number, ip: string, port: number }[] = this.loadListeningPorts(tcp, tcp6);
const sockets = this.getSockets(procSockets);
const socketMap = sockets.reduce((m, socket) => {
m[socket.socket] = socket;
return m;
}, {} as Record<string, typeof sockets[0]>);
const processMap = processes.reduce((m, process) => {
m[process.pid] = process;
return m;
}, {} as Record<string, typeof processes[0]>);
connections.filter((connection => socketMap[connection.socket])).forEach(({ socket, ip, port }) => {
const command = processMap[socketMap[socket].pid].cmd;
if (!command.match(/.*\.vscode-server-[a-zA-Z]+\/bin.*/) && (command.indexOf('out/vs/server/main.js') === -1)) {
ports.push({ host: ip, port, detail: processMap[socketMap[socket].pid].cmd });
}
});
return ports;
}
private getSockets(stdout: string): { pid: number, socket: number }[] {
const lines = stdout.trim().split('\n');
const mapped: { pid: number, socket: number }[] = [];
lines.forEach(line => {
const match = /\/proc\/(\d+)\/fd\/\d+ -> socket:\[(\d+)\]/.exec(line)!;
if (match && match.length >= 3) {
mapped.push({
pid: parseInt(match[1], 10),
socket: parseInt(match[2], 10)
});
}
});
return mapped;
}
private loadListeningPorts(...stdouts: string[]): { socket: number, ip: string, port: number }[] {
const table = ([] as Record<string, string>[]).concat(...stdouts.map(this.loadConnectionTable));
return [
...new Map(
table.filter(row => row.st === '0A')
.map(row => {
const address = row.local_address.split(':');
return {
socket: parseInt(row.inode, 10),
ip: this.parseIpAddress(address[0]),
port: parseInt(address[1], 16)
};
}).map(port => [port.ip + ':' + port.port, port])
).values()
];
}
private parseIpAddress(hex: string): string {
let result = '';
if (hex.length === 8) {
for (let i = hex.length - 2; i >= 0; i -= 2) {
result += parseInt(hex.substr(i, 2), 16);
if (i !== 0) {
result += '.';
}
}
} else {
for (let i = hex.length - 4; i >= 0; i -= 4) {
result += parseInt(hex.substr(i, 4), 16).toString(16);
if (i !== 0) {
result += ':';
}
}
}
return result;
}
private loadConnectionTable(stdout: string): Record<string, string>[] {
const lines = stdout.trim().split('\n');
const names = lines.shift()!.trim().split(/\s+/)
.filter(name => name !== 'rx_queue' && name !== 'tm->when');
const table = lines.map(line => line.trim().split(/\s+/).reduce((obj, value, i) => {
obj[names[i] || i] = value;
return obj;
}, {} as Record<string, string>));
return table;
return findPorts(tcp, tcp6, procSockets, processes);
}
}