Hapi.js Proxy - change the response before reply - javascript

I'm trying to test Hapi.js for a new project I'm working on
My goal is to trigger a request on my Hapi server and this request will trigger another request on a remote server using Hapi proxy, and the response will be only specific properties from the remote response
To test this a request to /api/type3/name must be made which will invoke an API call to GitHub's API to get the user object
Code:
server.route({
method: 'GET',
path: '/api/type3/{name}',
handler: {
proxy: {
mapUri: function(request,callback){
callback(null,'https://api.github.com/users/'+request.params.name);
},
onResponse: function(err, res, request, reply, settings, ttl){
console.log(res);
reply(res);
},
passThrough: true,
xforward: true
}
}
});
The response from the above code is the response object from GitHub
Now I need to save this response so I can manipulate it and return only what I need
But when I debug the response object it contains a lot of data and I can't find the response payload inside of it
So how to extract only the response from GitHub from the response object ?
Thanks a lot

The res object is an http.IncomingMessage. If you want to work with the body of the response you need to read all the data off it first.
You have 2 choices for doing this.
1) Vanilla Node
onResponse: function(err, res, request, reply, settings, ttl){
var body = '';
res.on('data', function (chunk) {
body += chunk;
});
res.on('end', function () {
console.log(body);
reply(body);
});
}
2) Using Wreck (a module for working with HTTP in Node made by the Hapi folks)
var Wreck = require('wreck');
onResponse: function(err, res, request, reply, settings, ttl){
Wreck.read(res, null, function (err, payload) {
console.log(payload);
reply(payload);
});
}

In the wreck case above, you could do
payload = payload.toString();
To convert the buffer to actual data.

I want to load xml data from remote server and convert the response to json.
This thread helped me lot to find a solution that worked for me. But in my case the code above doesn't worked too, because the response from the remote server was gzipped. In my console there were only unreadable data.
I tried to enable automatic payload parsing for the proxy but it was not successful. Finally i had to unzip the response by myself with 'zlib'.
This code is working for me:
'use strict'
const url = require('url')
const h2o2 = require('h2o2')
const Hapi = require('hapi')
const parseString = require('xml2js').parseString
var zlib = require('zlib')
const _ = require('lodash')
var remoteServerUrl = 'http://some-url.com:2434'
var originUrl = 'http://localhost:3000'
// Create a server with a host and port
const server = new Hapi.Server()
server.connection({
host: 'localhost',
port: 3001,
routes: {
cors: {
origin: [originUrl],
},
},
})
server.register({
register: h2o2,
}, function (err) {
server.start(function (err) {
console.log('Proxy Server started at: ' + server.info.uri)
})
})
server.route({
method: '*',
path: '/proxy/{path*}',
config: {
payload: {
// parse: true, // didn't worked for me
// parse: 'gzip', // didn't worked for me
output: 'stream',
},
},
handler: {
proxy: {
passThrough: true,
timeout: 15000,
acceptEncoding: false,
mapUri (request, callback) {
callback(null, url.format({
protocol: url.parse(remoteServerUrl).protocol,
host: url.parse(remoteServerUrl).host,
port: url.parse(remoteServerUrl).port,
pathname: request.params.path,
query: request.query,
}))
},
onResponse (err, res, request, reply, settings, ttl) {
if (err) {
console.error('Hapi server error: ', err)
}
// let's unzip the response
var gunzip = zlib.createGunzip()
var xmlStr = ''
gunzip.on('data', function (data) {
xmlStr += data.toString()
})
gunzip.on('end', function () {
// do something with the string
// in this case convert xml to json string
parseString(xmlStr, {}, function (err, result) {
// send result back
reply(result)
})
})
res.pipe(gunzip)
},
},
},
})
Maybe this helps someone else ;)

Related

Proxying an endpoint to avoid CORS issues

I am using an external api that doesn't allow client side POST request. I can make a POST request using node.js and I am getting my desired response on the server. I am stuck trying to figure out how to get the response from the server into my HTML file.
const https = require("https");
const data = JSON.stringify({
key: "value"
});
const options = {
hostname: "url",
port: 443,
path: "/path",
method: "POST",
headers: {
"Content-Type": "application/json",
"Content-Length": data.length
}
};
const req = https.request(options, res => {
console.log(
`statusCode: ${res.statusCode} statusMessage: ${res.statusMessage}`
);
res.setEncoding("utf8");
res.on("data", chunk => {
console.log(chunk);
});
});
req.on("error", error => {
console.error(error);
});
req.write(data)
req.end();
This is my server.js file, I'm not sure what the next step is to get in a file.

