Node.js

for Hackers

Ulises Gascón

Senior software engineer en GuideSmiths y orgulloso co-organizor de la comunidad Open Source Weekends (OSW)

 

Colaborador activo en la Comunidad Open Source

 

Trabaja como freelance, además de ser profesor en Fictizia.

  • Sin live coding ni ejecución de demos

  • Sin teoría pesada

  • Se adapta el nivel para tod@s (mayoría)

DISCLAIMER

  • Conceptos
  • JS
  • Nodejs
  • Librerías
  • Extras
  • Nuestro Taller!

AGENDA

CONCEPTOS

Conceptos

JS y su historia

JavaScript

Características

  • Imperativo y estructurado
  • Tipado dinámico
  • Objetual
  • Evaluación en tiempo de ejecución
  • Funcional (Funciones de primera clase, closures, etc...)
  • Prototípico (prototype, Funciones  constructoras)
  • Funciones variádicas

  • Funciones como métodos

  • Arrays y la definición literal de objetos

  • Expresiones regulares

  • JSON (JavaScript Object notation)

  • Depende de un entorno de ejecución

Versiones

Versión Fecha Notas
1 Jun'97 Primera versión
2 Jun'98 Alineación con ICO/IEC 16262
3 Dic'99 Builtin enriquecido
4 - Abandonado
5 Dic'09 getters, settter, JSON, etc...
5.1 Jun'11 Alineación con ISO/IEC 16262:2011
6 Jun'15 Módulos, Proxy, Promesas y más...
7 Jun'16 Mejoras generales
8 Jun'17 Async/Await
9 Jun'18 Mejoras generales

Conceptos

Sintaxis

Objetivos

  • Tipos
  • Condicionales
  • Bucles
  • Números, Math y Fechas
  • Cadenas de texto
  • Arrays
  • Objetos
  • Funciones

Resumen

Resumen

Conceptos

Asincronía

Objetivos

  • Callback
  • Promesas
  • Async/Await

Callbacks

getUser(id, function(err, user) {
  getProfileInfo(user, function(err, profile) {
    sendEmailToManager(profile, function(details, err) {
      //...
    })
  })
})

Típico Callback Hell (pre-ES6)

getUser(id, (err, user) => {
  getProfileInfo(user, (err, profile) => {
    sendEmailToManager(profile, (details, err) => {
      //...
    })
  })
})

Típico Callback Hell (ES6)

Promesas

getUser(id)
  .then(getProfileInfo)
  .then(sendEmailToManager)
  .then(details => {
    //...
  })
  .catch(error => {
    //error...
  })
getUser(id, (err, user) => {
  getProfileInfo(user, (err, profile) => {
    sendEmailToManager(profile, (details, err) => {
      //...
    })
  })
})

Async/Await

(async () => {
  try {
    const user = await getUser(id);
    const profile = await getProfileInfo(user);
    const details = await sendEmailToManager(profile);
    //...
  } catch (err) {
    //error...
  }
})
getUser(id, (err, user) => {
  getProfileInfo(user, (err, profile) => {
    sendEmailToManager(profile, (details, err) => {
      //...
    })
  })
})

Resumen

Resumen

Conceptos

ECMA y Browser

Claves

Lo que esta fuera del ECMA:

  • El objeto document 
  • Los eventos document.addEventListener
  • Fetch (Ajax)
  • APIs del navegador (geolocation, etc...)
  • Librerías de terceros
  • Frameworks (Angular, React, etc..)
  • El propio interprete que desarrollan los navegadores
  • Manipulación del DOM/BOM, etc...

Conceptos

Nodejs y su Historia

Node.js

Node.js es un entorno en tiempo de ejecución multiplataforma, de código abierto, para la capa del servidor (pero no limitándose a ello) basado en el lenguaje de programación ECMAScript, asíncrono, con I/O de datos en una arquitectura orientada a eventos y basado en el motor V8 de Google. Fue creado con el enfoque de ser útil en la creación de programas de red altamente escalables, como por ejemplo, servidores web. Fue creado por Ryan Dahl en 2009 y su evolución está apadrinada por la empresa Joyent, que además tiene contratado a Dahl en plantilla - Wikipedia

Filosofía

Event Loop

Puntos Fuertes

  • Asincronía (no bloqueo)
  • Backend completo
  • NPM (comunidad)
  • Single thread (paralelismo)
  • Librerías propias
  • Utilidades
  • Código abierto
  • Basado en el V8 (escrito en C++) de Google
  • Multiplataforma
  • Orientado a Eventos
  • No se limita solo a servidores HTTP

Conceptos

CORE de Nodejs

Librerías

  • Assertion Testing
  • Async Hooks
  • Buffer
  • C++ Addons
  • C/C++ Addons - N-API
  • Child Processes
  • Cluster
  • Command Line Options
  • Console
  • Crypto
  • Debugger
  • DNS
  • Domain
  • ECMAScript Modules
  • Errors
  • Events
  • File System
  • Globals
  • HTTP
  • HTTP/2
  • HTTPS
  • Inspector
  • Internationalization
  • Modules
  • Net
  • OS
  • Path

