Página principal
Artículos y trucos
Catálogo de productos
Ejemplos y descargas
Mis libros
Cursos de formación
Investigación y desarrollo
Libros recomendados
Mis páginas favoritas
Acerca del autor
 
En colaboración con Amazon
 
Intuitive Sight

La galleta caduca a la hora del té

Al igual que el "yo" es una simple ilusión psicológica, que asigna una continuidad
a lo que realmente es una sucesión de estados de conciencia (o eso dicen),
una aplicación para Internet no es realmente una aplicación, sino una sucesión de páginas
independientes, que utilizan ingeniosos mecanismos de traspaso de parámetros para dar
también la sensación de continuidad
(tranquilícese, el resto no es tan solemne).

En las tiendas virtuales, en particular, el truco más socorrido consiste en asignar identificadores de conexión para cada "sesión" que inicia un usuario. El servidor que programamos debe buscar en cada petición que recibe un "parámetro" especial y recuperar su valor. Si no encuentra el parámetro, asume que se trata de un individuo que acaba de sentarse frente a su máquina. Entonces crea una nueva sesión, y se las ingenia para que cada nueva petición de página que haga ese mismo usuario vuelva a transmitir el identificador asignado. Por supuesto, este no es el lugar para explicar cómo se logra todo esto.

LAS GALLETAS DE LA SUERTE

De todos modos, la técnica más generalizada entre los programadores con pocas ganas de trabajar se "reduce" a pasar el identificador de sesión en una cookie. Se trata de una técnica posible gracias a la complicidad de los navegadores de Internet. Un servidor, al devolver una página Web a un navegador, puede enviarle también instrucciones en la cabecera de la respuesta para que éste haga unas "anotaciones" especiales en un fichero del ordenador del cliente. Esta anotación contiene un nombre de parámetro, un valor, una fecha de caducidad (sí, tiene que ver con el título del truco) y un destinatario, que casi siempre es la URL del propio servidor. Digo que el navegador es cómplice porque, además de hacerle caso al servidor y crear ese pequeño fichero, cada vez que tiene que pedir algo a la URL del destinatario se ocupa también de enviarle la información de la cookie. Como comprenderá, es un mecanismo estupendo para mantener un identificador de sesión o conexión...

... lo malo es que el usuario puede aguar la fiesta y exigirle a su complaciente navegador que ignore las malignas sugerencias del servidor, y no acepte cookies. Por lo tanto, es una política pésima hacer que el funcionamiento de una aplicación para Internet dependa de las cookies. Afortunadamente, existe una amplia variedad de técnicas para sustituirlas; las principales son la reescritura de URLs y la inclusión de campos ocultos. Tampoco es éste el momento de explicaciones.

Ahora llegamos a lo nuestro: de todos modos, es positivo aprovechar las cookies cuando el usuario no las ha desactivado. Hay que verlas como un recurso que puede que esté y puede que no. Volvamos al ejemplo de la tienda. La primera vez que se conecta un señor, le damos un número de sesión, y mientras esa persona siga navegando, cada nueva página que pida va a estar "infectada" por el identificador asignado. Pero, ¿qué pasaría si el usuario apaga el ordenador y vuelve a encenderlo? Las técnicas de sustitución de cookies no pueden hacer nada en este caso. Sería algo tan complicado o imposible de implementar como la transmigración de almas. Y aquí es donde pueden intervenir las cookies: si el usuario no las ha desactivado, podemos hacer que nos echen un cabo para identificar la conexión "resucitada".

Lo único que nos quedaría es tomar una decisión sin mayor importancia: ¿cuánto tiempo le daremos de vida a la cookie con el número de conexión? Si nadie compartiese su ordenador con otras personas, no había problemas en otorgarle una vida eterna a la galletita de la suerte; claro, los que se ven obligados a compartir ordenador pondrían el grito en el cielo... Lo normal es tener una variable de configuración con el número de minutos de vida de la cookie. La fecha de caducidad puede indicarse durante la creación de la misma:

with Response.Cookies.Add do
begin
  Name := 'CONNID';
  Value := IntToStr(FIDConexion);
  Expires := Now + FMinutos / (24 * 60);  // ¡Aquí hay gato encerrado!
end;

Expires es una propiedad de tipo TDateTime. Recuerde que este tipo representa los tiempos como fracciones de un día. Como un día tiene 24 horas, y cada hora 60 minutos, hay que dividir los minutos por esa cantidad para obtener la fracción del día que representan, y sumárselo al resultado de la función Now, que como sabemos devuelve la hora de...

UNIVERSAL TIME COORDINATES

...vale, acepto que a veces soy un poco idiota. Now devuelve la hora local, que en este caso es la hora nacional. Pero usted no tiene por qué vivir en el mismo huso horario que yo. Por este motivo, las fechas en HTTP suelen especificarse de acuerdo a un sistema de referencia global. Y ese sistema global se llama Universal Time Coordinates, que se puede abreviar como UTC.

Para todos los propósitos prácticos, el sistema UTC utiliza la hora del meridiano de Greenwich (de aquí, lo de "la hora del té"), pero ignorando los desplazamientos provocados por el horario de verano. UTC especifica además, para su representación interna, un "momento cero": el 1 de enero del 1970.

Como comprenderá, si asignamos la fecha de caducidad utilizando Now, estaremos introduciendo un intervalo espurio, y las cookies caducarán antes o después, en dependencia del meridiano local. En España, en el mes de mayo, por ejemplo, sería dos horas después del momento deseado.

¿Cómo obtener la hora actual en el sistema UTC? Es muy fácil, porque el API de Windows nos ofrece la función GetSystemTime. He aquí una versión de Now que puede sernos útil:

function UTCNow: TDateTime;
var
   S: TSystemTime;
begin
   GetSystemTime(S);
   Result := EncodeDate(S.wYear, S.wMonth, S.wDay) +
             EncodeTime(S.wHour, S.wMinute, S.wSecond, S.wMilliseconds);
end;

Como es lógico, hay que modificar el fragmento de código donde se envía la cookie:

with Response.Cookies.Add do
begin
  Name := 'CONNID';
  Value := IntToStr(FIDConexion);
  Expires := UTCNow + FMinutos / (24 * 60);  // ¡Ahora sí!
end;