mirror of
https://github.com/FlareSolverr/FlareSolverr.git
synced 2025-12-06 01:28:37 +01:00
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1b01caaa78 | ||
|
|
447c8f67a1 | ||
|
|
9dae74bc28 | ||
|
|
4199db5a41 | ||
|
|
2a4fae37c0 | ||
|
|
232ddca512 | ||
|
|
8572fab781 | ||
|
|
fdb3eae051 | ||
|
|
6dd8206a10 |
@@ -22,8 +22,8 @@ ENV PUPPETEER_PRODUCT=chrome \
|
|||||||
PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium-browser
|
PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium-browser
|
||||||
RUN npm install && \
|
RUN npm install && \
|
||||||
npm run build && \
|
npm run build && \
|
||||||
rm -rf src tsconfig.json && \
|
npm prune --production && \
|
||||||
npm prune --production
|
rm -rf /home/node/.npm
|
||||||
|
|
||||||
EXPOSE 8191
|
EXPOSE 8191
|
||||||
ENTRYPOINT ["/usr/bin/dumb-init", "--"]
|
ENTRYPOINT ["/usr/bin/dumb-init", "--"]
|
||||||
|
|||||||
@@ -146,6 +146,7 @@ headers | Optional. To specify user headers.
|
|||||||
maxTimeout | Optional, default value 60000. Max timeout to solve the challenge in milliseconds.
|
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.
|
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.
|
returnOnlyCookies | Optional, default false. Only returns the cookies. Response data, headers and other parts of the response are removed.
|
||||||
|
returnRawHtml | Optional, default false. The response data will be returned without JS processing. This is useful for JSON or plain text content.
|
||||||
|
|
||||||
Example response from running the `curl` above:
|
Example response from running the `curl` above:
|
||||||
|
|
||||||
|
|||||||
40
package-lock.json
generated
40
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "flaresolverr",
|
"name": "flaresolverr",
|
||||||
"version": "1.2.8",
|
"version": "1.2.9",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "flaresolverr",
|
"name": "flaresolverr",
|
||||||
"version": "1.2.5",
|
"version": "1.2.8",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"await-timeout": "^1.1.1",
|
"await-timeout": "^1.1.1",
|
||||||
@@ -2096,9 +2096,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/glob-parent": {
|
"node_modules/glob-parent": {
|
||||||
"version": "5.1.1",
|
"version": "5.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
|
||||||
"integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==",
|
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"is-glob": "^4.0.1"
|
"is-glob": "^4.0.1"
|
||||||
@@ -2913,9 +2913,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/merge-deep": {
|
"node_modules/merge-deep": {
|
||||||
"version": "3.0.2",
|
"version": "3.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/merge-deep/-/merge-deep-3.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/merge-deep/-/merge-deep-3.0.3.tgz",
|
||||||
"integrity": "sha512-T7qC8kg4Zoti1cFd8Cr0M+qaZfOwjlPDEdZIIPPB2JZctjaPM4fX+i7HOId69tAti2fvO6X5ldfYUONDODsrkA==",
|
"integrity": "sha512-qtmzAS6t6grwEkNrunqTBdn0qKwFgNWvlxUbAV8es9M7Ot1EbyApytCnvE0jALPa46ZpKDUo527kKiaWplmlFA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"arr-union": "^3.1.0",
|
"arr-union": "^3.1.0",
|
||||||
"clone-deep": "^0.2.4",
|
"clone-deep": "^0.2.4",
|
||||||
@@ -3178,9 +3178,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/normalize-url": {
|
"node_modules/normalize-url": {
|
||||||
"version": "4.5.0",
|
"version": "4.5.1",
|
||||||
"resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz",
|
||||||
"integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==",
|
"integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
@@ -6860,9 +6860,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"glob-parent": {
|
"glob-parent": {
|
||||||
"version": "5.1.1",
|
"version": "5.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
|
||||||
"integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==",
|
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"is-glob": "^4.0.1"
|
"is-glob": "^4.0.1"
|
||||||
@@ -7523,9 +7523,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"merge-deep": {
|
"merge-deep": {
|
||||||
"version": "3.0.2",
|
"version": "3.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/merge-deep/-/merge-deep-3.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/merge-deep/-/merge-deep-3.0.3.tgz",
|
||||||
"integrity": "sha512-T7qC8kg4Zoti1cFd8Cr0M+qaZfOwjlPDEdZIIPPB2JZctjaPM4fX+i7HOId69tAti2fvO6X5ldfYUONDODsrkA==",
|
"integrity": "sha512-qtmzAS6t6grwEkNrunqTBdn0qKwFgNWvlxUbAV8es9M7Ot1EbyApytCnvE0jALPa46ZpKDUo527kKiaWplmlFA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"arr-union": "^3.1.0",
|
"arr-union": "^3.1.0",
|
||||||
"clone-deep": "^0.2.4",
|
"clone-deep": "^0.2.4",
|
||||||
@@ -7739,9 +7739,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"normalize-url": {
|
"normalize-url": {
|
||||||
"version": "4.5.0",
|
"version": "4.5.1",
|
||||||
"resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz",
|
||||||
"integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ=="
|
"integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA=="
|
||||||
},
|
},
|
||||||
"oauth-sign": {
|
"oauth-sign": {
|
||||||
"version": "0.9.0",
|
"version": "0.9.0",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "flaresolverr",
|
"name": "flaresolverr",
|
||||||
"version": "1.2.8",
|
"version": "1.2.9",
|
||||||
"description": "Proxy server to bypass Cloudflare protection.",
|
"description": "Proxy server to bypass Cloudflare protection.",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node ./dist/index.js",
|
"start": "node ./dist/index.js",
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const os = require('os');
|
const os = require('os');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
const process = require('process')
|
||||||
import log from './log'
|
import log from './log'
|
||||||
import { createServer, IncomingMessage, ServerResponse } from 'http';
|
import { createServer, IncomingMessage, ServerResponse } from 'http';
|
||||||
import { RequestContext } from './types'
|
import { RequestContext } from './types'
|
||||||
@@ -118,7 +119,15 @@ function validateIncomingRequest(ctx: RequestContext, params: BaseAPICall) {
|
|||||||
// init
|
// init
|
||||||
log.info(`FlareSolverr ${version}`);
|
log.info(`FlareSolverr ${version}`);
|
||||||
log.debug('Debug log enabled');
|
log.debug('Debug log enabled');
|
||||||
|
|
||||||
|
process.on('SIGTERM', () => {
|
||||||
|
// Capture signal on Docker Stop #158
|
||||||
|
log.info("Process interrupted")
|
||||||
|
process.exit(0)
|
||||||
|
})
|
||||||
|
|
||||||
validateEnvironmentVariables();
|
validateEnvironmentVariables();
|
||||||
|
|
||||||
testChromeInstallation()
|
testChromeInstallation()
|
||||||
.catch(e => {
|
.catch(e => {
|
||||||
log.error("Error starting Chrome browser.", e);
|
log.error("Error starting Chrome browser.", e);
|
||||||
|
|||||||
@@ -20,8 +20,8 @@ export default async function resolveChallenge(url: string, page: Page, response
|
|||||||
}
|
}
|
||||||
log.info('Cloudflare detected');
|
log.info('Cloudflare detected');
|
||||||
|
|
||||||
if (await page.$('.cf-error-code')) {
|
if (await page.$('span[data-translate="error"]') || (await page.content()).includes('error code: 1020')) {
|
||||||
throw new Error('Cloudflare has blocked this request (Code 1020 Detected).')
|
throw new Error('Cloudflare has blocked this request. Probably your IP is banned for this site, check in your web browser.')
|
||||||
}
|
}
|
||||||
|
|
||||||
let selectorFoundCount = 0;
|
let selectorFoundCount = 0;
|
||||||
@@ -73,7 +73,9 @@ export default async function resolveChallenge(url: string, page: Page, response
|
|||||||
} catch (error)
|
} catch (error)
|
||||||
{
|
{
|
||||||
log.debug("Unexpected error: " + error);
|
log.debug("Unexpected error: " + error);
|
||||||
break
|
if (!error.toString().includes("Execution context was destroyed")) {
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.debug('Waiting for Cloudflare challenge...')
|
log.debug('Waiting for Cloudflare challenge...')
|
||||||
@@ -169,7 +171,7 @@ export default async function resolveChallenge(url: string, page: Page, response
|
|||||||
}
|
}
|
||||||
|
|
||||||
// submit captcha response
|
// submit captcha response
|
||||||
challengeForm.evaluate((e: HTMLFormElement) => e.submit())
|
await challengeForm.evaluate((e: HTMLFormElement) => e.submit())
|
||||||
response = await page.waitForNavigation({ waitUntil: 'domcontentloaded' })
|
response = await page.waitForNavigation({ waitUntil: 'domcontentloaded' })
|
||||||
|
|
||||||
if (await page.$('input[name="cf_captcha_kind"]')) {
|
if (await page.$('input[name="cf_captcha_kind"]')) {
|
||||||
@@ -185,7 +187,8 @@ export default async function resolveChallenge(url: string, page: Page, response
|
|||||||
throw new Error('No challenge selectors found, unable to proceed')
|
throw new Error('No challenge selectors found, unable to proceed')
|
||||||
} else {
|
} else {
|
||||||
// reload the page to make sure we get the real response
|
// reload the page to make sure we get the real response
|
||||||
response = await page.reload()
|
// do not use page.reload() to avoid #162 #143
|
||||||
|
response = await page.goto(url, { waitUntil: 'domcontentloaded' })
|
||||||
await page.content()
|
await page.content()
|
||||||
log.info('Challenge solved.');
|
log.info('Challenge solved.');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,9 +36,9 @@ interface BaseRequestAPICall extends BaseAPICall {
|
|||||||
proxy?: any, // TODO: use interface not any
|
proxy?: any, // TODO: use interface not any
|
||||||
download?: boolean
|
download?: boolean
|
||||||
returnOnlyCookies?: boolean
|
returnOnlyCookies?: boolean
|
||||||
|
returnRawHtml?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
interface Routes {
|
interface Routes {
|
||||||
[key: string]: (ctx: RequestContext, params: BaseAPICall) => void | Promise<void>
|
[key: string]: (ctx: RequestContext, params: BaseAPICall) => void | Promise<void>
|
||||||
}
|
}
|
||||||
@@ -86,7 +86,9 @@ async function resolveChallengeWithTimeout(ctx: RequestContext, params: BaseRequ
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function resolveChallenge(ctx: RequestContext, { url, proxy, download, returnOnlyCookies }: BaseRequestAPICall, page: Page): Promise<ChallengeResolutionT | void> {
|
async function resolveChallenge(ctx: RequestContext,
|
||||||
|
{ url, proxy, download, returnOnlyCookies, returnRawHtml }: BaseRequestAPICall,
|
||||||
|
page: Page): Promise<ChallengeResolutionT | void> {
|
||||||
|
|
||||||
let status = 'ok'
|
let status = 'ok'
|
||||||
let message = ''
|
let message = ''
|
||||||
@@ -132,6 +134,8 @@ async function resolveChallenge(ctx: RequestContext, { url, proxy, download, ret
|
|||||||
// fix since I am short on time
|
// fix since I am short on time
|
||||||
response = await page.goto(url, { waitUntil: 'domcontentloaded' })
|
response = await page.goto(url, { waitUntil: 'domcontentloaded' })
|
||||||
payload.result.response = (await response.buffer()).toString('base64')
|
payload.result.response = (await response.buffer()).toString('base64')
|
||||||
|
} else if (returnRawHtml) {
|
||||||
|
payload.result.response = await response.text()
|
||||||
} else {
|
} else {
|
||||||
payload.result.response = await page.content()
|
payload.result.response = await page.content()
|
||||||
}
|
}
|
||||||
@@ -197,6 +201,12 @@ async function setupPage(ctx: RequestContext, params: BaseRequestAPICall, browse
|
|||||||
let callbackRunOnce = false
|
let callbackRunOnce = false
|
||||||
const callback = (request: Request) => {
|
const callback = (request: Request) => {
|
||||||
|
|
||||||
|
// avoid loading resources to speed up page load
|
||||||
|
if(request.resourceType() == 'stylesheet' || request.resourceType() == 'font' || request.resourceType() == 'image') {
|
||||||
|
request.abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if (callbackRunOnce || !request.isNavigationRequest()) {
|
if (callbackRunOnce || !request.isNavigationRequest()) {
|
||||||
request.continue()
|
request.continue()
|
||||||
return
|
return
|
||||||
|
|||||||
Reference in New Issue
Block a user