pic-js-mops
Version:
An Internet Computer Protocol canister testing library for TypeScript and JavaScript.
144 lines • 6.11 kB
JavaScript
import { ServerRequestTimeoutError } from './error.js';
import { isNil, poll } from './util/index.js';
export const JSON_HEADER = {
'Content-Type': 'application/json',
};
export class Http2Client {
baseUrl;
processingTimeoutMs;
constructor(baseUrl, processingTimeoutMs) {
this.baseUrl = baseUrl;
this.processingTimeoutMs = processingTimeoutMs;
}
request(init) {
const timeoutAbortController = new AbortController();
const requestAbortController = new AbortController();
const cancelAfterTimeout = async () => {
return await new Promise((_, reject) => {
const timeoutId = setTimeout(() => {
requestAbortController.abort();
reject(new ServerRequestTimeoutError());
}, this.processingTimeoutMs);
timeoutAbortController.signal.addEventListener('abort', () => {
clearTimeout(timeoutId);
reject(new ServerRequestTimeoutError());
});
});
};
const makeRequest = async () => {
const url = `${this.baseUrl}${init.path}`;
const res = await fetch(url, {
method: init.method,
headers: init.headers,
body: init.body,
signal: requestAbortController.signal,
});
timeoutAbortController.abort();
return res;
};
return Promise.race([makeRequest(), cancelAfterTimeout()]);
}
async jsonGet(init) {
// poll the request until it is successful or times out
return await poll(async () => {
const res = await this.request({
method: 'GET',
path: init.path,
headers: { ...init.headers, ...JSON_HEADER },
});
const resBody = await getResBody(res);
if (isNil(resBody)) {
return resBody;
}
// server encountered an error, throw and try again
if ('message' in resBody) {
console.error('PocketIC server encountered an error', resBody.message);
throw new Error(resBody.message);
}
// the server has started processing or is busy
if ('state_label' in resBody) {
// the server is too busy to process the request, throw and try again
if (res.status === 409) {
throw new Error('Server busy');
}
// the server has started processing the request
// this shouldn't happen for GET requests, throw and try again
if (res.status === 202) {
throw new Error('Server started processing');
}
// something weird happened, throw and try again
throw new Error('Unknown state');
}
// the request was successful, exit the loop
return resBody;
}, { intervalMs: POLLING_INTERVAL_MS, timeoutMs: this.processingTimeoutMs });
}
async jsonPost(init) {
const reqBody = init.body
? new TextEncoder().encode(JSON.stringify(init.body))
: undefined;
// poll the request until it is successful or times out
return await poll(async () => {
const res = await this.request({
method: 'POST',
path: init.path,
headers: { ...init.headers, ...JSON_HEADER },
body: reqBody,
});
const resBody = await getResBody(res);
if (isNil(resBody)) {
return resBody;
}
// server encountered an error, throw and try again
if ('message' in resBody) {
console.error('PocketIC server encountered an error', resBody.message);
throw new Error(resBody.message);
}
// the server has started processing or is busy
if ('state_label' in resBody) {
// the server is too busy to process the request, throw and try again
if (res.status === 409) {
throw new Error('Server busy');
}
// the server has started processing the request, poll until it is done
if (res.status === 202) {
return await poll(async () => {
const stateRes = await this.request({
method: 'GET',
path: `/read_graph/${resBody.state_label}/${resBody.op_id}`,
});
const stateBody = (await stateRes.json());
// the server encountered an error, throw and try again
if (isNil(stateBody) ||
'message' in stateBody ||
'state_label' in stateBody) {
throw new Error('Polling has not succeeded yet');
}
// the request was successful, exit the loop
return stateBody;
}, {
intervalMs: POLLING_INTERVAL_MS,
timeoutMs: this.processingTimeoutMs,
});
}
// something weird happened, throw and try again
throw new Error('Unknown state');
}
// the request was successful, exit the loop
return resBody;
}, { intervalMs: POLLING_INTERVAL_MS, timeoutMs: this.processingTimeoutMs });
}
}
async function getResBody(res) {
try {
return (await res.clone().json());
}
catch (error) {
const message = await res.text();
console.error('Error parsing PocketIC server response body:', error);
console.error('Original body:', message);
throw new Error(message);
}
}
const POLLING_INTERVAL_MS = 10;
//# sourceMappingURL=http2-client.js.map