Internet se basa en el protocolo TCP/IP
La world wide web se basa en el protocolo HTTP (Hypertext Transfer Protocol), un protocolo cliente/servidor que funciona por encima del protocolo TCP/IP
El funcionamiento básico de HTTP es el siguiente:
Las respuestas del servidor se envían en formato MIME (Multipurpose Internet Mail Extensions). Este protocolo define las cabeceras y los contenidos de varios tipos de mensajes que pueden enviarse por correo electrónico
El tipo básico de mensaje es texto sin más, el cual se denota como text/plain.
Un posible mensaje de saludo en formato MIME sería:
content-type: text/plain
Hola en texto
Un tipo de contenido muy habitual en la Web es texto en formato HTML, el cual se denota como text/html.
Un posible mensaje de saludo sería:
content-type: text/html
<html>
<head><title>Saludo</title>
<body>
<h1>Página de saludo</h1>
<p> Hola en HTML </p>
</body>
En una operación cliente/servidor habitual, el cliente solicita una dirección URL al servidor y éste devuelve el contenido. Dicho contenido suele ser un texto en formato HTML
Mediante el protocolo CGI ó Common Gateway Interface es posible crear contenido dinámico
en Internet. Cuando el servidor reconoce que una dirección es un CGI, en lugar de
devolver su contenido, ejecuta dicho CGI y devuelve al cliente el resultado en formato MIME
(habitualmente text/html).
Para que el servidor reconozca que la dirección es un programa CGI, existen diversas técnicas
dependiendo de la configuración del servidor.
En algunos servidores, los CGI's se identifican por la extensión .cgi.
En otras ocasiones, los programas CGI's residen en un directorio determinado (normalmente /cgi-bin).
Para implementar un programa ejecutable en Haskell, es necesario definir
una función main::IO() que debe exportarse. Para ello, se define la
siguiente línea
> module Main(main) where
> import System (getEnv) -- Para usar la función (getEnv)
>
> main :: IO ()
> -- main = ... programa que corresponda
> mainHola =
> do { putStrLn "content-type: text/plain"
> ; putStrLn ""
> ; putStrLn "Hola"
> }
Para que el programa anterior pueda ejecutarse como un CGI existen 2 posibilidades
#! runhugs
Con dicha línea, ciertos cervidores reconocen que deben
llamar al programa "runhugs" pasándole como entrada el propio fichero.
Para que esta opción funcione, es necesario que el sistema pueda ejecutar el programa runhugs,
lo cual puede conseguirse cuando la variable PATH incluye la ruta de dicho programa
Un truco habitual cuando no se dispone de un servidor Web en el cual instalar los programas CGI's es hacer que nuestro propio ordenador sea el servidor. Para ello, se debe instalar el software que implemente el protocolo HTTP servidor en nuestro ordenador. Entre las múltiples opciones, Apache da buenos resultados en Linux, mientras Xitami hace lo propio en Windows.
Una vez que nuestro ordenador tiene el software servidor funcionando, la dirección 127.0.0.1
permite hacer referencia a la dirección IP de nuestro propio ordenador. De esta forma, no es necesario
tener activada una conexión Internet al exterior para poder practicar a realizar programas CGI.
Habitualmente, los programas CGI devuelven código HTML que corresponde con una página Web
> mainHtml =
> do { putHeaderHtml
> ; putStrLn "<html><head><title>Saludo</title>"
> ; putStrLn "<body><h1>Página de saludo</h1>"
> ; putStrLn "<p> Hola en HTML </p></body></html>"
> }
>
> putHeaderHtml =
> do { putStrLn "content-type: text/html"
> ; putStrLn ""
> }
El formato HTML consiste en una serie de marcas predefinidas. Algunas de estas marcas son:
html: Delimita el documento HTMLhead: Delimita la cabecera del documentotitle: Indica el título de la página webbody: Delimita los contenidos del documento h1: Indica un encabezado de nivel 1h2: Indica un encabezado de nivel 2h3: Indica un encabezado de nivel 3p: Indica un párrafoul: Delimita una lista de ítems sin numerarol: Delimita una lista de ítems numeradosli: Indica un ítem de una listaa: Indica un enlace. form: Indica un formulario para la petición de informacióntable: Delimita la creación de una tablaimg: Indica una imagenLa comunicación entre el servidor y el programa CGI se realiza mediante una serie de variables de entorno. Las principales son:
SERVER_SOFTWARE: Nombre y versión del software del servidor que responde la petición.
Formato nombre/versión
SERVER_NAME: Nombre del servidorGATEWAY_INTERFACE: versión de la especificación CGI con el formato CGI/versión
SERVER_PROTOCOL: Nombre y revisión del protocolo con el formato protocolo/versión
SERVER_PORT: Número de puerto al que se ha enviado la peticiónREQUEST_METHOD: Método con el que se realiza la petición, en HTTP suele ser GET,
POST, PUT, etc.PATH_INFO: Información extra de la ruta para acceder al escritoPATH_TRANSLATED: Traducción realizada por el servidor de PATH_INFO
SCRIPT_NAME: Ruta virtual al escrito que se va a ejecutar.QUERY_STRING: Información que sigue al signo ? en la URL. Esta es la información
de la pregunta.REMOTE_HOST: Nombre del host que realiza la peticiónREMOTE_ADDR: Dirección IP del host que realiza la peticiónAUTH_TYPE: Si se soporta algún método de autentificación para validar al usuario,
esta variable indica de qué método se trata. REMOTE_USER: Si se soporta algún método de validación de usuarios, indica el nombre del usuarioREMOTE_IDENT: Identificación del usuario.CONTENT_TYPE: Para preguntas con información adjunta, tales como HTTP PUT, indica
el tipo del contenidoCONTENT_LENGTH: La longitud del contenidoHTTP_ACCEPT: Tipos de MIME que puede aceptar el clienteHTTP_USER_AGENT: Visualizador que está usando el cliente con el formato software/versión
> mainVars =
> do { putHeaderHtml
> ; putStrLn "<html><head><title>Vars</title>"
> ; putStrLn "<body><h1>Variables de Entorno</h1>"
> ; putVarsHtml
> ; putStrLn "</body>"
> }
>
> putVarsHtml =
> do { putStrLn "<ul>"
> ; vs <- cgiEnv
> ; putStrLn $ concat ["<li>" ++ v ++ " = " ++ n ++ "</li>\n" | (v,n) <- vs]
> ; putStrLn "</ul>"
> }
>
> cgiEnv :: IO [(String,String)]
> cgiEnv = do { cgiValues <- mapM getEnvSafe cgiVars
> ; return (zip cgiVars cgiValues)
> }
>
> cgiVars = [ "SERVER_SOFTWARE", "SERVER_NAME", "SERVER_PROTOCOL", "SERVER_PORT"
> , "REQUEST_METHOD", "PATH_INFO", "PATH_TRANSLATED", "SCRIPT_NAME"
> , "QUERY_STRING", "REMOTE_HOST", "REMOTE_ADDR", "AUTH_TYPE"
> , "REMOTE_USER", "CONTENT_TYPE", "CONTENT_LENGTH"
> , "HTTP_ACCEPT", "HTTP_USER_AGENT" ]
>
En la definición anterior se utiliza la función getEnvSafe
que actúa de forma similar a la función getEnv
de la librería System. La única diferencia es que cuando no
encuentra el valor de una variable, en lugar de lanzar una excepción, devuelve la cadena vacía
""
> getEnvSafe :: String -> IO String
> getEnvSafe var = catch (getEnv var) (\e -> return "")
Una aplicación habitual de los programas CGI es la búsqueda de información en una base de datos. Este tipo de aplicaciones solicitan una información al usuario y cuando el usuario introduce dicha información enlazan con otro programa que muestra el resultado
Una técnica habitual es que la acción consista en ejecutar el mismo programa CGI
La función getUrl permite obtener la dirección del CGI actual
> getUrl :: IO String
> getUrl = do { host <- getEnv "SERVER_NAME"
> ; script <- getEnv "SCRIPT_NAME"
> ; return $ "http://" ++ host ++ script
> }
Existen dos métodos para realizar programas interactivos que contengan formularios.
El método GET, que es el utilizado por defecto, añade el carácter ? a la dirección
URL especificada en el atributo action y adjunta los valores de los campos codificados
con el formato application/x-www-form-urlencoded.
Dicha codificación tiene el formato
nombre1=valor1&nombre2=valor2&...nombreN=valorN
Los espacios en blanco son substituidos por el carácter + y los caracteres no alfanuméricos
son substituidos por
%HH
, donde HH son dos dígitos hexadecimales que representan el código ASCII del carácter.
Los saltos de línea son substituidos por pares CR LF, es decir
%%0D%0A
El contenido de los datos también se asocia a la variable QUERY_STRING
El siguiente programa muestra la variable QUERY_STRING
y solicita el nombre y apellidos.
Obsérvese cómo se modifica el valor de dicha variable.
> mainAsk =
> do { putHeaderHtml
> ; putStrLn "<html><head><title>Ask Nombre</title>"
> ; putStrLn "<body>"
> ; thisUrl <- getUrl
> ; queryStr <- getEnvSafe "QUERY_STRING"
> ; putStrLn ("QUERY_STRING = " ++ queryStr ++ "<br/>")
> ; putStrLn ("<form action=\"" ++ thisUrl ++ "\" >")
> ; putStrLn "Nombre: <input type=\"text\" name=\"nombre\" />"
> ; putStrLn "Apellidos: <input type=\"text\" name=\"apellidos\" />"
> ; putStrLn "<INPUT type=\"submit\" value=\"Enviar\"> "
> ; putStrLn "<INPUT type=\"reset\" value=\"Borrar\"></form>"
> ; putStrLn "</body>"
> }
El segundo método, denominado POST debe especificarse mediante el atributo method
En este caso, los valores del formulario son accedidos a través de la entrada estándard del programa CGI.
En la variable CONTENT_TYPE se indica el tipo de contenido y en CONTENT_LENGTH
el número de bytes que se envían