Variables de entorno en Windows Azure

Una de las necesidades que puede tener nuestro servicio en la nube es registrar variables de entorno dentro de nuestras máquinas virtuales. En este post vamos a ver las distintas opciones que tenemos para llevar a cabo esta tarea.

Sección Runtime en ServiceDefinition.csdef

La primera posibilidad a la hora de declarar variables de entorno sería a través de la sección Runtime en el archivo de definición del role.
01<?xml version="1.0" encoding="utf-8"?>
02<ServiceDefinition name="WindowsAzureEnvironmentVariables"xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceDefinition">
03  <WebRole name="WebRole">
04    <Runtime executionContext="elevated">
05      <Environment>
06        <Variable name="EnviromentVariable" value="Hello from environment variable!"/>
07      </Environment>
08    </Runtime>
09    <Sites>
10      <Site name="Web">
11        <Bindings>
12          <Binding name="Endpoint1" endpointName="Endpoint1" />
13        </Bindings>
14      </Site>
15    </Sites>
16    <Endpoints>
17      <InputEndpoint name="Endpoint1" protocol="http" port="80" />
18    </Endpoints>
19    <Imports>
20      <Import moduleName="Diagnostics" />
21    </Imports>
22    <ConfigurationSettings>
23    </ConfigurationSettings>
24  </WebRole>
25  <WorkerRole name="WorkerRole">
26    <Imports>
27      <Import moduleName="Diagnostics" />
28    </Imports>
29  </WorkerRole>
30</ServiceDefinition>
Sin embargo desde la aparición de Full IIS donde nuestro servicio tiene total disponibilidad sobre IIS, e Internet Information Service Hosteable Web Core pasa a un segundo plano, esta opción ya no funciona con propiedad en aquellos roles de tipo Web. El motivo es que esta sección solamente nos permite declarar variables de entorno a nivel de proceso. Para comprobarlo podemos comentar la sección Sites, la cual habilita Full IIS, en el archivo de definición y comprobar de esta forma que podemos recuperar dicha variable a través de la clase Environment sin problemas.
1Environment.GetEnvironmentVariable("EnviromentVariable");
De lo contrario, al intentar realizar la misma acción mostrada en el código anterior, el resultado de la misma será null.
En el caso de los worker role no es necesario tomar ninguna medida adicional, a no ser que utilicemos múltiples procesos para llevar a cabo las tareas.
Nota: Es importante especificar el contexto de ejecución como elevated ya que, de no ser así, el servicio no arrancará (suele aparecer el mensaje de Aborted…).

Clase Environment

La opción más efectiva a la hora de declarar las variables de entorno sigue siendo a través de la claseEnvironment. Dentro de la misma tenemos varios métodos que nos permitirán definir las variables necesarias con distintos ámbitos. Para ello podemos definir una variable cualquiera de la siguiente forma:
1Environment.SetEnvironmentVariable("MyNewVariable","New variable for Windows Azure!");
De esta manera lo que estamos haciendo es declarar la variable llamada MyNewVariable con el valor que le sigue en la declaración pero a nivel de proceso, es decir que si cualquier otro proceso que intentara recuperar dicha variable obtendría como resultado null. Por ello, a través de una sobrecarga en este mismo método podemos especificar cuál es el ámbito que queremos que tenga nuestra variable (funcionalidad todavía no disponible en la sección Runtime de ServiceDefinition.csdef :( ). Generalmente podemos necesitar tres tipos de ámbitos distintos:
01using System;
02using Microsoft.WindowsAzure.ServiceRuntime;
03
04namespace WebRole
05{
06    public class WebRole : RoleEntryPoint
07    {
08        public override bool OnStart()
09        {
10            Environment.SetEnvironmentVariable("MyNewVariable""New variable for Windows Azure!"); //Current Process
11            Environment.SetEnvironmentVariable("MachineVariable""Hello", EnvironmentVariableTarget.Machine); //All processes
12            Environment.SetEnvironmentVariable("UserVariable""Hello", EnvironmentVariableTarget.User); //All user processes
13
14            return base.OnStart();
15        }
16    }
17}
Como podemos ver en el código anterior, dependiendo del target asignado a la hora de la definición de la variable la misma tendrá menor o mayor acceso por parte de los procesos que intenten recuperarlas.

Sección Startup en ServiceDefinition.csdef

Por último una de las necesidades más comunes es la modificación de variables ya existentes como es el caso de Path. Para ello la manera más eficaz sería utilizando el comando REG ADD desde la sección Startup donde podemos lanzar ejecutables. Lo primero que debemos saber es dónde están los valores de las variables de entorno en el registro. Para evitaros la búsqueda, las mismas se encuentran en HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment :) .
En el caso de Path vamos a crear un archivo txt e incluimos el siguiente comando:
1REG ADD "HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment" /v "Path" /t REG_EXPAND_SZ /d "%PATH%;%ProgramFiles%\Microsoft SQL Server\100\Tools\Binn" /f
Con él vamos a conseguir agregar al contenido que ya tenía Path la ruta de Microsoft SQL Server.
Espero que sea de utilidad :D
¡Saludos!

¿Que son las Cookies (HTTP)?


En el post anterior dije medio al pasar que HTTP no tiene estado, esto quiere decir que cada transacción es única, no sigue una secuencia y no hay relación entre ellas, esto es a nivel protocolo, o sea que HTTP no provee mecanismos para mantener la sesión “out-of-the-box”, si bien en la web que conocemos podemos entrar a Facebook, ingresar nuestros datos y seguir “logeados”, una clara demostración de que Facebook mantiene el “estado” porque recuerda que ya nos “loegamos” (estoy muy comillero).

Qué son las cookies o sea las galletitas

