JavaScript, Snippets

The same code with Callbacks vs Promises vs Async/Await

Sometimes in JavaScript to have to deal with different flavours of asynchronous code, so it is handy to be able to map back and forth between them.

Callback

Functions that do something asynchronously are typically implemented using the callback pattern, and their implementation might look like this:

const myAsyncFunction = (parameter, callback) => {
    const result = //do something async here...
    callback(result, error);
};

They may be invoked in the following way:

const main = () => {
    myAsyncFunction("parameter", (result, error) => {
       //use the result or error here
    });
};

However, things may quickly get out of hand if we need the result of an asynchronous function to invoke another asynchronous function, and then we need that to invoke another one and so on…:

const main = () => {
    myAsyncFunction("parameter", (result, error) => {
       myOtherAsynchronousFunction(result, (otherResult, otherError) => {
           myFinalAsynchronousFunction(otherResult, (finalResult, finalError) => {
              //whew!
           });
       });
    });
};

Promise

We can use Promises to solve the indentation mess above. For example, this is how we might change our main function if we want to wrap the myAsyncFunction in a promise

const main = () => {

    const myPromise = new Promise((resolve, reject) => {
        myAsyncFunction("parameter", (result, error) => {
            if (!error) { //or other equivalent check
                resolve(result);
            } else {
                reject(error);
            }
        });
    });

    myPromise
    .then((result) => { /* handle result here */ })
    .catch((error) => { /* handle error here */ })
};

Promises can be chained, so now we don’t have to use nesting in order to use the result of an asynchronous operation:

const main = () => {

   //...

    myPromise
    .then(myOtherPromise)
    .then(myFinalPromise)
    .catch((error) => { /* handle error here */ })
};

More on Promises and chaining in the official docs.

Async/Await

Once we are already dealing with promises, it is possible to ditch the .then() and .catch() functions from the Promise API and use the async/await syntactic sugar:

const main = async () => {

    const myPromise = new Promise((resolve, reject) => {
        myAsyncFunction("parameter", (result, error) => {
            if (!error) { //or other equivalent check
                resolve(result);
            } else {
                reject(error);
            }
        });
    });

   const result = await myPromise;
};

(Remember to change the main function to be async!)

This way the code can look much more similar to synchronous code

const main = async () => {

   //...
   const result = await myPromise;
   const otherResult = await myOtherPromise;
   
   return result + otherResult;
};

Networking, Snippets, Unix

SSH Multiplexing and Master Mode

When using SSH bastion hosts it is common to set up new connections for many of the use cases discussed in the previous section throughout the day.
Normally we would start a new TCP connection for each one of them. However, open TCP connection are a finite resource on any machine, and each one of them takes some time to set up.

Multiplexing is a feature provided by SSH which alleviates these problems. It allows a single TCP connection to carry multiple SSH sessions. The TCP connection will be established and kept alive for a specific period of time and new SSH sessions will be established over that connection.

It works by creating a “control socket” file which will be used every time we want start a new connection.

We need to pass two command line arguments in order to leverage this feature:

Continue reading
Networking, Snippets, Unix

SSH X11 Forwarding

If you are using SSH between Unix-like operating systems, you can also forward GUI applications over SSH. This is especially useful if your server doesn’t really have a user interface, but you need to check something on the fly with a web browser running on it.

Descarga Navegador Firefox — Rápido, privado y gratis — de Mozilla

This is possible because all Unix-like systems share a common GUI windowing system called X11, which is what provides the basic framework for the desktop environment: drawing and moving windows on the display device and interacting with a mouse and keyboard, etc.

Continue reading
Networking, Snippets, Unix

SSH Tunnelling and Port Forwarding

In the previous section we saw how to make use of a jump host as a proxy to run commands into a remote machine.
Sometimes however, having a shell is not necessary, and the connectivity aspect of having a secure channel to the remote host is way more interesting.
SSH’s port forwarding feature allows us to create a secure channel to the remote host, and then use it to carry any type of traffic back and forth.

Local Port Forwarding

Scenario: you want to reach a specific application (which listens on a specific port) on the remote server.

For example (as in the image) we might want to connect to a PostgreSQL database application that is listening on port 5432 on the remote server, which is in a private subnet.

Continue reading
Networking, Snippets, Unix

Jumping SSH Hosts

Sometimes some servers are unreachable to us due to network topology barriers, which are put in place for security reasons.

A common way to allow developers and sysadmins access is to provide public “bastion” or jump hosts, where one can obtain a shell and then use it to connect to one’s destination.

Looking at the picture above, things may seem pretty simple – but how is authentication handled when more than two parties are involved in a connection?

Continue reading
Networking, Snippets, Unix

SSH Agent

If you are using key pair based authentication with a passphrase for your keys, things can quickly get tedious as you have to input the passphrase every time you want to connect somewhere. If you want to avoid that, you can optionally use another preinstalled tool: ssh-agent.

The ssh-agent is a little helper program that keeps track your identity keys and their passphrases. The agent is consulted by the SSH client during the authentication process instead of the user having to specify a key – and having to type its passphrase all over again.

Adding keys

Simply add your private key file to the agent like this

Continue reading
Networking, Snippets, Unix

SSH Known Hosts

Much like how the authorized_keys file is used to authenticate clients on the server, there is another file in the ~/.ssh folder called known_hosts, which is used to authenticate servers to the client.

Whenever SSH is configured on a new server it always generates a public and private key pair for the server, just like you did for your user in the previous section. Every time you connect to any SSH server, it shows you its public key first, together with a proof that it possesses the corresponding private key. If you do not have its public key yet, then your computer will ask for it and add it into the known_hosts file. 

This way, the client can check that the server is a known one, and not some rogue server trying to pass off as the right one.

That’s why when you connect to a server for the first time, you might get a message like this:

Continue reading
Networking, Snippets, Unix

SSH Authentication methods

There are two ways of authenticating to a server with SSH: user/password based authentication (which is now by many considered outdated and insecure) and key pair based authentication. Let’s start from the legacy one and build up to the modern way of doing things:

Username and Password Based Authentication

In this version, all you will need to to connect to your server is to run

Continue reading
Files, Snippets, Unix

File Limits and how the “Too many open files” error can pop up unexpectedly

I have recently come across a nasty Too many open files error, and noticed the information on the internet about what that might mean or how to solve it doesn’t always paint a clear picture.

Here I try to put together a more complete guide by gathering all the resources I found as I was dealing with the issue myself.

What are file limits and file descriptors?

In Unix-like systems, each process is associated with sets of usage limits, which specify the amount of system
resources they can use. One of those is the limit of open file descriptors (RLIMIT_NOFILE).

But what is a file descriptor?

Continue reading