Librerías

  • Policies
  • Process
  • Punycode
  • Query Strings
  • Readline
  • REPL
  • Report
  • Stream
  • String Decoder
  • Timers
  • TLS/SSL
  • Trace Events
  • TTY
  • UDP/Datagram
  • URL
  • Utilities
  • V8
  • VM
  • Worker Threads
  • Zlib

HTTP

//import http from 'http';
const http = require('http');

const port = 3000;
const ip = "127.0.0.1";
const msg = 'Hola a todos, ahora usando HTTP\n';


http.createServer((req, res) => {
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end(msg);
}).listen(puerto, direccion);
console.log(`Server running at http://${ip}:${port}/`);

HTTP

// import http from 'http';
const https = require('https');
const url = "hackmadrid.org/";
const tiempo = 3500;

setInterval(() => {
  https.get({
    host: url
  }, res => {
    if (res.statusCode === 200) {
      console.log(`Sin errores en ${url}`);
    } else {
      console.log(`Respuesta Http ${res.statusCode} en ${url}`);
    }
  }).on('error', e => {
    console.log(`Con errores -> La respuesta de ${url} es ${e.message}`);
  });
}, tiempo);

URL

const url = require('url');

const demoURL = "http://localhost:3000/ruta?parametro=dato#detalle";

console.log(`El host: ${url.parse(demoURL).hostname}
El puerto: ${url.parse(demoURL).port}
La ruta: ${url.parse(demoURL).pathname}
La parametro: ${url.parse(demoURL).query}
El hash(#): ${url.parse(demoURL).hash}`);

File System

//import fs from 'fs';
const fs = require('fs');

fs.readFile('archivo.txt', 'utf8', (err, data) => {
    if (!err) {
      console.log(data);
    } else {
      throw err;
    }
});

File System

//import fs from 'fs';
const fs = require('fs');

const data = "y esto... se guardará en el archivo.txt";
fs.writeFile('archivo.txt', data, err => {
  if (!err) {
    console.log('Datos guardados en el archivo.txt');
  } else {
    throw err;
  }
});

Events

//import eventos from 'events';
const eventos = require('events');

const EmisorEventos = eventos.EventEmitter;
const ee = new EmisorEventos();

ee.on('datos', fecha => {
   console.log(fecha);
});

setInterval(() => {
   ee.emit('datos', Date.now());
}, 500);

Process

console.log(`
===================================
- Id: ${process.pid}
- Título: ${process.title}
- Ruta: ${process.execPath}
- Directorio Actual: ${process.cwd()}
- Node Versión: ${process.version}
- Plataforma: ${process.platform}
- Arquitectura: ${process.arch}
- Tiempo activo: ${process.uptime()}
- Argumentos: ${JSON.stringify(process.argv)}
===================================`);

Buffer

const buf1 = Buffer.alloc(5);
console.log(buf1); // <Buffer 00 00 00 00 00>

const buf2 = Buffer.from('HackMadrid');
console.log(buf2) // <Buffer 48 61 63 6b 4d 61 64 72 69 64>
console.log("buf2:", buf2.toString()); // HackMadrid

Streams

Streams

a.pipe(b).pipe(c).pipe(d)

// Es equivalente a:
a.pipe(b);
b.pipe(c);
c.pipe(d);

// En Linux, es equivalente a:
$ a | b | c | d

Streams

Streams

// readable.pipe(writable)

readable.on('data', (chunk) => {
  writable.write(chunk);
});

readable.on('end', () => {
  writable.end();
});

Streams

const fs = require('fs');

const read = fs.createReadStream('file.txt');
const write = fs.createWriteStream('file2.txt');

read.pipe(write);

Streams

const http = require('http'),
    fs = require('fs');

http.createServer((req, res) => {
  const song = 'song.mp3';
  const stat = fs.statSync(song);

  res.writeHead(200, {
    'Content-Type': 'audio/mpeg',
    'Content-Length': stat.size
  });

  const readableStream = fs.createReadStream(song);
  readableStream.pipe(res);

}).listen(process.env.PORT);

Child Process

Child Process

Child Process

const  {exec} = require('child_process');

// cat solo funciona en UNIX
exec('cat texto.txt', (err, stdout, stderr) => {
  if(!err){  
      console.log('El contenido:', stdout)
  } else {
      console.log('Error: '+err)
  }
})

Como parte de Nodejs

cat texto.txt

Sin Nodejs (Terminal)

Child Process

const {spawn} = require('child_process'),
  ping = spawn('ping', ['hackmadrid.org']);

ping.stdout.setEncoding('utf8');
ping.stdout.on('data', console.log);

Como parte de Nodejs

ping hackmadrid.org

Sin Nodejs (Terminal)

