Skip to content
Merged
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
43 changes: 40 additions & 3 deletions apps/cli/e2e/server/basic.e2e-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ describe('Server - Basic', () => {

beforeAll(async () => {
mockedBackend = jestMockBackend();
server = new ADCServer({ listen: new URL('http://127.0.1:3000') });
server = new ADCServer({
listen: new URL('http://127.0.1:3000'),
listenStatus: 3001,
});
});

it('test mocked load backend', async () => {
Expand Down Expand Up @@ -85,7 +88,7 @@ describe('Server - Basic', () => {

it('test server listen', async () => {
const url = new URL(`http://127.0.0.1:48562`);
const server = new ADCServer({ listen: url });
const server = new ADCServer({ listen: url, listenStatus: 3001 });
await server.start();

const { status, data } = await axios.put(`${url.origin}/sync`, {
Expand All @@ -108,6 +111,7 @@ describe('Server - Basic', () => {
const url = new URL(`https://127.0.0.1:48562`);
const server = new ADCServer({
listen: url,
listenStatus: 3001,
tlsCert: readFileSync(
join(__dirname, '../assets/tls/server.cer'),
'utf-8',
Expand Down Expand Up @@ -145,6 +149,7 @@ describe('Server - Basic', () => {
readFileSync(join(__dirname, '../assets/tls/', fileName), 'utf-8');
const server = new ADCServer({
listen: url,
listenStatus: 3001,
tlsCert: readCert('server.cer'),
tlsKey: readCert('server.key'),
tlsCACert: readCert('ca.cer'),
Expand Down Expand Up @@ -198,7 +203,7 @@ describe('Server - Basic', () => {

it('test server listen (with UDS)', async () => {
const url = new URL(`unix:///tmp/adc-test.sock`);
const server = new ADCServer({ listen: url });
const server = new ADCServer({ listen: url, listenStatus: 3001 });
await server.start();

const { status, data } = await new Promise<{
Expand Down Expand Up @@ -250,4 +255,36 @@ describe('Server - Basic', () => {

await server.stop();
});

it('test status listen', async () => {
const server = new ADCServer({
listen: new URL(`http://127.0.0.1:3000`),
listenStatus: 3001,
});
await server.start();

const { status, data } = await axios.get(
`http://127.0.0.1:3001/healthz/ready`,
);
expect(status).toEqual(200);
expect(data).toEqual('OK');

await server.stop();
});

it('test status listen (custom port)', async () => {
const server = new ADCServer({
listen: new URL(`http://127.0.0.1:3000`),
listenStatus: 30001,
});
await server.start();

const { status, data } = await axios.get(
`http://127.0.0.1:30001/healthz/ready`,
);
expect(status).toEqual(200);
expect(data).toEqual('OK');

await server.stop();
});
});
67 changes: 42 additions & 25 deletions apps/cli/src/command/ingress-server.command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { BaseCommand, BaseOptions, processCertificateFile } from './helper';

type IngressServerOptions = {
listen?: URL;
listenStatus?: number;
caCertFile?: string;
tlsCertFile?: string;
tlsKeyFile?: string;
Expand All @@ -16,7 +17,7 @@ export const IngressServerCommand = new BaseCommand<IngressServerOptions>(
'server',
)
.option<URL>(
'--listen <listen>',
'--listen <string>',
'listen address of ADC server, the format is scheme://host:port',
(val) => {
try {
Expand All @@ -27,6 +28,19 @@ export const IngressServerCommand = new BaseCommand<IngressServerOptions>(
},
new URL('http://127.0.0.1:3000'),
)
.option<number>(
'--listen-status <number>',
'status listen port',
(val) => {
const port = parseInt(val, 10);
if (!port || isNaN(port) || port < 1 || port > 65535)
throw new commander.InvalidArgumentError(
'The status listen port must be a number between 1 and 65535',
);
return port;
},
3001,
)
.addOption(
new Option(
'--ca-cert-file <string>',
Expand Down Expand Up @@ -60,28 +74,31 @@ export const IngressServerCommand = new BaseCommand<IngressServerOptions>(
),
),
)
.handle(async ({ listen, tlsCertFile, tlsKeyFile, caCertFile }) => {
if (listen.protocol === 'https:' && (!tlsCertFile || !tlsKeyFile)) {
console.error(
chalk.red(
'Error: When using HTTPS, both --tls-cert-file and --tls-key-file must be provided',
),
.handle(
async ({ listen, listenStatus, tlsCertFile, tlsKeyFile, caCertFile }) => {
if (listen.protocol === 'https:' && (!tlsCertFile || !tlsKeyFile)) {
console.error(
chalk.red(
'Error: When using HTTPS, both --tls-cert-file and --tls-key-file must be provided',
),
);
return;
}
const server = new ADCServer({
listen,
listenStatus,
tlsCert: tlsCertFile ? readFileSync(tlsCertFile, 'utf-8') : undefined,
tlsKey: tlsKeyFile ? readFileSync(tlsKeyFile, 'utf-8') : undefined,
tlsCACert: caCertFile ? readFileSync(caCertFile, 'utf-8') : undefined,
});
await server.start();
console.log(
`ADC server is running on: ${listen.protocol === 'unix:' ? listen.pathname : listen.origin}`,
);
return;
}

const server = new ADCServer({
listen,
tlsCert: tlsCertFile ? readFileSync(tlsCertFile, 'utf-8') : undefined,
tlsKey: tlsKeyFile ? readFileSync(tlsKeyFile, 'utf-8') : undefined,
tlsCACert: caCertFile ? readFileSync(caCertFile, 'utf-8') : undefined,
});
await server.start();
console.log(
`ADC server is running on: ${listen.protocol === 'unix:' ? listen.pathname : listen.origin}`,
);
process.on('SIGINT', () => {
console.log('Stopping, see you next time!');
server.stop();
});
});
process.on('SIGINT', () => {
console.log('Stopping, see you next time!');
server.stop();
process.exit(0);
});
},
);
70 changes: 47 additions & 23 deletions apps/cli/src/server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,28 @@ import { syncHandler } from './sync';

interface ADCServerOptions {
listen: URL;
listenStatus: number;
tlsCert?: string;
tlsKey?: string;
tlsCACert?: string;
}
export class ADCServer {
private readonly express: Express;
private readonly expressStatus: Express;
private server?: http.Server | https.Server;
private serverStatus?: http.Server;

constructor(private readonly opts: ADCServerOptions) {
this.express = express();
this.express.disable('x-powered-by');
this.express.disable('etag');
this.expressStatus = express();
[this.express, this.expressStatus].forEach(
(app) => (app.disable('x-powered-by'), app.disable('etag')),
);
this.express.use(express.json({ limit: '100mb' }));
this.express.put('/sync', syncHandler);
this.expressStatus.get('/healthz/ready', (_, res) =>
res.status(200).send('OK'),
);
}

public async start() {
Expand All @@ -48,30 +56,46 @@ export class ADCServer {
this.server = http.createServer(this.express);
break;
}
return new Promise<void>((resolve) => {
const listen = this.opts.listen;
if (listen.protocol === 'unix:') {
if (fs.existsSync(listen.pathname)) fs.unlinkSync(listen.pathname);
this.server.listen(listen.pathname, () => {
fs.chmodSync(listen.pathname, 0o660);
resolve();
});
} else {
this.server.listen(parseInt(listen.port), listen.hostname, () =>
resolve(),
);
}
});
return Promise.all([
new Promise<void>((resolve) => {
const listen = this.opts.listen;
if (listen.protocol === 'unix:') {
if (fs.existsSync(listen.pathname)) fs.unlinkSync(listen.pathname);
this.server.listen(listen.pathname, () => {
fs.chmodSync(listen.pathname, 0o660);
resolve();
});
} else {
this.serverStatus = this.server.listen(
parseInt(listen.port),
listen.hostname,
() => resolve(),
);
}
}),
new Promise<void>((resolve) => {
this.expressStatus.listen(this.opts.listenStatus, () => resolve());
}),
]);
}

public async stop() {
return new Promise<void>((resolve) => {
if (this.server) {
this.server.close(() => resolve());
} else {
resolve();
}
});
return Promise.all([
new Promise<void>((resolve) => {
if (this.server) {
this.server.close(() => resolve());
} else {
resolve();
}
}),
new Promise<void>((resolve) => {
if (this.serverStatus) {
this.serverStatus.close(() => resolve());
} else {
resolve();
}
}),
]);
}

public TEST_ONLY_getExpress() {
Expand Down
Loading