Translate

lunes, 12 de noviembre de 2018

Programacion Web con AllegroServe

Breve introducción

Típicamente las páginas web son creadas en hipertexto (HTML) que le dicen al navegador como mostrar la pagina, incluyendo donde insertar imágenes y links hacia otras paginas. Por ejemplo una pagina en HTML luce asi:

<html>
   <head>
       <title>Hola</title>
   </head>

   <body>
      <p>Hola mundo!</p>
      <p>Esta es una imagen: <img src="imagen.gif"></p>
      <p>Este es un <a href="another-page.html">link</a> a otra pagina.</p>
   </body>
</html>



El navegador y el server se comunican usando un protocolo llamado Protocolo de Transferencia de Hipertexto (HTTP). Aunque no necesitas saber los detalles del protocolo es necesario entender que consiste de una secuencia de solicitudes iniciadas por el navegador y respondidas por el servidor. Esto es así, el navegador se conecta al servidor web y envía una solicitud que incluye al menos la URL deseada y la versión del protocolo HTML que utiliza el navegador. El navegador puede usar también datos en esta solicitud que le indican al servidor como mostrar el form HTML.

Para contestar una solicitud el servidor envía una respuesta que consta de un conjunto de cabeceras y un cuerpo. Las cabeceras contienen información sobre el cuerpo, como de que tipo de dato se trata (por ejemplo HTML, texto plano o una imagen), y el cuerpo es el dato mismo, que es mostrado por el navegador. El servidor puede enviar también una respuesta de error diciendo al navegador que esta solicitud no puede ser respondida por alguna razón. Una vez que el navegador ha recibido la respuesta completa desde el servidor, no hay comunicación entre ambos hasta la próxima vez que el navegador decide solicitar una pagina al servidor. Esto es lo principal sobre programación web, no hay manera de que el código se ejecute sobre el servidor para afectar lo que el usuario ve en su navegador sin que el navegador envíe una nueva solicitud al servidor. Algunas paginas web, llamadas estáticas, son simples archivos .html almacenados en el servidor web y ejecutadas cuando el navegador las solicita. Por otro lado, las páginas dinámicas consisten en HTML generado cada vez que la pagina es solicitada por el navegador. Por ejemplo una página dinámica puede ser generada a través de una consulta a una base de datos y luego construyendo el HTML que representa el resultado de la consulta. Cuando es generada la respuesta a una consulta, el código del lado del servidor tiene cuatro piezas principales de información. La primera pieza de información es la URL solicitada. Sin embargo, a menudo la URL es usada por el servidor web para determinar que código es responsable de generar la respuesta. Luego, si la URL contiene un signo de pregunta, todo lo que sigue al signo
de pregunta se considera como un "string de consulta", que es ignorada normalmente por el servidor web, excepto que esta disponible para la generación de código de la respuesta. La mayoria de las veces, la cadena de consulta contiene un conjunto de pares clave/valor. Las solicitudes desde el navegador también pueden contener "datos post", que también consisten usualmente en pares clave/valor. Los datos post son comúnmente usados para enviar formularios HTML. Los pares clave/valor suministrados tanto en la cadena de consulta como en los datos post son los llamados: "parámetros de consulta". Luego de haber enviado los string como una secuencia de solicitudes desde el navegador, el código ejecutándose en el servidor puede "configurar una cookie", enviando una cabecera especial en su respuesta al navegador, que contienen datos opacos llamados como "cookie". Después de que una cookie es configurada desde un servidor particular, el navegador enviará la cookie con cada solicitud enviada hacia ese servidor. Al navegador no le interesa los datos de la cookie, este solo regresa de vuelta hacia el servidor ese código a ser interpretado.
Hay 99% de elementos primitivos desde el lado del servidor. El navegador envía una solicitud, el servidor busca algún código para manejar la solicitud y ejecutarla, y el código usa tanto parámetros de consulta como cookies para determinar que hacer.

AllegroServe