Ejecutables

#!/usr/bin/env node
console.log('Soy un script!');
chmod +x mi_escript_archivo.js
./mi_escript_archivo.js <parámetro>

Añadir el shebang

Hacer el script ejecutable

Ejecutando el script

Resumen

Conceptos

NPM y el ecosistema

Ecosistema

Más de 1 millón de paquetes disponibles!

Comandos

$ npm init
$ npm install cool-module
$ npm uninstall cool-module
$ npm install --global cowsay && cowsay hackmadrid!
$ npx cowsay hackMadrid!

package.json

{
  "name": "my_package",
  "description": "",
  "version": "1.0.0",
  "main": "index.js",
  "dependencies": {
    "express": "^4.17.1"
  },
  "devDependencies": {
    "standard": "12.0.1"
  },
  "scripts": {
    "start": "node server.js"
  },
  "repository": {
    "type": "git",
    "url": "https://github.com/ulisesGascon/my_package.git"
  },
  "keywords": [],
  "author": "Ulises Gascón",
  "license": "AGPL-3.0",
}

LIBRERIAS

Librerías

Mundo HTTP

Librerías

Express

const express = require('express')
const app = express()
const port = 3000

app.get('/', function (req, res) {
    res.send('Hello World!')
})

app.listen(port, function () {
    console.log(`Example app listening on port ${port}!`)
})

GOT

const got = require('got');
 
(async () => {
    try {
        const response = await got('hackmadrid.org');
        console.log(response.body);
        //=> '<!DOCTYPE HTML> ...'
    } catch (error) {
        console.log(error.response.body);
        //=> 'Internal server error ...'
    }
})();

Librerías

Node en escritorio

Librerías

Electron

# Clone this repository
git clone https://github.com/electron/electron-quick-start
# Go into the repository
cd electron-quick-start
# Install dependencies
npm install
# Run the app
npm start

Robotjs

// Move the mouse across the screen as a sine wave.
var robot = require("robotjs");

// Speed up the mouse.
robot.setMouseDelay(2);

var twoPI = Math.PI * 2.0;
var screenSize = robot.getScreenSize();
var height = (screenSize.height / 2) - 10;
var width = screenSize.width;

for (var x = 0; x < width; x++)
{
	y = height * Math.sin((twoPI * x) / width) + height;
	robot.moveMouse(x, y);
}

Robotjs

Librerías

Node en Mobile

Librerías

Librerías

CLI

Librerías

SurvAPP

SurvAPP

Librerías

Scraping

Librerías

Puppeteer

const puppeteer = require('puppeteer')
try {
    const browser = await puppeteer.launch()
    const page = await browser.newPage()
    await page.goto('https://booking.com')
    await page.type('#ss', 'Berlin')
    await page.click('.sb-searchbox__button')
    await page.waitForSelector('#hotellist_inner')
    const hotels = await page.evaluate(() => {
        const anchors = Array.from(document.querySelectorAll('span.sr-hotel__name'))
        return anchors.map(anchor => anchor.textContent.trim()).slice(0, 10)
    })
    console.log(hotels)
    await browser.close()
} catch (err) {
    console.error(err)
}

T3chfest'17

Librerías

Hardware e IOT

Librerías

PigPio

// Use PWM to pulse the LED connected to GPIO17 
// from fully off to fully on continuously.

const Gpio = require('pigpio').Gpio;
const led = new Gpio(17, {mode: Gpio.OUTPUT});
let dutyCycle = 0;

setInterval(() => {
  led.pwmWrite(dutyCycle);

  dutyCycle += 5;
  if (dutyCycle > 255) {
    dutyCycle = 0;
  }
}, 20);

PigPio

Johnny-five

const five = require("johnny-five");
const board = new five.Board();

board.on("ready", () => {
  // Create an Led on pin 13
  const led = new five.Led(13);
  // Blink every half second
  led.blink(500);
});

EXTRAS

OWASP Projects

El maravilloso mundo de los metadatos

NUESTRO TALLER

¿Cómo me preaparo?

  • Aprende y juega
  • Familiarizate con la sintaxis de JavaScript
  • Olvídate del browser y centrate en Node
  • Juega con paquetes de NPM
  • Ponte cómodo con los módulos de Nodejs (import, export...)
  • No te agobies por la asincronía
  • Centrate en promesas (resolve, reject, catch, then...)

Requisitos Previos

Tener Nodejs (8 o superior), NPM (5 o más) y Docker

¿Qué haremos?

  • Checksum
  • Hasheado
  • PGP
  • Info personal (IP, Mac, sistema, puertos, etc..)
  • Child Process
  • Proxy y TOR
  • Aplicaciones de escritorio con Electron
  • Scraping con JSDom y Pupetter
  • Ingeniería Inversa (Batimagen, node-zowi)

PREGUNTAS

¡Gracias!

Los sueños son sumamente importantes. Nada se hace sin que antes se imagine.  

- George Lucas