Create and agent based on another agent initial options nodeJs - node.js

I have a library where I am making the api calls. The client used to make the calls is passed an agent created with HttpsAgent from agentkeepalive.
The edge case I am trying to handle is that when the endpoint that I am trying to reach matches let's say https://somedomain.com, because of SSL certificate issue I need to update the servicename/hostname of the request so it will go through.
The problem is that I know how to do that through nodeHTTPS.Agent and now I need to find a way to preserve the initial configuration passed to the agent in the first place. Consider this example
// node api
const response = await makeReq({
...etcOptions,
agent: keepAliveAgent // agentkeepalive instance with some configurations
})
// now, in make request library
function makeReq(options) {
if (options.address.match(/^https:\/\/(.*)\.somedomain\.com$/)) {
// here, either I do something like, if options.agent -> options.agent.servicename = "*.etcsomedomain.com"
// or
options.agent = new nodeHTTPS.Agent({
...(options.agent && {...options.agent.options}), // trying to retrieve initial passed options for this agent
servername: '*.etcsomedomain.com'
});
}
}
Another problem is that passed agent.options strucutre might be different since that agent is created through a library that might differ from nodeHTTPS.Agent
Any idea how I can handle this edge case correctly?

Related

Nodejs prevent new request before send response to last request

How to prevent new requests before sending the response to the last request. on On the other hand just process one request at the same time.
app.get('/get', function (req, res) {
//Stop enter new request
someAsyncFunction(function(result){
res.send(result);
//New Request can enter now
}
}
Even tho I agree with jfriend00 that this might not be the optimal way to do this, if you see that it's the way to go, I would just use some kind of state management to check if it's allowed to access that /get request and return a different response if it's not.
You can use your database to do this. I strongly recommend using Redis for this because it's in-memory and really quick. So it's super convenient. You can use mongodb or mysql if you prefer so, but Redis would be the best. This is how it would look, abstractly -
Let's say you have an entry in your database called isLoading, and it's set to false by default.
app.get('/get', function (req, res) {
//get isloading from your state management of choice and check it's value
if(isLoading == true) {
// If the app is loading, notify the client that he should wait
// You can check for the status code in your client and react accordingly
return res.status(226).json({message: "I'm currently being used, hold on"})
}
// Code below executes if isLoading is not true
//Set your isLoading DB variable to true, and proceed to do what you have
isLoading = true
someAsyncFunction(function(result){
// Only after this is done, isLoading is set to false and someAsyncFunction can be ran again
isLoading = false
return res.send(result)
}
}
Hope this helps
Uhhhh, servers are designed to handle multiple requests from multiple users so while one request is being processed with asynchronous operations, other requests can be processed. Without that, they don't scale beyond a few users. That is the design of any server framework for node.js, including Express.
So, whatever problem you're actually trying to solve, that is NOT how you should solve it.
If you have some sort of concurrency issue that is pushing you to ask for this, then please share the ACTUAL concurrency problem you need to solve because it's much better to solve it a different way than to handicap your server into one request at a time.

Unique configuration per vhost for Micro

I have a few Zeit micro services. This setup is a RESTful API for multiple frontends/domains/clients
I need to, in my configs that are spread throughout the apps, differentiate between these clients. I can, in my handlers, setup a process.env.CLIENT_ID for example that I can use in my config handler to know which config to load. However this would mean launching a new http/micro process for each requesting domain (or whatever method I use - info such as client id will prob come in a header) in order to maintain the process.env.CLIENT_ID throughout the request and not have it overwritten by another simultaneous request from another client.
So I have to have each microservice check the client ID, determine if it has already launched a process for that client and use that else launch a new one.
This seems messy but not sure how else to handle things. Passing the client id around with code calls (i.e. getConfg(client, key) is not practical in my situation and I would like to avoid that.
Options:
Pass client id around everywhere
Launch new process per host
?
Is there a better way or have I made a mistake in my assumptions?
If the process per client approach is the better way I am wondering if there is an existing solution to manage this? Ive looked at http proxy, micro cluster etc but none seem to provide a solution to this issue.
Well I found this nice tool https://github.com/othiym23/node-continuation-local-storage
// Micro handler
const { createNamespace } = require('continuation-local-storage')
let namespace = createNamespace('foo')
const handler = async (req, res) => {
const clientId = // some header thing or host
namespace.run(function() {
namespace.set('clientId', clientId)
someCode()
})
})
// Some other file
const { getNamespace } = require('continuation-local-storage')
const someCode = () => {
const namespace = getNamespace('foo')
console.log(namespace.get('clientId'))
}

Implementing isRequestFromAssistant in Node.js on actions-on-google project fulfillment

I am having trouble implementing the isRequestFromAssistant method to verify requests to my fulfillment webhook. Using Node.js, I instantiate the following variables at the start of my index.js file:
const App = require('actions-on-google').ApiAiApp;
const app = new App({ request, response });
I then use "app" with the .ask and .tell and other methods throughout my functions.
The code I see in the docs for implementing isRequestFromAssistant is:
const app = new ActionsSdkApp({request, response});
app.isRequestFromAssistant('my-project-id')
.then(() => {
app.ask('Hey there, thanks for stopping by!');
})
.catch(err => {
response.status(400).send();
});
If I leave out the first line and use my existing app variable, created with the .ApiAi method instead of the .ActionsSdkApp method, it doesn't work. If I create a new variable App1 and app1 using the .ActionsSdkApp method and change the above code to be app1.isRequestFromAssistant, it also doesn't work. I have tried other variations with no luck.
When I say it doesn't work, I mean I receive a 500 Internal Server Error when I call it. I am hosting it with NGROK currently. I am still a beginner with Node.js, although I have managed to get the other 700 lines of code working just fine, learning mostly from Google searches and reading these forums.
You have a few things going on here which, individually or separately, may be causing the problem.
First - make sure you have the most recent version of the actions-on-google library. The isRequestFromAssistant() function was added in version 1.6.0, I believe.
Second - Make sure you're creating the right kind of App instance. If you're using Dialogflow (formerly API.AI), you should be creating it with something like
const App = require('actions-on-google').DialogflowApp;
const app = new App( {request, response} );
or
const { DialogflowApp } = require('actions-on-google');
const app = new DialogflowApp( {request, response} );
(They both do the same thing, but you'll see both forms in documentation.) You should switch to DialogflowApp from ApiAiApp (which your example uses) to reflect the new name, but the old form has been retained.
If you're using the Actions SDK directly (not using Dialogflow / API.AI), then you should be using the ActionsSdkApp object, something like
const { ActionsSdkApp } = require('actions-on-google');
const app = new ActionsSdkApp({request: request, response: response});
(Again, you'll see variants on this, but they're all fundamentally the same.)
Third - Make sure you're using the right function that matches the object you're using. The isRequestFromAssistant() function is only if you are using the Actions SDK.
If you are using Dialogflow, the corresponding function is isRequestFromDialogflow(). The parameters are different, however, since it requires you to set confirmation information as part of your Dialogflow configuration.
Finally - If you're getting a 500 error, then check your logs (or the output from stderr) for the node.js server that is running. Typically there will be an error message there that points you in the right direction. If not - posting that error message as part of your StackOverflow question is always helpful.
Set the secure (randomly generated) auth header & key values in the dialogflow Fulfillment page, then in nodejs:
if (app.isRequestFromDialogflow("replace_with_key", "replace_with_value")) {
console.log("Request came from dialogflow!");
// rest of bot
} else {
console.log("Request did not come from dialogflow!");
response.status(400).send();
}
Also see: https://developers.google.com/actions/reference/nodejs/DialogflowApp#isRequestFromDialogflow

How to run Alexa skill with the alexa-sdk on own server with Node.js without Lambda drop-in?

The Alexa skill docs will eventually allow you to send webhooks to https endpoints. However the SDK only documents lambda style alexa-sdk usage. How would one go about running Alexa applications on one's own server without anything abstracting Lambda? Is it possible to wrap the event and context objects?
You can already use your own endpoint. When you create a new skill, in the configuration tab, just choose HTTPS and provide your https endpoint. ASK will call your endpoint where you can run anything you want (tip, check ngrok.com to tunnel to your own dev machine). Regarding the event and context objects; your endpoint will receive the event object information. You don't need the context object for anything, that just lets you interact with Lambda-specific stuff (http://docs.aws.amazon.com/lambda/latest/dg/python-context-object.html). Just make sure that you comply with the (undocumented) timeouts by ASK and you are good to go.
Here's a way to do this that requires only a small change to your Skill code:
In your main index.js entry point, instead of:
exports.handler = function (event, context) {
use something like:
exports.myAppName = function (funcEvent, res) {
Below that, add the following workaround:
var event = funcEvent.body
// since not using Lambda, create dummy context with fail and succeed functions
const context = {
fail: () => {
res.sendStatus(500);
},
succeed: data => {
res.send(data);
}
};
Install and use Google Cloud Functions Local Emulator on your laptop. When you start and deploy your function to the emulator, you will get back a Resource URL something like http://localhost:8010/my-project-id/us-central1/myAppName.
Create a tunnel with ngrok. Then take the ngrok endpoint and put it in place of localhost:8010 in the Resource URL above. Your resulting fulfillment URL will be something like: https://b0xyz04e.ngrok.io/my-project-id/us-central1/myAppName
Use the fulfillment URL (like above) under Configuration in the Alexa dev console, selecting https as the Service Endpoint Type.

Data chunks in node-curl (node.js)

I am using node-curl as a HTTPS client to make requests to resources on the web and the code runs on a machine behind a proxy facing the internet.
The code I am using to co:
var curl = require('node-curl');
//Call the curl function. Make a curl call to the url in the first argument.
//Make a mental note that the callback to be invoked when the call is complete
//the 2nd argument. Then go ahead.
curl('https://encrypted.google.com/', {}, function(err) {
//I have no idea about the difference between console.info and console.log.
console.info(this.body);
});
//This will get printed immediately.
console.log('Got here');
node-curl detects the proxy settings from the environment and gives back the expected results.
The challenge is: the callback gets fired after the entire https-response gets downloaded, and as far as I can tell there are no parallels for the 'data' and 'end' events from the http(s) modules.
Further, after going through the source code, I found that indeed the node-curl library receives the data in chunks: reference line 58 in https://github.com/jiangmiao/node-curl/blob/master/lib/CurlBuilder.js . It seems that no events are emitted presently in this case.
I need to forward the possibly-sizable-response back to the another computer on my LAN for processing, so this is a clear concern for me.
Is using node-curl recommended for this purpose in node?
If yes, how can I handle this?
If no, then what would be a suitable alternative?
I would go for the wonderful request module, at least if the proxy settings are no more advanced than what it supports. Just read the proxy settings from the environment yourself:
var request = require('request'),
proxy = request.defaults({proxy: process.env.HTTP_PROXY});
proxy.get('https://encrypted.google.com/').pipe(somewhere);
Or if you don't want to pipe it:
var req = proxy.get({uri: 'https://encrypted.google.com/', encoding: 'utf8'});
req.on('data', console.log);
req.on('end', function() { console.log('end') });
Above, I also pass the encoding I expect in the response. You could also specify that in the defaults (the call to request.defaults() above), or you could leave it in which case you will get Buffers in the data event handler.
If all you want to do is to send it to another URL, request is perfect for that:
proxy.get('https://encrypted.google.com/').pipe(request.put(SOME_URL));
Or if you'd rather POST it:
proxy.get('https://encrypted.google.com/').pipe(request.post(SOME_URL));
Or, if you want to proxy the request to the destination server as well:
proxy.get('https://encrypted.google.com/').pipe(proxy.post(SOME_URL));

Resources