Skip to content

Commit

Permalink
remove dependence on express
Browse files Browse the repository at this point in the history
  • Loading branch information
kkoooqq committed Nov 9, 2021
1 parent c2013ac commit b3dd647
Show file tree
Hide file tree
Showing 5 changed files with 159 additions and 58 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ FakeBrowser automatic login demo:
To use FakeBrowser in your project, run:

```bash
yarn add puppeteer axios express fakebrowser
yarn add puppeteer axios fakebrowser
```

---
Expand Down
74 changes: 68 additions & 6 deletions demo/demo6-connect-to-instance.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,86 @@
const {FakeBrowser} = require('../dist/cjs/FakeBrowser');
const {spawn, execSync} = require('child_process');
const path = require('path');

process.on('unhandledRejection', (error, p) => {
console.log('=== UNHANDLED REJECTION ===');
console.dir(error.stack);
});

!(async () => {
const dd = require('../device-hub/Windows.json');
const userDataDir = path.resolve(__dirname, './fakeBrowserUserData');

execSync(`rm -rf ${userDataDir}`);
execSync(`mkdir ${userDataDir}`);

const dd = require('../device-hub/macOS.json');
const child = spawn('/Applications/Google Chrome 93.0.4577.82.app/Contents/MacOS/Google Chrome',
[
'--no-default-browser-check',
'--no-first-run',
`--window-position=0,0`,
`--window-size=${dd.window.outerWidth},${dd.window.outerHeight}`,
`--user-data-dir=${userDataDir}`,
'--remote-debugging-port=9222',
],
);

child.stdout.on('data', (data) => {
});

child.stderr.on('data', (data) => {
const dataStr = data.toString();
if (dataStr.includes('DevTools listening on ')) {
const wsEndPoint = dataStr.substr(dataStr.indexOf('ws://'));
console.log(wsEndPoint);

launchFBPure(wsEndPoint).then(r => r);
}
});

const launchFBPure = async (wsEndPoint) => {
const {addExtra, PuppeteerExtra} = require('puppeteer-extra');
const pptr = addExtra(require('puppeteer'));
const browser = await pptr.connect({browserWSEndpoint: wsEndPoint});
const page = await browser.newPage();
await page.goto('https://nike.com/ca/');
};

const launchFB = async (wsEndPoint) => {
const builder = new FakeBrowser.Builder()
.deviceDescriptor(dd)
.displayUserActionLayer(false)
.doNotHook(false)
.vanillaConnectOptions({
browserWSEndpoint: 'ws://127.0.0.1:9222/devtools/browser/a060d8a6-82ad-4035-8e24-2fbf35ee4de9',
browserWSEndpoint: wsEndPoint,
})
.userDataDir('./fakeBrowserUserData');
.userDataDir(userDataDir);

const fakeBrowser = await builder.connect();

const page = await fakeBrowser.vanillaBrowser.newPage();
await page.goto('https://nike.com/ca');

console.log('wait #hf_title_signin_membership appeared');
await page.waitForSelector('#hf_title_signin_membership');

await page.waitForTimeout(5 * 1000);

console.log('click login button');
const signInButton = await page.$('#hf_title_signin_membership');
await fakeBrowser.userAction.simClickElement(signInButton);

const emailTextField = await page.$('input[name="emailAddress"]');
const passwordField = await page.$('input[name="password"]');
const loginButton = await page.$('.loginSubmit');

await fakeBrowser.userAction.simClickElement(emailTextField);
await fakeBrowser.userAction.simKeyboardType('[email protected]');

await fakeBrowser.userAction.simClickElement(passwordField);
await fakeBrowser.userAction.simKeyboardType('passowdd+34.3710-');

await fakeBrowser.userAction.simClickElement(loginButton);


// await fakeBrowser.shutdown();
})();
};