Técnicamente una cookie es un fragmento de información que se almacena del lado del cliente. Dentro de los muchos headers (encabezados) que podemos ver en una respuesta HTTP hay uno de ellos que sirven para controlar las cookies, la cosa es asi:
  • Escenario A:
    • El servidor indica el cliente que debe guardar una cookie con un nombre y valor determinado.
    • El cliente la guarda localmente (de manera segura).
    • Con cada nueva petición HTTP el mismo host el cliente envía esa cookie ignorando para qué la necesita el servidor.
  • Escenario B:
    • Desde javascript se genera un cookie (lado del cliente).
    • La información se guarda localmente.
    • Sólamante es visible desde el ámbito que se creó la cookie (idealmente).
    • El servidor desconoce todo esto.

Para mantener el estado usamos cookies

Vamos a concentarnos en el escenario A que es el que permite el funcionamiento del estado sobre HTTP, para eso un dibujito:
image
Sencillo, en cierto momento que hacemos una petición HTTP al servidor nos response con el header set-cookie, el navegador guarda localmente el nombre y valor de esa cookie y lo envía simpre en cada nueva petición al servidor.

Ejemplo real

Vamos a ver con Fiddler qué pasa con las cookies cuando entramos a un sitio cualquiera. Vamor awww.bing.com
image
simplemente escribimos la dirección en la barra del navegador (Firefox en este caso) y vemos que se generan 15 peticiones….vamos a analizarlo.
En la primera petición nos responde 200 OK y ocho headers set-cookie, quién sabe para qué, pero Bing los usa para algo, en el contenido del primer mensaje está el HTML de la página solicitada. Nuestro navegador va a recordar todas estas cookies y enviarlas con cada petición nueva al host.
Vamos a analizar la segunda petición:
image

Y como era de esperar el navegador envía todas las mismas ocho cookies que le indicó el servidor en la respuesta anterior.

Por qué se generaron quince peticiones si hicimos nada más que una?

La respuesta es simple, el HTML de Bing hace referencia a muchos otros recursos también localizados en internet, por ejemplo las imágenes, los archivos de script, las hojas de estilo, etc. el navegador genera todas estas peticiones a medida que analiza el HTML y determina que necesitas los recursos.

Cuando las cosas se “Cachean”

Vamos a ver qué pasa si repetimos la petición original:
image
Si presionamos F5 y recargamos la misma págna vemos en Fiddler que muchos recursos tiene un ícono con forma de disco, esto quiere decir que el navegador los recuperó del disco local y no los volvió a pedir para optimizar tiempos de respuesta, a esto se le llama caché. O sea el navegador recuerda que ya pedimos ese recurso (por la URL) y lo trae del disco para que sea más rápido, esto pasa mucho con imágenes pero puede pasar con todo, si hacemos Ctrl + F5 obligamos a Firefox a que pida todo de nuevo ignorando el caché.

Podemos desde el servidor obligar a que siempre se pida el recurso sin usar el cache?

Sí, podemos, desde el servidor agregamos el header Cache-Control, por ejemplo con el modificador max-age podemos indicar cuál es el máximo tiempo en segundos que el navegador debe mantener un recurso, en el caso de Bing este header estaba presente en la página por defecto, es por ese que el navegador volvió a pedir el recurso y no lo trajo del cache como los siguientes.
image

El estado y las cookies

Todo esto era para explicar cómo se puede mantener el estado sobre HTTP, el funcionamiento más simple es utilizar una cookies para que el navegador la envíe siempre y entonces identificamos que se trata del mismo cliente, sería así:
  • El cliente se conecta
  • El cliente se logea correctamente
  • El servidor le dice que guarde la cookie “session_id” con un valor generado único para ese cliente
  • El navegador guarda la cookie con dicho valor y la envía con cada petición
  • El servidor revisa siempre el valor de esa cookie e identifica el cliente que se acaba de logear
Es un mecanismo sencillo, pero efectivo, vamos a verlo en funcionamiento.
Un ejemplo sencillo de ver es el modo en que ASP.NET mantiene la sesión, la cookie que usa se llama….asp.net_session y se ve más o menos así:
image

Si las cookies son tan importantes para la sesión, cómo las borro?

No se borran, se hacen expirar, se agrega detrás de la información de la cookie la expiración, la forma más simple es poner una fecha en el pasado para que el navegador detecte que le llegó la hora y la borre. Esto se logra agregando detrás del nombre y valor de la cookies el atributo expires, por ejemplo:
Set-Cookie: lastlogin=1254773024; expires=Mon, 02-Nov-2009 20:12:04 GMT
Entonces cuando un sitio nos da la opción de mantenernos logeados para siempre en parte hace que esa (o esas) cookies que nos identifican no expiren nunca.
Modificando el web server para comprobar el uso de las cookies
Vamos a agregar una pequeña lógica para probar que el navegador va a recibir la orden de guardar una cookies y después va a enviarla siempre, finalmente vamos a forzarlo a hacerla expirar y no enviarla más, el código modificado queda así:
image
Simple, en la primera petición se hace set-cookie (con la instrucción de ASP.NET Cookies.Add) y en la tercera obligamos a que la borre, indicando para esa cookie una fecha de expiración en el pasado. Probemos la primera petición y vemos qué dice Fiddler.
image
Joya, vemos que nuestro servidor envía el header esperado, vamos a hacer una petición a otra página en el mismo sitio para comprobar si el navegador envía la cookie.
image
Adicionalmente envíamos la indicación para que expire la cookie, entonces en la siguiente petición no debería ser envíada, vamos a ver….
image
Notable, funcionó, con esto comprobamos el funcionamiento de las cookies y el comportamiento del navegador.
Hasta la próxima.