How to pipe POST requests - node.js

I'm a bit confused about how to pipe some data.
I've got some pipes working and chained such that I not have an output stream containing the data I want to input to a request POST
var options = {
host: 'localhost',
port: 8529,
path: '/_api/cursor',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': data.length
}
}
var req = http.request(options);
I would normally just action 'mystreams2.pipe(req)' but how do I set the 'data.length' value ?
(I'm using the streams2 interface not the old stream format)

By assumption that you don't have a massive amount of data in your buffer, you first need to collect the data in order to find its length.
var source = /* a readable stream */;
var data = '';
source.on('data', function(chunk) {
data += chunk;
});
source.on('end', function() {
var options = {
host: 'localhost',
port: 8529,
path: '/_api/cursor',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': data.length
}
};
var req = http.request(options);
req.end(data);
});

Related

Core https library vs. npm 'request' library

I am running into a very strange issue when trying to use the built-in node https library.
Request Headers:
let requestDetails = {
hostname: 'api.domain.com',
method: 'POST',
path: '/endpointIWant/goHere
headers: {
'Client-ID': clientId,
'Content-Type': 'application/json',
Authorization: bearerToken
},
};
Request body:
let body = JSON.stringify({
"content_type": "application/json",
"message" : message
});
This is my standard call using the default https library of node:
let req = https.request(requestDetails, function (res){
let responseBody = undefined;
res.on('body', function(res) {
responseBody = '';
});
res.on('data', function(chunk) {
responseBody += chunk;
});
res.on('end', function() {
console.log(responseBody);
});
});
req.write(body);
req.on('error', function(e) {
console.log(e);
});
req.end();
Now whenever I send this request to the relevant server I get a:
Your browser sent a request that this server could not understand.
Reference #7.24507368.1554749705.3185b29b
However when I use the popular 'request' library on NPM it works fine and I get the response I expect.
This leads be to believe there is something different in maybe the 'encoding' or 'chunking' of the requests between these two libraries, but I cannot figure out what.
Does anyone have experience with the Node https library and understand any gotcha's there?
I prefer to use built-in libraries as much as possible to keep my package size low.
When using native http or https modules, you need to use the querystring module to stringify your body.
const querystring = require('querystring');
let body = querystring.stringify({
"content_type": "application/json",
"message" : message
});
//also include the content length of your body as a header
let requestDetails = {
hostname: 'api.domain.com',
method: 'POST',
path: '/endpointIWant/goHere
headers: {
'Client-ID': clientId,
'Content-Type': 'application/json',
'Content-Length' : body.length
Authorization: bearerToken
},
};
'request' is built on top of the native modules and does this internally when you pass it a json body

"Body" of a post request always in unicode

