Implementación cliente HTTP 2ª Parte

Buenas, debido al chapucero código de mi anterior artículo me decidí por hacer una implementación decente. En este caso intentaré hablar lo menos posible sobre el protocolo y centrarme principalmente en el código, aun así sed libres de preguntar lo que querais 😉

La primera versión (no publicada) fué escrita íntegramente en C. Funcionaba pero no era sencilla de usar y yo lo que buscaba era algo sencillo de usar, que curlpara bibliotecas complejas ya existen algunas como por ejemplo curl. Curl es muy potente y con infinidad de funciones pero es necesario echarle unas horas para aprender a usarlo. Otro día os hablare de curl, que también es una biblioteca muy estandar en el mundo GNU/Linux.

Después decidí aprovechar ese código y realicé un híbrido C/C++ (la versión del otro artículo) pero el código que quedó era muy feo y de muchas líneas así que dije “ala, a empezar desde cero pero siguiendo un diseño orientado a objetos” y me puse con ello.

El cambio más radical fué la nueva forma de almacenar la información. Como ya dije, la estructura de datos en html son “nombre de variable” “valor”, podemos verlo tanto en las cabeceras (ej: Host localhost) o los formularios (ej: usuario maxpowel). Se que esta explicación es muy abstracta pero en el otro artículo hablo mas detenidamente sobre ello (después de las DNS). Para guardar estos datos antes usaba listas enlazadas y vale, serán muy eficientes pero hoy en día no merece la pena para esta tarea. Además, todo el que haya trabajado con punteros sabrá lo que se sufre cuando el programa te salta con una violación de segmento, aaahhh! que no cunda el pánico!! Se puede arreglar pero puedes encontrar el fallo en 2 minutos o estar 2 días y sin ver donde está el fallo.

Menos mal que el sistema operativo mata al proceso que intenta acceder a memoria que no es suya....

Menos mal que el sistema operativo mata al proceso que intenta acceder a memoria que no es suya....

Para programadores de otros lenguajes, por ejemplo php, pensarán “vaya chorrada, yo uso un array con un string como índice y listo” pero por la naturaleza de los arrays de C/C++ esto no es posible aunque por suerte existen otros métodos. Una de las novedades que trae C++ es una clase llamada map. No se si quien hizo esta clase lo hizo pensando en mí porque la verdad que me venía perfecto. Cumple las mismas funciones que mis listas enlazadas y alguna más de regalo pero lo mejor esque el número de líneas de mi programa se reducía drásticamente y seguro que sus algoritmos son mucho mas eficientes que los míos.

Un map nos permite crear una lista de elementos cuyos índices pueden ser cualquier tipo de dato (string, int, incluso tipos de datos propios…) y como valor lo mismo, cualquier tipo de dato. Para esta caso, necesitamos un mapa cuyo índice sera un string (ej. usuario) y el valor otro string (ej. maxpowel). Map trae métodos para insertar, buscar, modificar, eliminar… así que, a jugar! Además, sabemos que en un formulario html no puede haber dos campos con el mismo valor (o por lo menos no debería) y hasta en esto map no favorece ya que si intentas insertar un elemento cuyo índice ya existe lo que hace es modificar el existente. Ayy la de quebramientos de cabeza que me hizo pasar conseguir que mis listas enlazadas hicieran eso…

La clase donde uso estos mapas se llama AFEBLcontainer, en los archivos afeblcontainer.h y afeblcontainer.cpp.
Ahora al usar mapas podemos acceder siemple a un valor de igual manera que en un array de php

string usuario=mapa[“usuario”];

Aunque para hacerlo mas seguro solo se puede acceder al mapa mediante los metodos que implemente la clase.
El resto de los cambios del programa son básicamente el uso de strings en vez de arrays de chars, que hace un código infinitamente más sencillo y fácil y leer.

En este nuevo programa, la clase con la que trabajamos la he llamado AFEBLbrowser. Mediante esta clase se puede hacer todo lo que necesitamos. Esta a su vez trabja con otras tres clases container, una para las cabeceras, otra para los inputs y la última para las cookies. Decir que tanto el contenedor de los inputs como el de las cookies son clases especiales porque realizan alguna función más. Luego heredan las propiedades principales de un contenedor normal. Para comprenderlo un poco mejor pongo un esquemita (pincha sobre él para verlo más grande).

esquema
Como podeis comprobar, el diseño es bien sencillote. También vemos que para hacer unas pocas tareas he creado bastantes clases. Todo esto se podría haber empaquetado en una única clase browser con varios maps y todos lo métodos juntos pero eso sería un GRAN ERROR. La experiencia nos lleva diciendo durante años que la clave es la MODULARIZACIÓN.
Imaginad que por lo que sea queremos hacer un cambio en el contenedor, por ejemplo para que cada uno tenga un nombre. Tal como está hecho ahora solo modificamos una clase (AFEBLcontainer) mientras que si lo tenemos todo junto tendriamos que modificar cada mapa, que este caso serían tres. Otra ventaja es que si por ejemplo quiero añadir más metodos a la clase que maneja las cookies solo modifico esa, dejando intacta la clase contenedor. Este el motivo por el que hice otras dos clases (AFEBLpostData y AFEBLcookies) en vez de modificar el contenedor (AFEBLcontainer) a pesar de que cada clase tiene solo método, pero de esta manera abro la puertas a futuras mejoras y evitar así tener reescribir código para añadir nuevas funcionalidades.
Además, viendo este sencillito esquema observamos que para las cabeceras siemplemente necesitamos almacenar datos (porque no ha ninguna clase intermediaria) mientras que las otras dos seguro que van a procesar esos datos.
Ésta es la gracia de la modularización, dividir problemazas en problemillas. Porque problemillas son más fáciles de resolver que problemazas, y en la programación en concreto es una medida para evitar que cuando arregles algo rompas otra cosa. Y recordad, un buen diseño de un programa es lo más importante. Escribir el código es lo último, hay que ir paso a paso. Seguramente se podría haber hecho un diseño mejor pero es una aplicación tan pequeña no merece gastar mucho tiempo jeje. Un ejemplo de lo que sucede cuando no se piensa antes de programar es lo que me pasó a mi con esta misma aplicación. Al principio la hice a lo loco porque solo lo quería para un bot sencillito de rapidshare (no voy a bajar 70 partes a mano, que lo haga el ordenador por mi jaja) y luego cuando quise mejorarlo un poco para un proyecto que tenemos entre manos Dicren y yo me vi con el problema de que poco se podía hacer con ese código…

