-
Notifications
You must be signed in to change notification settings - Fork 266
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
Getting "violation of fair-usage policy" when starting the app with an empty cache #633
Comments
Hi I ran into this issue today as well. I've tried to trace the root cause of the issue, it appears that the library being used to make download requests is using default settings, and this may not be preferred by the Fair use policy The export server is currently using a nodeJS http proxy to download files. It:
The proxy agent is built here with no options. The cache check is here where it will re-force downloads if any module is wrong. Was there a change to make the fair use policy a bit more strict in regards to the user agent? |
I also ran into this issue yesterday. It's really annoying and, honestly, worrisome. We bundle the export server with the version of Highcharts we want to use, so there is absolutely no need to go fetch anything. I couldn't find any option to let the export server know "please, use the Highcharts files from node_modules instead of fetching them". This blocked our CI/CD system for a whole day, since we couldn't even pass out tests. The fact that Highcharts can, unilaterally, break our systems is mindblowing. This is what I ended up doing. I'm not proud of it, but it's the only solution I could come up with. I created a Local CDN Server just for Highcharts. Here's the code in case someone can benefit from it. import express, { Response } from 'express';
import * as http from 'http';
import { readFileSync } from 'fs';
import path from 'path';
let server: http.Server;
interface LocalHighchartsCDNServerInterface {
start: () => void;
close: () => void;
}
const LocalHighchartsCDNServer: LocalHighchartsCDNServerInterface = {
start: () => {
const app = express();
const sendFile = (
req: { params: { version: string; filename: string } },
res: Response,
basePath: string,
) => {
const filePath = path.join(path.resolve(), basePath, req.params.filename);
res.status(200).send(readFileSync(filePath));
};
app.get('/cdn/:version/:filename', (req, res) => {
sendFile(req, res, 'node_modules/highcharts/');
});
app.get('/cdn/:version/modules/:filename', (req, res) => {
sendFile(req, res, 'node_modules/highcharts/modules/');
});
app.get('/cdn/maps/:version/modules/:filename', (req, res) => {
sendFile(req, res, 'node_modules/highcharts/modules/');
});
server = app.listen(8080, () => {
console.log(`[server]: Server is running at http://localhost:${8080}`);
});
},
close: () => {
if (server) {
server.close();
}
},
};
export default LocalHighchartsCDNServer; Then, I specify this URL as the cdnURL: highcharts: {
version: '11.4.8', // When using the local CDN, this version string is not used. It uses whatever you have in node_modules
forceFetch: false,
cdnURL: 'http://localhost:8080/cdn/',
customScripts: [],
indicatorScripts: [],
}, Notice how I'm passing an empty array to Then, we call |
We're seeing the exact same issue without any change in our dependencies or the number of modules we need to download (~60).
We noticed this as well, it means that we can't attempt to incrementally build the cache by starting up the server multiple times.
I think that this policy may refer to another product, but it could well be the same as the https://code.highcharts.com/ policy? It seems to refer to a hosted version of the export server. If the headers are the issues, we could potentially create a proxy which adds them. Why does the CDN not advertise the rate limit in its response headers? We don't know if we're allowed to make zero requests, one request, or 30 requests. We don't know the window of the rate limit. We only get told the
I think that this must be the case. We don't build our project using HighCharts often, but we had a successful run on the 10th of February. Workarounds we've considered:
|
@lewis-jackson-bots We really shouldn't need any workarounds for those of us who want to use the Highcharts version we have in node_modules. Unless I'm missing something, I couldn't find any combination of configurations or settings that would stop the export server from fetching all files from the CDN and use the local version instead. Because even if we add the headers to the request, we are still relying on the Highcharts As mentioned in their Fair use policy:
So, at any point, they might detect a high load and block everyone. Interfering with people's builds is unacceptable when there is no need to fetch anything. |
Agreed, but I have no power to control that. All we can do is complain and add a workaround in the meantime. |
We are facing the same issue since today an I already have created a support ticket for this as well |
We have the same issues. My understanding is that the fair use policy is supposed to protect the officially hosted export server from heavy usage on the export.highcharts.com domain. It looks like this fair usage policy has recently been applied to the code.highcharts.com domain as well. Since this is supposed to be a CDN I can only assume this is a mistake on HighCharts part and will be fixed in time 🙏 In the meantime, the easiest fix I have found is by setting the HIGHCHARTS_CDN_URL environment variable to the cloudflare mirror
|
This is a rather nice workaround, and stops us from hosting an additional express server. |
Hi all, Thank you for reporting this, and apologies for the inconvenience. This issue is due to recent changes in the fair usage policy and rate limiting of incoming requests to https://code.highcharts.com/. I agree that the current implementation of script fetching and caching is not ideal, and there is room for improvement. This mainly includes adding an option to use the scripts stored locally (e.g., from the npm package). The 'fetch per module' approach was implemented this way due to the possibility of combining modules, indicators, and custom scripts that users can select. Additionally, while the public export server requires the Although it has worked fine up until now, I recommend not relying on the public Highcharts CDN in a production environment. For now, some possible workarounds include using free CDNs like jsDelivr or CDNJS (I know it’s not perfect, but it would allow the initial script fetching for the server and is not as rate-limited, so it should be fine for now), or using previously generated cache content. Additionally, thank you for all your suggestions and workarounds! I'll mark this as an enhancement to implement npm script usage. |
This is a great suggestion, unfortunately it doesn't support two files that are grabbed by default (unsure if we need them):
|
Can the rate limit be tweaked to allow us to at least start the app with the default modules once per 15 minutes for example? At the moment the code in this repo can't be run by anyone. |
Hit that too... Happy now with a riff on pjanaya solution: // start_express_cdn.js
// Runs a simple Express server that serves Highcharts JS files from node_modules on local fs
const express = require('express');
const path = require('path');
const fs = require('fs');
const app = express();
const port = 9080;
const sendFile = (req, res, basePath) => {
const fileLocation = req.params.dirname ? `${req.params.dirname}/${req.params.filename}` : req.params.filename;
const filePath = path.join(process.cwd(), basePath, fileLocation);
console.log(`[server]: ${req.url} -> ${basePath}${req.params.filename}`);
try {
const fileContent = fs.readFileSync(filePath);
res.status(200).send(fileContent);
} catch (error) {
res.status(404).send(`File not found: ${req.params.filename}`);
}
};
// e.g. http://localhost:9080/cdn/12.1.2/highcharts.js
// -> ./node_modules/highcharts/highcharts.js
app.get('/cdn/:version/:filename', (req, res) => {
sendFile(req, res, 'node_modules/highcharts/');
});
// modules
// e.g. http://localhost:9080/cdn/12.1.2/modules/exporting.js
// -> ./node_modules/highcharts/modules/exporting.js
app.get('/cdn/:version/modules/:filename', (req, res) => {
sendFile(req, res, 'node_modules/highcharts/modules/');
});
// maps modules
// e.g. http://localhost:9080/cdn/maps/12.1.2/modules/map.js
// -> ./node_modules/highcharts/modules/map.js
app.get('/cdn/maps/:version/modules/:filename', (req, res) => {
sendFile(req, res, 'node_modules/highcharts/modules/');
});
// stock
// e.g. http://localhost:9080/cdn/stock/12.1.2/indicators/indicators-all.js
// -> ./node_modules/highcharts/indicators/indicators-all.js
app.get('/cdn/stock/:version/:dirname/:filename', (req, res) => {
sendFile(req, res, 'node_modules/highcharts/');
});
// Only start the server if this file is run directly
if (require.main === module) {
const server = app.listen(port, () => {
console.log(`[server]: Express CDN server is running at http://localhost:${port}`);
});
// Handle graceful shutdown
process.on('SIGTERM', () => {
console.log('SIGTERM signal received: closing HTTP server');
server.close(() => {
console.log('HTTP server closed');
process.exit(0);
});
});
}
// Still allow importing as a module if needed
module.exports = app; #!/bin/sh
# run_and_stop_server_to_populate_cache.sh
# POSIX (support different containers and shells)
# this is used in docker build to ensure scripts from CDN are already cached (otherwise our firewall will block the downloads in prod)
# https://github.com/highcharts/node-export-server/issues/415#issuecomment-2041409265
INSTALL_DIR=$1
(cd ./cdn && npm install highcharts@"${HIGHCHARTS_VERSION}" --prefix .)
(cd ./cdn && npm install [email protected] --prefix .)
(cd ./cdn && node start_express_cdn.js &)
pid_express_cdn=$!
highcharts-export-server --enableServer "1" &
pid_export_server=$!
until test -f "$INSTALL_DIR"/.cache/manifest.json && test -f "$INSTALL_DIR"/.cache/sources.js; do
sleep 1
done
kill $pid_express_cdn
kill $pid_export_server ...
ENV HIGHCHARTS_VERSION=12.1.2
ENV HIGHCHARTS_CDN_URL=http://localhost:9080/cdn/
RUN ./scripts/run_and_stop_server_to_populate_cache.sh ${INSTALL_DIR}
... Cutting out the highcharts CDNs to use something local, populated from NPM at build-time May have to maintain the list of mappings when upgrading versions, but don't mind the trade-off |
It does seem a bit more urgent than an enhancement, since the app now cannot run using its default configuration I would suggest it is more like a bug
Perhaps this should become the default for HIGHCHARTS_CDN_URL? |
Hi, Regarding this from my message yesterday:
Now it does. A hotfix has been applied to the |
When starting the server it will download scripts from the CDN
When starting with an empty directory, the number of generated requests sent to the CDN triggers "violation of fair-usage policy" errors
It feels like the server should be able to download all the scripts it needs in one go
Perhaps there is a way for us to ask the server to download these scripts more slowly, do some rate-limiting?
The text was updated successfully, but these errors were encountered: