Introduzione

Quante volte vi siete chiesti cosa si scambia il browser con il server quando navighiamo e otteniamo delle pagine HTML?

Spesso la nostra curiosità è stata frenata dalla dimensione dell’RFC che descrive il protocollo HTTP: leggere circa 200 pagine è un po’ scoraggiante, soprattutto se è pura curiosità. In questo articolo presenterò una piccola utility che permette di spiare in modo del tutto invisibile al browser la nostra connessione. Questa utility è stata pensata e scritta come supporto per lo sviluppo di una piccola applicazione HTTP. Questo genere di utility si rivela molto interessante sia perché permette di spiare gli header HTTP inviati dal browser, sia per verificare effettivamente la validità delle informazioni trasmesse al server.

Prima di iniziare a descrivere il codice e le varie classi che compongono il programma vediamo come funziona una richiesta eseguita tramite il protocollo HTTP. Possiamo dividere le richieste HTTP in due diversi tipi.

I sorgenti descritti in questo articolo sono disponibili nell’archivio httpspyesempi.zip.

Connessione diretta al server web

In questo tipo di connessione il client esegue direttamente la connessione verso il server web. Questa connessione può essere schematizzata nei seguenti passi:

1. Risoluzione del nome in indirizzo IP (questo punto viene eseguito solo nel caso in cui sia necessario). 2. Il client si connette al server web aprendo una connessione. 3. Il client inoltra la sua richiesta al server. 4. Il server elabora la risposta ed invia i dati al client.

Connessione tramite proxy server

In questo tipo di connessione il client esegue la connessione, invece che verso il server richiesto, verso il proxy server, il quale a sua volta elabora l’header inviatogli dal browser. Anche qui lo schema della richiesta è molto simile al precedente.

1. Risoluzione del nome in indirizzo IP (questo punto viene eseguito solo nel caso in cui sia necessario). 2. Il client si connette al proxy server aprendo una connessione. 3. Il client inoltra la sua richiesta al proxy. 4. Il proxy elabora l’header inviatogli dal client. 5. Il proxy verifica che la pagina richiesta non sia presente nella sua cache; se presente non esegue il punto 6. 6. Il proxy richiede la pagina o immagine al server web. 7. Il proxy invia i dati ricevuti al client. 8. Il proxy archivia la pagina o immagine appena scaricata nella sua cache.

Confrontando i due tipi di connessione in maniera poco attenta, si potrebbe pensare che le connessioni dirette siano in genere più veloci delle altre, dato che il numero di passaggi eseguiti è inferiore. Sicuramente il numero di passaggi eseguiti con le connessioni via proxy è superiore, ma nonostante questo la velocità della connessione è in media maggiore di quella delle connessioni dirette. Questo è dovuto principalmente a due fattori molto importanti:

  • Il proxy server possiede una cache di grandi dimensioni.
  • La velocità della linea utilizzata dal proxy. In genere le connessioni eseguite via modem non superano i 4 KB al secondo nella migliore delle ipotesi. Al contrario la connessione eseguita dal proxy (che in genere è ospitato sulla macchina del vostro provider) esegue una connessione al massimo della potenza della sua banda passante, che può variare da 32 kbit ad un massimo di 2 Mbit.

HTTPSpy è stata pensata per essere inserita tra il proxy del vostro provider ed il browser che utilizzate durante la vostra connessione. L’applicazione può essere benissimo considerata come un piccolo gateway; infatti, con piccolissime modifiche, è possibile trasformare HTTPSpy in un programma di gateway.

Di seguito possiamo vedere la stessa richiesta eseguita con due browser.

Richiesta eseguita da Netscape

GET http://127.0.0.1/ HTTP/1.0
Proxy-Connection: Keep-Alive
User-Agent: Mozilla/4.06 [en] (Win95; I)
Host: 127.0.0.1
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, image/png, */*
Accept-Encoding: gzip
Accept-Language: en
Accept-Charset: iso-8859-1,*,utf-8

Richiesta eseguita da Internet Explorer 3.0

GET http://127.0.0.1/ HTTP/1.0
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg,
  application/vnd.ms-excel, application/msword,
  application/vnd.ms-powerpoint, */*
Accept-Language: it
UA-pixels: 800x600
UA-color: color16
UA-OS: Windows 95
UA-CPU: x86
User-Agent: Mozilla/2.0 (compatible; MSIE 3.02; Windows 95)
Host: 127.0.0.1
Proxy-Connection: Keep-Alive

Risposta

HTTP/1.0 200 OK
Server: Microsoft-IIS/3.0
Date: Fri, 11 Sep 1998 11:28:10 GMT
Content-Type: text/html
Set-Cookie: ASPSESSIONID=OUUSJJSWMYSVWFDY; path=/
Cache-Control: private
Age: 0
X-Cache: MISS from 127.0.0.1
Proxy-Connection: close

Come si può notare la stessa richiesta genera due header non del tutto simili. Bisogna sottolineare che tutte le richieste contengono le informazioni necessarie ad assicurare la corretta interpretazione della richiesta. Le altre informazioni sono quelle aggiunte dai vari costruttori di browser per cercare di creare una più facile integrazione utente/informazioni.

Il programma

| Nome classe | Descrizione | |---|---| | HTTPSpy | Contiene il main dell’applicazione e la gestione della finestra di trace. | | ControlFrame | Classe per il controllo degli eventi del Frame principale. | | ProxySessionInfo | Contiene le informazioni sulla connessione da utilizzare come proxy. | | ServerThreadSpy | Contiene la gestione del ServerSocket per creare una porta in listen. | | ClientThreadSpy | Questa classe si occupa di monitorare il passaggio dei dati tra il client connesso ed il server proxy. |

Data la semplicità dell’applicazione e del codice che compone i vari oggetti non mi dilungherò troppo sulla sua descrizione. L’unica parte che richiede un minimo di descrizione è una funzione contenuta nella classe ClientThreadSpy che rimane comunque di facilissima interpretazione.

/**
 * Legge le informazioni dal socket del client.
 */
private String readClientSocketInfo(BufferedReader inRead) {
  String strTemp = "";
  Vector strVector = new Vector();
  try {
    do {
      strVector.addElement(inRead.readLine());
    } while (((String) strVector.lastElement()).length() != 0);
  } catch (IOException e) {
    System.out.println(e.getMessage());
  }
  for (int ndx = 0; ndx != strVector.size(); ndx++) {
    strTemp = strTemp + (String) strVector.elementAt(ndx) + "\n";
  }
  return strTemp;
}

Questa funzione permette di leggere la richiesta di dati eseguita dal client. Purtroppo non possiamo interpretare la chiusura del Socket come fine richiesta, perché il protocollo HTTP prevede la risposta sullo stesso canale da cui viene eseguita la richiesta. L’unico elemento a noi disponibile per determinare la fine del messaggio è intercettare la fine dell’header HTTP che, secondo lo standard, termina con due caratteri 0xA. Nota: nell’esempio riportato non verifico il terminatore, ma preferisco aspettare il time-out di lettura sul socket.

Come utilizzare il programma

Per utilizzare il programma è necessario eseguire i seguenti passi:

1. Impostare il proxy del browser prescelto per eseguire la prova nel seguente modo: indirizzo 127.0.0.1, porta 8080. 2. Lanciare il programma HTTPSpy, ad esempio: java HTTPSpy proxy.localhost.it 80.

Note: il programma è stato testato in ambiente Windows 95 e Windows NT 4.0 SP3 con JDK 1.1.5.