Send new request response with Node HTTP server - node.js

Stack Overflow community, greetings. I'm trying to pass the response of a new request on the request object using the Node HTTP Module for a basic autocomplete search app for my website (i.e using Node as a proxy that will transform and redirect the requests within the server).
The flow basically is:
Client Browser - Node - ElasticSearch - Node - Client Browser
I've started with:
Listen to requests with http.createServer (function (req,res)
Get the body from the req object and use it in a new request with http.request(options, function (newReqResponse)
Get the body from that newReqResponse object and send it back to the client on the res object
The problem is that the content of newReqResponse is always outdated (trails behind the last typed character). i.e.:
If I type "te", the content of newReqResponse corresponds to that if I had typed only "t".
If I type "test", it corresponds to that if I had typed "tes".
And so on.
I've tried to solve it using Node.js streams and using the file system module to write and read files sync and async, but the result is the same. Here's a sample of the whole -code- picture:
var http = require('http');
var fs = require('fs');
http.createServer(function (req, res) {
var reqBody = '';
var newReqResponseBody = "";
req.on('data', function (chunk) {
reqBody += chunk;
fs.writeFile('reqbody.json', reqBody, function(err) {
if (err) {
throw err;
}
});
var options = {
hostname: '127.0.0.1',
port: 9500,
method: 'GET',
path: '/_search',
headers: { host: 'es',
'content-length': Buffer.byteLength(reqBody),
'content-type': 'application/json',
accept: 'application/json' },
};
var newReq = http.request(options, function (newReqResponse) {
newReqResponse.setEncoding("UTF-8");
newReqResponse.on('data', function (ch) {
newReqResponseBody += ch;
});
newReqResponse.on("end", function() {
fs.writeFile("newReqResponseBody.json", newReqResponseBody, function(err) {
if (err) {
throw err;
}
});
});
});
newReq.on("error", function(err) {
console.log(`problem with request: ${err.message}`);
});
newReq.write(reqBody);
newReq.end();
});
req.on('end', function() {
var responseBody = fs.readFileSync('newReqResponseBody.json', 'utf8');
console.log(responseBody);
res.end(responseBody);
});
}).listen(3000, '127.0.0.1');
Is there a workaround to work with requests and responses within the http server? If there isn't, I'll be very grateful if you give me any directions on how to solve this.
Since the planned use for Node is rather basic, I prefer to stick with core modules rather than having to get new ones from npm, unless that it's necessary.
Thanks in advance.
EDIT:
All I had to do was to call res.end(responseBody) within the newReqResponse.on("end") callback, which is totally counterintuitive for me, but... it works.

Glad you solved your own problem. However, I see room for improvement (not sure if you're new), especially if you're transferring data. Which you can do with streams.
You can see that I didn't calculate the content length, you're asked not to and should get ignore (for this specific case) according to HTTP specification as streams pass data in chunks with 'Transfer-Encoding': 'chunked' header.
const fs = require('fs');
const http = require('http');
const options = {
hostname: '127.0.0.1',
port: 9500,
method: 'GET',
path: '/_search',
headers: {
'Content-Type': 'application/json'
}
};
http.createServer((req, res) => {
req.pipe(fs.createWriteStream('reqBody.json'));
let request = http.request(options, (newRes) => {
newRes.pipe(res);
});
fs.createReadStream('reqBody.json').pipe(request);
res.setHeader('Content-Type', 'application/json');
}).listen(3000, '127.0.0.1');
You can shorten this snippet more if you don't want your data saved in the future and only want to pipe the req stream to request.

Related

How to make an https version of a Unirest example

I would like to use the https library in node.js to send a request to this api:
https://rapidapi.com/dimas/api/NasaAPI?endpoint=apiendpoint_b4e69440-f966-11e7-809f-87f99bda0814getPictureOfTheDay
The given example on the RapidAPI website uses Unirest, and I would like to only use the https library. I've tried to write it like this:
const https = require('https');
var link = "https://NasaAPIdimasV1.p.rapidapi.com/getPictureOfTheDay";
var options = {host: "https://NasaAPIdimasV1.p.rapidapi.com/getPictureOfTheDay",
path: "/", headers: {"X-RapidAPI-Key": "---MY KEY(Yes, I've replaced it)---", "Content-Type": "application/x-www-form-urlencoded"}}
https.get(link, options, (resp) => {
let data = '';
resp.on('data', (chunk) => {
data += chunk;
});
resp.on('end', () => {
console.log(data);
});
}).on("error", (err) => {
console.log("https error 4: " + err.message);
});
But that returns the following response:
{"message":"Endpoint\/ does not exist"}
Thanks for any help
There are several mistakes.
First, you essentially pass URL in https twice - first as link param, second as combination of host and path properties for options param.
Second, your host is actually the full path - but it shouldn't be. In the end, looks like the library got confused and sent request to https://NasaAPIdimasV1.p.rapidapi.com/ instead.
Finally, this particular API requires using 'POST', not 'GET' method. That's actually mentioned in the documentation. That's why you have 'endpoint does not exist' error even on correctly formed request.
One possible approach is dropping link altogether, sending URL as part of options:
var options = {
host: 'NasaAPIdimasV1.p.rapidapi.com',
method: 'POST',
path: '/getPictureOfTheDay',
headers: {/* the same */}
};
https.request(options, (resp) => { /* the same */ }).end();

Setting request headers in Node.js

I have been working with node.js to set up a proxy server that will handle incoming client request and will verify that they have the proper certificates to get connected to the server.
What I want to do is to be able to add the client's certificate to their header to craft a user name, that I will pass on to the server.
function (req, res) {
//Here is the client certificate in a variable
var clientCertificate = req.socket.getPeerCertificate();
// Proxy a web request
return this.handle_proxy('web', req, res);
};
What I want to be able to do is this : req.setHeader('foo','foo')
I know that the proxy.on('proxyReq) exist, but the way the code is set up, I need to be able to use the req parameter.
Is there a way to do this?
Please let me know if I need to clarify my question.
You can craft your own http request with the headers provided in the original request plus any extra headers that you'd like by using http.request. Just receive the original request, copy the headers into the new request headers, add the new headers and send the new request.
var data = [];
var options = {
hostname: 'www.google.com',
port: 80,
path: '/upload',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': postData.length
}
};
var req = http.request(options, function(res) {
res.setEncoding('utf8');
res.on('data', function (chunk) {
data.push(chunk);
});
res.on('end', function() {
console.log(data.join(""));
//send the response to your original request
});
});
req.on('error', function(e) {
console.log('problem with request: ' + e.message);
});
// Set headers here i.e. req.setHeader('Content-Type', originalReq.getHeader('Content-Type'));
// write data to request body
req.write(/*original request data goes here*/);
req.end();

NodeJS: sending/uploading a local file to a remote server

I have used the Winston module to create a daily log file for my offline app. I now need to be able to send or upload that file to a remote server via POST (that part already exists)
I know I need to write the file in chunks so it doesn't hog the memory so I'm using fs.createReadStream however I seem to only get a 503 response, even if sending just sample text.
EDIT
I worked out that the receiver was expecting the data to be named 'data'. I have removed the createReadSteam as I could only get it to work with 'application/x-www-form-urlencoded' and a synchronous fs.readFileSync. If I change this to 'multipart/form-data' on the php server would I be able to use createReadStream again, or is that only if I change to physically uploading the json file.
I've only been learning node for the past couple of weeks so any pointers would be gratefully received.
var http = require('http'),
fs = require('fs');
var post_options = {
host: 'logger.mysite.co.uk',
path: '/',
port: 80,
timeout: 120000,
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
}
var sender = http.request(post_options, function(res) {
if (res.statusCode < 399) {
var text = ""
res.on('data', function(chunk) {
text += chunk
})
res.on('end', function(data) {
console.log(text)
})
} else {
console.log("ERROR", res.statusCode)
}
})
var POST_DATA = 'data={['
POST_DATA += fs.readFileSync('./path/file.log').toString().replace(/\,+$/,'')
POST_DATA += ']}'
console.log(POST_DATA)
sender.write(POST_DATA)
sender.end()
After gazillion of trial-failure this worked for me. Using FormData with node-fetch. Oh, and request deprecated two days ago, btw.
const FormData = require('form-data');
const fetch = require('node-fetch');
function uploadImage(imageBuffer) {
const form = new FormData();
form.append('file', imageBuffer, {
contentType: 'image/jpeg',
filename: 'dummy.jpg',
});
return fetch(`myserver.cz/upload`, { method: 'POST', body: form })
};
In place of imageBuffer there can be numerous things. I had a buffer containing the image, but you can also pass the result of fs.createReadStream('/foo/bar.jpg') to upload a file from drive.
copied from https://github.com/mikeal/request#forms
var r = request.post('http://service.com/upload', function optionalCallback (err, httpResponse, body) {
if (err) {
return console.error('upload failed:', err);
}
console.log('Upload successful! Server responded with:', body);
})
var form = r.form()
form.append('my_field1', 'my_value23_321')
form.append('my_field2', '123123sdas')
form.append('my_file', fs.createReadStream(path.join(__dirname, 'doodle.png')))
Have a look at the request module.
It will provide you the ability to stream a file to POST requests.

Node.js proxy to return exact response from the external website

I'm trying to create the express.js proxy to an external website to obtaining audio data from there. I know about modules like http-proxy, but think they are excessive for the case when only one url-bound request goes through proxy. I'm using the code below:
express.get('/proxy', function (req, res) {
var options ={
host: "website.com",
port: 80,
path: "/audio/test.mp3",
method: 'GET'
};
http.get(options, function (audioRes) {
var data = [], dataLen = 0;
audioRes.on('data', function(chunk) {
data.push(chunk);
dataLen += chunk.length;
})
.on('end', function() {
var buf = new Buffer(dataLen);
res.set(audioRes.headers);
res.send(buf);
});
})
.on('error', function (error) {
console.log(error.message);
});
});
I get response, but it cannot be decoded as a valid audio. While debugging with the Fiddler, I found out that the number of bites sent by a server mismatches the number specified in the Content-Length header (which indicates fewer bytes being retrieved).
I cannot figure out how to properly return the exact response that's been retrieved from the remote server. Would be grateful for any help.
To send request via proxy, you can set the proxy url in Host header. Also you have to specify the full URL of the external resource you are trying to access via proxy.
var http = require("http");
var options = {
host: "proxy",
port: 8080,
path: "http://www.google.com", //full URL
headers: {
Host: "10.1.2.3" //your proxy location
}
};
http.get(options, function(res) {
console.log(res);
});
I am not sure why it is not returning full response. Can you post your options.
Update
Try this inside /proxy after putting the options
http.get(options, function (audioRes) {
audioRes.pipe(res);
})
.on('error', function (error) {
console.log(error.message);
});

How to post the data in Node js

In my application i need to post the dynamic data into my main page(mani page means if i run my url(localhost:3456) in browser means that will display one page na that page).How can i post that data.I have tried this but i couldn't post the data.Can anyone help me to fix the issue.
app.js
var http = require('http');
var server = http.createServer(function(req, res){
res.writeHead(200, ['Content-Type', 'text/plain']);
res.write('Hello ');
res.end('World');
});
server.listen(3456);
postdata.js
var data={"errorMsg":{"errno":34,"code":"ENOENT","path":"missingFile.txt"},"date":"2013-0402T11:50:22.167Z"}
var options = {
host: 'localhost',
port: 3456,
path: '/',
method: 'POST',
data:data,
header: {
'content-type': 'application/json',
'content-length': data.length
}
};
var http=require('http');
var req;
req = http.request(options, function(res) {
var body;
body = '';
res.on('data', function(chunk) {
body += chunk;
});
return res.on('end', function() {
console.log('body is '+body);
});
});
req.on('error', function(err) {
console.log(err);
});
req.write(data);
req.end();
Do you have express installed along with Node, if so you can set up Rest Api's which you can use them in your jQuery and bind data dynamically. Please try to look into
http://expressjs.com/
Hope this helps.
//this is a string
var jsonString = '{"errorMsg":{"errno":34,"code":"ENOENT","path":"missingFile.txt"},"date":"2013-04-03T05:29:15.521Z"}';
//this is an object
var jsonObj = {"errorMsg":{"errno":34,"code":"ENOENT","path":"missingFile.txt"},"date":"2013-04-03T05:29:15.521Z"};
note the single quotes in for string
request.write(chunk, [encoding]) requires chunk to be either Buffer or string (see: http://nodejs.org/api/http.html#http_request_write_chunk_encoding)
Two things. First:
var data={"errorMsg:{"errno":34,"code":"ENOENT","path":"missingFile.txt"},"date":"2013-0402T11:50:22.167Z"}
is missing a double-quote, so it's invalid syntax... hence why the syntax highlighting is having trouble.
Second:
req.write(data);
should be:
req.write(JSON.stringify(data));
EDIT:
Based on your comment, I think you might be asking how to read from the body of an HTTP POST request (you're question is very ambiguously worded). If so, that's already very well documented in the Node.js API. Something along the lines of:
var server = http.createServer(requestHandler);
server.listen(3456);
function requestHandler (req, res) {
req.setEncoding('utf8');
var body = '';
req.on('data', function (chunk) { body += chunk; });
req.on('end', function () {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('The body of your request was: ' + body);
});
}
If that's not what you're asking, you'll need to clarify your question. Terms like 'main page' have no meaning unless you explicitly define what they are and what the expected outcome is.

Resources