initial
This commit is contained in:
135
ts/classes.socketclient.ts
Normal file
135
ts/classes.socketclient.ts
Normal file
@@ -0,0 +1,135 @@
|
||||
import * as plugins from './plugins.js';
|
||||
import type { ISocketClientOptions, IApiResponse } from './interfaces/index.js';
|
||||
import { SmartVMError } from './interfaces/index.js';
|
||||
|
||||
/**
|
||||
* HTTP client that communicates with Firecracker over a Unix domain socket.
|
||||
* Uses @push.rocks/smartrequest with the `http://unix:<socket>:<path>` URL format.
|
||||
*/
|
||||
export class SocketClient {
|
||||
private socketPath: string;
|
||||
|
||||
constructor(options: ISocketClientOptions) {
|
||||
this.socketPath = options.socketPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the Unix socket URL for a given API path.
|
||||
*/
|
||||
private buildUrl(apiPath: string): string {
|
||||
return `http://unix:${this.socketPath}:${apiPath}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a GET request.
|
||||
*/
|
||||
public async get<T = any>(apiPath: string): Promise<IApiResponse<T>> {
|
||||
const url = this.buildUrl(apiPath);
|
||||
try {
|
||||
const response = await plugins.SmartRequest.create()
|
||||
.url(url)
|
||||
.get();
|
||||
|
||||
const statusCode = response.status;
|
||||
let body: T;
|
||||
try {
|
||||
body = await response.json() as T;
|
||||
} catch {
|
||||
body = undefined as any;
|
||||
}
|
||||
return {
|
||||
statusCode,
|
||||
body,
|
||||
ok: response.ok,
|
||||
};
|
||||
} catch (err) {
|
||||
throw new SmartVMError(
|
||||
`GET ${apiPath} failed: ${(err as Error).message}`,
|
||||
'SOCKET_REQUEST_FAILED',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a PUT request with a JSON body.
|
||||
*/
|
||||
public async put<T = any>(apiPath: string, body?: Record<string, any>): Promise<IApiResponse<T>> {
|
||||
const url = this.buildUrl(apiPath);
|
||||
try {
|
||||
let request = plugins.SmartRequest.create().url(url);
|
||||
if (body !== undefined) {
|
||||
request = request.json(body);
|
||||
}
|
||||
const response = await request.put();
|
||||
|
||||
const statusCode = response.status;
|
||||
let responseBody: T;
|
||||
try {
|
||||
responseBody = await response.json() as T;
|
||||
} catch {
|
||||
responseBody = undefined as any;
|
||||
}
|
||||
return {
|
||||
statusCode,
|
||||
body: responseBody,
|
||||
ok: response.ok,
|
||||
};
|
||||
} catch (err) {
|
||||
throw new SmartVMError(
|
||||
`PUT ${apiPath} failed: ${(err as Error).message}`,
|
||||
'SOCKET_REQUEST_FAILED',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a PATCH request with a JSON body.
|
||||
*/
|
||||
public async patch<T = any>(apiPath: string, body?: Record<string, any>): Promise<IApiResponse<T>> {
|
||||
const url = this.buildUrl(apiPath);
|
||||
try {
|
||||
let request = plugins.SmartRequest.create().url(url);
|
||||
if (body !== undefined) {
|
||||
request = request.json(body);
|
||||
}
|
||||
const response = await request.patch();
|
||||
|
||||
const statusCode = response.status;
|
||||
let responseBody: T;
|
||||
try {
|
||||
responseBody = await response.json() as T;
|
||||
} catch {
|
||||
responseBody = undefined as any;
|
||||
}
|
||||
return {
|
||||
statusCode,
|
||||
body: responseBody,
|
||||
ok: response.ok,
|
||||
};
|
||||
} catch (err) {
|
||||
throw new SmartVMError(
|
||||
`PATCH ${apiPath} failed: ${(err as Error).message}`,
|
||||
'SOCKET_REQUEST_FAILED',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the Firecracker API socket is ready by polling GET /.
|
||||
*/
|
||||
public async isReady(timeoutMs: number = 5000): Promise<boolean> {
|
||||
const start = Date.now();
|
||||
while (Date.now() - start < timeoutMs) {
|
||||
try {
|
||||
const response = await this.get('/');
|
||||
if (response.ok || response.statusCode === 200 || response.statusCode === 400) {
|
||||
return true;
|
||||
}
|
||||
} catch {
|
||||
// Socket not ready yet
|
||||
}
|
||||
await plugins.smartdelay.delayFor(100);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user