Reputation: 2704
Okay so currently I've got a ExpressJS API that I've wrote that works alongside Puppeteer however if I send concurrent requests then if request 1 updates device_imei
then request 2 will get that value instead of it's own state?
My script looks like so:
let proxy;
let recaptcha_solution;
let page;
let browser = false;
let device_imei;
let domains;
let token;
let userProfile;
let tokenRefId;
let requestNo;
let success = true;
let errorCode;
let emails;
let errorMessage;
let requestId;
let bitmap;
let validationId;
const handle_response = async (response) => {
const url = response.url();
try {
const req = response.request();
const orig = req.url();
let status;
let text;
if(response.status) {
status = response.status();
}
if(
status
&& !(status > 299 && status < 400)
&& !(status === 204)
&& (req.resourceType() === 'xhr')
) {
text = await response.text();
if(text.includes('Access Denied')) {
success = false;
return;
}
const json = JSON.parse(text);
if(json['reCaptchaDetail']) {
if(json['reCaptchaDetail']['token'] && json['reCaptchaDetail']['tokenRefId']) {
token = json['reCaptchaDetail']['token'];
tokenRefId = json['reCaptchaDetail']['tokenRefId'];
await client.set('captcha_solution', JSON.stringify(json['reCaptchaDetail']), 'EX', 3600);
}
} else if(json['error']) {
success = false;
if(json['error']['errorId'].startsWith('UNLOCK')) {
errorCode = json['error']['details'][0]['code'];
errorMessage = json['error']['details'][0]['message'];
}
}
}
} catch (err) {
console.error(`Failed getting data from: ${url}`);
console.error(err);
}
};
const createOrder = async (req, res) => {
let params = await parser.parseStringPromise(req.body.parameters);
device_imei = params.PARAMETERS.IMEI;
//config
axios.defaults.withCredentials = true;
axios.defaults.timeout = 15000;
userProfile = faker.entity.user();
page = await browser.newPage();
page.on('response', handle_response);
//a lot more stuff that accesses/updates all the let vars and working with page var.
}
app.post('/api/index.php', async function (req, res) {
res.setHeader('Content-Type', 'application/json; charset=utf-8');
res.setHeader('X-Powered-By', 'DHRU-FUSION');
res.setHeader('dhru-fusion-api-version', '6.1');
res.removeHeader('pragma');
res.removeHeader('server');
res.removeHeader('transfer-encoding');
res.removeHeader('cache-control');
res.removeHeader('expires');
if (Object.keys(req.body).length === 0) {
return res.send(error_response('Invalid Request'));
}
if (typeof req.body.apiaccesskey === undefined || req.body.apiaccesskey !== process.env.SITE_API_KEY) {
return res.send(error_response('Invalid API Key'));
}
switch(req.body.action) {
case "placeimeiorder":
createOrder(req, res).catch(function ignore() {});
return res.send(success_response({
'MESSAGE': 'Order Received!'
}));
default:
return res.send(error_response('Invalid Action'));
}
});
app.listen(3000);
Would I have to implement mutli-threading so my variables can maintain state? or is their a much easier solution?
Upvotes: 0
Views: 752
Reputation: 11070
You're having issues due to making all of your variables top-level, as if they're static or global. But really the variables each depend on a specific execution of your handler function, so they need to be declared in the function! If you're struggling with passing the variables to your handle_response
function, I recommend containing them all into an object and passing that on. Something like this:
const handle_response = async (response, state) => {
const url = response.url();
try {
const req = response.request();
const orig = req.url();
let status;
let text;
if(response.status) {
status = response.status();
}
if(
status
&& !(status > 299 && status < 400)
&& !(status === 204)
&& (req.resourceType() === 'xhr')
) {
text = await response.text();
if(text.includes('Access Denied')) {
state.success = false;
return;
}
const json = JSON.parse(text);
if(json['reCaptchaDetail']) {
if(json['reCaptchaDetail']['token'] && json['reCaptchaDetail']['tokenRefId']) {
state.token = json['reCaptchaDetail']['token'];
state.tokenRefId = json['reCaptchaDetail']['tokenRefId'];
await client.set('captcha_solution', JSON.stringify(json['reCaptchaDetail']), 'EX', 3600);
}
} else if(json['error']) {
state.success = false;
if(json['error']['errorId'].startsWith('UNLOCK')) {
state.errorCode = json['error']['details'][0]['code'];
state.errorMessage = json['error']['details'][0]['message'];
}
}
}
} catch (err) {
console.error(`Failed getting data from: ${url}`);
console.error(err);
}
};
const createOrder = async (req, res) => {
let params = await parser.parseStringPromise(req.body.parameters);
let state = {}; //put all your variables as properties of this object
state.device_imei = params.PARAMETERS.IMEI;
//config
axios.defaults.withCredentials = true;
axios.defaults.timeout = 15000;
state.userProfile = faker.entity.user();
let page = await browser.newPage(); //make things local if they dont need to be passed
page.on('response', response => handle_response(response, state));
//a lot more stuff that accesses/updates all the object properties and working with page var.
}
To reiterate: When building any asynchronous application, absolutely do not reuse the same global variables in your functions... as you've seen, it just leads to their values being overwritten by a concurrent request. Keep all your variables locally scoped to the scope of the request, and pass them along to any functions that need them.
Upvotes: 1