If I make a POST request to a specific server using a Chrome plugin, I can see the "response body" comes back just fine in a JSOn format.
However when I try to do this with either "request" or "https.request", the "body" comes back as unicode which I can't seem to decipher. Anyone know how I can the body to come back as regular JSON, or how I could decipher this unicode? I tried a few stackoverflow solutions to decipher the unicode but no luck.
raw body:
��RPP�M-.NLOUR�RP�I,�K�P�,V��/QHL.�,KU��J�O�*─
�⎽�����wt�
⎽U��┬�>H ---
If I do this: JSON.stringify(body, null, 4)
"\┤001°�\␉\┤0000\┤0000\┤0000\┤0000\┤0000\┤0000\┤0000��RPP�M-.NLOUR�RP�I,�K�P�,V��/QHL.�,KU�\┤0001�J�O�*─\°�⎽����\┤000°�┬├\┤000␊�\°⎽U�\┤0005\┤0000�┬�>H\┤0000\┤0000\┤0000"
And here's the two code snippets I use to try and make POST requests:
request({
'url': 'https://api.nike.com/launch/entries/v2',
'method': 'POST',
'json': entriesPayload,
'headers': {
'authorization': authId,
"Accept": "application/json, text/plain, */*",
'Content-Type': "application/json;charset=utf-8"
},
},
...
and:
var options = {
hostname: 'api.nike.com',
port: 443,
path: '/launch/entries/v2',
method: 'POST',
json: entriesPayload,
headers: {
'authorization': authId,
"Accept": "application/json, text/plain, */*",
'Content-Type': 'application/json;charset=utf-8'
}
};
var req = https.request(options, (res) => {
res.on('data', (d) => {
process.stdout.write(d);
});
});
req.on('error', (e) => {
console.error(e);
});
req.write(postData);
req.end();
Also I get some real weird stuff happening after I make the request, in my IDE's logs. This is what shows up - it like translates everything into some different symbols (the last 3 lines are how I restarted my nodejs server).
"⎻⎺⎼├": 443,
"␤⎺⎽├┼▒└␊": "▒⎻␋.┼␋┐␊.␌⎺└",
"␤▒⎽␤": ┼┤┌┌,
"⎽␊▒⎼␌␤": ┼┤┌┌,
"─┤␊⎼≤": ┼┤┌┌,
"⎻▒├␤┼▒└␊": "/┌▒┤┼␌␤/␊┼├⎼␋␊⎽/┴2",
"⎻▒├␤": "/┌▒┤┼␌␤/␊┼├⎼␋␊⎽/┴2",
"␤⎼␊°": "␤├├⎻⎽://▒⎻␋.┼␋┐␊.␌⎺└/┌▒┤┼␌␤/␊┼├⎼␋␊⎽/┴2"
£,
"└␊├␤⎺␍": "POST",
"␤␊▒␍␊⎼⎽": π
"▒┤├␤⎺⎼␋≥▒├␋⎺┼": "B␊▒⎼␊⎼ ␊≤J␤␉G␌␋O␋JSU≥I1N␋I⎽I└├⎻ZCI6I┘␌2YWI1NT␤┐LWM┬ZTM├NGV␤Y␋05MT┌┘LTJ┐Y┘A3Y┘F┘N2N␤MHN⎻Z≤J9.␊≤J0␌┼V≥␍CI6MTA┬LCJ⎻YXQ␋O┘E1NDM2N≥␌┬ODA⎽I└V4␌CI6MTU0M≥Y4MDY4MC┬␋▒XN≥I┘⎺␋␉2F1␍G±≤YWN┘I␋┬␋▒┼R⎻I┘⎺␋ZGV␋MTZ┘M2Q├YT±│OS00OTE3LWF␤MD┐├NWV␤N≥R└M┘U┬MWM4I␋┬␋␉GF0I┘⎺│NTQ≥N┘␌3MD±┬LCJ␤␍WQ␋O␋J┘␉20┤␉└┌⎼ZS5┐▒W␍⎻␍GF⎽I␋┬␋␌3V␋I┘⎺␋Y29├L└5⎻▒2U┤Y29├␉WV≤Y2U┤␌25⎼␌┼M┤␍2V␋I␋┬␋␌2J0I┘⎺␋␉└┌⎼ZT⎻␤␌HA␋LCJ≥Y3A␋O┌⎽␋Y29├␉WV≤Y2U␋XS┬␋␌HJ┤I┘⎺␋M└I≥M≥J┐Y└U├MWE1Y≤00ZWJ┐LT┐0Z└Y├MGU┬N≥V┘OGV┘NGQ│I␋┬␋␌HJ0I┘⎺␋␉└┌⎼ZT⎻┬␉HV≥I┼0.I┴▒┘└U2W≤␉__┬AD└J±⎻Z≥␉␋-VVUV┘H⎽⎼EI├┼T┌␍1I9°┬OSL▒┬┤8≥0Z3┐C␍␊G⎽␊O≤│≥␌␉3RB┌GKXV┤SDRDH±IYZ␉H1X⎼5␉Q-┼R≥└┬I␤│␉M0─I⎺R┤␋␍␤▒I␋├NC␍⎺G±6⎺XQ␋°R┴W5␍⎺SZ┼⎻4YR2TN6U␍9Q≥⎻61NS⎺⎻F␌2V13NJ└0P7│K5-09⎻▒1│6P␍M≤┼⎽IWF─␤II⎼G≤K┘HO⎻BV└┌┤␋A≥°┼AF┤K1GC┌ZGD⎺TC␋8⎻JY_⎺HI-E8D±M┐O4KSN⎻H97KLHO-┴Z│2┬YLJ2°␉0⎼F┐D≤≤└A␍K⎼9┴┬┤9┼│XF␍⎼≥⎽┘22≤39KD⎻-⎽⎻R┐I⎺MD2▒└␋┼L1CA│8─-␉L│DY└┬GQ02C├I0─┘±",
"A␌␌␊⎻├": "▒⎻⎻┌␋␌▒├␋⎺┼/┘⎽⎺┼, ├␊│├/⎻┌▒␋┼, */*",
"C⎺┼├␊┼├-T≤⎻␊": "▒⎻⎻┌␋␌▒├␋⎺┼/┘⎽⎺┼;␌␤▒⎼⎽␊├=┤├°-8",
"␌⎺┼├␊┼├-┌␊┼±├␤": 575
£
£
£
[┼⎺␍␊└⎺┼] ⎼␊⎽├▒⎼├␋┼± ␍┤␊ ├⎺ ␌␤▒┼±␊⎽...
[┼⎺␍␊└⎺┼] ⎽├▒⎼├␋┼± ◆┼⎺␍␊ ▒⎻⎻.┘⎽◆
E│⎻⎼␊⎽⎽ ⎽├▒⎼├␊␍ ⎺┼ ⎻⎺⎼├ 3000
The response body is not unicode encoded, but GZIP encoded (compressed data). We can check if a response is compressed with the Content-Encoding header:
var encoding = res.headers['content-encoding'];
https doesn't unzip the response body automatically like a browser does, and so you get all those strange characters instead of a JSON string. However, we can use the built-in zlib library and decdode the response to a string.
const https = require('https');
const zlib = require('zlib');
var options = {
hostname: 'api.nike.com',
port: 443,
path: '/launch/entries/v2',
method: 'POST',
headers: {
'Authorization': 'my-token',
'Accept': 'application/json, text/plain, */*',
'Content-Type': 'application/json;charset=utf-8'
}
};
var data = JSON.stringify({});
var req = https.request(options, (res) => {
var encoding = res.headers['content-encoding'];
console.log('code:', res.statusCode);
console.log('encoding:', encoding);
deflate = zlib.createGunzip();
res.pipe(deflate);
deflate.on('data', (chunk) => {
console.log('data: ' + chunk.toString());
});
});
req.on('error', (e) => {
console.error(e);
});
req.write(data);
req.end();
The chrome plugin that you're using to POST is probably setting the content-type correctly, while your regular request from your server isn't.
Can you change your request so it includes
request({
'url': externalUrl,
'method': 'POST',
'json': entriesPayload,
'Content-Type': 'application/x-www-form-urlencoded'
});

nodejs requestify.request() crashes when the method is given in the options

Why does requestify.request() crash because it doesn't think it was given the method POST
var postBody = querystring.stringify(dat);
var postOptions = {
host: 'https://www.example.com'
, path: '/admin'
, method: 'post' // POST, 'POST', post
, headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(postBody) }
};
var postReq = requestify.request(postOptions, function (err, resp, respBody) {
resp.setEncoding('utf8');
resp.on('data', function (chunk) {
console.log('Response: ' + chunk);
});
});
It looks like you are using an old version of the documentation for the newer version of requestify
Read the API reference section of https://github.com/ranm8/requestify
And read just a little lower down it see a requestify.request() example.
Options doesn't have host, path, ...

