HTTP POST in Espruino for Microcontrollers - javascript

I recently got myself an esp8266-12e module and loaded the ESPRUINO.js firmware on it. I am trying execute a post request from the device, but the device always returns a 'no connection' error when trying to POST.
To troubleshoot I have ran a GET request to the same URL, and the request was successful, this means that internet is working on the device and communication with the intended server is possible.
I then moved on to see if there were errors in my HTTP POST code, I ran the same code in a node.js app and it successfully posted to the server.
Here is the code below, I removed the exact address of my server and my wifi/pass info.
var http = require("http");
var wifi = require("Wifi");
var sdata = {
deviceID: 'esp-12',
};
var options = {
hostname: 'immense-XXXXXX-XXXXX.herokuapp.com',
method: 'POST',
path:'/MXXXXXX',
headers: {
'Content-Type': 'application/json'
}
};
var req = http.request(options, function(res) {
console.log('Status: ' + res.statusCode);
console.log('Headers: ' + JSON.stringify(res.headers));
res.setEncoding('utf8');
res.on('data', function(body) {
console.log('Body: ' + body);
});
});
req.on('error', function(e) {
console.log('problem with request: ' + e.message);
});
payload = JSON.stringify(sdata);
req.write(payload);
req.end();
terminal response from device after execution
problem with request: no connection
Here is the documentation for Espruino.js HTTP module.
https://www.espruino.com/Reference#http
Can any of the JS gurus see an issue with the request?

Turns out the http post request requires the 'content-length' header to function correctly.
Here is the working post request model for anyone who may need it. Note: Payload has already been formatted as a JSON object.
function postX(payload) {
var options = {
host: 'url',
port: '80',
path:'/ext',
method:'POST',
headers: { "Content-Type":"application/json", "Content-Length":payload.length }
};
var req = require("http").request(options, function(res) {
res.on('data', function(data) {
console.log("-->"+data);
});
res.on('close', function(data) {
console.log("==> Closed.");
ticksSinceConnect = 0;
});
});
req.end(payload);}

Related

HTTPS handshake failure on IoT device

I am running buildroot and iotjs on an iot device and I'm trying to make a post request to login and get an auth token. But I'm unable to call the API at all because I run into an error that says
"API Failed, problem with request: handshake failed: domian"
The same API works given I use the http version. The server I'm trying to call is hosted on an AWS EC2 instance behind cloudflare.
The code (not exactly the same but similar) used to call the API:
var https = require('https');
var options = {
host: 'www.google.com',
port: 443,
path: '/upload',
method: 'POST'
};
var req = https.request(options, function(res) {
console.log('STATUS: ' + res.statusCode);
console.log('HEADERS: ' + JSON.stringify(res.headers));
res.setEncoding('utf8');
res.on('data', function (chunk) {
console.log('BODY: ' + chunk);
});
});
req.on('error', function(e) {
console.log('problem with request: ' + e.message);
});
// write data to request body
req.write('data\n');
req.write('data\n');
req.end();
Any help is appreciated thanks!

How to load JSON/API data using https.request and Node.JS