AllegroServe esta incluido en la version de Allegro disponible desde Franz. AllegroServe provee un modelo de programación similar en espíritu a los Servlets de Java, tanto sobre archivos individuales como contenidos de un directorio. Cada vez que un navegador solicita una página, AllegroServe analiza la solicitud y observa un objeto, llamado "entidad", que maneja la solicitud. Algunas clases de entidad proveen como parte de AllegroServe información sobre como servir contenido estático. En otros casos, se ejecutara código Lisp arbitrario para generar la respuesta. La función start del paquete net.aserve arranca al servidor. Esta toma un numero de parámetros, pero el único que necesitas pasarle es :port, que especifica el puerto a ser activado. Si utilizas un sistema operativo derivado del Unix, probablemente deberías usar un puerto alto como 2001 en lugar del puerto por defecto para servidores HTTP, 80, debido a que en estos sistemas operativos solo el usuario root puede activar puertos por debajo de 1024. Para ejecutar AllegroServe activando el puerto 80 en Unix, necesitarías iniciar Lisp como root y luego usar los parámetros :setuid y :setgid para cambiar la identidad al iniciar después de abrir el puerto. Puedes arrancar un servidor activando el puerto 2001 de la siguiente forma:

> (use-package :net.aserve)
> (start :port 2001)
#<WSERVER port 2001 @ #x72511c72>

El servidor esta ahora corriendo en tu Lisp. Es posible que obtengas un error que diga algo sobre "port already in use" ("el puerto ya esta siendo utilizado") cuando intentes arrancar el servidor. Esto ocurre cuando el puerto 2001 está siendo utilizado por algún otro servidor en tu máquina. En ese caso, la manera más fácil de resolverlo es usar un puerto distinto, cambiando por otro valor en lugar de 2001 en las URLs usadas en este texto. Puedes continuar interactuando con Lisp a través del terminal debido a que AllegroServe arranca su propio hilo para manejar solicitudes desde navegadores. Esto ocurre, entre otras cosas, para que puedas usar el terminal Lisp para obtener una vista dentro de las entrañas de tu servidor mientras se está ejecutando, lo que permite corregir y testear una parte de manera más fácil que si el servidor fuera una completa caja negra. Asumiendo que estas ejecutando Lisp en la misma máquina que tu navegador, puedes chequear que ese servidor está activo y ejecutándose accediendo a la dirección en tu navegador http://localhost:2001/. En este punto deberías obtener un mensaje de error de página no encontrada en el navegador pues no tienes nada publicado aun. Pero el mensaje de error será desde AllegroServe; este lo mostrará en la parte superior de la página. Por otro lado, si el navegador mostrara un dialogo de error que diga algo como "La conexión fue rechazada cuando intentaba conectarse a localhost:2001", eso ocurre tanto si el servidor no se está ejecutando como así también cuando has iniciado con un puerto diferente del 2001.
Ahora puedes publicar algunos archivos. Supongamos que tienes un archivo hola.html en el directorio c:/servidor/ con el siguiente contenido: (Nota: Si utilizas Windows, el archivo del directorio que es "C:\Servidor\hola.html" se escribe en Lisp como "c:/Servidor/hola.html". Si el contenido se encontrara en la carpeta "Documentos" de un usuario llamado "RenePC" por ejemplo se accedería en Lisp así: "c:/Users/RenePC/Documents/hola.html")

<html>
   <head>
      <title>Hola</title>
   </head>

   <body>
      <p>Hola mundo!</p>
   </body>
</html>

Puedes publicar esto individualmente con la función publish-file:

> (publish-file :path "/hola.html" :file "c:/servidor/hola.html")
#<NET.ASERVE::FILE-ENTITY @ #x725eddea>

El argumento :path es el path que aparecerá en la URL solicitada por el navegador, mientras el argumento :file es el nombre del archivo en el sistema de archivos. Después de evaluar la expresión publish-file, puedes acceder a la dirección http://localhost:2001/hola.html, y debería mostrar una página como esta:



También puedes publicar el directorio entero c:/servidor/ (y todos sus subdirectorios) con la función publish-directory.