Implementing CoAP protocol on node.js

Do you know any guides or tutorials about implementing CoAP protocol connection on node.js? I have to implement simple server and client application. I've checked all the resources I've found, including of course their documentation:
https://github.com/mcollina/node-coap
but it is still unclear for me.
Thank you for any help.
EDIT:
If this is implementation of server, how should look client like?
var coap = require('coap')
, server = coap.createServer()
server.on('request', function(req, res) {
res.end('Hello ' + req.url.split('/')[1] + '\n')
})
// the default CoAP port is 5683
server.listen(function() {
var req = coap.request('coap://localhost/Matteo')
req.on('response', function(res) {
res.pipe(process.stdout)
res.on('end', function() {
process.exit(0)
})
})
req.end()
})
or like this , an example for coap client
const coap = require('coap'),
bl = require('bl');
//construct coap request
var req = coap.request({
observe: false,
host: '192.168.0.93',
pathname: '/',
port: 5683,
method: 'get',
confirmable: 'true',
retrySend: 'true',
//query:'',
options: {
// "Content-Format": 'application/json'
}
})
//put payload into request
var payload = {
username: 'aniu',
}
req.write(JSON.stringify(payload));
//waiting for coap server send con response
req.on('response', function(res) {
//print response code, headers,options,method
console.log('response code', res.code);
if (res.code !== '2.05') return process.exit(1);
//get response/payload from coap server, server sends json format
res.pipe(bl(function(err, data) {
//parse data into string
var json = JSON.parse(data);
console.log("string:", json);
// JSON.stringify(json));
}))
});
req.end();
It should be like this:
const coap = require('coap')
req = coap.request('coap://localhost')
console.log("Client Request...")
req.on('response' , function(res){
res.pipe(process.stdout)
})
req.end()
Source: https://github.com/mcollina/node-coap/blob/master/examples/client.js

transform $.get to node.js request

i'm using geolocation, i was handling everything on client side, now I wat to handle this from
Currently using it as;
var url = "youtube.com",
options = {
key: API_KEY,
video: "vid_id"
};
$.get(url, options, function(data) {
console.log(data)
})
I want to use it with nodeJS HTTPS, so i tried;
var https = require("https"),
url = "youtube.com",
options = {
key: API_KEY,
video: "vid_id"
};
https.get(url, options, function(data) {
console.log(data)
})
but i cant get it work I hope someone can convert this.
Try using the request module for node.js. Install it by running:
npm install request.
var request = require('request');
request(`youtube.com/?key=${key}&video=${video_id}`, function (error, response, body) {
console.log('error:', error); // Print the error if one occurred
console.log('body:', body); // Print body of the response.
});

Seneca-web timeout configuration

First of all I would like to say that I am new in senecajs.
I am testing this configuration.
I have configured Senecjs microservice running on port 9007, which is running and handling request correctly. When I request this service directly I receive response after cca 10s (it is request for oracle db data).
But when I request for same data but through the Hapi + Seneca-web I receive this error: "statusCode":504,"error":"Gateway Time-out"
["client","invalid_origin",{"port":9007,"pin":"mc:bankgtw","pg":"mc:bankgtw","type":"web","id":"pg:mc:bankgtw,pin:mc:bankgtw,port:9007","role":"transport","hook":"client","plugin$":{"name":"client$"},"fatal$":true,"meta$":{"mi":"wbn8u45tb7uh","tx":"o3f8eyia3f4n","id":"wbn8u45tb7uh/o3f8eyia3f4n","pattern":"hook:client,role:transport,type:web","action":"(q1yytemztu3k)","plugin_name":"transport","plugin_tag":"-","prior":{"chain":[],"entry":true,"depth":0},"start":1487199713842,"sync":true},"tx$":"o3f8eyia3f4n","host":"0.0.0.0","path":"/act","protocol":"http","timeout":5555,"max_listen_attempts":11,"attempt_delay":222,"serverOptions":{}},{"kind":"res","res":null,"error":{"isBoom":true,"isServer":true,"output":{"statusCode":504,"payload":{**"statusCode":504,"error":"Gateway Time-out**","message":"Client request timeout"},"headers":{}}},"sync":true,"time":{"client_recv":1487199799177}}]
A few seconds before microservice return data.
And this is my configuration:
const Hapi = require('hapi');
const Seneca = require('seneca');
const SenecaWeb = require('seneca-web');
const config = {
adapter: require('seneca-web-adapter-hapi'),
context: (() => {
const server = new Hapi.Server();
server.connection({
port: 3001,
routes: {
cors: true,
payload:{timeout:60000},
timeout:{server: 60000, socket:90000}
}
});
server.route({
path: '/routes',
method: 'get',
handler: (request, reply) => {
const routes = server.table()[0].table.map(route => {
return {
path: route.path,
method: route.method.toUpperCase(),
description: route.settings.description,
tags: route.settings.tags,
vhost: route.settings.vhost,
cors: route.settings.cors,
jsonp: route.settings.jsonp,
server: server.info
}
})
reply(routes)
}
});
return server;
})()
};
const seneca = Seneca({timeout: 99999})
.use(SenecaWeb, config)
.use(require('./hapi_api.js'))
.client({ port:9007, pin:'mc:bankgtw' })
.ready(() => {
const server = seneca.export('web/context')();
server.start(() => {
server.log('server started on: ' + server.info.uri);
});
});
What I am doing wrong or what timeout is causing this?
I've had the same issue, fixed it, but its VERY BAD PRACTICE.
Go to 'transport.js' at seneca-transport folder.
You will see 'timeout: 5555'
Go ahead and change that to whatever you need.
I'm not sure why this is not getting USER defaults.
To the best of my knowledge, this is referring to client timeout. make sure you still use server timeout.