5 changes: 1 addition & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "fakebrowser",
"version": "0.0.51",
"version": "0.0.52",
"description": "🤖 Fake fingerprints to bypass anti-bot systems. Simulate mouse and keyboard operations to make behavior like a real person.",
"repository": {
"type": "git",
Expand Down Expand Up @@ -56,18 +56,15 @@
},
"peerDependencies": {
"axios": "*",
"express": "*",
"puppeteer": "*"
},
"devDependencies": {
"@types/axios": "*",
"@types/express": "*",
"@types/fs-extra": "^9.0.13",
"@types/puppeteer": "*",
"axios": "^0.24.0",
"cpy-cli": "^3.1.1",
"cross-env": "^7.0.3",
"express": "^4.17.1",
"jest": "^27.3.1",
"puppeteer": "^11.0.0",
"rimraf": "^3.0.2",
Expand Down
111 changes: 64 additions & 47 deletions src/core/BrowserLauncher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ import * as fs from "fs-extra";
import * as path from "path";
import * as URLToolkit from 'url-toolkit'
import * as http from "http";
import {IncomingMessage, ServerResponse} from "http";
import * as url from "url";

import axios from "axios";
import express, {Application} from "express";
import {Agent} from "https";
import {strict as assert} from 'assert';

Expand All @@ -20,8 +21,7 @@ export class BrowserLauncher {

static _fakeBrowserInstances: FakeBrowser[] = []
static _checkerIntervalId: NodeJS.Timer | null = null
static _app: Application | null = null
static _appServer: http.Server | null = null
static _httpServer: http.Server | null = null

private static checkLaunchOptionsLegal(options?: VanillaLaunchOptions) {
if (!options || !options.args || !options.args.length) {
Expand Down Expand Up @@ -161,69 +161,80 @@ export class BrowserLauncher {
}

private static async bootInternalHTTPServer() {
if (!this._app) {
this._app = express()
if (!this._httpServer) {
this._httpServer = http.createServer()

this._app.get('/hb', async (req, res) => {
res.send(kInternalHttpServerHeartbeatMagic)
})
this._httpServer.on('request', async (req: IncomingMessage, res: ServerResponse) => {
assert(req.url)
const {query, pathname} = url.parse(req.url, true)

if (pathname === '/hb') {
res.write(kInternalHttpServerHeartbeatMagic)
res.end()
}

this._app.get('/patchWorker', async (req, res) => {
const relUrl = req.query['relUrl'] as string
const workerUrl = req.query['workerUrl'] as string
const uuid = req.query['uuid'] as string
if (pathname === '/patchWorker') {
const relUrl = query['relUrl'] as string
const workerUrl = query['workerUrl'] as string
const uuid = query['uuid'] as string

const fullUrl = URLToolkit.buildAbsoluteURL(relUrl, workerUrl)
const fullUrl = URLToolkit.buildAbsoluteURL(relUrl, workerUrl)

console.log('request worker content from: ', fullUrl)
console.log('request worker content from: ', fullUrl)

// Object.fromEntries ES2019
const reqHeaders = Object.fromEntries(
Object.entries(
req.headers
).map(
e => ([e[0], e[1]![0]])
// Object.fromEntries ES2019
const reqHeaders = Object.fromEntries(
Object.entries(
req.headers
).map(
e => ([e[0], e[1]![0]])
)
)
)

delete reqHeaders.host
delete reqHeaders.host

const jsResp = await axios.get(
fullUrl, {
headers: reqHeaders,
httpsAgent: new Agent({
rejectUnauthorized: false
})
}
)

let jsContent = jsResp.data
const browser = BrowserLauncher.getBrowserWithUUID(uuid)
// TODO: get through proxy
const jsResp = await axios.get(
fullUrl, {
headers: reqHeaders,
httpsAgent: new Agent({
rejectUnauthorized: false
})
}
)

if (browser) {
jsContent = await PptrPatcher.patchWorkerJsContent(browser, jsContent)
}
let jsContent = jsResp.data
const browser = BrowserLauncher.getBrowserWithUUID(uuid)

for (const {name, value} of Object.entries(jsResp.headers).map(e => ({
name: e[0],
value: e[1] as string
}))) {
if (name.toLowerCase() != 'content-length') {
res.header(name, value)
if (browser) {
jsContent = await PptrPatcher.patchWorkerJsContent(browser, jsContent)
}
}

res.send(jsContent)
res.writeHead(
jsResp.status,
jsResp.statusText,
jsResp.headers as NodeJS.Dict<string>
)

res.write(jsContent)
res.end()
}
})

// If the port listens to errors, determine if the heartbeat interface is successful
try {
this._appServer = this._app.listen(FakeBrowser.globalConfig.internalHttpServerPort)
this._httpServer.listen(FakeBrowser.globalConfig.internalHttpServerPort)
} catch (ex: any) {
const hbUrl = `http://127.0.0.1:${FakeBrowser.globalConfig.internalHttpServerPort}/hb`
try {
const hbData = (await axios.get(hbUrl)).data
if (hbData === kInternalHttpServerHeartbeatMagic) {
try {
this._httpServer.close()
} finally {
this._httpServer = null
}

return
}
} catch (ignore: any) {
Expand Down Expand Up @@ -268,8 +279,14 @@ export class BrowserLauncher {
// If all browsers have exited, close internal http service
if (this._fakeBrowserInstances.length === 0) {
// console.log('close appserver')
this._appServer!.close()
this._app = null

if (this._httpServer) {
try {
this._httpServer.close()
} finally {
this._httpServer = null
}
}
}
}
}
Loading

0 comments on commit b3dd647

Please sign in to comment.