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 destination parsing on some platforms and add support to stop traceroute #20

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ try {
});

tracer.trace('github.com');

// and you can terminate traceroute by calling `kill` method
setTimeout(() => {
tracer.kill()
}, 5000)
} catch (ex) {
console.log(ex);
}
Expand Down
64 changes: 40 additions & 24 deletions src/process.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import events from 'events';
import readline from 'readline';
import validator from 'validator';

import { spawn } from 'child_process';
import { ChildProcessWithoutNullStreams, spawn } from 'child_process';
import { Readable } from 'stream';

export interface Hop {
hop: number;
Expand All @@ -13,45 +14,60 @@ export interface Hop {
}

export abstract class Process extends events.EventEmitter {
process: ChildProcessWithoutNullStreams|null = null

constructor(private command: string, private args: string[]) {
super();
}

private readProcessStd(input: Readable) {
let isDestinationCaptured = false
readline.createInterface({
input,
terminal: false
})
.on('line', (line) => {
if (!isDestinationCaptured) {
const destination = this.parseDestination(line);
if (destination !== null) {
this.emit('destination', destination);

isDestinationCaptured = true;
}
}

const hop = this.parseHop(line);
if (hop !== null) {
this.emit('hop', hop);
}
});
}

public trace(domainName: string): void {
if (!this.isValidDomainName(domainName)) {
throw "Invalid domain name or IP address";
}

this.args.push(domainName);

const process = spawn(this.command, this.args);
process.on('close', (code) => {
this.process = spawn(this.command, this.args);
this.process.on('close', (code) => {
this.emit('close', code);
});

this.emit('pid', process.pid);

let isDestinationCaptured = false;
if (process.pid) {
readline.createInterface({
input: process.stdout,
terminal: false
})
.on('line', (line) => {
if (!isDestinationCaptured) {
const destination = this.parseDestination(line);
if (destination !== null) {
this.emit('destination', destination);
this.emit('pid', this.process.pid);

isDestinationCaptured = true;
}
}
if (this.process.pid) {
this.readProcessStd(this.process.stdout)
this.readProcessStd(this.process.stderr)
}
}

const hop = this.parseHop(line);
if (hop !== null) {
this.emit('hop', hop);
}
});
public kill(signal?: NodeJS.Signals | number) {
if (this.process) {
const result = this.process.kill(signal)
this.removeAllListeners()
return result
}
}

Expand Down
65 changes: 39 additions & 26 deletions test/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,42 @@ import validator from 'validator';
import Traceroute from '../src/index';

describe('Traceroute', () => {
it('should verify pid, destination, hops and close code', (wait) => {
const tracer = new Traceroute();

tracer
.on('pid', (pid) => {
expect(Number.isInteger(pid)).toBeTruthy();
})
.on('destination', (destination) => {
expect(validator.isIP(destination)).toBeTruthy();
})
.on('hop', (hopObj) => {
const { hop, ip, rtt1 } = hopObj;

expect(Number.isInteger(hop)).toBeTruthy();
expect(validator.isIP(ip) || ip === '*').toBeTruthy();
expect(/^\d+\.\d+\sms$/.test(rtt1) || rtt1 === '*').toBeTruthy();
})
.on('close', (code) => {
expect(Number.isInteger(code)).toBeTruthy();

wait();
});

tracer.trace('github.com');
}, 60000);
});
it('should verify pid, destination, hops and close code', (wait) => {
const tracer = new Traceroute();

tracer
.on('pid', (pid) => {
expect(Number.isInteger(pid)).toBeTruthy();
})
.on('destination', (destination) => {
expect(validator.isIP(destination)).toBeTruthy();
})
.on('hop', (hopObj) => {
const { hop, ip, rtt1 } = hopObj;

expect(Number.isInteger(hop)).toBeTruthy();
expect(validator.isIP(ip) || ip === '*').toBeTruthy();
expect(/^\d+\.\d+\sms$/.test(rtt1) || rtt1 === '*').toBeTruthy();
})
.on('close', (code) => {
expect(Number.isInteger(code)).toBeTruthy();

wait();
});

tracer.trace('github.com');
}, 60000);

it('should exit trace by calling kill method', (done) => {
const tracer = new Traceroute();

tracer.trace('github.com');

setTimeout(() => {
const killResult = tracer.kill()
expect(tracer.process?.killed).toBeTruthy()
expect(killResult).toBeTruthy()
done()
}, 5000)
}, 10000);
});