mirror of
https://github.com/FlareSolverr/FlareSolverr.git
synced 2025-12-05 17:18:19 +01:00
Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
93d8350097 | ||
|
|
d34b43e0a8 | ||
|
|
2bf4dc62da | ||
|
|
bb0d757755 | ||
|
|
fc1fa601eb | ||
|
|
9b1f8332c7 | ||
|
|
6175fee75a | ||
|
|
bb4fa9cabc | ||
|
|
c951ba2523 | ||
|
|
6c598d5360 | ||
|
|
2893f72237 | ||
|
|
cd221bbbf1 | ||
|
|
68fb96f0d8 |
@@ -56,6 +56,10 @@ docker run -d \
|
||||
ghcr.io/flaresolverr/flaresolverr:latest
|
||||
```
|
||||
|
||||
If your host OS is Debian, make sure `libseccomp2` version is 2.5.x. You can check the version with `sudo apt-cache policy libseccomp2`
|
||||
and update the package with `sudo apt install libseccomp2=2.5.1-1~bpo10+1` or `sudo apt install libseccomp2=2.5.1-1+deb11u1`.
|
||||
Remember to restart the Docker daemon and the container after the update.
|
||||
|
||||
### Precompiled binaries
|
||||
|
||||
This is the recommended way for Windows users.
|
||||
@@ -104,6 +108,7 @@ This also speeds up the requests since it won't have to launch a new browser ins
|
||||
Parameter | Notes
|
||||
|--|--|
|
||||
session | Optional. The session ID that you want to be assigned to the instance. If isn't set a random UUID will be assigned.
|
||||
proxy | Optional, default disabled. Eg: `"proxy": {"url": "http://127.0.0.1:8888"}`. You must include the proxy schema in the URL: `http://`, `socks4://` or `socks5://`. Authorization (username/password) is not supported.
|
||||
|
||||
#### + `sessions.list`
|
||||
|
||||
@@ -141,7 +146,7 @@ session | Optional. Will send the request from and existing browser instance. If
|
||||
maxTimeout | Optional, default value 60000. Max timeout to solve the challenge in milliseconds.
|
||||
cookies | Optional. Will be used by the headless browser. Follow [this](https://github.com/puppeteer/puppeteer/blob/v3.3.0/docs/api.md#pagesetcookiecookies) format.
|
||||
returnOnlyCookies | Optional, default false. Only returns the cookies. Response data, headers and other parts of the response are removed.
|
||||
proxy | Optional, default disabled. Eg: `"proxy": {"url": "http://127.0.0.1:8888"}`. You must include the proxy schema in the URL: `http://`, `socks4://` or `socks5://`. Authorization (username/password) is not supported.
|
||||
proxy | Optional, default disabled. Eg: `"proxy": {"url": "http://127.0.0.1:8888"}`. You must include the proxy schema in the URL: `http://`, `socks4://` or `socks5://`. Authorization (username/password) is not supported. (When the `session` parameter is set, the proxy is ignored; a session specific proxy can be set in `sessions.create`.)
|
||||
|
||||
:warning: If you want to use Cloudflare clearance cookie in your scripts, make sure you use the FlareSolverr User-Agent too. If they don't match you will see the challenge.
|
||||
|
||||
|
||||
3369
package-lock.json
generated
3369
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
25
package.json
25
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "flaresolverr",
|
||||
"version": "2.2.0",
|
||||
"version": "2.2.3",
|
||||
"description": "Proxy server to bypass Cloudflare protection.",
|
||||
"scripts": {
|
||||
"start": "tsc && node ./dist/server.js",
|
||||
@@ -20,27 +20,26 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"await-timeout": "^1.1.1",
|
||||
"body-parser": "^1.19.0",
|
||||
"body-parser": "^1.20.0",
|
||||
"console-log-level": "^1.4.1",
|
||||
"express": "^4.17.1",
|
||||
"puppeteer": "^13.1.2",
|
||||
"express": "^4.17.3",
|
||||
"puppeteer": "^13.5.2",
|
||||
"uuid": "^8.3.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/await-timeout": "^0.3.1",
|
||||
"@types/body-parser": "^1.19.1",
|
||||
"@types/express": "^4.17.13",
|
||||
"@types/jest": "^27.0.2",
|
||||
"@types/node": "^16.11.7",
|
||||
"@types/puppeteer": "^5.4.4",
|
||||
"@types/supertest": "^2.0.11",
|
||||
"@types/jest": "^27.4.1",
|
||||
"@types/node": "^16.11.27",
|
||||
"@types/supertest": "^2.0.12",
|
||||
"@types/uuid": "^8.3.1",
|
||||
"archiver": "^5.3.0",
|
||||
"archiver": "^5.3.1",
|
||||
"nodemon": "^2.0.13",
|
||||
"pkg": "^5.5.2",
|
||||
"pkg": "^5.6.0",
|
||||
"supertest": "^6.1.6",
|
||||
"ts-jest": "^27.0.7",
|
||||
"ts-node": "^10.3.0",
|
||||
"typescript": "^4.4.4"
|
||||
"ts-jest": "^27.1.4",
|
||||
"ts-node": "^10.7.0",
|
||||
"typescript": "^4.6.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,13 +6,17 @@ import log from "../services/log";
|
||||
* This class contains the logic to solve protections provided by CloudFlare
|
||||
**/
|
||||
|
||||
const BAN_SELECTORS = ['.text-gray-600'];
|
||||
const CHALLENGE_SELECTORS = [
|
||||
// the selector '.text-gray-600' is not working well because it can be hidden
|
||||
// <span style="display: none;" class="text-gray-600" data-translate="error">error code: 1020</span>
|
||||
const BAN_SELECTORS: string[] = [];
|
||||
const CHALLENGE_SELECTORS: string[] = [
|
||||
'#trk_jschal_js', '.ray_id', '.attack-box', '#cf-please-wait', // CloudFlare
|
||||
'#link-ddg', // DDoS-GUARD
|
||||
'td.info #js_info' // Custom CloudFlare for EbookParadijs, Film-Paleis, MuziekFabriek and Puur-Hollands
|
||||
];
|
||||
const CAPTCHA_SELECTORS = ['input[name="cf_captcha_kind"]'];
|
||||
const CAPTCHA_SELECTORS: string[] = [
|
||||
'input[name="cf_captcha_kind"]'
|
||||
];
|
||||
|
||||
export default async function resolveChallenge(url: string, page: Page, response: HTTPResponse): Promise<HTTPResponse> {
|
||||
|
||||
@@ -37,7 +41,7 @@ export default async function resolveChallenge(url: string, page: Page, response
|
||||
}
|
||||
|
||||
if (await findAnySelector(page, BAN_SELECTORS)) {
|
||||
throw new Error('Cloudflare has blocked this request. Probably your IP is banned for this site, check in your web browser.')
|
||||
throw new Error('Cloudflare has blocked this request. Probably your IP is banned for this site, check in your web browser.');
|
||||
}
|
||||
|
||||
// find Cloudflare selectors
|
||||
|
||||
@@ -39,6 +39,11 @@ process.on('SIGTERM', () => {
|
||||
process.exit(0)
|
||||
})
|
||||
|
||||
process.on('uncaughtException', function(err) {
|
||||
// Avoid crashing in NodeJS 17 due to UnhandledPromiseRejectionWarning: Unhandled promise rejection.
|
||||
log.error(err)
|
||||
})
|
||||
|
||||
validateEnvironmentVariables();
|
||||
|
||||
testWebBrowserInstallation().then(() => {
|
||||
|
||||
@@ -57,8 +57,12 @@ function buildExtraPrefsFirefox(proxy: Proxy): object {
|
||||
|
||||
// proxy.url format => http://<host>:<port>
|
||||
if (proxy && proxy.url) {
|
||||
log.debug(`Using proxy: ${proxy.url}`)
|
||||
const [host, portStr] = proxy.url.replace(/.+:\/\//g, '').split(':');
|
||||
const port = parseInt(portStr);
|
||||
if (!host || !portStr || !port) {
|
||||
throw new Error("Proxy configuration is invalid! Use the format: protocol://ip:port")
|
||||
}
|
||||
|
||||
const proxyPrefs = {
|
||||
"network.proxy.type": 1,
|
||||
@@ -118,7 +122,8 @@ export async function testWebBrowserInstallation(): Promise<void> {
|
||||
oneTimeSession: true
|
||||
})
|
||||
const page = await session.browser.newPage()
|
||||
await page.goto(testUrl)
|
||||
const pageTimeout = Number(process.env.BROWSER_TIMEOUT) || 40000
|
||||
await page.goto(testUrl, {waitUntil: 'domcontentloaded', timeout: pageTimeout})
|
||||
webBrowserUserAgent = await page.evaluate(() => navigator.userAgent)
|
||||
|
||||
// replace Linux ARM user-agent because it's detected
|
||||
@@ -134,6 +139,8 @@ export async function testWebBrowserInstallation(): Promise<void> {
|
||||
}
|
||||
|
||||
export async function create(session: string, options: SessionCreateOptions): Promise<SessionsCacheItem> {
|
||||
log.debug('Creating new session...')
|
||||
|
||||
const sessionId = session || UUIDv1()
|
||||
|
||||
// NOTE: cookies can't be set in the session, you need to open the page first
|
||||
@@ -141,7 +148,7 @@ export async function create(session: string, options: SessionCreateOptions): Pr
|
||||
const puppeteerOptions: any = {
|
||||
product: 'firefox',
|
||||
headless: process.env.HEADLESS !== 'false',
|
||||
timeout: process.env.BROWSER_TIMEOUT || 40000
|
||||
timeout: Number(process.env.BROWSER_TIMEOUT) || 40000
|
||||
}
|
||||
|
||||
puppeteerOptions.extraPrefsFirefox = buildExtraPrefsFirefox(options.proxy)
|
||||
|
||||
@@ -89,7 +89,11 @@ async function resolveChallenge(params: V1Request, session: SessionsCacheItem):
|
||||
// is response is ok
|
||||
// reload the page to be sure we get the real page
|
||||
log.debug("Reloading the page")
|
||||
response = await gotoPage(params, page);
|
||||
try {
|
||||
response = await gotoPage(params, page);
|
||||
} catch (e) {
|
||||
log.warn("Page not reloaded (do not report!): Cause: " + e.toString())
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
status = "error";
|
||||
@@ -129,14 +133,17 @@ async function resolveChallenge(params: V1Request, session: SessionsCacheItem):
|
||||
}
|
||||
|
||||
async function gotoPage(params: V1Request, page: Page): Promise<HTTPResponse> {
|
||||
let response: HTTPResponse;
|
||||
if (params.method != 'POST') {
|
||||
response = await page.goto(params.url, {waitUntil: 'domcontentloaded'});
|
||||
let pageTimeout = params.maxTimeout / 3;
|
||||
let response: HTTPResponse
|
||||
try {
|
||||
response = await page.goto(params.url, {waitUntil: 'domcontentloaded', timeout: pageTimeout});
|
||||
} catch (e) {
|
||||
// retry
|
||||
response = await page.goto(params.url, {waitUntil: 'domcontentloaded', timeout: pageTimeout});
|
||||
}
|
||||
|
||||
} else {
|
||||
if (params.method == 'POST') {
|
||||
// post hack
|
||||
// first request a page without cloudflare
|
||||
response = await page.goto(params.url, {waitUntil: 'domcontentloaded'});
|
||||
await page.setContent(
|
||||
`
|
||||
<!DOCTYPE html>
|
||||
|
||||
Reference in New Issue
Block a user