I'm trying to load weather data. I have front end code that was doing this perfectly but I need to move this to back end. I moved a library of my functions over to Node.JS. I was using $.getJSON but was told I should use https.request for the new version. Here's my code:
getTextWeatherUsingStationUsingRequest: function(theStation){
const http = require("http");
const https = require("https");
thePath = 'stations/' + theStation + '/observations/current';
// theURL = 'https://api.weather.gov/stations/' + theStation + '/observations/current';
function requestTheData(){
var options = {
protocol: "https:",
hostname: "https://api.weather.gov/",
path: thePath,
port: 80,
method: "GET"
};
var instaRequest = https.request(options);
instaRequest.on("response", function(res){
console.log(`STATUS: ${res.statusCode}`);
console.log(`HEADERS: ${JSON.stringify(res.headers)}`);
res.setEncoding('utf8');
res.on('data', (chunk) => {
console.log(`BODY: ${chunk}`);
});
res.on('end', () => {
console.log('No more data in response.');
});
console.log("response");
console.log(res.statusCode);
console.log(res.statusMessage);
});
instaRequest.on('error', (e) => {
console.error(`problem with request: ${e.message}`);
});
instaRequest.end();
}
requestTheData();
I'm getting this error and can't figure out what's going on:
problem with request: getaddrinfo ENOTFOUND https://api.weather.gov/stations/ https://api.weather.gov/stations/:80
HTTPS generally uses port 443, so lets change that. Also, the API shows the hostname should be the raw URL and the path should be the remainder of the route (with a leading slash) similar to this:
thePath = '/stations/' + theStation + '/observations/current';
...
var options = {
hostname: "api.weather.gov",
path: thePath,
port: 443,
method: "GET"
};
Before even seeing any answers I got it working by:
protocol: "https:",
hostname: "api.weather.gov",
but then I was getting a STATUS:
403 Forbidden You don't have permission to access
"http://api.weather.gov/" on this server.
I seemed to remember that you are required to pass something in through headers so I added this under "method: "GET","
method: "GET",
headers: {
'Accept' : 'application/json',
'Content-Type': 'application/json',
'User-Agent' : 'MY-UA-STRING'
}
And, voila, now I'm getting JSON weather data. It didn't work until I added the 'User-Agent'. Do you guys know what this needs to be (and/or point me to a place that describes this)?

node.js http and bing search api

I am trying to use the Bing Search API to return a JSON string. I first tried using the following url as per Azure's explore website (https://datamarket.azure.com/dataset/explore/5BA839F1-12CE-4CCE-BF57-A49D98D29A44):
'https://api.datamarket.azure.com/Bing/Search/v1/Composite?Sources=%27web%27&Query=%27NGI%20SPA%27&Market=%27en-US%27'
After, I found a SO thread Using the new Bing API (nodejs) which suggested I use a url of the form:
https://user:<YourDefaultAccountKey>#api.datamarket.azure.com/Bing/SearchWeb/Web?Query=%27leo%20fender%27&Market=%27en-US%27&$top=50&$format=JSON
Both of these return status 401 (Authentication Failure):
STATUS: 401
HEADERS: {"content-type":"application/json; charset=utf-8","server":"Microsoft-IIS/8.0","jsonerror":"true","x-powered-by":"ASP.NET","access-control-allow-origin":"*","access-control-allow-credentials":"false","access-control-allow-headers":"Authorization, DataServiceVersion, MaxDataServiceVersion","access-control-expose-headers":"DataServiceVersion, MaxDataServiceVersion","access-control-allow-methods":"GET, POST, OPTIONS","access-control-max-age":"604800","date":"Wed, 02 Jul 2014 17:23:29 GMT","content-length":"91"}
BODY: {"Message":"There was an error processing the request.","StackTrace":"","ExceptionType":""}
I have also tried other various combinations of URLs to no avail. My code is below:
var url = require('url');
var http = require('http');
var serviceRootURL = 'https://api.datamarket.azure.com/Bing/Search/v1/Composite?Sources=%27web%27&Query=%27NGI%20SPA%27&Market=%27en-US%27'
var params = 'hi';
var dataURL = url.parse(serviceRootURL);
var post_options = {
hostname: dataURL.hostname,
port: dataURL.port || 80,
path: dataURL.path,
method: 'GET',
headers: {
'Content-Type': 'application/json; charset=utf-8',
'Content-Length': params.length
}
};
var req = http.request(post_options, function(res) {
console.log('STATUS: ' + res.statusCode);
console.log('HEADERS: ' + JSON.stringify(res.headers));
res.setEncoding('utf8');
res.on('data', function (chunk) {
console.log('BODY: ' + chunk);
});
});
req.on('error', function(e) {
console.log('problem with request: ' + e.message);
});
// write data to request body
req.write('data\n');
req.write('data\n');
req.end();
Any idea why I am getting an authentication failure?
You can use this module that encapsulates the requests, so you can use it
like:
var Bing = require('node-bing-api')({ accKey: "your-account-key" });
Bing.web("leo fender", function(error, res, body){
console.log(body);
},
{
top: 50,
market: 'en-US'
});
It works with the Azure version. You only have to replace your account key.
Got it working with request...weird
var request = require('request');
var _ = require('underscore');
var searchURL = 'https://user:<TIPE YOUR KEE HEER>#api.datamarket.azure.com/Bing/SearchWeb/v1/Web?Query=%27xbox%27&$top=10&$format=JSON';
var http = request( searchURL, function(err, resp, body)
{
if ( err )
{
throw err;
}
var a = JSON.parse(body);
console.log(a.d.results);
});
you can use jsearch module. install ;
npm install jsearch
usage;
js.bing('queryStringYouWant',10,function(response){
console.log(response) // for Bing results
})

Verifiy iOS Receipt with Node.js

After struggling a few days trying to get something to work and getting no where, I was wondering if someone has gotten iOS Receipt Validation working on Node.js. I have tried the node module iap_verifier found here but I could not get it to work properly for me. the only response I received back form Apples servers is 21002, data was malformed.
One thing that has worked for me was a client side validation request to apples servers that I got directly from the tutorials provided by Apple here, with the code shown below.
// The transaction looks ok, so start the verify process.
// Encode the receiptData for the itms receipt verification POST request.
NSString *jsonObjectString = [self encodeBase64:(uint8_t *)transaction.transactionReceipt.bytes
length:transaction.transactionReceipt.length];
// Create the POST request payload.
NSString *payload = [NSString stringWithFormat:#"{\"receipt-data\" : \"%#\", \"password\" : \"%#\"}",
jsonObjectString, ITC_CONTENT_PROVIDER_SHARED_SECRET];
NSData *payloadData = [payload dataUsingEncoding:NSUTF8StringEncoding];
// Use ITMS_SANDBOX_VERIFY_RECEIPT_URL while testing against the sandbox.
NSString *serverURL = ITMS_SANDBOX_VERIFY_RECEIPT_URL;
// Create the POST request to the server.
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:serverURL]];
[request setHTTPMethod:#"POST"];
[request setHTTPBody:payloadData];
NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:request delegate:self];
[conn start];
I have a bunch of different code I have been using to send a wide array of things to my node server. and all of my different attempts have failed. I have even tried just funneling the "payloadData" I constructed in the client side validation example above to my server and sending that to Apples servers with the following code:
function verifyReceipt(receiptData, responder)
{
var options = {
host: 'sandbox.itunes.apple.com',
port: 443,
path: '/verifyReceipt',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': Buffer.byteLength(receiptData)
}
};
var req = https.request(options, function(res) {
res.setEncoding('utf8');
res.on('data', function (chunk) {
console.log("body: " + chunk);
});
});
req.write(receiptData);
req.end();
}
Where the function is passed the payloadData. The response received from Apple is always 21002. I'm still basically a node novice,so I can't figure out what exactly is going wrong. I think there might be some data corruption happening when I am sending the data from ObjC to my Node server, so perhaps I am not transmitting right.
If anyone can point me in the right direction, or provide some example of how they got receipt validation to work in node for them, it would be a great help. It would be great if anyone has had any experience with the iap_verifier module, and exactly what data it requires. I'll provide any code example I need to, as I have been fighting this process for a few days now.
Thanks!
For anyone using the npm library "request", here's how to avoid that bothersome 21002 error.
formFields = {
'receipt-data': receiptData_64
'password': yourAppleSecret
}
verifyURL = 'https://buy.itunes.apple.com/verifyReceipt' // or 'https://sandbox.itunes.apple.com/verifyReceipt'
req = request.post({url: verifyURL, json: formFields}, function(err, res, body) {
console.log('Response:', body);
})
This is my working solution for auto-renewable subscriptions, using the npm request-promise library.
Without JSON stringify-ing the body form, I was receiving 21002 error (The data in the receipt-data property was malformed or missing)
const rp = require('request-promise');
var verifyURL = 'https://sandbox.itunes.apple.com/verifyReceipt';
// use 'https://buy.itunes.apple.com/verifyReceipt' for production
var options = {
uri: verifyURL,
method: 'POST',
headers: {
'User-Agent': 'Request-Promise',
'Content-Type': 'application/x-www-form-urlencoded',
},
json: true
};
options.form = JSON.stringify({
'receipt-data': receiptData,
'password': password
});
rp(options).then(function (resData) {
devLog.log(resData); // 0
}).catch(function (err) {
devLog.log(err);
});
Do you have composed correctly receiptData? Accordlying with Apple specification it should have the format
{"receipt-data": "your base64 receipt"}
Modifying your code wrapping the base64 receipt string with receipt-data object the validation should works
function (receiptData_base64, production, cb)
{
var url = production ? 'buy.itunes.apple.com' : 'sandbox.itunes.apple.com'
var receiptEnvelope = {
"receipt-data": receiptData_base64
};
var receiptEnvelopeStr = JSON.stringify(receiptEnvelope);
var options = {
host: url,
port: 443,
path: '/verifyReceipt',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': Buffer.byteLength(receiptEnvelopeStr)
}
};
var req = https.request(options, function(res) {
res.setEncoding('utf8');
res.on('data', function (chunk) {
console.log("body: " + chunk);
cb(true, chunk);
});
res.on('error', function (error) {
console.log("error: " + error);
cb(false, error);
});
});
req.write(receiptEnvelopeStr);
req.end();
}