Node HTTPS Request – MalformedJsonException

I'm trying to make a POST request using node/HTTPS, but I keep getting this error:
BODY: {"message":"MalformedJsonException:
com.google.gson.stream.MalformedJsonException: Use
JsonReader.setLenient(true) to accept malformed JSON at line 1 column
11 path $","mdcKey":""}
Here's the code that initiates the request:
const https = require('https');
const querystring = require('querystring');
const POSTData = querystring.stringify({
'addressTo': 'myemail#address.com',
'subject': 'testing your email template',
'templateName': 'genericTemplate',
'bodyText': 'this is a test'
});
console.log(POSTData);
const HTTPOptions = {
hostname: 'url.com',
port: 00000,
path: '/path',
method: 'POST',
headers: {
'Content-Type': 'application/json'
}
};
const HTTPSrequest = https.request(HTTPOptions, (response) => {
console.log(`STATUS: ${res.statusCode}`);
console.log(`HEADERS: ${JSON.stringify(res.headers)}`);
response.setEncoding('utf8');
response.on('data', (chunk) => {
console.log(`BODY: ${chunk}`);
});
response.on('end', () => {
console.log('No more data in response.');
});
});
HTTPSrequest.on('error', (error) => {
console.log(`problem with request: ${error}`);
});
// write data to request body
HTTPSrequest.write(POSTData);
HTTPSrequest.end();
I'm assuming it's the POSTData that is the "malformed JSON", here's what it looks like stringified:
addressTo=name%40companyname.com&subject=testing%20your%20email%20template&templateName=genericTemplate&bodyText=this%20is%20a%20test
I'm not sure what I'm doing that is causing the malformed JSON. What am I missing?
You're sending an application/x-www-form-urlencoded payload but telling the server it's application/json. The server is complaining because of this mismatch.
The simple solution should be to just change querystring.stringify to JSON.stringify.
You may also need to specify a Content-Length header since some servers may not support chunked requests (which is the default in node). If you do add this, make sure you use Buffer.byteLength(POSTData) as the header value instead of just POSTData.length since the former works for multi-byte characters and the latter returns the number of characters (not bytes).
Help more with explication from #mscdex
Your options object after querystring.stringify has returned. Otherwise you won't know the length of the stringified body data.
It is important to note that you need two extra headers in your options object in order to make a successful post request. These are :
'Content-Type': 'application/json',
'Content-Length': POSTData.length
Check example:
const HTTPOptions = {
hostname: 'url.com',
port: 00000,
path: '/path',
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Length': POSTData.length
}
};
You can see this examples too: 1st post.
JsonReader.setLenient(true) to accept malformed JSON at line 1 column 11 path $","mdcKey":""}

