sábado, 31 de diciembre de 2011

Empezar con MongoDB en Mac OS X en dos minutos

1. Descargar e instalar
Instalar MongoDB en Mac OS X es tan trivial como rápido usando el gestor de paquetes Homebrew:
$ brew update
$ brew install mongodb

2. Crear directorio para los datos
Por defecto MongoDB almacenará los datos en /data/db, pero no crear el directorio, por lo que es necesario hacerlo de manera explícita:
$ sudo mkdir -p /data/db/
$ sudo chown `id -u` /data/db

3. Arrancar y conectarse al servidor
Lo primero es arrancar el servidor en un terminal:
$ mongod

Abrimos otra pestaña del terminal y arrancamos el shell de mongo, que se conecta por defecto a localhost:
$ mongo

Y probamos un par de comandos en MongoDB (guardar un documento y buscar):
> db.foo.save( { a : 1 } )
> db.foo.find()

martes, 27 de diciembre de 2011

Server-Side JavaScript Injection en node.js y NoSQL

Node.js está creciendo de manera exponencial. Su comunidad y el interés que suscita no para de crecer. Como ejemplo, se puede ver que node.js es el segundo proyecto más seguido en GitHub y poco le queda ya para ponerse el número uno.  No hay duda de que la flexibilidad y potencia de esta herramienta son su principal atractivo, pero en estas características se encuentra también el talón de Aquiles de muchas aplicaciones, puesto que pueden exponer importantes vulnerabilidades. Para ilustrar, concienciar y aprender a protegernos, he traducido (de aquella manera) este interesante whitepaper de Bryan Sullivan, del Adobe Secure Software Engineering Team.

Antecedentes: XSS
XSS es un acrónimo conocido en el entorno web. XSS significa Cross-Site Scripting, y es un mecanismo por el que se ejecuta código javascript arbitrario en un navegador que está visitando otro dominio. Esto supone una vulnerabilidad muy importante, por la que los "malos" son capaces de robar sesiones y suplantar identidades en las aplicaciones web afectadas (mediante el robo de información de cookies de sesión del navegador), pueden hacer phishing (introduciendo en el portal afectado pantallas de login que permita captura credenciales), captura de teclado, etc. Según la Open Web Application Security Project (OWASP), los ataques XSS suponen la segunda amenaza más importante en aplicaciones web (justo por debajo de inyecciones de SQL).

La vulnerabilidad es, además, demasiado común, con un porcentaje muy alto de sitios de internet que están afectados. Una de las razones de esta proliferación de la vulnerabilidad a XSS es que resulta muy sencillo introducirla accidentalmente. Pongamos el siguiente ejemplo de código que se utiliza para obtener la cotización de un valor:
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
  if ((xhr.readyState == 4) && (xhr.status == 200)) {
    var informacionValor = eval('(' + xhr.responseText + ')');
    alert('La cotización actual de ' + informacionValor.nombre + 
            ' es $' + informacionValor.cotizacion + '€');      
    }
  }
  function realizarPeticion(ticker) {
    xhr.open("POST","servicio_cotizacion",true);
    xhr.send("{\"ticker\" : \"" + ticker + "\"}");
  }

El código parece bastante directo, pero la llamada a eval está introduciendo una vulnerabilidad potencial. Si un atacante fuese capaz de manipular la respuesta del servicio de cotización, podría inyectar código arbitrario mediante la llamada a eval, que ejecutaría el código en el contexto del navegador de la víctima. El ataque podría en ese momento extraer información de autenticación, cookies de session, manipular el dom para inducir al usuario a introducir datos, activar un keylogger, o utilizar el navegador para realizar peticiones arbitrarias al servidor del dominio como si fuese el usuario, etc.

Un nuevo vector de ataque: Server-Side Javascript Injection