Adding a new document in Couchdb ( iriscouch ) POST request fails with Status 500

A couple of months back,the code below worked perfectly and was used to add a new document in couchdb (iriscouch).
Now I'm getting a HTTP Status 500. Is there a workaround?
Code (in Node.js) :
var http=require('http');
var options = {
host: 'sbose78.iriscouch.com',
path: '/bosedb1',
method: 'POST',
headers:{
'Content-Type':'application/json',
'accept':'application/json'
}
};
var data={
'servings' : 4,
'subtitle' : "Delicious with fresh bread",
'title' : "Fish Stew------"
};
var req = http.request(options, function(res) {
console.log('STATUS: ' + res.statusCode);
console.log('HEADERS: ' + JSON.stringify(res.headers));
var body="";
res.on('data', function (chunk) {
body+=chunk;
console.log('BODY(inside listener):\n ' + body);
});
console.log('BODY (outside listener): ' + body);
});
req.on('error', function(e) {
console.log('problem with request: ' + e.message);
});
//write data to request body
req.write(JSON.stringify(data));
req.end();
The response:
STATUS: 500
HEADERS: {"content-type":"text/plain","content-length":"239"}
BODY(inside listener):
Internal routing error
Sorry, we cannot connect to the intended server.
We have just been notified of this problem. We will correct it as soon as possible.
Feel free to contact us if you have any questions: support#iriscouch.com
Looks like http://www.iriscouch.com/ is down at the moment:
Host not found: www.iriscouch.com
Have you considered using an abstraction layer, at least to do http?
request
Your codebase is going to have a lot of http code if you don't :)
Personally I wrote and maintain the node.js CouchDB client which is based on request, if you are curious about it you can find out more at github

Categories

Resources