> (publish-directory :prefix "/" :destination "c:/Servidor/")
#<NET.ASERVE::DIRECTORY-ENTITY @ #x72625aa2>

En este caso, el argumento :prefix especifica el inicio del path de las URLs que deberían ser manejadas por esta entidad. De esta manera, si el servidor recibe una solicitud para http://localhost:2001/foo/bar.html, el path es c:/foo/bar.html. Este path es luego traducido al nombre reemplazando el prefijo "/" con el lugar de destino (destination) c:/tmp/html/. Así, la URL http://localhost:2001/hola.html será traducido en la solicitud por el archivo c:/servidor/hola.html.

Generando Contenido Dinámico con AllegroServe


Publicar entidades que generen contenido dinámico es casi tan simple como publicar contenido estático. Las funciones publish y publish-prefix son los análogos dinámicos de publish-file y publish-directory. La idea básica de estas dos funciones es que tu publicas una función que sera llamada para generar la respuesta a una solicitud tanto para una URL especifica como para cualquier URL con un prefijo dado. La función sera llamada con dos argumentos: un objeto representando la solicitud y la entidad publicada. La mayoría de las veces no necesitas hacer nada con el objeto entidad excepto para pasarle un par de macros de la que hablaremos en un momento. Por otro lado, usaras el objeto solicitud para obtener información enviada por los parámetros de consulta del navegador incluida en la URL o los datos post usando un formulario HTML.

Para un ejemplo trivial del uso de una función que genera contenido dinámico, se muestra una función que genera una pagina con un numero aleatorio distinto cada vez que es solicitada.

(defun numero-aleatorio (solicitud entidad)
   (with-http-response (solicitud entidad :content-type "text/html")
      (with-http-body (solicitud entidad)
         (format
            (request-reply-stream solicitud)
               "<html>~@
                  <head><title>Numero al azar</title></head>~@
                  <body>~@
                     <p>Numero Aleatorio: ~d</p>~@
                  </body>~@
                </html>~@
               "
               (random 1000)))))

Las macros with-http-response y with-http-body son parte de AllegroServe. El primero inicia el proceso para generar una respuesta HTTP y puede ser usado, como en este caso, para especificar cosas como el tipo de contenido que será devuelto. El with-http-body actualmente envía la cabeceras HTTP y luego ejecuta su cuerpo, que debería contener el código que genera el contenido de la respuesta. Dentro de with-http-response pero antes de whit-http-body, puedes agregar o cambiar las cabeceras HTTP para enviarlas en la respuesta. La función request-reply-stream es también parte de AllegoServe y devuelve el stream que se escribirá como salida para ser enviada al navegador. Como lo muestra esta función, puedes usar solo FORMAT para imprimir el HTML al stream devuelto por request-reply-stream.
En la próxima sección, se mostrara la manera mas conveniente para generarla por programa.
NOTA: El ~@ seguido por una nueva linea le dice a FORMAT que ignore los espacios en blanco después de la nueva linea, lo que te permite indentar tu código en forma amigable sin necesidad de agregar un grupo de espacios al HTML. Los espacios en blanco no significan nada en HTML, y no les interesan al navegador, pero esto hace que el código HTML generado luzca un poco mas amigable a las personas. Ahora estas listo para publicar esta función:

(publish :path "/numero-aleatorio.html" :function 'numero-aleatorio)
#<COMPUTED-ENTITY @ #x7262bab2>

Así como lo hace la función publish-file, el argumento :path especifica el path de la URL de esta función al ser invocada. El argumento :function especifica tanto el nombre como el objeto de la función actual. Usando el nombre de una función, como se muestra aquí, te permite redefinir la función sin tener que volver a publicarla y AllegroServe usará la nueva definición de la función. Después de evaluar la llamada a publish, puedes escribir la dirección en tu navegador http://localhost:2001/numero-aleatorio.html para obtener una página con un numero aleatorio en ella, como muestra la figura.



No hay comentarios:

Publicar un comentario