Cliente HTTP en Java Objetivos Desarrollar, usando Java, un cliente básico del protocolo web que permita enviar una solicitud HTTP a un servidor web y procesar la respuesta. Requerimientos de la aplicación: La aplicación cliente básico de web debe: - Obtener la información del URL que se usará para hacer la petición web - Abrir una conexión al servidor (dominio) seleccionado por el usuario por medio de un socket. - Escribir en el socket (enviar al servidor) un mensaje de petición HTTP por medio de un PrintWriter del OutputStream sobre el socket - Leer el mensaje de respuesta, realizar un procesamiento mínimo de los encabezados y descargar en un archivo el cuerpo de la respuesta Diagrama de clases Como propuesta de solución se ha definido un conjunto de clases para procesar las peticiones. HttpClient Es la clase que coordina (controla) el funcionamiento de la aplicación. Básicamente, esta clase realiza la conexión por un socket al servidor web, invoca al RequestProcessor para enviar el mensaje de solicitud al servidor e invoca al ResponseProcessor para obtener y procesar el mensaje de respuesta. RequestProcessor Esta clase envía el mensaje de solicitud al servidor web. El método sendRequestMessage recibe como parámetro un OutputStream que es usado para enviar la solicitud. ResponseProcessor Esta clase obtiene y procesa el mensaje de respuesta. El método processResponse recibe como parámetro un InputStream que es usado para leer el mensaje. Instrucciones Paso a paso 1. El cliente se debe conectar al servidor por medio de un socket. 2. El usuario debe indicar la petición HTTP que el cliente envía al servidor. 3. El cliente retorna como respuesta un mensaje HTTP con las siguiente estructura: 4. El programa debe leer el mensaje de respuesta, byte por byte, usando un buffer de Entrada. Cada byte del buffer representa un carácter. Como el objetivo es descargar únicamente el cuerpo del mensaje de respuesta, el programa puede importar todos los encabezados de la respuesta. El programa puede buscar el renglón vacío para reconocer el final de los encabezados. a. Los caracteres de fin de línea (es decir, los caracteres <CR><LF>) pueden leerse en java usando el texto “\r\n”. Para identificar el fin de los encabezados, el programa puede buscar el punto en donde esta secuencia se encuentre dos veces (es decir, el final de los encabezados del mensaje y el renglón vacío). A partir de ese punto, el texto corresponde con el cuerpo del mensaje que se debe guardar. 5. El programa debe guardar el cuerpo del mensaje en un archivo, con el nombre que indique el usuario. Creación del proyecto Estructura general del proyecto El proyecto base se entrega con la siguiente estructura: HttpClient | ---src | | | ------client | | | | | -------HttpClient.java | | | | | -------RequestProcessor.java | | | | | -------ResponseProcessor.java | | | ---------ui | | | | | -------ClientUI.java | ---JRE System Library | ---descarga La clase RequestProcessor.java La responsabilidad de esta clase es enviar, por medio del socket, la petición al servidor web. Ésta clase es un Singleton, tiene un atributo donde vamos a guardar el histórico de peticiones realizadas desde el cliente web. Para enviar la petición contamos con el método sendRequestMessage que recibe como parámetros el OutputStream del socket, el mensaje y el host. Complete el método con las siguientes instrucciones: public void sendRequestMessage(OutputStream output, String message, String host) throws IOException{ output.write((message+"\r\n").getBytes()); output.write(("Host:"+host+"\r\n").getBytes()); output.write("\r\n".getBytes()); output.write("\r\n".getBytes()); output.flush(); historic.add(host + " - " + message); } La clase ResponseProcessor.java La responsabilidad de esta clase es procesar la respuesta del servidor, la clase es un Singleton y procesa la respuesta por medio del método RequestProcessor que recibe como parámetros el InputStream y el nombre del archivo donde se quiere guardar el cuerpo del mensaje. La idea del proceso es leer el archivo Byte por Byte, debe buscar la secuencia “\r\n\r\n” que indica el fin del encabezado del mensaje y el inicio del cuerpo que debe guardar en un archivo con el nombre ingresado por el usuario. El archivo debe ser ubicado en la carpeta “descarga” en la raíz del proyecto. Complete el método con las siguientes instrucciones: public File processResponse(InputStream input, String fileName) throws IOException{ byte[] buffer = new byte[1024]; File file = new File("descarga/"+fileName); BufferedOutputStream bOutput = new BufferedOutputStream(new FileOutputStream(file)); int int int int int tReaded = 0; tArchivo = 0; tReadedi = 0; tPatterns = 0; tWaitedChar = '\r'; boolean inContent = false; //En cada paso del ciclo lee la entrada y lo guarda en el array buffer, //el ciclo sigue si el numero de bytes es mayor que 0 while((tReadedi = input.read(buffer))>0){ if(inContent){ // A este bloque entra una vez ha encontrado // el inicio del mensaje. bOutput.write(buffer,0,tReadedi); tArchivo += tReadedi; } else { //buscamos en el buffer el patron '\r\n\r\n' // y guardamos en caso de encontrarlo for(int i=0; i<tReadedi; i++){ int chari = buffer[i]; if(chari==tWaitedChar){ if(chari=='\r'){ tWaitedChar='\n'; } else if(chari=='\n'){ tPatterns++; tWaitedChar='\r'; } inContent = tPatterns==2; if(inContent){ //Entrar a esta parte significa // que encontramos '\r\n\r\n' //Guardamos el buffer desde la posición i+1 bOutput.write(buffer,i+1,tReadedi-(i+1)); tArchivo+=tReadedi-(i+1); break; } } else { tPatterns = 0; tWaitedChar = '\r'; } } } tReaded += tReadedi; buffer = new byte[1024]; } bOutput.close(); return file; } La clase HttpClient La responsabilidad de esta clase es gestionar la conexión con el servidor y coordinar el envío del mensaje y la recepción de la respuesta. En su constructor recibe el host y el puerto de conexión para iniciarla. El método processRequest recibe como parámetros el mensaje de la petición, el host y el nombre del archivo, gestiona las clases RequestProcessor y ResponseProcessor para guardar el cuerpo de la petición y retorna como respuesta el archivo guardado.