Si bien hoy en día hay servidores de páginas web gratuitos y de calidad como pueden ser Apache, LightHTTP, IIS, etc. no está de más crear nuestro propio servidor de páginas web para tener nuestra propia intranet en nuestro hogar o incluso en nuestra oficina. Si a eso le sumamos los conocimientos que ya tenemos de Delphi respecto al acceso a bases de datos podemos crear pequeñas aplicaciones web muy interesantes.
Como viene siendo habitual en esta serie de artículos vamos a utilizar los componentes Indy. Lo que vamos a hacer es que el usuario que se conecte al servidor entre a una página principal y luego para entrar a nuestra zona privada tenga que identificarse con usuario y contraseña.
CREANDO LA VENTANA DEL SERVIDORAl igual que hicimos con el servidor de Telnet, sería interesante monitorizar las conexiones de los usuarios que se van a conectar a nuestro servidor. Por ello vamos a crear un nuevo proyecto cuya ventana principal es la siguiente:
En este formulario hemos introducido dos botones para conectar y desconectar el servidor y un componente de la clase
TMemo llamado
Log donde iremos mostrando los eventos que ocurren. Y por último añadimos el componente más importante:
IdHTTPServer situado en la pestaña
Indy Servers.
El código asociado a los botones
Activar y
Desactivar no puede ser más sencillo:
procedure TFServidorHTTP.BActivarClick(Sender: TObject);
begin
Servidor.Active := True;
Log.Lines.Add( 'Servidor activado.' );
BActivar.Enabled := False;
BDesactivar.Enabled := True;
end;
procedure TFServidorHTTP.BDesactivarClick(Sender: TObject);
begin
Servidor.Active := False;
Log.Lines.Add( 'Servidor desactivado.' );
BActivar.Enabled := True;
BDesactivar.Enabled := False;
end;
Al ejecutar el programa y pulsar el botón
Activar veremos que salta el cortafuegos de Windows (o el que tengamos configurado por defecto) donde debemos pulsar el botón
Desbloquear para tener nuestro servidor operativo:
Después sólo tenemos que abrir nuestro navegador de Internet preferido y teclear
localhost o bien
http://127.0.0.1 y debe aparecer una página web en blanco.
Pues bien, cada vez que un usuario entra a nuestro servidor de páginas web con cualquier navegador, lo que realmente hace es enviarnos este comando por el puerto 80:
GET /Esto significa que debemos darle la página principal de entrada, que en nuestro caso va a ser un archivo HTML que vamos a crear con el Bloc de Notas de Windows y que va a contener lo siguiente:
El archivo lo guardamos con el nombre
index.html en el mismo directorio donde se encuentra el ejecutable del servidor.
Otra página que vamos a crear es la zona privada:
Este archivo lo guardamos con el nombre
zonaprivada.html. Ahora viene el punto fuerte, donde hay que responder a los comandos del cliente utilizando el evento
OnCommandGet del componente
IdHTTPServer:
procedure TFServidorHTTP.ServidorCommandGet(AContext: TIdContext;
ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
var
sDocumento: String;
begin
Log.Lines.Add( ARequestInfo.RemoteIP + ': ' +
ARequestInfo.Command + ARequestInfo.Document );
// ¿Va a entrar a la página principal?
if ARequestInfo.Document = '/' then
AResponseInfo.ServeFile( AContext, ExtractFilePath( Application.ExeName ) + 'index.html' )
else
begin
// Cargamos la página web que vamos a enviar
sDocumento := ExtractFilePath( Application.ExeName ) +
Copy( ARequestInfo.Document, 2, Length( ARequestInfo.Document ) );
// ¿Existe la página que ha solicitado?
if FileExists( sDocumento ) then
begin
// validamos al usuario
if not ( ( ARequestInfo.AuthUsername = 'admin' ) and
( ARequestInfo.AuthPassword = '1234' ) ) then
AResponseInfo.AuthRealm := 'ServidorHTTP'
else
AResponseInfo.ServeFile( AContext, sDocumento );
end
else
// No hemos encontrado la página
AResponseInfo.ResponseNo := 404;
end;
AResponseInfo.CloseConnection := True;
end;
Para poder compilar este ejemplo hay que añadir arriba la unidad
IdContext. Este evento se divide en las siguientes partes:
1º Escribimos en la ventana de nuestro servidor quien se está conectando y el documento (pagina web, archivo, etc.) solicita:
Log.Lines.Add( ARequestInfo.RemoteIP + ': ' +
ARequestInfo.Command + ARequestInfo.Document );
2º Si lo que el usuario solicita es la página principal pues entonces se la damos sin rechistar:
// ¿Va a entrar a la página principal?
if ARequestInfo.Document = '/' then
AResponseInfo.ServeFile( AContext, ExtractFilePath( Application.ExeName ) + 'index.html' )
else
...
El método
ServeFile envía cualquier archivo al navegador del cliente.
3º Traducimos el documento que nos solicitan en una ruta donde estamos ejecutando el servidor:
// Cargamos la página web que vamos a enviar
sDocumento := ExtractFilePath( Application.ExeName ) +
Copy( ARequestInfo.Document, 2, Length( ARequestInfo.Document ) );
4º Si el documento que nos solicitan no existe le mandamos un error
404. En el caso de que exista, significa que va a entrar en nuestra zona privada, con lo cual compruebo primero su usuario y password antes de dejarle pasar:
// ¿Existe la página que ha solicitado?
if FileExists( sDocumento ) then
begin
// validamos al usuario
if not ( ( ARequestInfo.AuthUsername = 'admin' ) and
( ARequestInfo.AuthPassword = '1234' ) ) then
AResponseInfo.AuthRealm := 'ServidorHTTP'
else
AResponseInfo.ServeFile( AContext, sDocumento );
end
else
// No hemos encontrado la página
AResponseInfo.ResponseNo := 404;
Una vez autentificado el usuario le enviamos la página web solicitada.
5º Por último cerramos la conexión con el cliente:
AResponseInfo.CloseConnection := True;
Ahora ejecutamos el programa y pulsamos el botón
Conectar:
Ejecutamos por ejemplo
Internet Explorer y escribimos
locahost:
Al pulsar el enlace
Ir a la zona privada hará que el navegador nos pida usuario y contraseña:
Escribimos
admin Y
1234 y saltará a la zona privada:
Si volvemos a ir a la página principal y e intentamos entrar en la zona privada ya no será necesario introducir usuario y contraseña, ya que lo memoriza automáticamente el navegador.
Si intentamos solicitar a nuestro servidor una página que no existe nos devolverá un error
404:
Mientras ha sucedido todo esto, nuestro servidor ha monitorizado todo el proceso:
En esto hay que ver tres cosas importantes:
1º Si el usuario vuelve a la página principal
index.html el servidor no se entera, ya que el navegador web la ha cogido de su caché de páginas, evitando el tráfico innecesario.
2º Cuando nos conectamos por primera vez a una página web, el navegador solicita el archivo
favicon.ico. Este icono es el que aparece al lado de la URL de la página web donde hemos entrado:
Si os interesa más información sobre el icono
favicon podéis verlo en esta página:
http://www.favicon.net/De todas formas no hay que tomarle mucha importancia a esto porque por ejemplo Firefox no lo pide.
3º El control de entrada de usuarios que hemos realizado sólo es eso, control de usuarios, pero no es una sesión. Una sesión es la encargada de guardar el estado del usuario en nuestra página. Eso lo veremos más adelante.
CONCLUSIÓNComo puede apreciarse en el código, con algo tan sencillo como esto ya tenemos un pequeño servidor web que permite servir páginas web a los clientes y tener una zona privada que podíamos validar con una pequeña base de datos de Internase o bien un pequeño archivo de texto encriptado.
Pero no hay que hacerse muchas ilusiones ya que un servidor web incluye muchas más cosas. Una cosa es dejar que un usuario entre a una zona privada y otra guardar el estado del usuario con una sesión mediante cookies. Eso lo veremos en el siguiente artículo. También sería interesante poder recoger información de usuarios y controlar eventos al estilo web 2.0, como puede ser la implementación de un blog, un foro, un chat online, etc.
MODIFICACIONES QUE HAY QUE REALIZAR PARA DELPHI 7El único inconveniente que tiene este código en Delphi 7 (cuya versión de los componentes Indy es más antigua) es que el objeto
AResponseInfo no tiene el método
ServeFile por lo que tenemos que dárle la página web a mano. Lo que he realizado en este ejemplo es cargar la página web en un
StringList y se la doy al cliente:
procedure TFServidorHTTP.ServidorCommandGet(AThread: TIdPeerThread;
ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
var
sDocumento: String;
S: TStringList;
begin
S := TStringList.Create;
Log.Lines.Add( ARequestInfo.RemoteIP + ': ' +
ARequestInfo.Command + ARequestInfo.Document );
// ¿Va a entrar a la página principal?
if ARequestInfo.Document = '/' then
begin
S.LoadFromFile( ExtractFilePath( Application.ExeName ) + 'index.html' );
AResponseInfo.ContentText := S.Text;
end
else
begin
// Cargamos la página web que vamos a enviar
sDocumento := ExtractFilePath( Application.ExeName ) +
Copy( ARequestInfo.Document, 2, Length( ARequestInfo.Document ) );
// ¿Existe la página que ha solicitado?
if FileExists( sDocumento ) then
begin
// validamos al usuario
if not ( ( ARequestInfo.AuthUsername = 'admin' ) and
( ARequestInfo.AuthPassword = '1234' ) ) then
AResponseInfo.AuthRealm := 'ServidorHTTP'
else
begin
S.LoadFromFile( sDocumento );
AResponseInfo.ContentText := S.Text;
end
end
else
AResponseInfo.ResponseNo := 404;
end;
AResponseInfo.CloseConnection := True;
S.Free;
end;
Igualmente se podía haber cargado el archivo con
AssignFile y
Reset o bien con un objeto
TStreamFile. Eso lo dejo al gusto del usuario.
En el próximo artículo veremos como sacarle más partido a este componente.
Pruebas realizadas en RAD Studio 2007 y Delphi 7.