Error: write after end - node post request (module) pipe post request in get

I have a REST API server which is running on one VM1. On other VM2 machine I have built a node js server which is running as proxy. On the same VM2 machine I have application (hosted with apache which serves only html, js and css files). My node js server only resends the api calls back to the API server. This is working fine, until new requirement arrive - to add a new API endpoint (on the node js server) to download files (csv). In order to make download happen, I need to use GET method. The thing is that the required data is available only on POST endpoint from the main API server, and I am calling the API endpoint to get the data and send it back. This is the code I am trying to work it out:
var express = require('express');
var cors = require('cors');
var request = require('request');
var http = require('http');
var csv = require("fast-csv");
var config = require("./config.js");
var corsOptions = {
origin: function(origin, callback){
var originIsWhitelisted = config.whitelist.indexOf(origin) !== -1;
callback(null, originIsWhitelisted);
}
};
var handler = function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello World\n');
};
var app = express();
// Enable CORS for all requests
app.use(cors(corsOptions));
app.options('*', cors(corsOptions)); // specially for pre-flight requests
app.get('/download', function(req, res){
var limit = req.query.limit;
var offset = req.query.offset;
var options = {
method: 'POST',
url: config.apiServerHost + '/search',
useQuerystring: true,
qs: {'limit': limit, 'offset': offset},
rejectUnauthorized: false,
body: 'from=date&to=date'
};
var filename = 'data.csv';
res.setHeader('Content-disposition', 'attachment; filename=\"data.csv\"');
res.setHeader('content-type', 'text/csv');
var csvStream = csv.createWriteStream({
headers: true,
objectMode: true,
transform: function (row) {
return row;
}
});
console.log(options);
function callback(error, response, body) {
if (!error && response.statusCode == 200) {
var data = JSON.parse(body);
for(var i = 0; i < data.length; i++)
csvStream.write({
"col1": "value1-"+data[0][i],
"col2": "value2-"+data[1][i],
"col3": "value3-"+data[2][i],
"col4": "value4-"+data[3][i]
});
}
csvStream.end();
}
else {
console.log("Error:", error, body);
}
}
req.pipe(request(options, callback));//.pipe(res)
csvStream.pipe(res);
});
app.use('/api', function(req, res) {
var url = config.apiServerHost + req.url;
console.log(url);
req.pipe(request({
"rejectUnauthorized": false,
"url": url
}, function(error, response, body){
if(error) {
console.log(new Date().toLocaleString(), error);
}
})).pipe(res);
});
This all code works fine when request method is POST (the same as main API server). However I receive "[Error: write after end]" when I add the body in options object. Can someone help me figure out what is happening and how to solve this problem? Thanks.
The [Error: write after end] show pip data after .end(), for your codes
req.pipe(request(options, callback));//.pipe(res)
csvStream.pipe(res);
In the callback function, the csvStream.end(); is called, then invoke csvStream.pipe could cause this error.

Categories