Is there a way to send a JPG with Jasmine NodeJS?

I'm trying to test out my API using FrisbyJS (which sits on top of Jasmine for Node). I was wondering if anyone knew how to submit/send an image using Jasmine?
http://frisbyjs.com/
My current code is...
var frisby = require('frisby');
var URL = 'http://localhost/api';
frisby.globalSetup({
request: {
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Accept': 'application/json',
'api': 'key'
}
},
timeout: (30 * 1000)
});
frisby.create('submit an image')
.post(URL + 'image', {images: '#test.jpg'}, {method: 'POST'})
.expectStatus(200)
.afterJSON(function(cart){
console.log('made it!');
})
}).toss();
And I get the following error:
1) Frisby Test: submit an image
[ POST http://localhost/api ]
Message:
timeout: timed out after 30000 msec waiting for HTTP Request timed out before completing
Stacktrace:
undefined
Finished in 31.458 seconds
And yes, the image test.jpg does exist in the same folder :) as the spec file and where I'm executing the spec itself (jasmine-node .).
I found a solution based on the example given at https://github.com/vlucas/frisby/blob/master/examples/httpbin_multipart_spec.js
var frisby = require('frisby');
var fs = require('fs');
var path = require('path');
var FormData = require('form-data');
var contentPath = path.resolve(__dirname, './path/to/image.jpg');
var form = new FormData();
form.append('file', fs.createReadStream(contentPath), {
knownLength: fs.statSync(contentPath).size
});
frisby.create('Create content asset')
.post(apiPath + '/assets', form, {
json: false,
headers: {
'content-type': 'multipart/form-data; boundary=' + form.getBoundary(),
'content-length': form.getLengthSync(),
}
})
.expectStatus(201)
.toss()

Resources