Skip to content

Commit

Permalink
Merge branch 'stable' into fix/options-type-validations
Browse files Browse the repository at this point in the history
  • Loading branch information
PaulDalek authored Dec 5, 2024
2 parents ecccd13 + 48d3794 commit bdcf3f4
Show file tree
Hide file tree
Showing 8 changed files with 191 additions and 259 deletions.
10 changes: 9 additions & 1 deletion lib/browser.js
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ export async function newPage(poolResource) {
*/
export async function clearPage(poolResource, hardReset = false) {
try {
if (!poolResource.page.isClosed()) {
if (poolResource.page && !poolResource.page.isClosed()) {
if (hardReset) {
// Navigate to about:blank
await poolResource.page.goto('about:blank', {
Expand All @@ -254,6 +254,7 @@ export async function clearPage(poolResource, hardReset = false) {
'<div id="chart-container"><div id="container"></div></div>';
});
}
return true;
}
} catch (error) {
logWithStack(
Expand All @@ -264,6 +265,7 @@ export async function clearPage(poolResource, hardReset = false) {
// Set the `workLimit` to exceeded in order to recreate the resource
poolResource.workCount = getOptions().pool.workLimit + 1;
}
return false;
}

/**
Expand Down Expand Up @@ -506,6 +508,12 @@ function _setPageEvents(poolResource) {

// Set the pageerror listener
poolResource.page.on('pageerror', async (error) => {
// It would seem like this may fire at the same time or shortly before
// a page is closed.
if (page.isClosed()) {
return;
}

await poolResource.page.$eval(
'#container',
(element, errorMessage) => {
Expand Down
29 changes: 13 additions & 16 deletions lib/export.js
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,12 @@ export async function puppeteerExport(page, chart, options) {
});

// Set final height and width for viewport
const viewportHeight = Math.ceil(size.chartHeight || exportOptions.height);
const viewportWidth = Math.ceil(size.chartWidth || exportOptions.width);
const viewportHeight = Math.abs(
Math.ceil(size.chartHeight || exportOptions.height)
);
const viewportWidth = Math.abs(
Math.ceil(size.chartWidth || exportOptions.width)
);

// Get the clip region for the page
const { x, y } = await _getClipRegion(page);
Expand Down Expand Up @@ -279,20 +283,13 @@ async function _createImage(page, type, encoding, clip, rasterizationTimeout) {
*/
async function _createPDF(page, height, width, encoding, rasterizationTimeout) {
await page.emulateMediaType('screen');
return Promise.race([
page.pdf({
// This will remove an extra empty page in PDF exports
height: height + 1,
width,
encoding
}),
new Promise((_resolve, reject) =>
setTimeout(
() => reject(new ExportError('Rasterization timeout', 408)),
rasterizationTimeout || 1500
)
)
]);
return page.pdf({
// This will remove an extra empty page in PDF exports
height: height + 1,
width,
encoding,
timeout: rasterizationTimeout || 1500
});
}

/**
Expand Down
5 changes: 4 additions & 1 deletion lib/highcharts.js
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,10 @@ export async function triggerExport(chartOptions, options, displayErrors) {
setOptions(globalOptions);
}

Highcharts[options.export.constr || 'chart'](
let constr = options.export.constr || 'chart';
constr = typeof Highcharts[constr] !== 'undefined' ? constr : 'chart';

Highcharts[constr](
'container',
finalOptions,
finalCallback
Expand Down
33 changes: 25 additions & 8 deletions lib/pool.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,15 +85,22 @@ export async function initPool(poolOptions = getOptions().pool, puppeteerArgs) {

// Set events
pool.on('release', async (resource) => {
log(4, `[pool] Pool resource [${resource.id}] - Releasing a worker.`);
// Clear page
const r = await clearPage(resource, false);
log(
4,
`[pool] Releasing a worker with ID ${resource.id}. Clear page status: ${r}.`
);
//// log(4, `[pool] Pool resource [${resource.id}] - Releasing a worker.`);
await clearPage(resource, false);
});

pool.on('destroySuccess', (_eventId, resource) => {
pool.on('destroySuccess', (eventId, resource) => {
log(
4,
`[pool] Pool resource [${resource.id}] - Destroyed a worker successfully.`
);
//// log(4, `[pool] Destroyed a worker with ID ${resource.id}.`);
resource.page = null;
});

Expand Down Expand Up @@ -248,13 +255,23 @@ export async function postWork(chart, options) {
if (result.message === 'Rasterization timeout') {
// Set the `workLimit` to exceeded in order to recreate the resource
workerHandle.workCount = options.pool.workLimit + 1;
workerHandle.page = null;
}

throw new ExportError(
(options.payload?.requestId
? `Request: ${options.payload?.requestId} - `
: '') + `Error encountered during export: ${exportCounter()}ms.`
).setError(result);
if (
result.name === 'TimeoutError' ||
result.message === 'Rasterization timeout'
) {
throw new ExportError(
'Rasterization timeout: your chart may be too complex or large, and failed to render within the allotted time.'
).setError(result);
} else {
throw new ExportError(
(options.payload?.requestId
? `For request with ID ${options.payload?.requestId} - `
: '') + `Error encountered during export: ${exportCounter()}ms.`
).setError(result);
}
}

// Check the Puppeteer export time
Expand Down Expand Up @@ -449,7 +466,7 @@ function _factory(poolOptions) {
}

// Check if the `page` is not valid
if (!poolResource.page) {
if (poolResource.page) {
// Check if the `page` is closed
if (poolResource.page.isClosed()) {
log(
Expand Down
2 changes: 1 addition & 1 deletion lib/server/error.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ function logErrorMiddleware(error, request, response, next) {
function returnErrorMiddleware(error, request, response, next) {
// Gather all requied information for the response
const { statusCode: stCode, status, message, stack } = error;
const statusCode = stCode || status || 500;
const statusCode = stCode || status || 400;

// Set and return response
response.status(statusCode).json({ statusCode, message, stack });
Expand Down
6 changes: 4 additions & 2 deletions lib/server/routes/ui.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ import { __dirname } from '../../utils.js';
export default (app) =>
!app
? false
: app.get('/', (request, response) => {
response.sendFile(join(__dirname, 'public', 'index.html'));
: app.get('/', (_request, response) => {
response.sendFile(join(__dirname, 'public', 'index.html'), {
acceptRanges: false
});
});
64 changes: 34 additions & 30 deletions lib/server/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,41 +39,40 @@ import ExportError from '../errors/ExportError.js';
// Array of an active servers
const activeServers = new Map();

// Enable parsing of form data (files) with Multer package
const upload = multer({
storage: multer.memoryStorage(),
limits: {
fieldSize: 50 * 1024 * 1024
}
});

// Create express app
const app = express();

// Disable the X-Powered-By header
app.disable('x-powered-by');

// Enable CORS support
app.use(
cors({
methods: ['POST', 'GET', 'OPTIONS']
})
);

// Enable body parser for JSON data
app.use(
express.json({
limit: 50 * 1024 * 1024
})
);

// Enable body parser for URL-encoded form data
app.use(
express.urlencoded({
extended: true,
limit: 50 * 1024 * 1024
})
);
app.use(cors());

// Getting a lot of RangeNotSatisfiableError exception.
// Even though this is a deprecated options, let's try to set it to false.
app.use((_req, res, next) => {
res.set('Accept-Ranges', 'none');
next();
});

// TODO: Read from config/env
// NOTE:
// Too big limits lead to timeouts in the export process when the
// rasterization timeout is set too low.
const uploadLimitMiB = 3;
const uploadLimitBytes = uploadLimitMiB * 1024 * 1024;

// Enable parsing of form data (files) with Multer package
const upload = multer({
storage: multer.memoryStorage(),
limits: {
fieldSize: uploadLimitBytes
}
});

// Enable body parser
app.use(express.json({ limit: uploadLimitBytes }));
app.use(express.urlencoded({ extended: true, limit: uploadLimitBytes }));

// Use only non-file multipart form fields
app.use(upload.none());
Expand Down Expand Up @@ -287,8 +286,13 @@ export function post(path, ...middlewares) {
* @param {http.Server} server - The HTTP/HTTPS server instance.
*/
function _attachServerErrorHandlers(server) {
server.on('clientError', (error) => {
logWithStack(1, error, `[server] Client error: ${error.message}`);
server.on('clientError', (error, socket) => {
logWithStack(
1,
error,
`[server] Client error: ${error.message}, destroying socket.`
);
socket.destroy();
});

server.on('error', (error) => {
Expand Down
Loading

0 comments on commit bdcf3f4

Please sign in to comment.