// // TutHttp.java // Copyright (c) 1996, Agustin Froufe // Todos los derechos reservados. // // No se asume ninguna responsabilidad por el uso o alteracion de este // software. Este software se proporciona COMO ES, sin garantia de ningun // tipo de su funcionamiento y en ningun caso sera el autor responsable de // daños o perjuicios que se deriven del mal uso del software, aun cuando // este haya sido notificado de la posibilidad de dicho daño. // // Compilador: javac 1.0 // Autor: Agustin Froufe // Creacion: 01-Dic-1996 18:34:15 // //-------------------------------------------------------------------------- // Esta informacion no es necesariamente definitiva y est  sujeta a cambios // que pueden ser incorporados en cualquier momento, sin avisar. //-------------------------------------------------------------------------- import java.net.*; import java.io.*; import java.util.*; // Clase de utilidades donde declaramos los tipos MIME y algunos gestores de // los errores que se pueden generar en HTML class HttpUtilidades { final static String version = "1.0"; final static String mime_text_plain = "text/plain"; final static String mime_text_html = "text/html"; final static String mime_image_gif = "image/gif"; final static String mime_image_jpg = "image/jpg"; final static String mime_app_os = "application/octet-stream"; final static String CRLF = "\r\n"; // Método que convierte un objeto String en una matriz de bytes. // Java gestiona las cadenas como objetos, por lo que es necesario // convertir las matrices de bytes que se obtienen a Strings y // viceversa public static byte aBytes( String s )[] { byte b[] = new byte[ s.length() ]; s.getBytes( 0,b.length,b,0 ); return( b ); } // Este método concatena dos matrices de bytes. El método // arraycopy() asombra por su rapidez public static byte concatenarBytes( byte a[],byte b[] )[] { byte ret[] = new byte[ a.length+b.length ]; System.arraycopy( a,0,ret,0,a.length ); System.arraycopy( b,0,ret,a.length,b.length ); return( ret ); } // Este método toma un tipo de contenido y una longitud, para // devolver la matriz de bytes que contiene el mensaje de cabecera // MIME con formato public static byte cabMime( String ct,int tam )[] { return( cabMime( 200,"OK",ct,tam ) ); } // Es el mismo método anterior, pero permite un ajuste más fino // del código que se devuelve y el mensaje de error de HTTP public static byte cabMime(int codigo,String mensaje,String ct, int tam )[] { Date d = new Date(); return( aBytes( "HTTP/1.0 "+codigo+" "+mensaje+CRLF+ "Date: "+d.toGMTString()+CRLF+ "Server: Java/"+version +CRLF+ "Content-type: "+ct+CRLF+ ( tam > 0 ? "Content-length: "+tam+CRLF : "" )+CRLF ) ); } // Este método construye un mensaje HTML con un formato decente // para presentar una condición de error y lo devuelve como // matriz de bytes public static byte error( int codigo,String msg,String fname)[] { String ret = ""+CRLF+"

"+codigo +" "+msg+"

"+CRLF; if( fname != null ) ret += "Error al buscar el URL: "+fname+CRLF; ret += ""+CRLF; byte tmp[] = cabMime( codigo,msg,mime_text_html,0 ); return( concatenarBytes( tmp,aBytes( ret ) ) ); } // Devuelve el tipo MIME que corresponde a un nombre de archivo dado public static String mimeTypeString( String fichero ) { String tipo; if( fichero.endsWith( ".html" ) || fichero.endsWith( ".htm" ) ) tipo = mime_text_html; else if( fichero.endsWith( ".class" ) ) tipo = mime_app_os; else if( fichero.endsWith( ".gif" ) ) tipo = mime_image_gif; else if( fichero.endsWith( ".jpg" ) ) tipo = mime_image_jpg; else tipo = mime_text_plain; return( tipo ); } } // Esta clase sirve para que nos enteremos de lo que está haciendo // nuestro servidor. En una implementación real, todos estos mensajes // deberían registrarse en algún fichero class HTTPlog { public static void error( String entrada ) { System.out.println( "Error: "+entrada ); } public static void peticion( String peticion ) { System.out.println( peticion ); } } // Esta es la clase principal de nuestro servidor Http class TutHttp { public static final int puerto = 80; final static String docRaiz = "/html"; final static String fichIndice = "index.html"; final static int buffer = 2048; public static final int RT_GET=1; public static final int RT_UNSUP=2; public static final int RT_END=4; // Indica que la petición no está soportada, por ejemplo POST y HEAD private static void ctrlNoSop(String peticion,OutputStream sout) { HTTPlog.error( "Peticion no soportada: "+peticion ); } // Este método analiza gramaticalmente la solicitud enviada con el // GET y la descompone en sus partes para extraer el nombre del // archivo que se está solicitando. Entonces lee el fichero que // se pide private static void ctrlGet( String peticion,OutputStream sout ) { int fsp = peticion.indexOf( ' ' ); int nsp = peticion.indexOf( ' ',fsp+1 ); String fich = peticion.substring( fsp+1,nsp ); fich = docRaiz+fich+( fich.endsWith("/") ? fichIndice : "" ); try { File f = new File( fich ); if( !f.exists() ) { sout.write( HttpUtilidades.error( 404, "No Encontrado",fich ) ); return; } if( !f.canRead() ) { sout.write( HttpUtilidades.error( 404, "Permiso Denegado",fich ) ); return; } // Ahora lee el fichero que se ha solicitado InputStream sin = new FileInputStream( f ); String cabmime = HttpUtilidades.mimeTypeString( fich ); int n = sin.available(); sout.write( HttpUtilidades.cabMime( cabmime,n ) ); byte buf[] = new byte[buffer]; while( ( n = sin.read( buf ) ) >= 0 ) sout.write( buf,0,n ); sin.close(); } catch( IOException e ) { HTTPlog.error( "Excepcion: "+e ); } } // Devuelve la cabecera de la solicitud completa del cliente al // método main de nuestro servidor private static String getPeticion( InputStream sin ) { try { byte buf[] = new byte[buffer]; boolean esCR = false; int pos = 0; int c; while( ( c = sin.read() ) != -1 ) { switch( c ) { case '\r': break; case '\n': if( esCR ) return( new String( buf,0,0,pos ) ); esCR = true; // Continúa, se ha puesto el primer \n en la cadena default: if( c != '\n' ) esCR = false; buf[pos++] = (byte)c; } } } catch( IOException e ) { HTTPlog.error( "Error de Recepcion" ); } return( null ); } private static int tipoPeticion( String peticion ) { return( peticion.regionMatches( true,0,"get ",0,4 ) ? RT_GET : RT_UNSUP ); } // Función principal de nuestro servidor, que se conecta al socket // y se embucla indefinidamente public static void main( String args[] ) throws Exception { ServerSocket ss = new ServerSocket( puerto ); while( true ) { String peticion; Socket s = ss.accept(); OutputStream sOut = s.getOutputStream(); InputStream sIn = s.getInputStream(); if( ( peticion = getPeticion( sIn ) ) != null ) { switch( tipoPeticion( peticion ) ) { case RT_GET: ctrlGet( peticion,sOut ); break; case RT_UNSUP: default: ctrlNoSop( peticion,sOut ); break; } HTTPlog.peticion( peticion ); } sIn.close(); sOut.close(); s.close(); } } } //------------------------------------------- Final del fichero TutHttp.java