xmlhttp request in a node-red function node - http

Is it possible to make http GET requests from within a node-red "function" node.
If yes could somebody point me to some example code please.
The problem I want to solve is the following:
I want to parse a msg.payload with custom commands. For each command I want to make an http request and replace the command with the response of a HTTP GET request.
expl:
msg.payload = "Good day %name%. It's %Time% in the %TimeOfDay%. Time for your coffee";
The %name%,%TimeOfDay% and %Time% should be replaced by the content of a Get request to http://nodeserver/name,..., http://nodeserver/Time.

thnx Hardilb,
After half a day searching I found out that the http-node can also be configured by placing a node just before it setting the
msg.url = "http://127.0.0.1:1880/" + msg.command ;
msg.method = "GET";
I used the following code to get a list of commands
var parts = msg.payload.split('%'),
len = parts.length,
odd = function(num){return num % 2;};
msg.txt= msg.payload;
msg.commands = [];
msg.nrOfCommands = 0;
for (var i = 0; i < len ; i++){
if(odd(i)){
msg.commands.push(parts[i]);
msg.nrOfCommands = msg.nrOfCommands + 1;
}
}
return msg;

You should avoid doing asynchronous or blocking stuff in function nodes.
Don't try to do it all in one function node, chain multiple function nodes with multiple http Request nodes to build the string up a part at a time.
You can do this by stashing the string in another variable off the msg object instead of payload.
One thing to looks out for is that you should make sure you clear out msg.headers before each call to the next http Request node

Related

DoGet with multiple parameters not being recognized

I'm currently trying to connect a Lua Script with a GS WebApp. The connection is working but due to my lack of knowledge in GScripting I'm not sure why it isn't saving my data correctly.
In the Lua side I'm just passing in a hard-code a random name and simple numerical userid.
local HttpService = game:GetService("HttpService")
local scriptID = scriptlink
local WebApp
local function updateSpreadSheet ()
local playerData = (scriptID .. "?userid=123&name:Jhon Smith")
WebApp = HttpService:GetAsync(playerData)
end
do
updateSpreadSheet()
end
On the Google Script side i'm only saving the data on the last row and then add the value of the userid and the name.
function doGet(e) {
console.log(e)
// console.log(f)
callName(e.parameter.userid,e.parameter.name);
}
function callName(userid,name) {
// Get the last Row and add the name provided
var sheet = SpreadsheetApp.getActiveSheet();
sheet.getRange(sheet.getLastRow() + 1,1).setValues([userid],[name]);
}
However, the only data the script is saving is the name, bypassing the the userid for reasons I have yet to discover.
setValues() requires a 2D array and range dimensions should correspond to that array. The script is only getting 1 x 1 range and setValues argument is not a 2D array. Fix the syntax or use appendRow
sheet.getRange(sheet.getLastRow() + 1,1,1,2).setValues([[userid,name]]);
//or
sheet.appendRow([userid,name])
References:
appendRow

Pause/Stop AjaxDataSource Bokeh Stream

I am able to set up my graph for streaming just fine. Here's the initialization:
self.data_source = AjaxDataSource(data_url='my_route',
polling_interval=1000, mode='append', max_size=300)
Now I want to 'pause' the polling of the AjaxDataSource. I couldn't find a way to do this in the documentation. I'm NOT running a bokeh server, so bokeh server solutions I am unable to use.
I came up with one possible solution: just return empty data set to the function that is appending the data via AjaxDataSource. So in the example above, the my_route function would look something like this:
def my_route:
if not self.is_paused:
data = normal_data_to_graph
else:
data = []
return data
Once you set the polling_interval = None in Python, it will not request it. In CustomJS, you can start the paused request. Here, the source is an AJaxDataSource instance.
source.polling_interval = 1000; // the interval you want
source.intialized = false;
source.setup();

Nginx-redis module returning string length along with the value from Redis

