//
// 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