Compare commits

...
13 Commits
Author SHA1 Message Date
ngosang 93d8350097 Bump version 2.2.3 2022-04-16 22:29:36 +02:00
ngosang d34b43e0a8 Fix 2000 ms navigation timeout 2022-04-16 21:39:50 +02:00
ngosang 2bf4dc62da Update README.md (libseccomp2 package in Debian) 2022-04-16 20:47:31 +02:00
termonio bb0d757755 Update README.md (clarify proxy parameter) (#307)
Clarify that `request.get` will not use the provided proxy when a session is set.
2022-04-16 20:08:48 +02:00
ngosang fc1fa601eb Update NPM dependencies 2022-04-16 19:19:08 +02:00
ngosang 9b1f8332c7 Disable Cloudflare ban detection 2022-04-16 18:32:58 +02:00
ilike2burnthing 6175fee75a Bump version 2.2.2 (#339) 2022-03-19 04:28:16 +00:00
Harold bb4fa9cabc Fix ban detection. Resolves #330 (#336) 2022-03-19 04:24:49 +00:00
ngosang c951ba2523 Bump version 2.2.1 2022-02-06 16:40:03 +01:00
ngosang 6c598d5360 Fix max timeout error in some pages 2022-02-06 16:35:52 +01:00
ngosang 2893f72237 Avoid crashing in NodeJS 17 due to Unhandled promise rejection 2022-02-06 13:31:30 +01:00
ngosang cd221bbbf1 Improve proxy validation and debug traces 2022-02-06 13:07:11 +01:00
ngosang 68fb96f0d8 Remove @types/puppeteer dependency 2022-02-06 12:53:59 +01:00
7 changed files with 1960 additions and 1488 deletions
+6 -1
View File
@@ -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.
+1906 -1461
View File
File diff suppressed because it is too large Load Diff
+12 -13
View File
@@ -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"
}
}
+8 -4
View File
@@ -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
+5
View File
@@ -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(() => {
+9 -2
View File
@@ -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)
+14 -7
View File
@@ -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>