Venga, al tema, la página del proyecto es http://www.nongnu.org/afebl/
Para bajarlo por subversion svn co svn://svn.savannah.nongnu.org/afebl/0.1
o directamente el tarball http://svn.savannah.gnu.org/viewvc/*checkout*/tarballs/afebl-0.1.tar.gz?root=afebl

Para probar que te compila bien y que tienes todo lo necesario entra en el direcotorio y ejecutas make. Si no da problemas perfecto ;), si te da problemas te agradecería que me lo hagas saber. En el archivo main.cpp hay un ejemplo de uso básico pero yo os voy a explicar como usarlo.

Lo primero de todo es incluir mis clases y las de entrada y salida estandar (recomiendo meter AFEBL en un directorio propio para que no se mezcle entre tus archivos)

	
#include 
#include "AFEBL/afeblbrowser.cpp"

Me permito el lujo de copiar el manual que hice para la página del proyecto
Las opciones que tenemos son o enviar información al servidor o recibirla. Para recibir una pagina usaremos el método “get”
Los nombres están en inglés por el tema de hacerlo mas internacional y esas cosas.

OBTENIENDO UNA PÁGINA
Prototipo:

	string get(string host);	

Ejemplo

	AFEBLbrowser *browser=new AFEBLbrowser();
	string host="http://www.congdegnu.es/";
	string response;
	response=browser->get(host);
	cout << response;
	delete browser;

Con este código mostraremos por pantalla el HTML de la página. Podemos usar la variable "response" para todo lo que queramos, teniendo en cuenta que ahí está el código HTML

Para enviar información en una página web solemos usar formularios, aquí os dejo un ejemplo.

ENVIAR UN FORMULARIO
Prototipos: (uso la palabra input para ver la semejanza con el campo input de html)

	
	void addInput(string inputName,string inputValue); //Crear nuevo input
	bool modifyInput(string inputName,string newValue);//Modificar input
	bool removeInput(string inputName); //Eliminar un input
	string getInputValue(string inputName); //Obtener el valor de un input
	void clearInputs(); //Borrar todos los inputs
	void showInputs(); //Mostrar los inputs

Ejemplo
Vamos a "emular" el siguiente formulario html

	
	AFEBLbrowser *browser=new AFEBLbrowser();
	string response;
	browser->addInput("username","maxpowel");
	browser->addInput("password","123456");
	browser->showInputs();
	response=browser->submit("http://congdegnu.es/index.php");//Usamos el método submit para enviar

Este código enviará la información al servidor, mostrará el contenido del formulario y guardará la respuesta del servidor en la variable response.

COOKIES
Las cookies son cargadas automáticamente cuando se obtiene una página
Prototipos:

		
	void clearCookies(); //Borrar cookies

Ejemplo
Borrar cookies (por ejemplo para terminar una sesión)

	AFEBLbrowser *browser=new AFEBLbrowser();
	string response;
        //Completamos el formulario de login
	browser->addInput("username","maxpowel");
	browser->addInput("password","123456");
	response=browser->submit("http://webpage.com/page.php");
	//Estamos autenticados en el servidor
	browser->clearCookies();
	//Cookies destruidas

Me he dado cuenta de que en algunos servidores tarda más tiempo del debido. Actualmente estoy trabajando en ello. Se que el problema está en los sockets (la herramienta que crea la conexión con el servidor de la página web) y a pesar de estar en modo no bloqueante sigue esperando a que el servidor finalice la conexión. Por el resto, todo debería funcionar bien pero ya se sabe que siempre hay bugs jeje.

Un último ejemplo práctico, basado en el del main.cpp. Quería hacer un búsquedas en google pero me he dado cuenta de que google no permite hacer POST así que esto es lo que hay jeje.

	
#include 
#include "afeblbrowser.cpp"
 
using namespace std;
int main()
{
 
AFEBLbrowser *browser=new AFEBLbrowser();
//Hago post localhost/index2.php, en el archivo php pones print_r($_POST) para que muestre los posteado
browser->addInput("q","alvaro");
cout << browser->submit("localhost/index2.php");
 
//Get the page localhost/index2.php
cout << browser->get("http://www.google.es/search?hl=es&q=congdegnu");
delete browser;
return 0;
}

Nos vemos!

You may also like...

1 Response

  1. 6 enero, 2009

    Información Bitacoras.com…

    Si lo deseas, puedes hacer click para valorar este post en Bitacoras.com. Gracias….

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos necesarios están marcados *