I am using redis2-nginx-module to serve html content stored as a value in redis. Following is the nginx config code to get value for a key from redis.
redis2_query get $fullkey;
redis2_pass localhost:6379;
#default_type text/html;
When the url is hit the following unwanted response is rendered along with the value for that key.
$14
How to remove this unwanted output? Also if key passed as an argument doesn't exist in the redis, how to check this condition and display some default page?
(Here's a similar question on ServerFault)
There's no way with just redis2 module, as it always return a raw Redis response.
If you only need GET and SET commands you may try with HttpRedisModule (redis_pass). If you need something fancier, like hashes, you should probably try filtering the raw response from Redis with Lua, e.g. something along the lines of
content_by_lua '
local res = ngx.location.capture("/redis",
{ args = { key = ngx.var.fullkey } }
)
local body = res.body
local s, e = string.find(body, "\r\n", 1, true)
ngx.print(string.sub(body, e + 1))
';
(Sorry, the code's untested, don't have an OpenResty instance at hand.)

NIO Http file server - connection closed prematurely

I want to create an HTTP static file server using java NIO and it works fine for small files, but seems to truncate the HTTP response for larger files (672 KB out of a 3.8 MB image is returned according to my Chrome Inspector, and my browser displays a a partially corrupted image). Is this code below incorrect?
(I know there are existing libraries for this and eventually I will use one in my project. But initially I want to implement a basic one myself to see if my project concept is feasible.)
Iterator<SelectionKey> keys = selector.selectedKeys().iterator();
while (keys.hasNext()) {
SelectionKey key = keys.next();
keys.remove();
if (key.isAcceptable()) {
// New Client encountered
serverSocket.accept().configureBlocking(false)
.register(selector, SelectionKey.OP_READ);
} else if (key.isReadable()) {
// Additional data for existing client encountered
SocketChannel selectedClient = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(548);
String requestedFile2 = getRequstedFile(key, selectedClient, buffer);
buffer.clear();
buffer.flip();
FileChannel fc = FileChannel.open(Paths.get(requestedFile2));
String string = "HTTP/1.1 200 Ok\nContent-Type: image/jpeg\nContent-Length: "
+ (Files.size(Paths.get(requestedFile2)) + "\n\n");
selectedClient.write(ByteBuffer.wrap(string.getBytes()));
while (fc.read(buffer) > -1) {
buffer.flip(); // read from the buffer
selectedClient.write(buffer);
buffer.clear();
}
selectedClient.close();
}
}
(Exception handling etc. omitted for brevity)
EDIT
I have a content-length-mismatch error message. So what is the right way to determine the HTTP response size when reading a file's contents using the NIO API?
buffer.clear();
That should be
buffer.compact();
and the loop should be
while (fc.read(buffer) > 0 || buffer.position() > 0)
You're assuming everything got written by the write.
Also you need to change the HTTP header line terminators to \r\n.
And you need to study RFC 2616 about the content length.
I guess you have to check return value from selectedClient.write(), check the SocketChannel.write() documentation:
Unless otherwise specified, a write operation will return only after writing all of the r requested bytes. Some types of channels, depending upon their state, may write only some of the bytes or possibly none at all.
Which could be the case here. Either add another inner loop which would write to output as long as there are bytes remaining in the buffer. Or you can amend the loop according to example in ByteBuffer.compact(): http://docs.oracle.com/javase/7/docs/api/java/nio/ByteBuffer.html#compact()
while (buffer.position() > 0 || fc.read(buffer) > 0) {
buffer.flip(); // read from the buffer
selectedClient.write(buffer);
buffer.compact();
}
And remember that the code supposes that selectedClient is blocking. If that wasn't the case, you would need to invoke another select() waiting on the selectedClient becoming writable...

How to check actual content length against Content-Length header?

A user can POST a document to our web service. We stream it elsewhere. But, at the end of the streaming, we need to be sure they didn't lie about their Content-Length.
I assume if headerContentLength > realContentLength, the request will just wait for them to send the rest, eventually timing out. So that's probably OK.
What about if headerContentLength < realContentLength? I.e. what if they keep sending data after they said they were done?
Is this taken care of by Node.js in any way? If not, what is a good way to check? I suppose I could just count up the bytes inside of some data event listeners---i.e., req.on("data", function (chunk) { totalBytes += chunk.length; }). That seems like a kludge though.
To check the actual length of the request, you have to add it up yourself. The data chunks are Buffers and they have a .length property that you can add up.
If you specify the encoding with request.setEncoding(), your data chunks will be Strings instead. In that case, call Buffer.byteLength(chunk) to get the length. (Buffer is a global object in node.)
Add up the total for each of your chunks and you'll know how much data was sent.
Here's a rough (untested) example:
https.createServer(function(req, res) {
var expected_length = req.headers['content-length']; // I think this is a string ;)
var actual_length = 0;
req.on('data', function (chunk) {
actual_length += chunk.length;
});
req.on('end', function() {
console.log('expected: ' + expected_length + ', actual: ' + actual_length);
});
});
Note: length refers to the maximum length of the Buffer's content, not the actual length. However, it works in this case because chunk buffers are always created at the exact correct length. Just be aware of that if you're working with buffers somewhere else.

Resources