Firebase schedule function subpub axios POST request - node.js

I'm using firebase functions to schedule a puppeteer task from a firebase pubsub namespace.
I also want to post the response back to an API endpoint outside google using axios.
I tried Axios directly from the worker but I get a 500 error.
My goal:
Use firebase schedule and pubsub namespace to generate puppeteer
jobs - complete
Post results back to external endpoint using axios - need your help :)
Code:
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const functions = require("firebase-functions");
const admin = require("firebase-admin");
const puppeteer = require('puppeteer');
const url = require('url');
// Request Data From A URL
var axios = require('axios');
var https = require('https');
// var cors = require("cors");
// Initalise App
admin.initializeApp();
const db = admin.firestore();
const workers = {
extract: async ({ uri, post_id, store_id, domain }) => {
let theTitle = null;
let thePrice = null;
let host = url.parse(uri).host;
let postId = post_id;
let storeId = store_id;
let theDomain = domain;
const SELECTORS = {
amazonPrice: '#priceblock_ourprice',
ebayPrice: '#prcIsum',
const browser = await puppeteer.launch({ args: ['--no-sandbox', '--disable-setuid-sandbox'] });
console.log('spawning chrome headless');
console.log(postId);
console.log(storeId);
console.log(domain);
const page = await browser.newPage();
// Goto page and then do stuff
console.log('going to ', uri);
await page.goto(uri, {
waitUntil: ["domcontentloaded", "networkidle0"]
});
console.log('waiting for page to load ');
console.log(host);
// theTitle = await page.title();
try {
theTitle = await page.title();
// find amazon price
if (host === 'www.amazon.co.uk' || 'amazon.co.uk') {
const priceInput = await page.$(SELECTORS.amazonPrice)
thePrice = await page.evaluate(element => element.textContent, priceInput)
}
// find ebay price
if (host === 'www.ebay.co.uk' || 'ebay.co.uk') {
const priceInput = await page.$(SELECTORS.ebayPrice)
thePrice = await page.evaluate(element => element.value, priceInput)
}
else {
console.log('failed scrape at', host);
}
}
catch (error) {
console.error('There was an error processing the page:', error);
}
finally {
// close browser
if (browser !== null) {
await browser.close();
}
}
console.log(theTitle);
console.log(thePrice);
const response = {
title: theTitle,
price: thePrice,
};
console.log('post' + postId + storeId + thePrice + theDomain);
axios.post('endpoint', {
price: thePrice,
store_id: storeId,
post_id: postId,
domain: theDomain,
},
{
headers: {
'Content-Type': 'multipart/form-data',
}
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
// axios post end
return response;
}
};
exports.taskRunner = functions.runWith({ memory: '2GB' }).pubsub
// export const taskRunner = functions.region('europe-west2').runWith( { memory: '2GB' }).pubsub
.schedule('*/15 * * * *').onRun(async (context) => {
// Consistent timestamp
const now = admin.firestore.Timestamp.now();
// Query all documents ready to perform
const query = db.collection('tasks').where('performAt', '<=', now).where('status', '==', 'scheduled');
const tasks = await query.get();
// Jobs to execute concurrently.
const jobs = [];
// Loop over documents and push job.
tasks.forEach(snapshot => {
const { worker, options } = snapshot.data();
const job = workers[worker](options)
// Update doc with status on success or error
// .then(() => snapshot.ref.update({ status: 'complete' }))
.catch((err) => snapshot.ref.update({ status: 'error' }));
jobs.push(job);
});
// Execute all jobs concurrently
return await Promise.all(jobs);
});
Error
Error: Request failed with status code 500
at createError (/srv/node_modules/axios/lib/core/createError.js:16:15)
at settle (/srv/node_modules/axios/lib/core/settle.js:17:12)
at IncomingMessage.handleStreamEnd (/srv/node_modules/axios/lib/adapters/http.js:236:11)
at emitNone (events.js:111:20)
at IncomingMessage.emit (events.js:208:7)
at endReadableNT (_stream_readable.js:1064:12)
at _combinedTickCallback (internal/process/next_tick.js:139:11)
at process._tickDomainCallback (internal/process/next_tick.js:219:9)
config:
{ url: 'endpoint',
method: 'post',
data: '{"£59.99":null,"store_id":32,"post_id":25,"domain":"amazon.co.uk"}',
headers:
{ Accept: 'application/json, text/plain, */*',
'Content-Type': 'multipart/form-data',
'User-Agent': 'axios/0.19.2',
'Content-Length': 65 },
transformRequest: [ [Function: transformRequest] ],
transformResponse: [ [Function: transformResponse] ],
timeout: 0,
adapter: [Function: httpAdapter],
xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN',
maxContentLength: -1,
validateStatus: [Function: validateStatus] },
request:
ClientRequest {
domain:
Domain {
domain: null,
_events: [Object],
_eventsCount: 1,
_maxListeners: undefined,
members: [Array] },
_events:
{ socket: [Function],
abort: [Function],
aborted: [Function],
error: [Function],
timeout: [Function],
prefinish: [Function: requestOnPrefinish] },
_eventsCount: 6,
_maxListeners: undefined,
output: [],
outputEncodings: [],
outputCallbacks: [],
outputSize: 0,
writable: true,
_last: true,
upgrading: false,
chunkedEncoding: false,
shouldKeepAlive: false,
useChunkedEncodingByDefault: true,
sendDate: false,
_removedConnection: false,
_removedContLen: false,
_removedTE: false,
_contentLength: null,
_hasBody: true,
_trailer: '',
finished: true,
_headerSent: true,
socket:
TLSSocket {
_tlsOptions: [Object],
_secureEstablished: true,
_securePending: false,
_newSessionPending: false,
_controlReleased: true,
_SNICallback: null,
servername: 'SERVERNAME',
npnProtocol: false,
alpnProtocol: false,
authorized: true,
authorizationError: null,
encrypted: true,
_events: [Object],
_eventsCount: 9,
connecting: false,
_hadError: false,
_handle: [Object],
_parent: null,
_host: 'HOST',
_readableState: [Object],
readable: true,
domain: [Object],
_maxListeners: undefined,
_writableState: [Object],
writable: false,
allowHalfOpen: false,
_bytesDispatched: 259,
_sockname: null,
_pendingData: null,
_pendingEncoding: '',
server: undefined,
_server: null,
ssl: [Object],
_requestCert: true,
_rejectUnauthorized: true,
parser: null,
_httpMessage: [Circular],
[Symbol(asyncId)]: 538,
[Symbol(bytesRead)]: 0 },
connection:
TLSSocket {
_tlsOptions: [Object],
_secureEstablished: true,
_securePending: false,
_newSessionPending: false,
_controlReleased: true,
_SNICallback: null,
servername: 'HOST',
npnProtocol: false,
alpnProtocol: false,
authorized: true,
authorizationError: null,
encrypted: true,
_events: [Object],
_eventsCount: 9,
connecting: false,
_hadError: false,
_handle: [Object],
_parent: null,
_host: 'HOST',
_readableState: [Object],
readable: true,
domain: [Object],
_maxListeners: undefined,
_writableState: [Object],
writable: false,
allowHalfOpen: false,
_bytesDispatched: 259,
_sockname: null,
_pendingData: null,
_pendingEncoding: '',
server: undefined,
_server: null,
ssl: [Object],
_requestCert: true,
_rejectUnauthorized: true,
parser: null,
_httpMessage: [Circular],
[Symbol(asyncId)]: 538,
[Symbol(bytesRead)]: 0 },
_header: 'POST ENDPOINT HTTP/1.1\r\nAccept: application/json, text/plain, */*\r\nContent-Type: multipart/form-data\r\nUser-Agent: axios/0.19.2\r\nContent-Length: 65\r\nHost: HOST\r\nConnection: close\r\n\r\n',
_onPendingData: [Function: noopPendingOutput],
agent:
Agent {
domain: null,
_events: [Object],
_eventsCount: 1,
_maxListeners: undefined,
defaultPort: 443,
protocol: 'https:',
options: [Object],
requests: {},
sockets:
more_vert
Logs are subject to Cloud Logging's

After some work and trial & error. I settled on using the Request package to solve for the issue - https://www.npmjs.com/package/request
Not had time to investigate why Axios was not working but will review and if enlightened I will post here.

Related

401 Unauthorized when consuming Business Central OData Web Service in NodeJs

I have been trying to connect and retrieve a list of data from Business Central.
The Web Service has been exposed from BC and the OData link works and displays the JSON for the data in "Articles".
I'm trying to consumer that Service with NodeJS using NTLM (i also tried basic authentication and the same issue persists.
Please find below the file responsible for consuming the BC Web Service followed by the 401 error.
I have already stored all the information needed for NTLM config in my .env file, I did try to hardcode them and the same issue
const express = require("express");
const axios = require("axios");
const router = express.Router();
const ntlm = require("express-ntlm");
//const Article = require("../models/Articles.js");
const domain = process.env.DOMAIN;
const username = process.env.USERNAME;
const password = process.env.PASSWORD;
// Define auth function
const auth = ntlm({
debug: console.log,
domain,
username,
password,
ntlm_version: 2,
reconnect: true,
send_401: function (res) {
res.sendStatus(401);
},
badrequest: function (res) {
res.sendStatus(400);
},
});
// Get customer data from Business Central
async function getArticlesFromBC() {
try {
const options = {
auth: {
username,
password,
workstation: process.env.WORKSTATION,
domain,
},
};
const companyId = "CRONUS France S.A.";
const encodedCompanyId = encodeURIComponent(companyId);
const url = `http://${process.env.SERVER}:7048/BC210/ODataV4/Company('${encodedCompanyId}')/ItemListec`;
const response = await axios.get(url, options);
return response.data;
} catch (error) {
console.error(error);
throw new Error("Error retrieving articles data from Business Central");
}
}
// Route to get article data
router.get("/", auth, async (req, res) => {
try {
const user = req.ntlm;
console.log(user);
let articles = await getArticlesFromBC();
res.json(articles);
} catch (error) {
console.error(error);
res
.status(500)
.send("Error retrieving articles data from Business Central");
}
});
module.exports = router;
Explanation
This is the auth function that will use the variables set in .env to authenticate the user in order to access the BC endpoint
// Define auth function
const auth = ntlm({
debug: console.log,
domain,
username,
password,
ntlm_version: 2,
reconnect: true,
send_401: function (res) {
res.sendStatus(401);
},
badrequest: function (res) {
res.sendStatus(400);
},
});
This is the function that connects to the endpoint URL and returns the JSON file, this is where the error triggers
// Get article data from Business Central
async function getArticlesFromBC() {
try {
const options = {
auth: {
username,
password,
workstation: process.env.WORKSTATION,
domain,
},
};
const companyId = "CRONUS France S.A.";
const encodedCompanyId = encodeURIComponent(companyId);
const url = `http://${process.env.SERVER}:7048/BC210/ODataV4/Company('${encodedCompanyId}')/ItemListec`;
const response = await axios.get(url, options);
return response.data;
} catch (error) {
console.error(error);
throw new Error("Error retrieving articles data from Business Central");
}
}
This is the route to access the nodejs api
// Route to get article data
router.get("/", auth, async (req, res) => {
try {
const user = req.ntlm;
console.log(user);
let articles = await getArticlesFromBC();
res.json(articles);
} catch (error) {
console.error(error);
res
.status(500)
.send("Error retrieving articles data from Business Central");
}
});
This is the 401 error json, you can see at the beginning that NTLM authenticates successfully but then it throws the catch block in the get function
[express-ntlm] No Authorization header present
[express-ntlm] No domaincontroller was specified, all Authentication messages are valid.
{
DomainName: 'DESKTOP-1EF91E4',
UserName: 'ahmed',
Workstation: 'DESKTOP-1EF91E4',
Authenticated: true
}
AxiosError: Request failed with status code 401
at settle (D:\ESPRIT\5eme\PFE\B2B-ERP-MERN-App\node_modules\axios\dist\node\axios.cjs:1900:12)
at IncomingMessage.handleStreamEnd (D:\ESPRIT\5eme\PFE\B2B-ERP-MERN-App\node_modules\axios\dist\node\axios.cjs:2944:11)
at IncomingMessage.emit (events.js:412:35)
at endReadableNT (internal/streams/readable.js:1317:12)
at processTicksAndRejections (internal/process/task_queues.js:82:21) {
code: 'ERR_BAD_REQUEST',
config: {
transitional: {
silentJSONParsing: true,
forcedJSONParsing: true,
clarifyTimeoutError: false
},
adapter: [ 'xhr', 'http' ],
transformRequest: [ [Function: transformRequest] ],
transformResponse: [ [Function: transformResponse] ],
timeout: 0,
xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN',
maxContentLength: -1,
maxBodyLength: -1,
env: { FormData: [Function], Blob: null },
validateStatus: [Function: validateStatus],
headers: AxiosHeaders {
Accept: 'application/json, text/plain, */*',
'User-Agent': 'axios/1.3.3',
'Accept-Encoding': 'gzip, compress, deflate, br'
},
auth: {
username: 'ahmed',
password: undefined,
workstation: 'DESKTOP-1EF91E4',
domain: undefined
},
method: 'get',
url: "http://desktop-1ef91e4:7048/BC210/ODataV4/Company('CRONUS%20France%20S.A.')/ItemListec",
data: undefined
},
request: <ref *1> ClientRequest {
_events: [Object: null prototype] {
abort: [Function (anonymous)],
aborted: [Function (anonymous)],
connect: [Function (anonymous)],
error: [Function (anonymous)],
socket: [Function (anonymous)],
timeout: [Function (anonymous)],
prefinish: [Function: requestOnPrefinish]
},
_eventsCount: 7,
_maxListeners: undefined,
outputData: [],
outputSize: 0,
writable: true,
destroyed: false,
_last: true,
chunkedEncoding: false,
shouldKeepAlive: false,
_defaultKeepAlive: true,
useChunkedEncodingByDefault: false,
sendDate: false,
_removedConnection: false,
_removedContLen: false,
_removedTE: false,
_contentLength: 0,
_hasBody: true,
_trailer: '',
finished: true,
_headerSent: true,
socket: Socket {
connecting: false,
_hadError: false,
_parent: null,
_host: 'desktop-1ef91e4',
_readableState: [ReadableState],
_events: [Object: null prototype],
_eventsCount: 7,
_maxListeners: undefined,
_writableState: [WritableState],
allowHalfOpen: false,
_sockname: null,
_pendingData: null,
_pendingEncoding: '',
server: null,
_server: null,
parser: null,
_httpMessage: [Circular *1],
[Symbol(async_id_symbol)]: 465,
[Symbol(kHandle)]: [TCP],
[Symbol(kSetNoDelay)]: false,
[Symbol(lastWriteQueueSize)]: 0,
[Symbol(timeout)]: null,
[Symbol(kBuffer)]: null,
[Symbol(kBufferCb)]: null,
[Symbol(kBufferGen)]: null,
[Symbol(kCapture)]: false,
[Symbol(kBytesRead)]: 0,
[Symbol(kBytesWritten)]: 0,
[Symbol(RequestTimeout)]: undefined
},
_header: "GET /BC210/ODataV4/Company('CRONUS%20France%20S.A.')/ItemListec HTTP/1.1\r\n" +
'Accept: application/json, text/plain, */*\r\n' +
'User-Agent: axios/1.3.3\r\n' +
'Accept-Encoding: gzip, compress, deflate, br\r\n' +
'Host: desktop-1ef91e4:7048\r\n' +
'Authorization: Basic YWhtZWQ6\r\n' +
'Connection: close\r\n' +
'\r\n',
_keepAliveTimeout: 0,
_onPendingData: [Function: noopPendingOutput],
agent: Agent {
_events: [Object: null prototype],
_eventsCount: 2,
_maxListeners: undefined,
defaultPort: 80,
protocol: 'http:',
options: [Object],
requests: {},
sockets: [Object],
freeSockets: {},
keepAliveMsecs: 1000,
keepAlive: false,
maxSockets: Infinity,
maxFreeSockets: 256,
scheduling: 'lifo',
maxTotalSockets: Infinity,
totalSocketCount: 1,
[Symbol(kCapture)]: false
},
socketPath: undefined,
method: 'GET',
maxHeaderSize: undefined,
insecureHTTPParser: undefined,
path: "/BC210/ODataV4/Company('CRONUS%20France%20S.A.')/ItemListec",
_ended: true,
res: IncomingMessage {
_readableState: [ReadableState],
_events: [Object: null prototype],
_eventsCount: 4,
_maxListeners: undefined,
socket: [Socket],
httpVersionMajor: 1,
httpVersionMinor: 1,
httpVersion: '1.1',
complete: true,
headers: [Object],
rawHeaders: [Array],
trailers: {},
rawTrailers: [],
aborted: false,
upgrade: false,
url: '',
method: null,
statusCode: 401,
statusMessage: 'Unauthorized',
client: [Socket],
_consuming: false,
_dumped: false,
req: [Circular *1],
responseUrl: "http://ahmed:#desktop-1ef91e4:7048/BC210/ODataV4/Company('CRONUS%20France%20S.A.')/ItemListec",
redirects: [],
[Symbol(kCapture)]: false,
[Symbol(RequestTimeout)]: undefined
},
aborted: false,
timeoutCb: null,
upgradeOrConnect: false,
parser: null,
maxHeadersCount: null,
reusedSocket: false,
host: 'desktop-1ef91e4',
protocol: 'http:',
_redirectable: Writable {
_writableState: [WritableState],
_events: [Object: null prototype],
_eventsCount: 3,
_maxListeners: undefined,
_options: [Object],
_ended: true,
_ending: true,
_redirectCount: 0,
_redirects: [],
_requestBodyLength: 0,
_requestBodyBuffers: [],
_onNativeResponse: [Function (anonymous)],
_currentRequest: [Circular *1],
_currentUrl: "http://ahmed:#desktop-1ef91e4:7048/BC210/ODataV4/Company('CRONUS%20France%20S.A.')/ItemListec",
[Symbol(kCapture)]: false
},
[Symbol(kCapture)]: false,
[Symbol(kNeedDrain)]: false,
[Symbol(corked)]: 0,
[Symbol(kOutHeaders)]: [Object: null prototype] {
accept: [Array],
'user-agent': [Array],
'accept-encoding': [Array],
host: [Array],
authorization: [Array]
}
},
response: {
status: 401,
statusText: 'Unauthorized',
headers: AxiosHeaders {
'content-length': '0',
server: 'Microsoft-HTTPAPI/2.0',
'www-authenticate': 'Negotiate',
date: 'Thu, 16 Feb 2023 11:49:51 GMT',
connection: 'close'
},
config: {
transitional: [Object],
adapter: [Array],
transformRequest: [Array],
transformResponse: [Array],
timeout: 0,
xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN',
maxContentLength: -1,
maxBodyLength: -1,
env: [Object],
validateStatus: [Function: validateStatus],
headers: [AxiosHeaders],
auth: [Object],
method: 'get',
url: "http://desktop-1ef91e4:7048/BC210/ODataV4/Company('CRONUS%20France%20S.A.')/ItemListec",
data: undefined
},
request: <ref *1> ClientRequest {
_events: [Object: null prototype],
_eventsCount: 7,
_maxListeners: undefined,
outputData: [],
outputSize: 0,
writable: true,
destroyed: false,
_last: true,
chunkedEncoding: false,
shouldKeepAlive: false,
_defaultKeepAlive: true,
useChunkedEncodingByDefault: false,
sendDate: false,
_removedConnection: false,
_removedContLen: false,
_removedTE: false,
_contentLength: 0,
_hasBody: true,
_trailer: '',
finished: true,
_headerSent: true,
socket: [Socket],
_header: "GET /BC210/ODataV4/Company('CRONUS%20France%20S.A.')/ItemListec HTTP/1.1\r\n" +
'Accept: application/json, text/plain, */*\r\n' +
'User-Agent: axios/1.3.3\r\n' +
'Accept-Encoding: gzip, compress, deflate, br\r\n' +
'Host: desktop-1ef91e4:7048\r\n' +
'Authorization: Basic YWhtZWQ6\r\n' +
'Connection: close\r\n' +
'\r\n',
_keepAliveTimeout: 0,
_onPendingData: [Function: noopPendingOutput],
agent: [Agent],
socketPath: undefined,
method: 'GET',
maxHeaderSize: undefined,
insecureHTTPParser: undefined,
path: "/BC210/ODataV4/Company('CRONUS%20France%20S.A.')/ItemListec",
_ended: true,
res: [IncomingMessage],
aborted: false,
timeoutCb: null,
upgradeOrConnect: false,
parser: null,
maxHeadersCount: null,
reusedSocket: false,
host: 'desktop-1ef91e4',
protocol: 'http:',
_redirectable: [Writable],
[Symbol(kCapture)]: false,
[Symbol(kNeedDrain)]: false,
[Symbol(corked)]: 0,
[Symbol(kOutHeaders)]: [Object: null prototype]
},
data: ''
}
}
Error: Error retrieving articles data from Business Central
at getArticlesFromBC (D:\ESPRIT\5eme\PFE\B2B-ERP-MERN-App\routes\ArticleRoutes.js:44:11)
at processTicksAndRejections (internal/process/task_queues.js:95:5)
at async D:\ESPRIT\5eme\PFE\B2B-ERP-MERN-App\routes\ArticleRoutes.js:53:20
Even though it works perfectly in postman (NTLM authentication on Server Instance is enabled for that to happen)
express-ntlm seemed to authenticate the user but dosent follow up for some reason.
I switched to httpntlm and it worked perfectly, here is the new code in the node API file :
const express = require("express");
const router = express.Router();
const httpntlm = require("httpntlm");
// Get article data from Business Central
async function getArticlesFromBC() {
const username = process.env.USERNAME;
const password = process.env.PASSWORD;
const domain = process.env.DOMAIN;
const workstation = process.env.WORKSTATION;
const encodedCompanyId = encodeURIComponent("CRONUS France S.A.");
const url = `http://${process.env.SERVER}:7048/BC210/ODataV4/Company('${encodedCompanyId}')/ItemListec`;
const options = {
url: url,
username: username,
password: password,
workstation: workstation,
domain: domain
};
return new Promise((resolve, reject) => {
httpntlm.get(options, (err, res) => {
if (err) {
console.error(err);
reject(err);
} else {
resolve(res.body);
}
});
});
}
// Route to get article data
router.get("/", async (req, res) => {
try {
const articles = await getArticlesFromBC();
res.json(articles);
} catch (error) {
console.error("Error retrieving articles data from Business Central:", error.message);
res.status(500).send("Error retrieving articles data from Business Central");
}
});
module.exports = router;

Unable to authenticate (okta) a post request to a route from within a route getting a 401 Unauthorized response. GET Request works

I have a nodejs app utilising express using #okta/okta-sdk-nodejs and #okta/oidc-middleware to handle authentication.
I have a number of routes that work fine and are authorised as expected. The following flow generates a 401 status code and I am struggling to work out why.
If I hit the route http://localhost:3000/b/f-e-info i get a response from an external API, this works, I then want to send this to another route /es/ingest/b/ts to get ingested I do this via a function callEs('/es/ingest/b/ts',t.symbols) that uses axios this basically accepts a URL and the response data as parameters and posts the data to the es route router.post('/ingest/b/ts', esParsersController.createTsDocs);. The route utilise the createTsDocs function as a call back which just takes care of ingesting the data into a database.
The error in the nodejs console:
POST /es/ingest/b/t 401 0.520 ms - 12
Error: Request failed with status code 401
at createError (login-portal/node_modules/axios/lib/core/createError.js:16:15)
at settle (login-portal/node_modules/axios/lib/core/settle.js:17:12)
at IncomingMessage.handleStreamEnd (login-portal/node_modules/axios/lib/adapters/http.js:260:11)
at IncomingMessage.emit (events.js:326:22)
at endReadableNT (_stream_readable.js:1252:12)
at processTicksAndRejections (internal/process/task_queues.js:80:21) {
config: {
url: '/es/ingest/b/ts',
method: 'post',
data: '{"data":[{..},{...},{...}]}',
headers: {
Accept: 'application/json, text/plain, */*',
'Content-Type': 'application/json;charset=utf-8',
'User-Agent': 'axios/0.21.1',
'Content-Length': 113195
},
baseURL: 'http://localhost:3000',
transformRequest: [ [Function: transformRequest] ],
transformResponse: [ [Function: transformResponse] ],
timeout: 3000,
adapter: [Function: httpAdapter],
xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN',
maxContentLength: -1,
maxBodyLength: -1,
validateStatus: [Function: validateStatus]
},
request: <ref *1> ClientRequest {
_events: [Object: null prototype] {
socket: [Function (anonymous)],
abort: [Function (anonymous)],
aborted: [Function (anonymous)],
connect: [Function (anonymous)],
error: [Function (anonymous)],
timeout: [Function (anonymous)],
prefinish: [Function: requestOnPrefinish]
},
_eventsCount: 7,
_maxListeners: undefined,
outputData: [],
outputSize: 0,
writable: true,
destroyed: false,
_last: true,
chunkedEncoding: false,
shouldKeepAlive: false,
_defaultKeepAlive: true,
useChunkedEncodingByDefault: true,
sendDate: false,
_removedConnection: false,
_removedContLen: false,
_removedTE: false,
_contentLength: null,
_hasBody: true,
_trailer: '',
finished: true,
_headerSent: true,
socket: Socket {
connecting: false,
_hadError: false,
_parent: null,
_host: 'localhost',
_readableState: [ReadableState],
_events: [Object: null prototype],
_eventsCount: 7,
_maxListeners: undefined,
_writableState: [WritableState],
allowHalfOpen: false,
_sockname: null,
_pendingData: null,
_pendingEncoding: '',
server: null,
_server: null,
parser: null,
_httpMessage: [Circular *1],
[Symbol(async_id_symbol)]: 744,
[Symbol(kHandle)]: [TCP],
[Symbol(kSetNoDelay)]: false,
[Symbol(lastWriteQueueSize)]: 0,
[Symbol(timeout)]: null,
[Symbol(kBuffer)]: null,
[Symbol(kBufferCb)]: null,
[Symbol(kBufferGen)]: null,
[Symbol(kCapture)]: false,
[Symbol(kBytesRead)]: 0,
[Symbol(kBytesWritten)]: 0,
[Symbol(RequestTimeout)]: undefined
},
_header: 'POST /es/ingest/b/ts HTTP/1.1\r\n' +
'Accept: application/json, text/plain, */*\r\n' +
'Content-Type: application/json;charset=utf-8\r\n' +
'User-Agent: axios/0.21.1\r\n' +
'Content-Length: 113195\r\n' +
'Host: localhost:3000\r\n' +
'Connection: close\r\n' +
'\r\n',
_keepAliveTimeout: 0,
_onPendingData: [Function: noopPendingOutput],
agent: Agent {
_events: [Object: null prototype],
_eventsCount: 2,
_maxListeners: undefined,
defaultPort: 80,
protocol: 'http:',
options: [Object],
requests: {},
sockets: [Object],
freeSockets: {},
keepAliveMsecs: 1000,
keepAlive: false,
maxSockets: Infinity,
maxFreeSockets: 256,
scheduling: 'fifo',
maxTotalSockets: Infinity,
totalSocketCount: 1,
[Symbol(kCapture)]: false
},
socketPath: undefined,
method: 'POST',
maxHeaderSize: undefined,
insecureHTTPParser: undefined,
path: '/es/ingest/b/ts',
_ended: true,
res: IncomingMessage {
_readableState: [ReadableState],
_events: [Object: null prototype],
_eventsCount: 3,
_maxListeners: undefined,
socket: [Socket],
httpVersionMajor: 1,
httpVersionMinor: 1,
httpVersion: '1.1',
complete: true,
headers: [Object],
rawHeaders: [Array],
trailers: {},
rawTrailers: [],
aborted: false,
upgrade: false,
url: '',
method: null,
statusCode: 401,
statusMessage: 'Unauthorized',
client: [Socket],
_consuming: true,
_dumped: false,
req: [Circular *1],
responseUrl: 'http://localhost:3000/es/ingest/b/ts',
redirects: [],
[Symbol(kCapture)]: false,
[Symbol(RequestTimeout)]: undefined
},
aborted: false,
timeoutCb: null,
upgradeOrConnect: false,
parser: null,
maxHeadersCount: null,
reusedSocket: false,
host: 'localhost',
protocol: 'http:',
_redirectable: Writable {
_writableState: [WritableState],
_events: [Object: null prototype],
_eventsCount: 2,
_maxListeners: undefined,
_options: [Object],
_ended: true,
_ending: true,
_redirectCount: 0,
_redirects: [],
_requestBodyLength: 113195,
_requestBodyBuffers: [],
_onNativeResponse: [Function (anonymous)],
_currentRequest: [Circular *1],
_currentUrl: 'http://localhost:3000/es/ingest/b/ts',
_timeout: Timeout {
_idleTimeout: -1,
_idlePrev: null,
_idleNext: null,
_idleStart: 2235827,
_onTimeout: null,
_timerArgs: undefined,
_repeat: null,
_destroyed: true,
[Symbol(refed)]: true,
[Symbol(kHasPrimitive)]: false,
[Symbol(asyncId)]: 750,
[Symbol(triggerId)]: 746
},
[Symbol(kCapture)]: false
},
[Symbol(kCapture)]: false,
[Symbol(kNeedDrain)]: false,
[Symbol(corked)]: 0,
[Symbol(kOutHeaders)]: [Object: null prototype] {
accept: [Array],
'content-type': [Array],
'user-agent': [Array],
'content-length': [Array],
host: [Array]
}
},
response: {
status: 401,
statusText: 'Unauthorized',
headers: {
'x-powered-by': 'Express',
'content-type': 'text/plain; charset=utf-8',
'content-length': '12',
etag: 'W/"c-dAuDFQrdjS3hezqxDTNgW7AOlYk"',
'set-cookie': [Array],
date: 'Wed, 17 Mar 2021 09:32:20 GMT',
connection: 'close'
},
config: {
url: '/es/ingest/b/t',
method: 'post',
data: '{"data":[{...},{...},{...}]}',
headers: [Object],
baseURL: 'http://localhost:3000',
transformRequest: [Array],
transformResponse: [Array],
timeout: 3000,
adapter: [Function: httpAdapter],
xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN',
maxContentLength: -1,
maxBodyLength: -1,
validateStatus: [Function: validateStatus]
},
request: <ref *1> ClientRequest {
_events: [Object: null prototype],
_eventsCount: 7,
_maxListeners: undefined,
outputData: [],
outputSize: 0,
writable: true,
destroyed: false,
_last: true,
chunkedEncoding: false,
shouldKeepAlive: false,
_defaultKeepAlive: true,
useChunkedEncodingByDefault: true,
sendDate: false,
_removedConnection: false,
_removedContLen: false,
_removedTE: false,
_contentLength: null,
_hasBody: true,
_trailer: '',
finished: true,
_headerSent: true,
socket: [Socket],
_header: 'POST /es/ingest/b/ts HTTP/1.1\r\n' +
'Accept: application/json, text/plain, */*\r\n' +
'Content-Type: application/json;charset=utf-8\r\n' +
'User-Agent: axios/0.21.1\r\n' +
'Content-Length: 113195\r\n' +
'Host: localhost:3000\r\n' +
'Connection: close\r\n' +
'\r\n',
_keepAliveTimeout: 0,
_onPendingData: [Function: noopPendingOutput],
agent: [Agent],
socketPath: undefined,
method: 'POST',
maxHeaderSize: undefined,
insecureHTTPParser: undefined,
path: '/es/ingest/b/ts',
_ended: true,
res: [IncomingMessage],
aborted: false,
timeoutCb: null,
upgradeOrConnect: false,
parser: null,
maxHeadersCount: null,
reusedSocket: false,
host: 'localhost',
protocol: 'http:',
_redirectable: [Writable],
[Symbol(kCapture)]: false,
[Symbol(kNeedDrain)]: false,
[Symbol(corked)]: 0,
[Symbol(kOutHeaders)]: [Object: null prototype]
},
data: 'Unauthorized'
},
isAxiosError: true,
toJSON: [Function: toJSON]
}
If I just hit a GET Route in the es file It is authenticated as expected.
app.js
var createError = require('http-errors');
var express = require('express');
var path = require('path');
var session = require('express-session');
var okta = require("#okta/okta-sdk-nodejs");
const { ExpressOIDC } = require('#okta/oidc-middleware');
const keys = require('./config/keys');
var bodyParser = require('body-parser')
var app = express();
app.use( bodyParser.json({limit: "15360mb", type:'application/json'}) );
app.use(bodyParser.urlencoded({limit: '100mb', extended: true}));
// Enabled the routes
const dashboardRouter = require("./routes/dashboard");
const usersRouter = require("./routes/users");
const bRouter = require("./routes/b");
const esRouter = require("./routes/es");
var oktaClient = new okta.Client({
orgUrl: keys.okta_orgUrl,
token: keys.okta_token
});
const oidc = new ExpressOIDC({
issuer: keys.okta_issuer,
client_id: keys.okta_client_id,
client_secret: keys.okta_client_secret,
appBaseUrl: keys.okta_appBaseUrl,
scope: keys.okta_scope,
routes: {
login: {
path: keys.okta_routes_login_path
},
loginCallback: {
path: keys.okta_routes_loginCallback_path,
afterCallback: keys.okta_routes_loginCallback_afterCallback
}
}
});
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(express.static(path.join(__dirname, 'public')));
app.use(session({
secret:keys.app_session_secret,
resave: true,
saveUnititialized: false
}));
app.use(oidc.router);
app.use((req, res, next) => {
if (!req.userContext) {
return next();
}
oktaClient.getUser(req.userContext.userinfo.sub)
.then(user => {
req.user = user;
res.locals.user = user;
next();
}).catch(err => {
next(err);
});
});
// redirect our users to the correct route
app.use('/', publicRouter);
app.use('/dashboard', oidc.ensureAuthenticated(), dashboardRouter);
app.use('/users', oidc.ensureAuthenticated(), usersRouter);
app.use('/b', oidc.ensureAuthenticated(), bRouter)
app.use('/es', oidc.ensureAuthenticated(), esRouter)
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
oidc.on('ready', () => {
app.listen(keys.app_web_server_port, () => console.log('app started'));
});
oidc.on('error', err => {
// An error occurred while setting up OIDC, during token revocation, or during post-logout handling
});
module.exports = app;
b.js route
const axios = require('axios');
const express = require("express");
const b = require('../models/b');
const keys = require('../config/keys');
const router = express.Router();
const esapi = axios.create({
baseURL: keys.app_web_server_addr+':'+keys.app_web_server_port,
timeout: 3000,
});
// function to call es
let callEs = (url, data) => {
esapi.post(
url,
{data})
.catch( err => console.log(err))
}
router.get("/f-e-info", (req, res) => {
fapi.get(b.bfapi+'eInfo')
.then((response) => {
// handle success
//console.log(response.data.symbols);
res.render("t",{response});
return response.data;
// send the data to es
}).then((t) => {
console.log("sending to es")
callEs('/es/ingest/b/ts',t.symbols)
}).catch( (error) => console.log(error));
});
module.exports = router;
es Route
const esParsersController = require('../controllers/esParsers');
const express = require("express");
const router = express.Router();
// This works fine!!!
router.get("/", (req, res) => {
res.render("es-test");
});
// This fails with a 401 unauthorised.
router.post('/ingest/b/ts', esParsersController.createTsDocs);
module.exports = router;
/controllers/esParsers
const keys = require('../config/keys');
const crypto = require("crypto");
const { createReadStream } = require('fs')
const split = require('split2')
const { Client } = require('#elastic/elasticsearch');
const { disconnect } = require('process');
require('array.prototype.flatmap').shim();
const createTsDocs = (req,res) => {
var datasource = []
req.body.data.forEach(function(value){
var doc = {}
doc.symbol = value.symbol;
// ... do stuff with data
datasource.push(doc)
});
ingestDocIntoEs(`${keys.esIndexName_prefix}ts`,datasource);
res.send("data entered")
}
module.exports = {
createTickerDocs
}
Sorry new to nodejs and trying to learn, can someone help me to understand why a post to the es route /ingest/b/ts gives me a 401 but a GET request to the es route / is authenticated as expected?
I was able to resolved this, forgot to come back an provide and answer. The issue was that I was making a post request with axios which was a new client instance so it did not have any scope of the current request headers. I had to get the current headers from the request and append them to my axios post request.
let config = {
headers : {
cookie: req.headers.cookie
}
}
esapi.post(url,data,config)
The only important part of the cookie is the connect.sid=COOKIE_GOES_HERE'
Another example using curl:
curl -X POST http://localhost:3000/route \
-H 'Content-Type: application/json' \
-H 'Cookie: connect.sid=COOKIE_GOES_HERE' \
-d '{"text": "hello again!", "toUserId": "USER_ID_COPIED_FROM_OKTA_DASHBOARD_URL"}'

unable to send file to express/multer backend

I have the following code listing the files in the folder. Then trying to send them to the post/upload in the express backend. But the files is always undefined. However it seems like size and other info get through.
Client send
const axios = require('axios');
var FormData = require('form-data');
//requiring path and fs modules
const path = require('path');
const fs = require('fs');
//joining path of directory
const directoryPath = path.join(__dirname, '');
//passsing directoryPath and callback function
fs.readdir(directoryPath, function (err, files) {
//handling error
if (err) {
return console.log('Unable to scan directory: ' + err);
}
//listing all files using forEach
var postInfo = files.forEach(async function (file) {
const form_data = new FormData();
form_data.append('file', fs.createReadStream(file));
const request_config = {
method: "post",
url: 'http://localhost:8080/upload',
port: 8080,
headers: {
"Content-Type": "multipart/form-data; boundary=form_data._boundary"
},
data: form_data
};
try {
var req = await axios(request_config);
// Do whatever you want to do with the file
console.log("Request: ", req);
} catch (e) {
console.error(e);
}
});
console.log(postInfo);
});
receiving code in the backend
const http = require('http')
const port = 8080
const express = require('express');
const app = express();
const multer = require('multer');
const server = http.createServer(app)
let storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, './uploads')
},
filename: function (req, file, cb) {
cb(null, file.fieldname + '-' + Date.now())
}
});
const upload = multer({ storage }).single('file');
app.post('/upload', upload, (req, res) => {
console.log(req.files) // this does log the uploaded image data.
})
// bind the server on port
server.listen(port, (err) => {
if (err) {
return console.log('something bad happened', err)
}
console.log(`server is listening on ${port}`)
})
Log:
node file-receive.js
server is listening on 8080
undefined
undefined
undefined
response: undefined,
isAxiosError: true,
toJSON: [Function] }
{ Error: socket hang up
at createHangUpError (_http_client.js:323:15)
at Socket.socketOnEnd (_http_client.js:426:23)
at Socket.emit (events.js:194:15)
at endReadableNT (_stream_readable.js:1103:12)
at process._tickCallback (internal/process/next_tick.js:63:19)
code: 'ECONNRESET',
config:
{ url: 'http://localhost:8080/upload',
method: 'post',
data:
FormData {
_overheadLength: 169,
_valueLength: 0,
_valuesToMeasure: [Array],
writable: false,
readable: true,
dataSize: 0,
maxDataSize: 2097152,
pauseStreams: true,
_released: true,
_streams: [],
_currentStream: null,
_insideLoop: false,
_pendingNext: false,
_boundary: '--------------------------046964089061111513973474',
_events: [Object],
_eventsCount: 1 },
headers:
{ Accept: 'application/json, text/plain, */*',
'Content-Type': 'application/x-www-form-urlencoded',
'User-Agent': 'axios/0.19.2' },
transformRequest: [ [Function: transformRequest] ],
transformResponse: [ [Function: transformResponse] ],
timeout: 0,
adapter: [Function: httpAdapter],
xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN',
maxContentLength: -1,
validateStatus: [Function: validateStatus],
port: 8080 },
request:
Writable {
_writableState:
WritableState {
objectMode: false,
highWaterMark: 16384,
finalCalled: false,
needDrain: false,
ending: false,
ended: false,
finished: false,
destroyed: false,
decodeStrings: true,
defaultEncoding: 'utf8',
length: 0,
writing: false,
corked: 0,
sync: true,
bufferProcessing: false,
onwrite: [Function: bound onwrite],
writecb: null,
writelen: 0,
bufferedRequest: null,
lastBufferedRequest: null,
pendingcb: 0,
prefinished: false,
errorEmitted: false,
emitClose: true,
bufferedRequestCount: 0,
corkedRequestsFree: [Object] },
writable: true,
_events:
[Object: null prototype] {
response: [Function: handleResponse],
error: [Function: handleRequestError] },
_eventsCount: 2,
_maxListeners: undefined,
_options:
{ protocol: 'http:',
maxRedirects: 21,
maxBodyLength: 10485760,
path: '/upload',
method: 'POST',
headers: [Object],
agent: undefined,
agents: [Object],
auth: undefined,
hostname: 'localhost',
port: '8080',
nativeProtocols: [Object],
pathname: '/upload' },
_redirectCount: 0,
_redirects: [],
_requestBodyLength: 983,
_requestBodyBuffers: [ [Object], [Object], [Object] ],
_onNativeResponse: [Function],
_currentRequest:
ClientRequest {
_events: [Object],
_eventsCount: 6,
_maxListeners: undefined,
output: [],
outputEncodings: [],
outputCallbacks: [],
outputSize: 0,
writable: true,
_last: true,
chunkedEncoding: true,
shouldKeepAlive: false,
useChunkedEncodingByDefault: true,
sendDate: false,
_removedConnection: false,
_removedContLen: false,
_removedTE: false,
_contentLength: null,
_hasBody: true,
_trailer: '',
finished: true,
_headerSent: true,
socket: [Socket],
connection: [Socket],
_header:
'POST /upload HTTP/1.1\r\nAccept: application/json, text/plain, */*\r\nContent-Type: application/x-www-form-urlencoded\r\nUser-Agent: axios/0.19.2\r\nHost: localhost:8080\r\nConnection: close\r\nTransfer-Encoding: chunked\r\n\r\n',
_onPendingData: [Function: noopPendingOutput],
agent: [Agent],
socketPath: undefined,
timeout: undefined,
method: 'POST',
path: '/upload',
_ended: false,
res: null,
aborted: undefined,
timeoutCb: null,
upgradeOrConnect: false,
parser: null,
maxHeadersCount: null,
_redirectable: [Circular],
[Symbol(isCorked)]: false,
[Symbol(outHeadersKey)]: [Object] },
_currentUrl: 'http://localhost:8080/upload' },
response: undefined,
isAxiosError: true,
toJSON: [Function] }
I was able to reproduce the issue and was able to fix the code with the following ajdustments (note that I'm uploading a single file for sake of simplicty).
On the client side you basically just provide the form-data to axios and call getHeaders() to get the already set up header - also you need to adjust the file-name in the form.
// client.js
...
const form_data = new FormData();
form_data.append('file', fs.createReadStream(__dirname + '/file-you-want-to-upload'));
const request_config = {
method: "post",
url: 'http://localhost:8080/upload',
port: 8080,
data: form_data,
headers: form_data.getHeaders()
};
...
On the backend side you need to make sure to access file not files as you're using upload.single and actually send a response in your handler as the resposse would hang otherwise:
//server.js
...
const upload = multer({storage}).single('file');
app.post('/upload', upload, (req, res) => {
console.log(req.file) // this does log the uploaded image data.
res.send("File uploaded");
});
...

nodejs client can`t signup to nodejs passport based authentication service

I am trying to implement nodejs express passport axios based server-client communication. My goal push files to the server using the terminal. But first I try to implement an authentication service. I have passport auth service which works well when I signing throw postman app.
Also, I have implemented nodejs based client, which try signup to the server but without success.
error:
{ Error: Request failed with status code 400
at createError (/home/gefalko/gepick-devenv/node_modules/axios/lib/core/createError.js:16:15)
at settle (/home/gefalko/gepick-devenv/node_modules/axios/lib/core/settle.js:18:12)
at IncomingMessage.handleStreamEnd (/home/gefalko/gepick-devenv/node_modules/axios/lib/adapters/http.js:201:11)
at emitNone (events.js:111:20)
at IncomingMessage.emit (events.js:208:7)
at endReadableNT (_stream_readable.js:1064:12)
at _combinedTickCallback (internal/process/next_tick.js:139:11)
at process._tickCallback (internal/process/next_tick.js:181:9)
config:
{ adapter: [Function: httpAdapter],
transformRequest: { '0': [Function: transformRequest] },
transformResponse: { '0': [Function: transformResponse] },
timeout: 0,
xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN',
maxContentLength: -1,
validateStatus: [Function: validateStatus],
headers:
{ Accept: 'application/json, text/plain, */*',
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
'User-Agent': 'axios/0.18.0',
'Content-Length': 45 },
method: 'post',
url: 'http://localhost:3005/login',
data: '{"username":"gefalko","password":"mypass"}' },
request:
ClientRequest {
domain: null,
_events:
{ socket: [Function],
abort: [Function],
aborted: [Function],
error: [Function],
timeout: [Function],
prefinish: [Function: requestOnPrefinish] },
_eventsCount: 6,
_maxListeners: undefined,
output: [],
outputEncodings: [],
outputCallbacks: [],
outputSize: 0,
writable: true,
_last: true,
upgrading: false,
chunkedEncoding: false,
shouldKeepAlive: false,
useChunkedEncodingByDefault: true,
sendDate: false,
_removedConnection: false,
_removedContLen: false,
_removedTE: false,
_contentLength: null,
_hasBody: true,
_trailer: '',
finished: true,
_headerSent: true,
socket:
Socket {
connecting: false,
_hadError: false,
_handle: [Object],
_parent: null,
_host: 'localhost',
_readableState: [Object],
readable: true,
domain: null,
_events: [Object],
_eventsCount: 8,
_maxListeners: undefined,
_writableState: [Object],
writable: false,
allowHalfOpen: false,
_bytesDispatched: 263,
_sockname: null,
_pendingData: null,
_pendingEncoding: '',
server: null,
_server: null,
parser: null,
_httpMessage: [Circular],
[Symbol(asyncId)]: 45,
[Symbol(bytesRead)]: 0 },
connection:
Socket {
connecting: false,
_hadError: false,
_handle: [Object],
_parent: null,
_host: 'localhost',
_readableState: [Object],
readable: true,
domain: null,
_events: [Object],
_eventsCount: 8,
_maxListeners: undefined,
_writableState: [Object],
writable: false,
allowHalfOpen: false,
_bytesDispatched: 263,
_sockname: null,
_pendingData: null,
_pendingEncoding: '',
server: null,
_server: null,
parser: null,
_httpMessage: [Circular],
[Symbol(asyncId)]: 45,
[Symbol(bytesRead)]: 0 },
_header: 'POST /login HTTP/1.1\r\nAccept: application/json, text/plain, */*\r\nContent-Type: application/x-www-form-urlencoded; charset=UTF-8\r\nUser-Agent: axios/0.18.0\r\nContent-Length: 45\r\nHost: localhost:3005\r\nConnection: close\r\n\r\n',
_onPendingData: [Function: noopPendingOutput],
agent:
Agent {
domain: null,
_events: [Object],
_eventsCount: 1,
_maxListeners: undefined,
defaultPort: 80,
protocol: 'http:',
options: [Object],
requests: {},
sockets: [Object],
freeSockets: {},
keepAliveMsecs: 1000,
keepAlive: false,
maxSockets: Infinity,
maxFreeSockets: 256 },
socketPath: undefined,
timeout: undefined,
method: 'POST',
path: '/login',
_ended: true,
res:
IncomingMessage {
_readableState: [Object],
readable: false,
domain: null,
_events: [Object],
_eventsCount: 3,
_maxListeners: undefined,
socket: [Object],
connection: [Object],
httpVersionMajor: 1,
httpVersionMinor: 1,
httpVersion: '1.1',
complete: true,
headers: [Object],
rawHeaders: [Array],
trailers: {},
rawTrailers: [],
aborted: false,
upgrade: false,
url: '',
method: null,
statusCode: 400,
statusMessage: 'Bad Request',
client: [Object],
_consuming: true,
_dumped: false,
req: [Circular],
responseUrl: 'http://localhost:3005/login',
redirects: [],
read: [Function] },
aborted: undefined,
timeoutCb: null,
upgradeOrConnect: false,
parser: null,
maxHeadersCount: null,
_redirectable:
Writable {
_writableState: [Object],
writable: true,
domain: null,
_events: [Object],
_eventsCount: 2,
_maxListeners: undefined,
_options: [Object],
_ended: true,
_ending: true,
_redirectCount: 0,
_redirects: [],
_requestBodyLength: 45,
_requestBodyBuffers: [],
_onNativeResponse: [Function],
_currentRequest: [Circular],
_currentUrl: 'http://localhost:3005/login' },
[Symbol(outHeadersKey)]:
{ accept: [Array],
'content-type': [Array],
'user-agent': [Array],
'content-length': [Array],
host: [Array] } },
response:
{ status: 400,
statusText: 'Bad Request',
headers:
{ 'x-powered-by': 'Express',
date: 'Wed, 20 Mar 2019 11:52:49 GMT',
connection: 'close',
'content-length': '11' },
config:
{ adapter: [Function: httpAdapter],
transformRequest: [Object],
transformResponse: [Object],
timeout: 0,
xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN',
maxContentLength: -1,
validateStatus: [Function: validateStatus],
headers: [Object],
method: 'post',
url: 'http://localhost:3005/login',
data: '{"username":"gefalko","password":"mypass"}' },
request:
ClientRequest {
domain: null,
_events: [Object],
_eventsCount: 6,
_maxListeners: undefined,
output: [],
outputEncodings: [],
outputCallbacks: [],
outputSize: 0,
writable: true,
_last: true,
upgrading: false,
chunkedEncoding: false,
shouldKeepAlive: false,
useChunkedEncodingByDefault: true,
sendDate: false,
_removedConnection: false,
_removedContLen: false,
_removedTE: false,
_contentLength: null,
_hasBody: true,
_trailer: '',
finished: true,
_headerSent: true,
socket: [Object],
connection: [Object],
_header: 'POST /login HTTP/1.1\r\nAccept: application/json, text/plain, */*\r\nContent-Type: application/x-www-form-urlencoded; charset=UTF-8\r\nUser-Agent: axios/0.18.0\r\nContent-Length: 45\r\nHost: localhost:3005\r\nConnection: close\r\n\r\n',
_onPendingData: [Function: noopPendingOutput],
agent: [Object],
socketPath: undefined,
timeout: undefined,
method: 'POST',
path: '/login',
_ended: true,
res: [Object],
aborted: undefined,
timeoutCb: null,
upgradeOrConnect: false,
parser: null,
maxHeadersCount: null,
_redirectable: [Object],
[Symbol(outHeadersKey)]: [Object] },
data: 'Bad Request' } }
my client:
const axios = require('axios')
const prompt = require('prompt')
var prompt_attributes = [
{
name: 'username',
validator: /^[a-zA-Z\s\-]+$/,
warning: 'Username is not valid, it can only contains letters, spaces, or dashes'
},
{
name: 'password',
hidden: true
}
]
prompt.start();
prompt.get(prompt_attributes, function (err, result) {
if (err) {
console.log(err);
return 1;
}else {
console.log('Command-line received data:');
const username = result.username
const password = result.password
const config = {
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
}
const reqBody = {
username: username,
password: password
}
axios.post('http://localhost:3005/login', reqBody, config).then(response => {
console.log(response.data);
}).catch(error => {
console.log(error);
})
}
});
also tried 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' but it not help.
server:
import * as express from 'express'
const app = express()
const passport = require('passport')
const LocalStrategy = require('passport-local').Strategy
const bcrypt = require('bcryptjs')
app.use(require('cookie-parser')())
app.use(require('body-parser').urlencoded({ extended: true }))
app.use(passport.initialize())
const port = 3005
/* DATABASE */
import { MongoClient, ObjectID } from 'mongodb'
const mongoUrl = "mongodb://localhost:27017/";
/* AUTHENTICATION */
MongoClient.connect(mongoUrl, async (err, db) => {
const myDB = db.db('mydb')
const Users = myDb.collection('users')
passport.serializeUser(function(user, done) {
done(null, user);
});
passport.deserializeUser(function(user, done) {
done(null, user);
});
passport.use(new LocalStrategy(
function(username, password, done) {
Users.findOne({ username: username }, async function(err, user) {
console.log('USER FROM DB:', user)
console.log('PASS from client', password)
if (err) { return done(err); }
if (!user){
return done(null, false, { message: 'Incorrect username.' });
}
const res = await bcrypt.compare(password, user.password)
return res ? done(null, user) : done(null, false, { message: 'Incorrect username.' })
})
}))
app.post('/login',passport.authenticate('local'), (req, res) =>
{
res.send('logined as ' + req.user.username)
}
)
app.listen(port, () => console.log('http server listening on port '+port+'!'))
})
Also, I found that my request body is parsed strange on the server when it`s come from node client and it is parsed correctly when it's coming from postman.
request body from nodejs axios client:
{ '{"username":"gefalko","password":"mypass"}': '' }
request body from postman client:
{"username":"gefalko","password":"mypass"}
Try this format :
var qs = require('qs');
axios.post('/foo', qs.stringify({ 'bar': 123 } , config);

Node.js / Axios won't connect to localhost

I have a cryptocurrency (Nano) node running locally on my computer. It has an RPC API and I've tested that I can successfully make calls to it using curl. E.g.
curl -d '{ "action": "account_balance", "account": "xrb_1aaprwcu9fac1tw3wesud5txb1zuiroti5xfr19bwozitjnnmbcbwpr1w95f" }' localhost:7076
However I'm trying to do the same thing in a node script and keep getting ECONNREFUSED
Here's my node script (the important parts).
const axios = require('axios')
const config = require('./config')
const rpc = axios.create({
baseURL: 'localhost:7076', // I've also tried 'http://localhost:7076'
// I've tried with and without proxy settings, I don't understand proxies very well though
/*proxy: {
host: '127.0.0.1',
port: 7077
}*/
})
function createAddress(accountIndex) {
// Ensure accountIndex is a string
accountIndex = accountIndex + ''
// Get a new private key
return rpc.post('/', {
action: 'deterministic_key',
index: accountIndex,
seed: config.walletSeed
})
// Add to the local wallet
.then(function(result){
return rpc.post('/', {
action: 'wallet_add',
key: result.private,
wallet: config.walletId
})
})
// Return the account address
.then(function(result){
return result.account
})
.catch(function(err) {
console.log('Error', err)
})
}
createAddress(52).then(function(address){
console.log(address)
})
And here's the error.
Error { Error: connect ECONNREFUSED 127.0.0.1:7076
at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1170:14)
errno: 'ECONNREFUSED',
code: 'ECONNREFUSED',
syscall: 'connect',
address: '127.0.0.1',
port: 7076,
config:
{ adapter: [Function: httpAdapter],
transformRequest: { '0': [Function: transformRequest] },
transformResponse: { '0': [Function: transformResponse] },
timeout: 0,
xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN',
maxContentLength: -1,
validateStatus: [Function: validateStatus],
headers:
{ Accept: 'application/json, text/plain, */*',
'Content-Type': 'application/json;charset=utf-8',
'User-Agent': 'axios/0.18.0',
'Content-Length': 117 },
method: 'post',
baseURL: 'http://localhost:7076',
url: 'http://localhost:7076/',
data: '{"action":"deterministic_key","index":"52","seed":"***"}' },
request:
Writable {
_writableState:
WritableState {
objectMode: false,
highWaterMark: 16384,
finalCalled: false,
needDrain: false,
ending: false,
ended: false,
finished: false,
destroyed: false,
decodeStrings: true,
defaultEncoding: 'utf8',
length: 0,
writing: false,
corked: 0,
sync: true,
bufferProcessing: false,
onwrite: [Function: bound onwrite],
writecb: null,
writelen: 0,
bufferedRequest: null,
lastBufferedRequest: null,
pendingcb: 0,
prefinished: false,
errorEmitted: false,
bufferedRequestCount: 0,
corkedRequestsFree: [Object] },
writable: true,
_events:
{ response: [Function: handleResponse],
error: [Function: handleRequestError] },
_eventsCount: 2,
_maxListeners: undefined,
_options:
{ protocol: 'http:',
maxRedirects: 21,
maxBodyLength: 10485760,
path: '/',
method: 'post',
headers: [Object],
agent: undefined,
auth: undefined,
hostname: 'localhost',
port: '7076',
nativeProtocols: [Object],
pathname: '/' },
_redirectCount: 0,
_requestBodyLength: 117,
_requestBodyBuffers: [ [Object] ],
_onNativeResponse: [Function],
_currentRequest:
ClientRequest {
_events: [Object],
_eventsCount: 6,
_maxListeners: undefined,
output: [],
outputEncodings: [],
outputCallbacks: [],
outputSize: 0,
writable: true,
_last: true,
upgrading: false,
chunkedEncoding: false,
shouldKeepAlive: false,
useChunkedEncodingByDefault: true,
sendDate: false,
_removedConnection: false,
_removedContLen: false,
_removedTE: false,
_contentLength: null,
_hasBody: true,
_trailer: '',
finished: false,
_headerSent: true,
socket: [Socket],
connection: [Socket],
_header: 'POST / HTTP/1.1\r\nAccept: application/json, text/plain, */*\r\nContent-Type: application/json;charset=utf-8\r\nUser-Agent: axios/0.18.0\r\nContent-Length: 117\r\nHost: localhost:7076\r\nConnection: close\r\n\r\n',
_onPendingData: [Function: noopPendingOutput],
agent: [Agent],
socketPath: undefined,
timeout: undefined,
method: 'POST',
path: '/',
_ended: false,
res: null,
aborted: undefined,
timeoutCb: null,
upgradeOrConnect: false,
parser: null,
maxHeadersCount: null,
_redirectable: [Circular],
[Symbol(isCorked)]: false,
[Symbol(outHeadersKey)]: [Object] },
_currentUrl: 'http://localhost:7076/' },
response: undefined }
I feel like I've tried countless variations of configuration. What am I not getting?
This is 4 months later but I've been trying to figure out the same thing.
Try changing your instance configuration like this:
const rpc = axios.create({
baseURL: 'localhost:7076',
proxy: false
})
I couldn't find the solution so I ended up digging through axios' code.
I found this on line 89 of /lib/adapters/http.js:
var proxy = config.proxy;
if (!proxy && proxy !== false) {
var proxyEnv = protocol.slice(0, -1) + '_proxy';
var proxyUrl = process.env[proxyEnv] || process.env[proxyEnv.toUpperCase()];
if (proxyUrl) {
var parsedProxyUrl = url.parse(proxyUrl);
proxy = {
host: parsedProxyUrl.hostname,
port: parsedProxyUrl.port
};
if (parsedProxyUrl.auth) {
var proxyUrlAuth = parsedProxyUrl.auth.split(':');
proxy.auth = {
username: proxyUrlAuth[0],
password: proxyUrlAuth[1]
};
}
}
}
if (proxy) {
options.hostname = proxy.host;
options.host = proxy.host;
options.headers.host = parsed.hostname + (parsed.port ? ':' + parsed.port : '');
options.port = proxy.port;
options.path = protocol + '//' + parsed.hostname + (parsed.port ? ':' + parsed.port : '') + options.path;...
The way I read it, if there is no proxy entry in your config or if your proxy entry is not false (the boolean). Then it makes the var 'proxy' equal to it's default proxy. So when it gets down to line 110...
if (proxy) {...
...there is a proxy, (the default it just created) and it will use it.
When I added proxy: false to my config axios worked as I expected.
Have you attempted to use your direct IP?
Type in your terminal:
netstat
192.168.1.?
Failing this simple answer/explaination, I set-up a Docker VM on my local machine and connect to my personal RPC API via this:
https://hub.docker.com/r/jlmconsulting/upcoind/
I was having the same issue, trying to make a request from a nodejs express app on port 8001 to another Nodejs express app running on port 8000 on my machine. What worked for me was to write the request address like so:
const testRequest = async () => {
try {
const test = await axios.get('http://localhost:8000/test');
//rather than just localhost:8000/test
console.log('TEST', test);
} catch (e) {
console.error('TEST ERROR:', e);
}
};
and it worked. I've seen in your comments that you've tried the same using the config, so I find it strange that it did not work...

Resources