Analicemos ahora un pedazo de código de javascript parecido. Está diseñado para leer peticiones JSON,  con la diferencia de que en este caso se ejecutará en el contexto de un servidor web con node.js.
var http = require('http');
http.createServer(function (request, response) {
  if (request.method === 'POST') {
   
  var data = '';
  request.addListener('data', function(chunk) { 
       data += chunk; });
   
  request.addListener('end', function() {
  var informacionValor = eval("(" + data + ")");
   recuperaCotizacionValor(informacionValor.ticker);
   …
});

La misma línea de código (ejecutar eval sobre los datos recibidos)  es la responsable de la vulnerabilidad frente a inyección de código en estas líneas y en el ejemplo de XSS. Sin embargo, en este caso los efectos son mucho más graves que la pérdida de las cookes de la víctima.

Para ilustradlo, veamos primero un mensaje legítimo, no malicioso. Podría ser como el siguiente:
{"ticker" : "SAN"}

La llamada a eval interpreta la siguiente línea:
({"ticker" : "SAN"})

Nada impide al atacante enviar su propio código javascript para que se evalúe en servidor:
response.end("success")

El código de servidor ejecutaría el comando inyectado y devolvería el texto "success" como el cuerpo de la respuesta HTTP. Sin un atacante envía esta request para probar el sistema y recibe "success" como respuesta, sabrá que el servidor puede ejecutar código javascript arbitrario, y entonces puede proceder a ataques más dañinos.

Denegación de servicio
Un ataque efectivo y sencillo de denegación de servicio se puede ejecutar simplemente enviando el comando:
while(1)

Este ataque hará que el servidor afectado use el 100% de su tiempo de procesador en procesar un bucle infinito. El servidor se colgará y será incapaz de procesar ninguna otra petición hasta que el administrador manualmente reinicie el proceso. Este ataque de DoS es tremendamente asimétrico, puesto que el atacante no tiene que ahogar al servidor con millones de peticiones; una única y diminuta petición hace todo el trabajo.

Otra alternativa de ataque DoS sería sencillamente salir o matar el proceso:
process.exit()
process.kill(process.pid)

Acceso al file system

Otro objetivo potencial de los atacantes es leer los contenidos de ficheros del sistema local. Node.js y muchas bases de natos NoSQL como CouchDB, usa la API CommonJS. El acceso al file system se soporta importando el módulo "fs" (mediante la instrucción require):
var fs = require('fs');

En cualquier momento de la ejecución se pueden agregar módulos, de manera que aunque el script originalmente no incluyese la referencia el módulo, el atacante puede agregar la funcionalidad simplemente incluyendo la instrucción require adecuada junto con el código de ataque.

Los siguientes ataques listarían los contenidos del directorio actual y superior respectivamente:
response.end(require('fs').readdirSync('.').toString())
response.end(require('fs').readdirSync('..').toString())

A partir de aquí, es bastante sencillo reconstruir la estructura completa del sistema de ficheros. Para acceder a los contenidos de un fichero sería tan sencillo como:
response.end(require('fs').readFileSync(filename))

La cosa resulta más grave, puesto que el atacante no solo puede leer los contenidos de los ficheros sino que puede escribir también. El siguiente ataque inserta la cadena "//hackeado!" al principio del fichero actualmente ejecutado (lógicamente, se pueden realizar cosas peores).
var fs = require('fs');
var ficheroActual = process.argv[1];
fs.writeFileSync(ficheroActual, '//hackeado!\n' + fs.readFileSync(ficheroActual));


Por último, señalaré que es posible crear ficheros arbitrarios en el servidor objetivo, incluidos ficheros ejecutables:
require('fs').writeFileSync(nombrefichero,data,'base64');

donde nombrefichero es el nombre del fichero resultante (por ejemplo, "foo.exe") y data sería el contenido del fichero codificado en base-64. El atacante ya solo necesitará ejecutar este fichero, como ilustraré a continuación.

Ejecución de ficheros binarios

Una vez que el atacante puede escribir ficheros binarios en el servidor, necesita ejecutarlos. Una inyección de javascript como la siguiente ilustra como puede hacerlo:
require('child_process').spawn(nombrefichero);

A partir de este punto, los límites del atacante están en su imaginación.

Inyección NoSQL
Las vulnerabilidades de inyección de código javascript no están limitadas solo a llamadas de eval dentro de scripts de node.js. Los motores de bases de datos NoSQL que procesen código javascript  también pueden ser vulnerables. Por ejemplo, MongoDB soporta el uso de funciones de javascript en las query y en operaciones de map/reduce. Puesto que las bases de datos MongoDB (igual que otras NoSQL) no tienen definidos esquemas estrictos, los desarrolladores pueden utilizar código javascript para realizar querys arbitrariamente complejas que consulten estructuras de documentos dispares.

Por ejemplo, supongamos que tenemos una collección de MongoDB que contiene documentos que representan libros, otros que representan peliculas y otros que representan libros. Esta query en javascript seleccionará todos los documentos de cada una de las colecciones que fuesen escritos, rodados o grabados en el año especificado:

function() {
  var anio_busqueda = input_value;

  return this.anioPublicacion == anio_busqueda||
         this.anioRodaje == anio_busqueda||
         this.anioGrabacion == anio_busqueda;
}

Si la aplicación estuviese programada en PHP el código fuente podría parecerse al siguiente:

$query = 'function() { '.
         '  var search_year = \'' . $_GET['anio'] . '\';' .
         '  return this.anioPublicacion == anio_busqueda || ' .
         '         this.anioRodaje == search_year || ' .
         '         this.anioGrabacion == search_year; ' . 
         '}';

$cursor = $collection->find(array('$where' => $query));

Este código utiliza el parámetro "anio" de la petición como el parámetro de búsqueda. Al igual que en la vulnerabilidad tradicional de inyección de SQL, cuando la query se realiza de manera ad-hoc, es decir, concatenando la query directamente con el input del usuario, el código es vulnerable a un ataque de inyección de código de javascript en servidor. Este ejemplo sería un ataque bastante efectivo de denegación de servicio DoS contra el sistema
http://server/app.php?anio=1995';while(1);var%20foo='bar

Inyección NoSQL ciega
Otro posible vector de ataque cuando se utilizan inyecciones SSJS contra bases de datos NoSQL es el uso de injecciones ciegas NoSQL para extraer los contenidos la base de datos. Para demostrar como funciona este ataque continuaré con el ejemplo de MongoDB utilizado anteriormente.

Para ejecutar un ataque de inyección ciego, el ataque necesita determinar la diferencia entre una condición verdadera y otra falsa en el servidor. Esto es trivial con una inyección SSJS, más incluso que con la clásica inyección sql “OR 1=1” :
http://server/app.php?anio=1995';return(true);var%20foo='bar
http://server/app.php?anio=1995';return(false);var%20foo='bar

Si existe alguna diferencia entre las respuestas de estas dos inyecciones, entonces el atacante solo necesita realizar preguntas del tipo cierto/falso, y con un número suficiente de preguntas será capaz de extraer todos los contenidos de la base de datos. Veámoslo.

La primera pregunta que hay que responder es cuantas colecciones existen en la base de datos, o más preciesamente, si hay exactamente una colección en la base de datos, o si hay exactamente dos colecciones, etc:
return(db.getCollectionNames().length == 1);
return(db.getCollectionNames().length == 2);
…

Una vez que el atacante ha establecido cuantas colecciones existen, el siguiente paso será obtener sus nombres. Se chequeará cada nombre de colección en el array, primero para determinar la longitud del nombre y después para el nombre mismo, probando un carácter cada vez:
return(db.getCollectionNames()[0].length == 1);
return(db.getCollectionNames()[0].length == 2);
…
return(db.getCollectionNames()[0][0] == 'a');
return(db.getCollectionNames()[0][0] == 'b');

Una vez que se han extraido los nombres de las colecciones, el siguiente paso consiste en recuperar la colección de datos. De nuevo, lo primero que el atacante necesita hacer es determinar cuantos documentos hay en cada colección. Si el nombre de la primera colección fuese "foo":
return(db.foo.find().length == 1);
return(db.foo.find().length == 2);
…

En un ataque SQL ciego tradicional, el siguiente paso sería determinar la estructura de columnas de cada tabla. Sin embargo, este concepto de de estructura de columnas no tiene sentido en documentos NoSQL, que carecen de un esquema común. Cada documento en una colección puede tener una estructura distinta del resto de documentos. Sin embargo, este hecho no impide la extraccción de los contenidos de la base de datos. El atacante simplemente llamaría al método "tojsononeline" (http://api.mongodb.org/js/1.5.3/symbols/_global_.html#tojsononeline)  para devolver el documento como un string de JSON, calcular su longitud  y extraer un carácter cada vez:
return(tojsononeline(db.foo.find()[0]).length == 1);
return(tojsononeline(db.foo.find()[0]).length == 2);
…
return(tojsononeline(db.foo.find()[0])[0] == 'a');
return(tojsononeline(db.foo.find()[0])[0] == 'b');
…

Al final, con este método se recuperarían todos los contenidos de cada documento de cada colección en la base de datos.

Conclusiones y prevención
En los ejemplos anteriores, se ha visto que la vulnerabilidad de inyección de javascript en servidor se parece más a las técnicas tradicionales de inyección de SQL que a las de cross-site scripting. Las inyecciones de SSJS no necesitan de la ingeniería social de una víctima intermedia de la forma que se hace habitualmente con XSS, sino que el ataque se realiza directamente con peticiones HTTP arbitrarias. Por este motivo los mecanismos de defensa son similares de los de inyecciones de SQL:
  • Evitar generar comandos de javascript "ad-hoc" mediante la concatencación de script con datos de proporcionados por el usuario.
  • Validar los datos del usuario en comandos SSJS con expresiones regulares.
  • Evitar el uso del comando eval. En particular, al parsear JSON, usar alternativas mucho más seguras como JSON.parse.

miércoles, 21 de diciembre de 2011

Step. Una librería de control de flujos de node.js

Una vez que he presentado algunos mecanismos para evitar el infierno de callbacks en node.js, vamos a ver una librería de control de flujos. Se trata de Step (https://github.com/creationix/step, npm:step), una librería de Tim Caswell para el control de flujos en node.js. Step permite gestionar ejecuciones en paralelo y en serie y ayuda en la gestión de errores.

Uso de Step

Para una ejecución en serie, hay que pasar this como el argumento de callback de la función asíncrona o bien devolver un valor si se trata de un código sincrono. El siguiente ejemplo de un código que se lee a sí mismo y se saca por consola en mayúsculas ilustra lo que quiero decir:

Step(
  function meLeo(){
    //Lee el codigo fuente de este fichero
    fs.readFile(__filename, this);
  },
  function pasarAMayusculas(err, text){
    //Ejemplo de código síncrono
    if(err) throw err;
    return text.toUpperCase();
  },
  function mostrar(err, texto){
    if(err) throw err;
    console.log(texto);
  }
)

Siguiendo el estándar de node.js (la convención que mencioné en el artículo anterior), los callbacks tienen siempre como primer argumento el error. Además, si no se definen callbacks en línea, sino que se le pasan en serie a Step, tenemos garantizada la captura de todas las excepciones, con la seguridad que ello conlleva.

El ejemplo anterior realiza tareas en serie. Realizar tareas en paralelo es igual de sencillo:
Step(
    //Cargar dos ficheros en paralelo
    function cargarFicheros(){
      fs.readFile(__filename, this.parallel());
      fs.readFile("/etc/passwd", this.parallel());
    },
    function mostrarContenido(err, codigo, usuarios){
      if(err) throw err;
      console.log(codigo);
      console.log(usuarios);
    }
)

Al usar this.parallel(), Step llevará, de manera transparente, la cuenta del número y orden de los callbacks. Al terminar la ejecución en paralelo, pasará al siguiente callback como parámetros los resultados de las tareas paralelizadas, ajustadas al orden en que fueron invocadas estas tareas.

Estructura de Step
Vamos a revisar como está estructurada esta librería para buscar elementos reseñables.

Step viene con un README detallado, un fichero package.json, un único fichero para la librería principal, y los tests separados en diferentes ficheros que abordan cada uno una característica principal de la libreria.

El soporte del módulo CommonJS  es condicional, lo que permite usar esta librería fuera de Node (por ejemplo, en un navegador):
// Hook into commonJS module systems
if (typeof module !== 'undefined' && "exports" in module) {
  module.exports = Step;
}

La estructura base de la principal función de Step es muy fácil de seguir:
function Step() {
  var steps = Array.prototype.slice.call(arguments),
      pending, counter, results, lock;

  // Define the main callback that's given as `this` to the steps.
  function next() {
    // ...
  }

  // Add a special callback generator `this.parallel()` that groups stuff.
  next.parallel = function () {
    // ...
  };

  // Generates a callback generator for grouped results
  next.group = function () {
    // ...
  };

  // Start the engine an pass nothing to the first step.
  next();
}

La función next se invoca al final de Step y es lo que arranca la ejecución. Esta función también tiene los métodos parallel y group a los que se puede acceder desde el this en los callbacks.

Gestión de la ejecución
El núcleo de la librería es la función next. Veamos cada una de sus partes principales.

Los contadores y los valores de retorno se usan para determinar que hay que ejecutar a continuación. El contador se usa en parallel y group. Estos valores se establecen cuando se llama a next:
  // Define the main callback that's given as `this` to the steps.
  function next() {
    counter = pending = 0;

El array de funciones pasados a Step se ejecutan en orden invocando shift en el array. Si no hay más pasos, si hay errores no gestionados se lanzan y si no la ejecución se completa:
    // Check if there are no steps left
    if (steps.length === 0) {
      // Throw uncaught errors
      if (arguments[0]) {
        throw arguments[0];
      }
      return;
    }

    // Get the next step to execute
    var fn = steps.shift();
    results = [];


Cada "step" se invoca utilizando apply de manera que el this en la función suministrada sea "next":
  // Run the step in a try..catch block so exceptions don't get out of hand.
    try {
      lock = true;
      var result = fn.apply(next, arguments);
    } catch (e) {
      // Pass any exceptions on through the next callback
      next(e);
    }

Los errores se capturan y se pasan al siguiente paso. La variable lock se usa por la funcionalidad de paralelización de tareas y agrupación de resultados. El valor devuelto de la función del paso se guarda. A continuación, el valor devuelto se utiliza para determinar si se ha utilizado una función síncrona y en caso afirmativo se invoca next de nuevo con el resultado:
    if (result !== undefined) {
    if (counter > 0 && pending == 0) {
      // If parallel() was called, and all parallel branches executed
      // syncronously, go on to the next step immediately.
      next.apply(null, results);
    } else if (result !== undefined) {
      // If a syncronous return is used, pass it to the callback
      next(undefined, result);
    }
    lock = false;

Ejecución paralela
El método parallel devuelve una función que recubre los callback para mantener los contadores y ejecutar el siguiente paso. El valor de la variable index queda fijada en cada llamada a parallel() y se utiliza para almacenar los valores devueltos por la funciones que se ejecutan en paralelo en las posición correcta:
// Add a special callback generator `this.parallel()` that groups stuff.
  next.parallel = function () {
    var index = 1 + counter++;
    pending++;

    return function () {
      pending--;
      // Compress the error from any result to the first argument
      if (arguments[0]) {
        results[0] = arguments[0];
      }
      // Send the other results as arguments
      results[index] = arguments[1];
      if (!lock && pending === 0) {
        // When all parallel branches done, call the callback
        next.apply(null, results);
      }
    };

Step.fn
La función Step.fn (sin documentar) crea factorías de función a partir de las llamadas a step. Se usa de esta forma:
var myfn = Step.fn(
  function (name) {
    fs.readFile(name, 'utf8', this);
  },
  function capitalize(err, text) {
    if (err) throw err;
    return text.toUpperCase();
  }
);

var selfText = fs.readFileSync(__filename, 'utf8');

expect('result');
myfn(__filename, function (err, result) {
  fulfill('result');
  if (err) throw err;
  assert.equal(selfText.toUpperCase(), result, "It should work");
});

Si el último argumento utilizado al llamar al conjunto de pasos agrupados en myfn es una función, esta se ejecutará como el último paso.

Últimas notas
Step lleva circulando ya un tiempo y Tim a estado trabajando activamente en ella. Es una librería pequeña que resuelve un problema habitual de control de flujos asíncronos. Según el registro de NPM, unas 45 librerías dependen de Step, y esto es sin duda un buen indicador de la popularidad de esta librería.

martes, 20 de diciembre de 2011

Evitando el infierno de los callbacks en node.js

El año pasado Isaac Schlueter (@izs) publicó en la Oakland Javascript Meetup una interesante presentación (http://joyeur.files.wordpress.com/2010/10/40366684-nodejs-controlling-flow.pdf) sobre el control de flujos en javascript (tanto valdría para servidor como para cliente). Este post está basada en dicha presentación


Hoy vamos a centrarnos en una característica tan potente como, en muchas ocasiones, problemática característica de node.js: la asincronía y como gestionarla  correctamente.

En javascript estamos muy acostumbrados a utilizar EventEmitters para controlar flujos de manera asíncrona. Responder a eventos es un problema ya resuelto (al menos para los "javascripters"). El algo muy similar al manejo de eventos de DOM que llevamos haciendo tantos años, por lo que realmente trasladar esto a javascript de servidor no tiene ningún misterio. Este patrón se puede resumir en  algo.on("evento", hazAlgo).

Un ejemplo, con Socket.IO se utiliza sería:
var io = require('socket.io').listen(80);

io.sockets.on('connection', function (socket) {
  socket.emit('news', { hello: 'world' });
  socket.on('my other event', function (data) {
    console.log(data);
  });
});

Otro mecanismo muy habitual en node.js y también en muchas librerías que se pueden utilizar en clientes es el de los callbacks. De hecho, las quejas más habituales sobre node.js son sobre este aspecto:
Programar en node.js es acabar con un feísimo código anidado e intentado hasta el infinito y más allá solo para conseguir abrir un fichero....
La cosa parece incluso peor: aunque muchos de los objetos de node.js son EvventEmitters (como http server/client), la mayoría de las funciones de bajo nivel necesitan callbacks (DNS, posix API, etc.) . Parece que ir más allá de un "hola mundo" la cosa se vuelve muy complicada.

Pero analicemos un momento por qué resulta en realidad difícil. Llegaremos este pequeño shortlist:
  • Realizar un puñado de tareas en un orden específico
  • Saber cuando se han terminado las tareas
  • Gestionar los fallos
  • Dividir la funcionalidad en partes (para evitar los callbacks anidados indefinidamente). 
Estas tareas se suelen ver salpicadas por un conjunto de errores habituales. A saber:
  • Abandonar las convenciones y la consistencia
  • Poner todos los callbacks anidados en línea
  • Usar librerías de terceros sin comprender adecuadamente lo que pasa por debajo (y con los microframeworks esto no suele ser difícil)
  • Intentar que el código asíncrono parezca asíncrono (aunque aquí podríamos argüir que las promesas son un método legítimo para conseguirlo).

Por lo tanto, para conseguir un control del flujo de javascript con tareas asíncronas, lo mejor es utilizar una librería que resuelva de manera sencilla todas estas dificultades y que a la vez que imponga una convención que prevenga los errores.

Existen tropecientos mil librerías distintas de control de flujo y cada autor cree que la suya es la mejor. Por lo elección de la librería es obvia: vamos a escribir una :)

La vamos a escribir con el objetivo de aprender, manteniendo un nivel de complejidad bajo y evitando los elementos "automágicos". No se trata, por tanto, de escribir la librería de control de flujos ideal.

Convenciones

Vamos a empezar estableciendo unas convenciones (ya sabes, el primer error de la lista fue abandonarlas).

Definamos dos tipos de funciones:

  • Actores: realizan acciones o tareas.
  • Callbacks: reciben los resultados de las acciones.
En esencia, lo que vamos a utilizar aquí es el patrón de continuación. Resulta muy parecido a las fibras, pero siendo más sencillo de implementar.  Además, ya que nodejs funciona de esta manera con las APIs de bajo nivel, resulta muy flexible en este contexto.

Callbacks

  • Hemos dicho que los callbacks reciben los resultados de las acciones. Son simples respondedores.
  • Deben estar siempre preparados para gestionar los errores. Por siempre, quiero decir SIEMPRE, sin excepción alguna. Para asegurarnos de que esto ocurre, por convención siempre se pasará el error como primer argumento.
  • En muchas ocasiones serán funciones anónimas en línea, pero no siempre lo serán.
  • Puede o bien gestionar el error y llamar a otros callbacks modificando los datos, o pasar directamente el error hacia arriba.
Actores

  • Son funciones que hacen las tareas. Su último argumento será el callback (cb) que invocar al terminar.
  • Si ocurre algún error que el actor no sepa gestionar, se lo pasa al callback y termina su ejecución.
  • No debe utilizar "throw" y el valor de retorno se ignora (para el ejemplo esto será cierto, sin embargo, existen librerías que utilizan el valor de retorno para discriminar entre ejecuciones síncronas y asíncronas)
  • Es decir, un "return x" se convertiría en "return cb(null, x);"
  • Y un "throw er" pasaría a ser "return cb(er)"

Es decir, un actor seguiría más o menos estas líneas maestras:

function actor (some, args, cb) {
  // el último argumento es el callback
  // con este mecanismo podemos
  // tener argumentos opcionales
  if (!cb &&
      typeof(args) === "function")
    cb = args, args = []
 
  // hace algo y a continuación:
  
  if (failed)  cb(new Error("failed!"))
  else cb(null, optionalData)
}

Pongamos un caso de uso concreto: un actor que nos diga si una ruta dada se corresponde con un directorio o un symlink (windows users: no confundir con accesos directos ;), http://en.wikipedia.org/wiki/NTFS_symbolic_link):

// devuelve true si el path es un symlink 
// o un directorio.

function isLinkOrDir (path, cb) {
  fs.lstat(path, function (er, s) {
    if (er) 
      return cb(er);
    var linkOrDir = s.isDirectory() || s.isSymbolicLink();
    return cb(null, linkOrDir);
  })
}

Detengámonos un ratito en las líneas anteriores:

Vemos que lo primero es el tratamiento de errores:
    if (er) 
      return cb(er);

Y si el resultado es satisfactorio, entonces se invoca el callback con null en el error y pasando el resultado en el siguiente parámetro:
    return cb(null, linkOrDir);

Una vez disponemos de un actor disponible, veamos como podemos realizar una composición de actores:
// devuelve true si el path es un symlink,
// un directorio y tambien termina en .bak

function isLinkDirBak (path, cb) {
   return isLinkOrDir(path,
     function (er, ld) {
       return cb(er, ld &&
         path.substr(-4) === ".bak")
}) }

En primer lugar se llama al primer actor con
   return isLinkOrDir(path,

Como callback, definimos una función en línea que incluye la lógica propia y exclusiva y que callback cb original
       return cb(er, ld &&
         path.substr(-4) === ".bak")

Y de momento aquí nos quedamos con los actores. Ahora un poco de chícha asíncrona.

asyncMap

Vamos a poner un ejemplo un poco complicado de tareas a realizar de forma asíncrona. Por ejemplo, queremos poder hacer cualquiera de estas tareas:

  • Tenemos una lista de 10 ficheros y necesitamos leer todos ellos y continuar una vez que se haya terminado la lectura.
  • Tenemos una docena de URLs y tenemos que leerlas todas y continuar cuando todas se hayan leido.
  • Tenemos 4 usuarios conectados y tenemos que enviar un mensaje a todos y cada uno ellos y continuar cuando se hayan completado las comunicación de todos los mensajes.


Es decir, todos los ejemplos anteriores encajan en el siguiente patrón:
Tengo una lista de "n" cosas y necesito "hacerAlgo" con cada una de dichas cosas, en paralelo,  y recuperar los resultados cuando se haya completado.
El código que nos permite resolver este requisito podría ser lo siguiente:
function asyncMap (list, fn, cb_) {
  var n = list.length
    , results = []
    , errState = null;
  function cb (er, data) {
    if (errState) 
      return;
    if (er) 
      return cb_(errState = er);
    results.push(data);
    if (-- n === 0)
      return cb_(null, results);
  }
  list.forEach(function (l) {
    fn(l, cb) 
  })
}


Como diría Jack el Destripador: vayamos por partes.

En primer lugar, se almacena el tamaño de la lista y se definen las variables que almacenarán el resultado y el error en caso de que ocurra.
function asyncMap (list, fn, cb_) {
  var n = list.length
    , results = []
    , errState = null;

A continuación, se define el callback interno que se utiliza tras invocar la función recibida fn para cada elemento de la lista. Realiza las siguientes tareas para cada elemento:

  1. Si hay algún error anterior no hace nada, sale de la función.
  2. Si se ha producido un error en la ejecución de la función, lo almacena y sale.
  3. Guarda el resultado.
  4. Si ha terminado invoca el callback original.
  function cb (er, data) {
    if (errState) 
      return;
    if (er) 
      return cb(errState = er);
    results.push(data);
    if (-- n === 0)
      return cb_(null, results);
  }

Y por último se invoca la función para cada elemento:
  list.forEach(function (l) {
    fn(l, cb) 
  })

Un ejemplo de uso de asyncMap creando una función que escriba en varios ficheros un contenido dado podría ser el siguiente:

function escribirFicheros (rutasFicheros, contenido, cb) {
  asyncMap( 
    rutasFicheros
    , function (rutaFichero, cb2) {
         fs.writeFile(rutaFichero, contenido, cb2);
      }
    , cb
  );
}
escribirFicheros([mi,lista,de,rutas,de,ficheros], "¡Hola, mundo!", cb);

En este ejemplo asyncMap es una función Actor, recibiendo el callback como último parámetro. Se puede incluso invocar asyncMap desde otro asyncMap.

Esta implementación está bien si no importa el orden de ejecución, pues se están lanzando todas las funciones del tirón. Pero, ¿cómo hacer si el órden de ejecución es importante?

Es decir, los resultados se están agregando al array de resultados según llegan, pero si ¿cómo hacer para que el array de respuesta tenga los resultados con el mismo índice que cada uno de los valores de entrada, manteniendo la correspondencia uno a uno?


function asyncMap (list, fn, cb_) {
  var n = list.length
    , results = []
    , errState = null;
  function cbGen (i) {
    return function cb (er, data) {
      if (errState) return
      if (er) 
        return cb(errState = er)
      results[i] = data
      if (-- n === 0)
        return cb_(null, results)
    }
  }
  list.forEach(function (l, i) {
    fn(l, cbGen(i))
  })
}

Al iterar sobre los elementos, hemos agregado el indice de iteración i, e invocamos a una nueva función cbGen
 list.forEach(function (l, i) {
    fn(l, cbGen(i))
  })

La nueva función cbGen prepara una función callback cb con el índice i fijado, de manera que cuando cb sea invocada almacenará el resultado en la posición correcta 
  function cbGen (i) {
    return function cb (er, data) {
      if (errState) return
      if (er) 
        return cb(errState = er)
      results[i] = data
      if (-- n === 0)
        return cb_(null, results)
    }
  }

Cadenas
Otro caso de uso es realizar tareas en cadena, en un cierto orden preestablecido.

Como ejemplo podría ser esta secuencia: leer las credenciales de un fichero, leer datos de una base de datos, escribir los datos en un fichero. Si algo  falla, no se continúa.

El código que resolvería esto podría ser: 
function chain(things, cb) {
  var len = things.length;
  (function LOOP (i) {
    if (i >= len) 
       return cb();
    things[i](function (er) {
      if (er) 
        return cb(er);
      LOOP(i + 1);
    })
  })(0)
}

Y ahora paso a paso:

En primer lugar, definimos la función que invocará iterativamente cada una de las cosas a realizar. La invocamos empezando con el índice 0 y nos guardamos la longitud de cosas a hacer:
function chain(things, cb) {
  var len = things.length;
  (function LOOP (i) {
     //...
  })(0)
}

A continuación, dentro de LOOP, lo primero será controlar que no se ha producido un error.
    if (i >= len) 
       return cb();

Después, invocamos la cosa que toque hacer (things[]), pasando un callback para poder continuar con lo siguiente:
    things[i](function (er) {
      if (er) 
        return cb(er);
      LOOP(i + 1);
    })

Sin embargo, el código anterior tiene unos pocos "peros". A saber:
  • Hay que proporcionar un array de funciones, que implica mucho trabajo y resulta tedioso si las funciones deben recibir parámetros: "function (cb){blah(a,b,c,cb)}"
  • Los resultados se desechan lo cual es una pena. 
  • No se pueden hacer ramas en la ejecución
Para realizar las tareas tediosas vamos a preparar unos métodos que nos simplifiquen la vida:

  • convertir un array de  [fn, args] en un actor que no reciba argumentos excepto cb.
  • parecido a  Function#bind pero ajustado a nuestras necesidades concretas
  • presentará las siguientes casos de uso:
    • bindActor(obj, "method", a, b, c) 
    • bindActor(fn, a, b, c) 
    • bindActor(obj, fn, a, b, c)
Como hemos hecho anteriormente, primero el código completo:

function bindActor () {
  var args = Array.prototype.slice.call(arguments) 
    , obj = null
    , fn;
  if (typeof args[0] === "object") {
    obj = args.shift();
    fn = args.shift();
    if (typeof fn === "string")
      fn = obj[ fn ]
  } 
  else 
    fn = args.shift();
  return function (cb) {
    fn.apply(obj, args.concat(cb)); 
  }
}

Analicemos las distintas partes: si el primer argumento resulta ser un objeto, almacenamos la referencia. Si el argumento de función es una cadena, se busca el método en el objeto recibido:
  if (typeof args[0] === "object") {
    obj = args.shift();
    fn = args.shift();
    if (typeof fn === "string")
      fn = obj[ fn ]
  } 


En caso contrario, recuperamos la función
  else 
    fn = args.shift();

A continuación, se devuelve una función anónima que ejecutará la función fn con apply, estableciendo el contexto this de fn al objeto pasado (si existe) y concatenando el callback al final de los argumentos.
  return function (cb) {
    fn.apply(obj, args.concat(cb)); 
  }

Una vez que hemos definido este bindActor volvamos de nuevo sobre el método chain que ejecutaba "cosas" en cadena:
function chain (things, cb) {
  var len = things.length;
  (function LOOP (i) {
    if(i >= len) 
      return cb();
    if(Array.isArray(things[i]))
      things[i] = bindActor.apply(null, things[i]);
    things[i](function (er) {
      if (er) 
        return cb(er);
      LOOP(i + 1)
    })
  })(0)
}

Ahora evaluamos cada "cosa" recibida. Si esa cosa es un array, utilizaremos el recien creado bindActor para sustituir en el array ese array por una función con el contexto y argumentos definido en el array. El array son los argumentos que se pasaran a bindActor:
    if(Array.isArray(things[i]))
      things[i] = bindActor.apply(null, things[i]);

Creando ramas. Lo primero que necesitamos es la posibilidad de anular ramas, para lo que se utilizan argumentos que si son false no ejecutan el elemento:
chain([ hazAlgo && [algo,a,b,c]
       , isFoo && [doFoo, "foo"]
       , subCadena &&
         [chain, [one, two]]
       ], cb)

Para eso introducimos:
  if(!things[i])
    return LOOP(i + 1, len)

dentro de chain
function chain (things, cb) {
  var len = things.length;
  (function LOOP (i) {
    if (i >= len) 
      return cb();
    if (Array.isArray(things[i]))
      things[i] = bindActor.apply(null, things[i]);
    if (!things[i])
      return LOOP(i + 1);
    things[i](function (er) {
      if (er) 
        return cb(er);
      LOOP(i + 1)
    })
  })(0)
}

Ahora vamos a conservar los resultados de la cadena:

  • Vamos a proporcionar un array para almacenar los resultados  
  • El último resultado estará siempre disponible en results[results.length - 1] 
  • Vamos a utilizar chain.first y chain.last como contenedores para referenciar el primer y último resultado hasta un punto dado.
function chain (things, res, cb) {
  if (!cb) cb = res , res = [];
  (function LOOP (i,len) {
    if (i >= len) 
      return cb(null,res)
    if (Array.isArray(things[i]))
      things[i] = bindActor.apply(null,
        things[i].map(function(i){
          return (i===chain.first) ? res[0]
           : (i===chain.last)
             ? res[res.length - 1] : i 
          }
        )
      )
    if (!things[i]) 
      return LOOP(i + 1)
    things[i](function (er, data) {
      res.push(er || data);
      if (er) 
        return cb(er, res);
      LOOP(i + 1)
    });
  })(0, things.length) 
}
chain.first = {};
chain.last = {};


Lo primero es controlar si pasan o no por argumentos el array que almacenará los resultados:
function chain (things, res, cb) {
  if (!cb) cb = res , res = [];

Creamos los contenedores en:
chain.first = {};
chain.last = {};

Usando map y la función anónima del código, cuando un parámetro de la función referencie a chain.first o chain.last se sustituirán en el momento por el valor correcto para su posterior evaluación en bindActor
        things[i].map(function(i){
          return (i===chain.first) ? res[0]
           : (i===chain.last)
             ? res[res.length - 1] : i 
          }
        )


Por tanto, al ejecutar la función, el callback almacenará el error o el resultado en la primera posición libre del array de resultados. Si se produce un error corta la ejecución y si no continúa:
    things[i](function (er, data) {
      res.push(er || data);
      if (er) 
        return cb(er, res);
      LOOP(i + 1)
    });


Un ejemplo de uso no trivial de esta cadena sería el siguiente guión:
  • Leer un conjunto ficheros de un directorio
  • Agregar los resultados
  • Hacer ping a un servicio web con el resultado en un servicio web
  • Guardar la respuesta en un fichero
  • Borrar el conjunto de ficheros
Suponiendo que tuviésemos correctamente definidos los métodos a usar el programa quedaría de la siguiente manera:

function myProgram (cb) {
  var res = [], last = chain.last
    , first = chain.first
  chain
    ( [ [fs, "readdir", "the-directory"]
      , [readFiles, "the-directory", last]
      , [sum, last]
      , [ping, "POST", "example.com", 80
        , "/foo", last]
      , [fs, "writeFile", "result.txt", last]
      , [rmFiles, "./the-directory", first]
      ]
, res , cb )
)

En la cadena que hemos definido, last y first se reemplazarán por el valor del primer resultado y el resultado de la ejecución anterior respectivamente.


lunes, 19 de diciembre de 2011

Tienes un bug o tienes dos problemas


Programar es cometer errores al escribir código. Programar bien es cometer errores al escribir código pero encontrarlos rápidamente y solventarlos. Por eso, tengo una máxima que repito constantemente a la gente de mi equipo. Les hace gracia, sobre todo cuando llevan poco tiempo en el equipo y son novatillos, pero siempre me acaban dando la razón. La máxima dice así:

  • Si tiras un par de líneas de código y fallan, tienes un bug.
  • Si tiras un par de líneas de código y funcionan, tienes dos problemas:
    1. Tienes un bug
    2. Y, además, no te has dado cuenta de que lo tienes.
Cuando la comento con otros desarrolladores, resulta que cuanta más experiencia tienen más de acuerdo están conmigo. Trabajar con esta máxima en la cabeza es fundamental para anticiparse a los problemas.

Y, sí, en efecto, estoy de acuerdo con vosotros en que se trata de una exageración.

No debería haber dicho "un par de líneas", debería haber dicho "una línea" ;)

sábado, 26 de noviembre de 2011

node v0.6.3 publicada. Como actualizar node.js con n

Actualización de 30 de octubre de 2013: este post se publicó en 2011 y las cosas han cambiado desde entonces. En junio de 2012, introdujeron unas modificaciones interesantes sobre el uso de n, como muy bien apunta Carlos Canicio en su comentario.

Ahora, para instalar la última versión estable hay que usar (en función de la instalación ya igual ni te hace falta el sudo):
$ n stable

Para instalar la última versión que probablemente será de número impar y eso significa que es inestable (ahora están en las 0.11.*):
$ n latest

Por ejemplo, si quieres jugar con las opciones de ES6 que tiene V8, deberías instalar una versión inestable y arrancar a node con el flag --harmony.

Otra opción que te puede interesar es arrancar n sin parámetros, ya que con la flechas del teclado puedes navegar por las versiones de node y seleccionar la que te interese.


Ayer publicaron la versión v0.6.3 de node.js. La principal novedad de esta versión es que incluye el gestor de paquetes npm, con lo que ya no hace falta instalarlo directamente.

Para actualizar la versión de node.js, una de las utilidades más sencillas es utilizar el paquete  "n"  (https://github.com/visionmedia/n).

En el portátil (Mac) en el que estoy escribiendo este post tengo instalada una versión anterior:

$ node -v
v0.4.12

Lo primero nos aseguramos de tener "n" instalado con npm.
$ sudo npm install -g n
Password:
/usr/local/bin/n -> /usr/local/lib/node_modules/n/bin/n
n@0.5.4 /usr/local/lib/node_modules/n 


Ahora ya se puede actualizar node. Esta tan sencillo como poner:

$ sudo n latest

Veremos algo como:

######################################################################## 100,0%
Checking for program g++ or c++          : /usr/bin/g++ 
Checking for program cpp                 : /usr/bin/cpp 
Checking for program ar                  : /usr/bin/ar 
Checking for program ranlib              : /usr/bin/ranlib 
Checking for g++                         : ok  
Checking for program gcc or cc           : /usr/bin/gcc 
Checking for gcc                         : ok  
Checking for library dl                  : yes 
Checking for openssl                     : not found 
Checking for function SSL_library_init   : yes 
Checking for header openssl/crypto.h     : yes 
Checking for library util                : yes 
Checking for library rt                  : not found 
Checking for fdatasync(2) with c++       : no 
'configure' finished successfully (0.918s)
Waf: Entering directory `/usr/local/n/node-v0.6.3/out'
DEST_OS: darwin
DEST_CPU: x64

.... (Y un largo etcétera)


Y revisamos de nuevo la versión de node.js para confirmar una correcta actualización

$ node -v
v0.6.3

Sobre n 

El paquete "n" permite la gestión de los binarios de node de manera muy sencilla.

"n" instala node por defecto en /usr/local/n/versions, donde puedes ver que tienes actualmente instalado. Puedes activar versiones anteriores de node si lo deseas. Lo node activados se instalan en el prefijo /usr/local, que si se desea se puede cambiar mediante la variable de entorno N_PREFIX. Para cambiar el comportamiento de n simplemente cambia N_PREFIX con el valor que prefieras.

"n" soporta los siguientes comandos:

   n                           Versiones instaladas
   n latest [config ...]       Instala o activa la ultima release de node 
   n <version> [config ...]    Instalar y/o usar node version <version>
   n use <version> [args ...]  Ejecutar node  <version> con argumentos [args ...] 
   n bin <version>             Informar path de binarios de <version>
   n rm <version ...="">       Eliminar version(es)
   n --latest                  Informar ultima version de node disponible
   n ls                        Informar de versiones disponibles
En los listados se marca con un círculo "ο" la versión activa y en el listado de las disponibles se marca con un asterisco las descargadas.
Por ejemplo, slistado las versiones disponibles con n ls me aparecen las siguientes:
n ls
    0.0.1
    0.0.2
    0.0.3
    0.0.4
    0.0.5
    0.0.6
    0.1.0
    0.1.1
    0.1.2
    0.1.3
    0.1.4
    0.1.5
    0.1.6
    0.1.7
    0.1.8
    0.1.9
    0.1.10
    0.1.11
    0.1.12
    0.1.13
    0.1.14
    0.1.15
    0.1.16
    0.1.17
    0.1.18
    0.1.19
    0.1.20
    0.1.21
    0.1.22
    0.1.23
    0.1.24
    0.1.25
    0.1.26
    0.1.27
    0.1.28
    0.1.29
    0.1.30
    0.1.31
    0.1.32
    0.1.33
    0.1.90
    0.1.91
    0.1.92
    0.1.93
    0.1.94
    0.1.95
    0.1.96
    0.1.97
    0.1.98
    0.1.99
    0.1.100
    0.1.101
    0.1.102
    0.1.103
    0.1.104
    0.2.0
    0.2.1
    0.2.2
    0.2.3
    0.2.4
    0.2.5
    0.2.6
    0.3.0
    0.3.1
    0.3.2
    0.3.3
    0.3.4
    0.3.5
    0.3.6
    0.3.7
    0.3.8
    0.4.0
    0.4.1
  * 0.4.2 
    0.4.3
    0.4.4
    0.4.5
    0.4.6
    0.4.7
    0.4.8
    0.4.9
    0.4.10
    0.4.11
    0.4.12
    0.5.0
    0.5.1
    0.5.2
    0.5.3
    0.5.4
    0.5.5
    0.5.6
    0.5.7
    0.5.8
    0.5.9
    0.5.10
    0.6.0
    0.6.1
  * 0.6.2 
  ο 0.6.3 

miércoles, 23 de noviembre de 2011

Expresiones de función invocadas inmediatamente ( IIFE )

NOTA IMPORTANTE: Este artículo es una traducción semilibre de este otro artículo de Ben Alman (@cowboy) que me resultó muy interesante: http://benalman.com/news/2010/11/immediately-invoked-function-expression/. A Ben le corresponde todo el mérito de la explicación.

Otro elemento a tener en cuenta es que lo que se trata en este artículo es sobre JavaScript, independientemente de que se ejecute en cliente o en servidor.



En caso de que no os hayáis dado cuenta, soy un poco purista de la terminología. Así que, después de escuchar demasiadas veces el popular y, sin embargo, engañoso término de javascript "función anónima autoejecutable", he decidido finalmente organizar mis pensamientos en un artículo.

Además de proporcionar una información muy completa acerca de cómo funciona realmente este patrón, hago una recomendación sobre cómo deberíamos llamarlo.

Por favor, ten en cuenta que este artículo no pretende ser uno del tipo "yo tengo razón y tú estás equivocado". Estoy realmente interesado en ayudar a la gente a comprender conceptos potencialmente complejos, y creo que el uso de una terminología coherente y precisa es una de las cosas más fáciles que la gente puede hacer para facilitar la comprensión.

Entonces, en cualquier caso, ¿de qué va todo esto?
En JavaScript, cada función, cuando se invoca, crea un nuevo contexto de ejecución. Dado que las variables y funciones definidas dentro de una función sólo se pueden acceder desde su interior, pero no desde fuera, ese contexto creado por la invocación de una función proporciona una manera muy fácil de crear "privacidad".

// Debido a que esta función devuelve otra función que tiene acceso
// a la variable "privada" i, la función devuelta tiene unos ciertos "privilegios"

function crearContador() {
  // `i` solo es accesible desde dentro de `crearContador`.
  var i = 0;

  return function() {
    console.log( ++i );
  };
}

// Fíjate en que tanto `contador` como `contador2` tienen 
// cada uno su propio `i` independiente.

var contador = crearContador();
contador(); // logs: 1
contador(); // logs: 2

var contador2 = makeCounter();
contador2(); // logs: 1
contador2(); // logs: 2

i; // ReferenceError: i is not defined (sólo existe dentro de crearContador)
  
En muchos casos, no necesitarás varias "instancias" de lo que sea que devuelva la función crearLoQueSea, pudiendo hacer lo necesario con una sola instancia y en otros casos ni siquiera estarás devolviendo de manera explícita un valor.

El fondo de la cuestión
Veamos: si se define una función como 'function foo(){}' o 'var foo = function(){}' , lo que acabas teniendo es un identificador de una función, que se puede invocar al poner paréntesis () detrás, como 'foo()'.

// Debido a que una funcion solo se puede invocar poniendo () después del nombre
// como foo(), y debido a que foo es solo la referencia a la expresión de una función
// como `function() { /* código */ }`...

var foo = function(){ /* código */ }

// ... no se podría deducir que la función se podría invocar poniendo () inmediatamente
// después de la expresión de la función?

function(){ /* código */ }(); // SyntaxError: Unexpected token (
 
Como puedes ver, esto falla.

Cuando el parser se encuentra con la palabras clave 'function' en el ámbito global o dentro de una función, la considera como la declaración de una función y no como una expresión de función. Si no le indicas al parser explícitamente que debe espirar una expresión, lo interpreta como la declaración de una función sin nombre y lanza una excepción de SyntaxError porque las declaraciones precisan siempre de un nombre.

Una digresión: las funciones, paréntesis, y SyntaxErrors
Curiosamente, si especificas el nombre de una función y pones los paréntesis inmediatamente después de la función, el parser también lanzaría un SyntaxError, pero por una razón diferente.

Mientras que los paréntesis colocados después de una expresión indican que la expresión es una función que se debe invocar, los paréntesis colocados después de una declaración se interpretan como totalmente separados de la declaración y actúan como un operador de agrupación (utilizado como un medio para controlar la precedencia de evaluación).

// Aunque la declaración de la función es sintácticamente válida, es una declaración
// y los parántesis que hay a continuación resultan erróneos porque como operador 
// de agrupación necesitan contener una expresión

function foo(){ /* código */ }(); // SyntaxError: Unexpected token )

// Ahora bien, si introduces una expresión dentro de los paréntesis
// no se lanza ninguna excepción...
// pero la función tampoco se ejecuta, porque esto:

function foo(){ /* código */ }( 1 );

// Es equivalente a lo siguiente, una declaración de función seguida de una 
// expresión completamente independiente.

function foo(){ /* código */ }

( 1 );
  
Expresión de función invocada inmediatamente o Immediately-Invoked Function Expression, IIFE
Afortunadamente, corregir SyntaxError es sencillo. La forma más aceptada para informar al parser que debe esperar una expresión de función es envolverla en paréntesis, porque en JavaScript, los paréntesis no pueden contener declaraciones. En este punto, cuando el parser encuentra la palabra clave 'function' sabe que debe analizarlo como una expresión de función y no una declaración de función.

// Cualquiera de los dos siguientes patrones se pueden usar para invocar
// de forma inmediata una expresión de función, utilizando el contexto 
// de ejecución de la función para crear "privacidad"

(function(){ /* código */ }()); // Crockford recomienda esta
(function(){ /* código */ })(); // Pero esta funciona igual de bien

// Puesto que el punto de las paréntesis o los operadores es forzar
// a desambiguar entre expresiones de funciones y declaraciones de funciones,
// se pueden evitar cuando el parser exprea una expresión (ver la nota más abajo)

var i = function(){ return 10; }();
true && function(){ /* código */ }();
0, function(){ /* código */ }();

// Si no te interesa el valor devuelto, o el hecho de que tu código 
// resulte un poco más ilegible, puedes ahorrarte algún byte prefijando
// la función con un operador unario

!function(){ /* código */ }();
~function(){ /* código */ }();
-function(){ /* código */ }();
+function(){ /* código */ }();

// Esta es otra variación  de @kuvos - No tengo clara las implicaciones
// en rendimiento, si las hubiera, por utilizar la palabra clave 'new', pero funciona.
// http://twitter.com/kuvos/status/18209252090847232

new function(){ /* código */ }
new function(){ /* código */ }() // Solo se necesita paréntesis si se pasan argumentos
  
Una nota importante sobre los paréntesis 
En los casos en los que los paréntesis extra de "desambiguación" que rodean la expresión de la función sean innecesarios (porque el parser ya espera una expresión), sigue siendo una buena idea usarlos cuando se realiza una asignación, por convención.

Dichos paréntesis suelen indicar que la expresión de la función se invocará de inmediato, y que la variable contendrá el resultado de la función, no la propia función. Esto puede ahorrarle a alguien que esté leyendo el código, la molestia de tener que desplazarse hasta el final de lo que podría ser una expresión de función muy larga para ver si finalmente se ha invocado o no.

Como regla general, aunque escribir código ambiguo podría ser técnicamente necesario para mantener el parser JavaScript de lanzar excepciones SyntaxError, escribir código sin ambigüedades es también es bastante bueno para evitar que otros desarrolladores te lanzan a tí excepciones del tipo "WTFError"!

Guardando el estado con closures
Al igual se pueden pasar argumentos a las funciones cuando se invocan mediante su identificador, también se pueden pasar cuando se invoca inmediatamente una expresión de función. Puesto que la función (el closure) referencia los valores pasados, estos quedan "fijados" de modo que una Immediately-Invoked Function Expression se puede utilizar para guardar eficazmente un estado.

Si deseas obtener más información sobre los 'closures', puedes consultar este artículo: http://skilldrick.co.uk/2011/04/closures-explained-with-javascript/

// Esto no funciona como podrías pensar en primera instancia,
// porque el valor de 'i' nunca queda fijado. Lo que ocurre es que
// cuando haces click en cada enlace (después de que se haya ejecutado
// el bucle), el alert informa del total de elementos porque es el valor
// que tiene la variable 'i' en ese punto

var elems = document.getElementsByTagName( 'a' );

for ( var i = 0; i < elems.length; i++ ) {

  elems[ i ].addEventListener( 'click', function(e){
    e.preventDefault();
    alert( ' Soy el link nº ' + i );
  }, 'false' );

}

// Esto SI funciona, porque dentro de cada closure IIFE, el valor de 'i'
// queda fijado como 'indiceFijado'. Tras ejecutarse el bucle, aunque
// el valor de 'i' es el del total de elementos, dentro de la IIFE
// el valor de 'indiceFijado' es el que se le pasase en cuando la expresión
// de función fue ejecutada, con lo que cuando un enlace se hace click
// el valor correcto aparece en el alert

var elems = document.getElementsByTagName( 'a' );

for ( var i = 0; i < elems.length; i++ ) {

  (function( indiceFijado ){

    elems[ i ].addEventListener( 'click', function(e){
      e.preventDefault();
      alert( ' Soy el link nº ' + indiceFijado);
    }, 'false' );

  })( i );

}

// Puedes usar un IIFE como este donde el closure afecta solo a la función
// handler del click y no a la asignación completa del 'addEventListener'.
// En ambos ejemplos la variable queda fijada, pero encuentro el anterior 
// más sencillo de leer

var elems = document.getElementsByTagName( 'a' );

for ( var i = 0; i < elems.length; i++ ) {

  elems[ i ].addEventListener( 'click', (function( lockedInIndex ){
    return function(e){
      e.preventDefault();
      alert( 'Soy el link nº' + lockedInIndex );
    };
  })( i ), 'false' );

}

// En el siguiente ejemplo se combina la variable fijada con el valor final
// de 'i'

var elems = document.getElementsByTagName( 'a' );

for ( var i = 0; i < elems.length; i++ ) {

  (function( indiceFijado ){

    elems[ i ].addEventListener( 'click', function(e){
      e.preventDefault();
      alert( ' Soy el link nº ' + indiceFijado + ' de ' + i);
    }, 'false' );

  })( i );

}
 
Tenga en cuenta que en los dos ejemplos del medio, 'indiceFijado' podría haber sido llamado 'i' sin ningún problema, pero el uso de un identificador diferente, como un argumento de la función hace que el concepto sea mucho más fácil de explicar/entender.

Uno de los efectos secundarios más ventajosos de las expresiones de función invocadas inmediatamente es que, la expresión de la función anónima se invoca de inmediato, sin necesidad de utilizar un identificador, y por tanto el closure puede ser utilizado sin contaminar el ámbito actual.

¿Qué hay de malo en la "función anónima autoejecutable"?
Ya has visto lo mencionado un par de veces, pero en el caso de que no quede claro, yo estoy proponiendo el término "expresión de función inmediatamente ejecutada", y "IIFE" (del inglés “Immediately-Invoked Function Expression”) si te gusta las siglas.

¿Qué es una IIFE? Es una expresión de función que se invoca de inmediato. Como dice su nombre, vamos.
Me gustaría ver a los miembros de la comunidad JavaScript adoptar el término "expresión de función inmediatamente ejecutada" y "IIFE" en sus artículos y sus presentaciones, porque me parece que hace que la comprensión de este concepto un poco más fácil, y porque el término "función anónima autoejecutable" no es nada preciso:

// Esto si es una función "autoejectuable"  Es una función que se ejecuta (o invoca)
// a si misma de forma recursiva:

function foo() { foo(); }

// Esta es una función anónima autoejectable. Debido a que no tiene 
// identificador, debe usar la propiedad `arguments.callee` (que
// especifica la función que se ejecuta actualmente) para ejecutarse a si misma.

var foo = function() { arguments.callee(); };

// Esto "podría" ser una función anónima autoejecutable, pero solo mientras
// el idenfificador 'foo' la referencia. Si cambiases 'foo' a otra cosa tendrías
// una función anónima "que solía auto ejecutarse"

var foo = function() { foo(); };

// Mucha gente llama esto "función anónima autoejecutable" aunque
// no se autoejecuta, porque no se invoca a si misma. Sin embargo,
// sí se invoca inmediatamente.

(function(){ /* código */ }());

// Agregando un identificador a una la expresión de una función (y creando
// por tanto una función de expresión con nombre) puede ser muy util
// en depuración. Sin embargo, una vez nombrada, la función deja de ser
// anónima

(function foo(){ /* code */ }());

// Las IIFEs también pueden ser autoejecutables, aunque probablemente este
// no sea el patrón más útil

(function(){ arguments.callee(); }());
(function foo(){ foo(); }());

// Un último apunte: esto causará un error en BlackBerry 5, debido a que
// dentro de la función con nombre, ese nombre es "undefined". Awesome, huh?

(function foo(){ foo(); }());
  
Espero que estos ejemplos hayan dejado claro por qué el término "autoejecutable" es algo engañoso, ya que la función no se ejecuta a sí misma, a pesar de que la función está siendo ejecutada. Además, "anónimo" es innecesariamente específico, ya que una expresión de función invocada inmediatamente puede ser tanto anónima como con nombre.

Por lo tanto, eso es todo. Esa es mi gran idea.

Dato curioso: puesto que 'arguments.callee' está en desuso en ECMAScript 5 en modo estricto, en realidad es técnicamente imposible crear una "auto-ejecución de la función anónima" en ECMAScript 5 modo estricto.

Una última acotación al margen: El patrón de módulo
Ya que he comentado las expresiones de función, sería negligente si por lo menos no mencionase el patrón de módulo. Si no estás familiarizado con el patrón de módulo en JavaScript, verás que es similar al primer ejemplo, pero devolviendo un objeto en lugar de una función (y que se implementa generalmente como un singleton, como en el siguiente ejemplo).

// Crea una función anónima que se invoca inmediatamente 
// y asigna un valor a una variable. Esto elimina el middleman
// de la función "creaLoQueSea".
// 
// Como se explicó más arriba, aunque los paréntesis no son necesarios
// en esta expresión de función, se usan como convención para 
// ayudar a clarificar que la variable se asigna con el 
// *resultado* de evaluar la función y no con la función misma.

var contador = (function(){
  var i = 0;

  return {
    get: function(){
      return i;
    },
    set: function( val ){
      i = val;
    },
    incrementa: function() {
      return ++i;
    }
  };
  
}());
 
Este enfoque del patrón de módulo no sólo es increíblemente poderoso, pero además es increíblemente sencillo. Con muy poco código, puedes crear espacios de nombre para propiedades y métodos relacionados, organizando módulos de código enteros de una manera que minimice la contaminación del ámbito global y cree privacidad.

Seguir leyendo
Espero que este artículo fuese ilustrativo y que haya respondido a algunas de tus preguntas. Por supuesto, si tienes más dudas que cuando empezaste a leer, te listo unos artículos (en inglés) con los que puedes ampliar tus conocimientos:


Me gustaría dar las gracias a Asen Bozhilov y John David Dalton por contribuir con su asesoramiento técnico, así como a Morgan Nick por sus puntos de vista.


sábado, 19 de noviembre de 2011

Publicada la versión v0.6 de node.js

Hace dos semanas publicaron la versión v0.6 de node.js (de hecho, ahora mismo ya vamos por la v0.6.2). Entre las mejoras que incluye se pueden destacar las siguientes que detallaré a continuación:
  • Balanceo de carga entre múltiples procesos
  • Mejoras en depuración por línea de comando
  • Módulo de compresión zlib
  • Mejor soporte para comunicación entre procesos

Balanceo de carga entre múltiples procesos

Una instancia de node.js corre en un único thread. Para aprovechar de las ventajas de los sistemas de varios núcleos, podemos lanzar un cluster de procesos de node para gestionar mejor la carga.

El módulo cluster permite crear una red de procesos que compartan los puertos del servidor

var cluster = require('cluster');
var http = require('http');
var numCPUs = require('os').cpus().length;

if (cluster.isMaster) {
  console.log("Cpus encontradas:"  + process.pid)
  // Creamos un proceso trabajador por core.
  for (var i = 0; i < numCPUs; i++) {
    cluster.fork();
  }

  cluster.on('death', function(worker) {
    console.log('El trabajador ' + worker.pid + ' ha muerto');
  });
} else {
  // Los procesos trabajadores tienen un servidor http.
  http.Server(function(req, res) {
    res.writeHead(200);
    res.end("Hola mundo desde el proceso " + process.pid+ "\n");
  }).listen(8000);
}

Recargando la página varias veces veo que saca  "Hola mundo desde el proceso 9020" y "Hola mundo desde el proceso 420".


Mejoras en depuración por línea de comando

El motor V8 viene un un potente depurador que se puede acceder mediante el protocolo TCP. Para simplificarnos la vida, node.js trae un cliente para dicho depurador. Para utilizarlo hay que arrancar node.js con el argumento debug. Aparecerá a un prompt en el que se pueden realizar las tareas de depuración. A continuación muestro un ejemplo donde se utilizan los siguiente comandos:

  • cont : continúa hasta el siguiente punto de interrupción
  • next :  ejecuta siguiente línea de comando
  • repl : abre el "repl" del debugger, que nos permite ejecutar en la linea de comandos evaluaciones contra el contexto de la aplicación
  • quit : salir

El ejercicio:

C:\node.js>node debug miscript.js
< debugger listening on port 5858
connecting... ok
break in C:\node.js\miscript.js:2
  1 // miscript.js
  2 x = 5;
  3 setTimeout(function () {
  4   debugger;
debug> cont
< Hola
break in C:\node.js\miscript.js:4
  2 x = 5;
  3 setTimeout(function () {
  4   debugger;
  5   console.log("mundo!");
  6 }, 1000);
debug> next
break in C:\node.js\miscript.js:5
  3 setTimeout(function () {
  4   debugger;
  5   console.log("mundo!");
  6 }, 1000);
  7 console.log("Hola");
debug> repl
Press Ctrl + C to leave debug repl
> x
5
> 2+2
4
debug> next
break in C:\node.js\miscript.js:6
  4   debugger;
  5   console.log("mundo!");
  6 }, 1000);
  7 console.log("Hola");
  8 });
< mundo!
debug> quit

El fichero miscript.js es el siguiente:
// miscript.js
x = 5;
setTimeout(function () {
  debugger;
  console.log("mundo!");
}, 1000);
console.log("Hola");

Es bastante parecido a lo que se puede hacer por línea de comando en el depurador de Chrome (aunque ahora con al utilidad de desarrolladores apenas se usa).

Tienes una referencia a los comandos de depuración aquí: http://nodejs.org/docs/v0.6.0/api/debugger.html#commands_reference

Módulo de compresión zlib

Con este módulo resulta muy sencillo utilizar gzip, tanto al servir páginas como al consumir servicios en modo cliente. Los siguientes código lo ilustran perfectamente:


//ejemplo de un cliente con soporte gzip
//www.apache.org devuelve la pagina con gzip. 34KB de tamaño 9KB de trafico con gzip
var zlib = require('zlib');
var http = require('http');
var fs = require('fs');
var request = http.get({ host: 'www.apache.org', 
                         path: '/',
                         port: 80,
                         headers: { 'Accept-Encoding': 'gzip,deflate' } });
request.on('response', function(response) {
  var output = fs.createWriteStream('www.apache.org.index.html');
  switch (response.headers['content-encoding']) {
    // se puede usar zlib.createUnzip() para gestionar ambos casos
    case 'gzip':
      response.pipe(zlib.createGunzip()).pipe(output);
      break;
    case 'deflate':
      response.pipe(zlib.createInflate()).pipe(output);
      break;
    default:
      response.pipe(output);
      break;
  }
});

Tras ejecutar este script y podremos ver en el fichero "www.apache.org.index.html" el código fuente descargado.

A nivel de servidor es igual de sencillo:


// ejemplo de servidor
// Nota: hacer un gzip en cada petición es muy caro. Sería más 
// eficiente cachear la respuesta comprimida

var zlib = require('zlib');
var http = require('http');
var fs = require('fs');
http.createServer(function(request, response) {
  var raw = fs.createReadStream('index.html');
  var acceptEncoding = request.headers['accept-encoding'];
  if (!acceptEncoding) {
    acceptEncoding = '';
  }

  // Nota: this is not a conformant accept-encoding parser.
  // See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.3
  if (acceptEncoding.match(/\bdeflate\b/)) {
    response.writeHead(200, { 'content-encoding': 'deflate' });
    raw.pipe(zlib.createDeflate()).pipe(response);
  } else if (acceptEncoding.match(/\bgzip\b/)) {
    response.writeHead(200, { 'content-encoding': 'gzip' });
    raw.pipe(zlib.createGzip()).pipe(response);
  } else {
    response.writeHead(200, {});
    raw.pipe(response);
  }
}).listen(1337);


Mejor soporte para comunicación entre procesos

El método fork() es un caso especial de spawn() para crear procesos de node.js. Adicionalmente a los métodos habituales de un proceso hijo (ChildProcess) creados por spawn, el objeto devuelto por fork tiene un canal de comunicaciones con su padre. A través del canal se envian mensajes con child.send(message, [sendHandle]) y los mensajes se reciben mediante el evento "message" en el hijo.

Los dos siguientes scripts lo ilustran perfectamente.
padre.js
var cp = require('child_process');
var hijo = cp.fork(__dirname + '/hijo.js');
hijo.on('message', function(m) {
  console.log('El PADRE recibió el mensaje:', m);
});
hijo.send({ hola: 'mundo' });

hijo.js
process.on('message', function(m) {
  console.log('El HIJO recibió el mensaje:', m);
});
process.send({ foo: 'bar' });

La ejecución queda como sigue:
C:\node.js>node padre.js
El PADRE recibió el mensaje: { foo: 'bar' }
El HIJO recibió el mensaje: { hola: 'mundo' }



sábado, 5 de noviembre de 2011

Herramientas para facilitar el desarrollo con node.js

Hoy estoy comenzando un pequeño ejercicio consistente en programar una pequeña aplicación web en node.js que optimice imágenes en formato png.

La idea es tener un formulario con el que se suba una imagen en formato png y se optimice con un optipng o alguna librería parecida. Si además, podemos ir informando al cliente del avance del proceso de upload y optimización en tiempo real, pues mejor que mejor.

En el ejercicio me gustaría probar las siguientes herramientas:
De momento, os presento unos ejemplos de como se usan las herramientas. En los siguientes posts iré contando los avances que haga en el ejercicio y como iré usando las distintas piezas (con más o menos acierto).

Express.js
Resulta muy cómodo para responder a urls amigables y agregar middleware:


var app = require('express').createServer();

//middleware para logs
app.use(express.logger({ format: ':method :url' }));

app.get('/', function(req, res){
  res.send('Hola mundo');
});

//urls amigables con parámetros
app.get('/usuario/:id', function(req, res){
    res.send('Usuario ' + req.params.id);
});

app.listen(8000);

Jade
Al más puro estilo haml, una plantilla como

!!! 5
html(lang="en")
  head
    title= pageTitle
    script(type='text/javascript')
      if (foo) {
         bar()
      }
  body
    h1 Jade - node template engine
    #container
      - if (youAreUsingJade)
        p You are amazing
      - else
        p Get on it!

pasa a ser

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>Jade</title>
    <script type="text/javascript"> 
      if(foo) {
        bar()
      }
    </script> 
  </head> 
  <body>
    <h1>Jade - node template engine</h1> 
    <div id="container"> 
      <p>You are amazing</p>
    </div> 
  </body>
</html>


Connect-form
Para gestionar  los uploads de ficheros (para una app de express):

  app.post('/', function(req, res, next){
  // connect-form adds the req.form object
  // we can (optionally) define onComplete, passing
  // the exception (if any) fields parsed, and files parsed
  req.form.complete(function(err, fields, files){
    if (err) {
      next(err);
    } else {
      console.log('\nuploaded %s to %s'
      ,  files.image.filename
      , files.image.path);
     res.redirect('back');
    }
  });

  // We can add listeners for several form
  // events such as "progress"
  req.form.on('progress', function(bytesReceived, bytesExpected){
    var percent = (bytesReceived / bytesExpected * 100) | 0;
     process.stdout.write('Uploading: %' + percent + '\r');
  });
});




Socket.io
La gestión de las comunicaciones entre cliente y servidor se realiza en base a eventos.

En el servidor:
var io = require('socket.io').listen(80);
io.sockets.on('connection', function (socket) {
  socket.emit('news', { hello: 'world' });
  socket.on('my other event', function (data) {
    console.log(data);
  });
});

En el cliente:

<script src="/socket.io/socket.io.js"></script>
<script>
  var socket = io.connect('http://localhost');
  socket.on('news', function (data) {
    console.log(data);
    socket.emit('my other event', { my: 'data' });
  });
</script>

miércoles, 2 de noviembre de 2011

Servidor https con node.js

Montar un servidor https con node.js es casi trivial. Realizando dos sencillas tareas, lo tendremos funcionando en muy poco tiempo. La primera y más tediosa (por decir algo) es generar la clave y el certificado. La segunda tarea es crear y arrancar el servidor.

1. Generar el certificado
Esta tarea se hace en tres pasos:

1.a Primero generamos la clave privada:

openssl genrsa -des3 -out localhost.key 1024

Nos pedira una password para la clave que utilizaremos en los siguientes pasos:

Generating RSA private key, 1024 bit long modulus
.....................++++++
.......++++++
e is 65537 (0x10001)
Enter pass phrase for localhost.key:

La clave tiene el siguiente aspecto:

cat localhost.key 
 
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: DES-EDE3-CBC,38E8987484A8E569

WrRipQoSD+Tf8EmIiZHpd0ucL5gP5wFmxPAeUdIvEvBGCobPafPHCGi6u7B8yHtc
fykQK3SO+1BueMBgkha0GGLd8t+aENUtZK9rcrfUg2XUL0a7BvVZD2LU46zChtD7
YJS7UHyc53m5Lj4OX80Xq2wzL5OyHI064cCMwLthkj75onVevyVGniZm8xfV9Ez2
VpnVlu/djODNIoKfOGhhRejKi36sZNaPouwa9ubtaZyW9xzGrsH5M3ufMZpXe1Gf
W9rQ5x9oTyGZE7fbHj4UKRnXf/DpgY0rXcgs77P9J+kQ0oa5NsK/qxPSwveS1Utt
0SD1TTAvAPnb2CUd7NgVkYsfPpD/A/p1b5mL8nQrhO6BNP/ARKG5k7MfDEYmvbyN
OeVaaMxm2gy7tn0Z3XI8oWstnXGV4NfebSyDfsb0aCtaaK5DFRkWgt6Navc2d2bL
71lM2ipEmC2ZpXm5RsAl65Ghw2BW/XWTAwlUpP274c8adOzbSYeG4FwMHAr8tnK9
kYc6BKkGqqh6NDwlOfD2HUo2zy+41GmoNm4l9Onv914Gac2QRgsw/EmetwZxGFtZ
5cyN4YddVJq/L5CVIdLThVym63qgSoGZ4BLtxJAaW2dAq+F9Z31NfxOYOFbIjaK8
sXF5GJhPlUIY2N/1ofdH7XbilgSepD2Dvvld4nmanix19dGRW2cKGgq8Pojmq6lj
kDGPGVMksjfiSulZJsRgrQl+xHOvFUY74fihqzHWIya+6nqE9dN/QwEyffaHrHk3
AHHko5RAHcRjG93fpgCke5zwFPgz4torf+qDBj0/NVJBF951pW8euA==
-----END RSA PRIVATE KEY-----

1.b. Lo siguiente es generar el CSR o (Certificate Signing Request, petición de firma de certificado)

openssl req -new -key localhost.key -out localhost.csr

Rellenamos los datos que se nos van pidiendo:

Enter pass phrase for localhost.key:
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:ES
State or Province Name (full name) [Some-State]:Madrid
Locality Name (eg, city) []:Madrid
Organization Name (eg, company) [Internet Widgits Pty Ltd]:NewCo,s.l.
Organizational Unit Name (eg, section) []:Dpto de IT    
Common Name (eg, YOUR name) []:NewCo
Email Address []:localhost@localhost

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:

El aspecto del csr es parecido:

-----BEGIN CERTIFICATE REQUEST-----
MIIB6DCCAVECAQAwgY0xCzAJBgNVBAYTAkVTMQ8wDQYDVQQIEwZNYWRyaWQxDzAN
BgNVBAcTBk1hZHJpZDANBDHAGA1UEChMKmV3Q28scy5sLjETNAJDA1UECxMKRHB0
byBkZSBJVDEOMAwGA1UEAxMFTmV3Q28xIjAgBgkqhkiG9w0BCQEWE2xvY2FsaG9z
dEBsb2NhbGhvc3QwgZ8wDQYJKoZIhvcNAQE9Jan(gY0AMIGJAoGBALeou1eIQ0x2
NOtVujG0L5Mulo7CZIBPWZvOSZgR46JTtLmH/NTHA9dPM/MO+vuxo5X0X551x/N3
+MDP+GO1JAYkN/mXvW4jRMzM0AEWdd1sODxxCQtizFwj0biTrQ1StceGRbdsW6+2
B4acAfQGvimAfotdSvoO6NianDh7XQGzAgMBAAGgGjAYBgkqhkiG9w0BCQcxCxMJ
Y2hhbGxlbmdlMA0GCSqGSIb3DQEBBQUAA4GBAID43dLdWhSl5ni+jjThllIdkAMB
FLZHityDjo6bRi17pNUWoCWjKPHHHAIDHNg6OvcBiwCk+lrj1uC3/X0R3m4oeKRR
J+CCkP6Bsv804mS6X1c34TDEzR/mulAgQMvAHd2isPyrRdla1QaVgipMyuKhDPyE
c6iNvcf8iJ/ASSBW
-----END CERTIFICATE REQUEST-----

1.c. Y por último generamos el certificado ssl.

openssl x509 -req -days 365 -in localhost.csr -signkey localhost.key -out localhost.crt

Nos pedira la password de la clave

Signature ok
subject=/C=ES/ST=Madrid/L=Madrid/O=NewCo,s.l./OU=Dpto de IT/CN=NewCo/emailAddress=localhost@localhost
Getting Private key
Enter pass phrase for localhost.key:

Y ya tenemos todo listo para arrancar. Lo comprobamos:

ls -l

-rw-r--r--  1 miusuario  staff  952  2 nov 21:40 localhost.crt
-rw-r--r--  1 miusuario  staff  737  2 nov 21:32 localhost.csr
-rw-r--r--  1 miusuario  staff  963  2 nov 21:23 localhost.key

2. Crear y arrancar el servidor

El código no puede ser más sencillo. Creamos el fichero https-server.js con el siguiente código:

var https = require('https');
var fs = require('fs');

var options = {
  key: fs.readFileSync('localhost.key'),
  cert: fs.readFileSync('localhost.crt')
};

https.createServer(options, function (req, res) {
  res.writeHead(200);
  res.end("¡Responidiendo por SSL!\n");
}).listen(8000);

Lo arrancamos con:

node.js https-server.js

Nos pedirá la password de la clave:

Enter PEM pass phrase:

Ahora solo hay que acceder a https://localhost:8000 para ver en acción al servidor.

martes, 1 de noviembre de 2011

Microframeworks en javascript

Cada vez más se están popularizando los llamados "microframeworks" de javascript. Estos microframeworks son pequeñas librerías que habitualmente pesan de menos de 5kb y que están centradas un temas muy concretos.

Uno de las frases que se utilizan para justificar el uso de estos microframeworks es que tan importante es lo que tienen como lo que no tienen. En librerías más tradicionales como jQuery hay mucha funcionalidad adicional que no se usa, código para compatibilidad con navegadores a los que tal vez no se vaya a dar soporte, etc. que hacen que las páginas pesen más, que tarden más en cargar, que tarden más en inicializarse, que se consumen más recursos del navegador, etc. Con los microframeworks solo utilizamos lo que sea imprescindible, pudiendo mejorar la experiencia del usuario.

Thomas Fuchs (autor de script.aculo.us, miembro del grupo que mantiene prototype.js y autor de la nueva y pequeña librería zepto.js) ha publicado recientemente un buscador de microframeworks llamado microjs en http://microjs.com.

Como no es oro todo lo que reluce, y sin ánimo de ser extensivo, voy a escribir algunos pros y contras de los microframeworks:

Pros
  • Los microframeworks suelen ser pequeños, compactos y poco acoplados, funcionando como módulos con los que componer aplicaciones
  • Los microframeworks se focalizan en resolver un problema y en resolverlo bien, eliminando duplicidades de código y piezas que no se usen.
  • Los microframeworks suelen tener un nivel de calidad de código bastante aceptable. Al tener que ser tan pequeños, exigen un esfuerzo a los desarrolladores que tendrán que aprender nuevas y mejores técnicas para poder resolver el objetivo concreto dentro de esos límites de tamaño establecidos.
  • Cada microframework, usado de manera individual resulta bastante sencillo de entender. En poquísimo tiempo se puede estar usando satisfactoriamente un microframework que se acabe de descubrir. Aunque la documentación no sea todo lo buena que quisiéramos, entrando en el código fuente podemos descubrir como funciona.
  • Muchos de estos microframeworks, al no estar acoplados a elementos de los navegadores, pueden usarse indistintamente en cliente y en servidor con node.js.

Contras

  • Las librerías tradicionales como jQuery, Dojo, Motools, etc. tienen una base de usuarios muy amplia y, gracias a ello, una madurez de la que carecen los microframeworks.
  • La compatibilidad con navegadores está garantizada dentro de lo posible. Cuando hay que dar soporte a usuarios con IE6, por ejemplo, esto es un gran activo.
  • La actualización de microframeworks puede convertirse en un problema si tenemos que estar manteniendo una aplicación que utilice muchos, mientras que con las librerías tradicionales, el upgrade de versiones resulta más directo.
  • La documentación está centralizada en un único sitio. Con los microframeworks habrá que revisar la documentación en un sitio por cada microframework, y esta documentación puede tener una calidad dispar. De cara a la mantenibilidad, esto puede impactar negativamente.
  • Si se empiezan a utilizar demasiados microframeworks a la vez, tal vez el peso conjunto supere al de una de estas librerías, perdiendo la ventaja inicial que proporcionaban.

Como siempre ocurre en tecnología, la decisiones sobre si utilizar una y otra aproximación dependerá de muchos factores, pero en mi caso, después de algunas experiencias con webapps para móviles con jquery mobile (24kb min+gzip  más los 31kb correspondientes de jquery), tengo muy claro que le voy a dar una oportunidad seria a los microframeworks.

Como muestra un botón

Hace poco descubrí un "micro" microframework con la funcionalidad de publish/subscribe que me resultó muy interesante.

Se usa de la siguiente manera:

    //suscribirse a un canal
    var handle = subscribe("/some/topic", function(msg){
        console.log(msg);
    });

    //publicar en el canal
    publish("/some/topic", ["first time"]);
    publish("/some/topic", ["second time"]);

    //eliminar la suscripcion
    unsubscribe(handle);

    //la suscripción ya no esta escuchando el canal
    publish("/some/topic", ["message will not be logged"]);


El código fuente, que minimizado y gzip pesa solo 198 bytes, es tan solo esto:

(function(d){var e=d.c_||{};d.publish=function(a,b){for(var c=e[a],f=c?c.length:0;f--;)c[f].apply(d,b||[])};d.subscribe=function(a,b){e[a]||(e[a]=[]);e[a].push(b);return[a,b]};d.unsubscribe=function(a){for(var b=e[a[0]],a=a[1],c=b?b.length:0;c--;)b[c]===a&&b.splice(c,1)}})(this);


El código fuente sin minimizar resulta bastante más inteligible. La librería se llama MinPubSub de Daniel Lamb y la podeis encontrar en https://github.com/daniellmb/MinPubSub .



Republicación automática en node.js

A la hora de desarrollar es muy cómodo que nuestra aplicación se republique automáticamente cada vez que guardemos las modificaciones de un fichero de node.js. Para conseguir esto, lo más cómodo que he visto hasta ahora es utilizar supervisor (https://github.com/isaacs/node-supervisor).

Con supervisor la aplicación se volverá a cargar cada vez que se modifique un fichero .js o cada vez que la aplicación casque.

Para instalar supervisor utilizamos npm con el parámetro -g (global). La idea es tenerlo disponible en todos los desarrollos pero que no vaya integrado en ellos:

sudo npm install supervisor -g

Una vez instalado para que desplegar una app cada vez que modifiquemos solo hay que:

supervisor miapp.js

Así, pongamos que mi app.js tiene el siguiente código:

var http = require('http');
var port = 8080;
http.createServer(function (req, res) {
   res.writeHead(200, {'Content-Type': 'text/plain'});
   res.end('Hello Node.js\n');
}).listen(port);
console.log('Server running at port: ' + port);


En la consola veremos lo siguiente:

DEBUG: Running node-supervisor with 
DEBUG:   program 'miapp.js' 
DEBUG:   --watch '.' 
DEBUG:   --extensions 'node|js' 
DEBUG:   --exec 'node' 
DEBUG: Starting child process with 'node miapp.js' 
DEBUG: Watching directory '/Users/miusuario/Documents/.' for changes.
Server running at port: 8080

Cambiamos el puerto a 8081, guardamos y en la consola vemos:

DEBUG: crashing child 
DEBUG: Starting child process with 'node miapp.js'
Server running at port: 8081


Si accedemos con el navegador a http://localhost:8081 vemos que efectivamente el servidor a cambiado al nuevo puerto.

lunes, 24 de octubre de 2011

node.js con windows. Instalar paquetes sin npm

Hoy tuve la necesidad de hacer un pequeño programilla con node.js. Tenía que hacerlo rapidito y en windows, que era lo que tenía a mano en ese momento. Me descargué la version de node.js ejecutable en windows, la versión unstable v.0.5.10. Hasta ahí todo bien.

Necesitaba utilizar el módulo http-proxy de nodejitsu (https://github.com/nodejitsu/node-http-proxy) y entonces me di cuenta de que no tenía a mano npm para descargarlo. Podía instalarlo, pero necesitaría instalar antes cygwin, etc. etc. etc y no tenía ni tiempo de ganas para probar en ese momento. Afortunadamente, npm no es necesario para instalar librerías (te acostumbras tan rápido a usar npm que a veces ni se te ocurre que se puede trabajar con node sin él), porque manualmente resulta bastante trivial.

El primer paso fue crear la carpeta node_modules donde tenía el fichero que quería ejecutar con node.

El segundo paso, para instalar los el módulo http-proxy, fue descargar la última versión de github, en un fichero llamado "nodejitsu-node-http-proxy-v0.7.3.7-gcdb4524.zip".  Lo descomprimí dentro de la carpeta node_modules, y renombre la carpeta nodejitsu-node-http-proxy-v0.7.3.7-gcdb4524 a http-proxy, que es la referencia que vamos utilizar en los ficheros js ( require("http-proxy") ).

Todavía no estaba listo, porque al no usar npm, no se descargaron las dependencias de http-proxy. Si ejecutamos un fichero con http-proxy, node se quejará de que faltan dichas dependencias. Para localizarlas, consultarmos el fichero node_modules/http-proxy/package.json. En la propiedad "dependencies" vemos que http-proxy depende de colors, optimist y pkginfo.

Repetí la operación para esas tres librerías y ya me pude centrar en desarrollar el código de mi proxy inverso.

La estructura quedó de la siguiente manera

carpeta aplicación
├── fichero.js
└── node_modules
        ├── http-proxy
        ├── pkginfo
        ├── optimist
        └── colors


Pd. Las otras tres librerías están en: