Rome, een dagboek

Dinsdag 19 juli
11:59

Ik ben al drie uur op de luchthaven. Vertrokken zonder veel vooruitzicht en in de veronderstelling dat ik wel in Rome zou raken, begaf ik me naar Leuven station voor een enkeltje Brussel Nationaal.

Brussels Airlines is te duur: “Wij doen niet aan last-minute.” Mij best, denk ik, en Connections redt de dag. Ik reis alleen, dus ben aan mezelf overgeleverd, wat naar alle waarschijnlijkheid de eerste keer is. Ik lieg als ik zeg dat ik het niet een beetje spannend vind. Ik kijk op mijn rechterkant uit op vier paar roltrappen die drie verdiepingen met elkaar verbinden, linksvoor zie ik een Quick, waarvan ik me afvraag of ik daar straks wél zin in zal hebben. Aan de overkant van wat men “De Luchtweg” heeft gedoopt, gooit een meisje uit verveling herhaaldelijk haar flesje omhoog, dat ze vervolgens deskundig terug opvangt. Omdat Seekers louter fictief zijn, verdenk ik haar enkel van een gezonde motoriek maar wacht ik toch geduldig tot ze haar flesje per abuis over de balustrade keilt om zo een nietsvermoedende roltrapreiziger iets sneller naar beneden te doen rollen dan gepland.

Honger begint te komen, inspiratie op. Nog lang geen tijd om te boarden, ik vlieg pas om 14:40 naar Amsterdam. Bill Bryson schreef mijn tijdverdrijf in 2003 en noemde het “A Short History of Nearly Everything”. Ik ben hem er dankbaar voor.

16:25

Een vertraagde vlucht later, ben ik in Schiphol gestrand. Geen tijd om rond te kijken, boarding start om 16:05. Dat was voordat het vliegtuig zoek was, of iets dergelijks, want waarom zou men anders plots van vliegmasjien veranderen? Gelukkig is Schiphol rijkelijk bezaaid met wegwijzers en vond ik bijgevolg gemakkelijk de grond waarop ik zit. Langs mijn zijde berispt een moeder, geheel herkenbaar en met een volmaakt Engels accent, haar oudste dochter omwille van het plagen van haar zus. Ik hoop dat ik hier goed zit, maar dat zal wel want ik hoor een taal die ik niet ken en dus waarschijnlijk Italiaans is. Wachten tot alles zichzelf oplost. Als mijn bagage meereist, vind ik het allemaal best.

16:57

De vlucht is ondertussen delayed tot 18:30, maar goed dat ik niet gehaast ben. Kinderen amuseren zich door een pingpongbal naar hun vader te ketsen, een ander zet uit verveling een roltrap in gang. De grote mensen staren wezenloos voor zich uit of gooien ongeduldige blikken op hun horloge of het informatiebord. Als ik wist hoe lang de vlucht naar Rome duurt, zou ik het me niet zitten afvragen. In de hoofdgebouwen van Schiphol klonken rustgevende muziek en natuurgeluiden. Deze vleugel vergaten ze. Ik wacht. Krijg nou wat, die roltrap werkt in beide richtingen. Niet tegelijkertijd, natuurlijk, we zijn tenslotte Schrödinger niet.

20:10

Het is koud op het vliegtuig, zo getuige een oude man met een muts. De arme dame aan het raam links van me begon de vlucht met een kruisteken, waarna het tafeltje aan de zetel voor haar los kwam en haar nagenoeg in het gezicht sloeg. Je mag daar niet mee lachen, maar het is moeilijk. God is een kleine sadist.

De piloot roept om dat het in Rome nog steeds 26 graden is. Laat me hopen dat dat over een halfuur nog steeds het geval is, want ik moet op zoek naar onderdak. De negen herbergen die ik vanuit Schiphol opbelde konden overnachting noch soelaas bieden. Het ziet er naar uit dat mijn eerste nacht er één op hotel zal worden. Gelukkig heb ik niets tegen luxe.

Wat turbulentie en een bloedneus later, (ik vermoed geen causaal verband,) zetten we de landing in, langs rechts vergezeld door een ondergaande zon die onze linkerzijde van een verticale regenboog voorziet.

00:47

Ik besef wat het nut is van een tweepersoonsbed voor een vrijgezel: je kan gaan slapen zonder eerst je bed te moeten ontruimen. Ik lig poedelnaakt op bed in een hotelkamertje dat veel te duur is, maar het mag er ook wel wezen. Ik ben in ieder geval begonnen met mijn centen er terug uit te halen: een vol bad, badschuim incluis. Morgen op zoek naar een goedkoper alternatief, maar pas na mijn uitgebreid ontbijt van 7 tot 10.

Woensdag 20 juli
10:41

De schaduw van het loof beschermt me tegen de zon. Ik zit op een bankje in Giardini del Quirinale en zit daar wel lekker. Mijn volgende vier nachten zijn verzekerd van onderdak en dat voor een prijs waarvoor ik op mijn laatste verblijfplaats nog geen twee nachten zou kunnen verblijven. Allemaal goed, allemaal wel.

Dorst, drieëntwintig graden. Dat belooft. Ik kan deze namiddag pas inchecken en socializen dus ben ik aan het solotrippen door deze mooie, vuile stad waar chauffeurs een claxonreflex hebben die sterker is dan hun remreflex. De wind steekt op en toont me waar ik heen moet.

Donderdag 21 juli
15:46

Mijn voeten laten zich voelen, ik zit in de bar van de herberg met een Duvel voor mij. Waarom iets nieuws proberen als het oude zo goed is? Dat strookt niet helemaal met de ingesteldheid die je verwacht van een alleenreiziger, maar dat kan me niet schelen. Ik geniet van een gulden slok goud.

Slecht geslapen, veel te warm, een laatste keer wakker geworden om 7u. Om halfnegen de stad ingetrokken met drie Amerikaanse europavaarders. Rome heeft zich aan me prijsgegeven hoewel ik me als een toerist gedroeg.

Of ik moe ben, wordt me gevraagd. Valt het dan zo op? Aan siësta’s doe ik principieel niet mee, maar het zou welkom zijn. Het is hier rustig. Nu wel. Vanavond verzamelt de voltallige Roomse bevolking zich hier weer om de boel op stelten te zetten. Dat is prettig, want ik doe sociaal. Het gevoel dringt zich aan me op dat ik geen zinnen meer kan vormen. Zonneslag of niet, hier laat ik het bij, maar echt niet omdat ik een siësta nodig heb.

Vrijdag 22 juli
14:44

Een schoen onder mijn stoel, de ander om mijn voet. Ook hier staken soms treinen, geen zee vandaag. Vanmorgen gewekt door een herbergmedewerkster die me aanspoorde uit te checken (omdat ik ook morgen weer in een andere kamer slaap, het gevolg van niet gereserveerd te hebben). Het was elf uur, dus ik was blij dat ze nog beleefd was. Zware avond gisteren, trage start vandaag. Twee uur rondgehangen op het station, wachtend op een trein die nooit kwam. Het duurde even voor de betekenis van soppresso tot ons doordrong. Zoals het elke gezonde jongeman betaamt, hang ik na die teleurstelling door te zakken aan de toog. Misschien zwem ik straks, als de hoofdpijn is gaan liggen.

Zaterdag 23 juli
10:44

“Thank you,” en vijf euro wisselgeld. Geen slecht ontbijt, zo’n egg and bacon sandwich, maar de capuccino heb ik beter gehad. Me afvragend wat Rome te bieden heeft als het nat en miezerig is, ga ik mijn tijdverdrijf halen dat boven naast mijn bed ligt. Opnieuw bedankt, Bill.
Ontbijt, nieuwe gezichten. Regen op straat. Mijn laatste volle dag, ik ben een plakker want de meesten verbleven hier minder lang dan ik. Nog wat socializen, de dag ietwat proberen vullen.

14:50

Van Morrison zingt Moondance, ik eet. Ieder doet waar ie goed in is. Hij neemt me terug naar vorige week, toen ik August Rush zag: een film die me door vrienden was aangeraden en mede verantwoordelijk was voor het feit dat ik me hier bevind. In het algemeen een aanrader. Inspirerend en ontroerend, ik laat graag eens een traan op die manier.

Met wat Engelsmannen (die hier trouwens dunbezaaid zijn, net zoals Belgen — ik heb nog maar één Vlaams gezin gehoord sinds ik hier ben) en twee Aussies (die daarentegen in statistisch ongebruikelijke proporties aanwezig zijn) nogmaals de Roomse binnenstad bezocht, die ik onderhand wat begin te kennen. Zonder kaart de groep rond de stad gidsen, het schept voldoening. De zon liet ons niet volledig in de steek.

Deze perzik is hard, maar toch zo goed als op, de rest gooi ik weg. Ik kom eraan, Bill.

Zondag 24 juli
09:22

Te vroeg opgestaan, uitgebreid ontbijt. Pas om 10u richting Stazione Termini, dus tijd om wat te schrijven. Een reizend drietal zit aan mijn tafel erg onreizig te wezen. Ze misten hun vroege trein en wachten drie uur op de volgende waar plaats op is. Tegenvaller, jongens. Een van hen slaapt, een ander zucht, de derde maakt de eerste wakker om naar de supermarkt te gaan. Ik wijs hen de weg.

10:00

Een jongedame schrijdt voorbij, mannenhoofden volgen haar tred. De uurregeling laat weten dat mijn trein naar de luchthaven vertrekt over 22 minuten. Geen dag te vroeg, blijkbaar: stapelwolken stapelen hun wollige wolkheid dicht opeen hoog in de lucht die ik tussen twee overdekte perrons kan zien. De temperatuur is gedaald. Rome rouwt om mijn vertrek en ik begrijp haar.

10:59

De Leonardo Express, noemen ze hem, de trein naar de luchthaven. Nu weet ik niet wat ‘express’ betekent in het Italiaans, maar ik vermoed niet dat het iets te maken heeft met traagheid of vertragingen. Mijn vlucht vertrekt over twee uur, dus het mag wel eens gaan opschieten. Ik maak me niet druk, want het is (nog steeds) vakantie.

11:55

De expresstrein blijkt ook nog eens te boemelen in elke tussenhalte. Ik sta sardienachtig in de wandelgang van de trein, mensen zweten rondom mij. Volgende halte is luchthaven. Mijn vlucht vertrekt over een uur, dit wordt lekker spannend.

12:47

Nog 7 minuten voor de vlieger vertrekt. Ik zit er juist op. Persoonlijk aangesproken en opgevangen aan gate B07 (“you are mister Hiele?”), werd me met de glimlach verteld dat ik niet eens de laatste was en mijn excuses dus overbodig waren. Scheenbeenspierpijn die ik nog lang zal voelen, ik ben nooit een loper geweest. De arme jongeman naast me is vast niet erg te spreken over de gevolgen die zo’n loopwedstrijd voor mijn lijfgeur met zich mee draagt. Het spijt me, maar niet mijn schuld. De Italiaanse spoorwegen doen er onze NMBS uitzien als een eersteklas klantvriendelijk bedrijf.

Geen tijd gehad om mijn lege flesje nog te vullen, mijn droge keel huilt. Geldt het noodzakelijke uitschakelen van de mobiele telefoon ook voor smartphones, of is vluchtmode toch voldoende? Ik steek ‘m in elk geval weg.

16:07

Terug in Nederland, en dat merk je. Het regent grijzigheid en er valt alleen Heineken te krijgen. Bedankt maar nee bedankt. Nog een dik halfuur luisteren naar de artificiële stem die om de 5 seconden met een verschrikkelijke “mind your step” waarschuwt voor het einde van de rollende voetgangersvloer en ik ben weer onderweg naar Brussel.

17:58

De trip zit erop. Zo meteen vertrekt deze trein naar Leuven en ligt de Romereis voor altijd achter me. Ik dank iedereen die ik ontmoet heb op mijn reis en ook iedereen die het onbegrijpelijke doorzettingsvermogen kon opbrengen om deze nagenoeg onafgebroken stroom van woorden van het begin tot het einde te trotseren. U was een fantastisch publiek.

Java HTTP Wrapper

Below is the source of two Java classes I wrote a while ago. The purpose of these classes is to make HTTP web requests in Java by means of a HttpWrapper object. The Cookie class is used by the HttpWrapper class to store cookies per HttpWrapper object (rather than per domain, which would be advisory). A simple example of use:

HttpWrapper wrapper = new HttpWrapper();
wrapper.get("http://www.google.com/");
assert(wrapper.getHtml().contains("Google"));

I’m aware that it is flawed, both in logic as well as in security (cookies are sent cross-domain, see above), but the goal of all this is to allow Java programs to mimic some basic browser behavior where wanted. That is also the reason that this class spoofs the Mozilla user-agent headers.

Feel free to use and modify.

Download the jar file here.
Find the documentation here.

HttpWrapper.java

package honoki.web;

import java.io.*;
import java.net.*;

/**
 * This class provides an environment to easily make web requests (both GET and POST) to a server.
 * It supports the use of cookies and proxies.
 *
 * @author Pieter Hiele
 * @version 2.0
 */
public class HttpWrapper {

  /**
   * Default constructor of a HttpWrapper.
   */
  public HttpWrapper() {
    cookie = new Cookie();
    html = "";
    this.noProxy();
  }

  /**
   * A method to get the content of a given page, with a given referer.
   *
   * @param url
   * 			The given URL.
   * @param referer
   * 			The given referer.
   * @throws IllegalStateException
   * 			Whenever an IO-related problem occurs.
   * @post
   * 			new.getHtml() will return the headers and content of the given URL.
   */
  public void get(String url, String referer) {
    try {
      URL url_ = new URL(url);
      HttpURLConnection conn;
      conn = (HttpURLConnection) url_.openConnection(getProxy());
      conn.setRequestMethod("GET");
      conn.setAllowUserInteraction(false);
      conn.setDoOutput(false);
      conn.setInstanceFollowRedirects(false);

      conn.setRequestProperty("User-Agent", "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9) Gecko/2008061015 Firefox/3.0");
      conn.setRequestProperty("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
      conn.setRequestProperty("Accept-Language", "en-us,en;q=0.5");
      //conn.setRequestProperty("Accept-Encoding", "gzip,deflate");
      conn.setRequestProperty("Accept-Charset", "ISO-8859-1,utf-8;q=0.7,*;q=0.7");
      conn.setRequestProperty("Keep-Alive","300");
      conn.setRequestProperty("Connection", "keep-alive");
      conn.setRequestProperty("Referer", referer);
      if(!cookie.toString().isEmpty()) conn.setRequestProperty("Cookie", cookie.toString());

          // Get response-headers
          String headers = "";
          for(String key: conn.getHeaderFields().keySet())
          	headers += ((key != null)?key + ": ":"") + conn.getHeaderField(key) + "\n";

      // Get content
      BufferedReader d = new BufferedReader(new InputStreamReader(new DataInputStream(conn.getInputStream())));
      String result = "";
      String line = null;
      while ((line = d.readLine()) != null) result += line +"\n";
      d.close();

      cookie.setCookies(conn);
      setLastPage(url);

      setHtml(headers + "\n" + result);
    } catch (IOException e) {
      throw new IllegalStateException("An IOException occurred:\n"+e.getMessage());
    }
  }

  /**
   * A method to get headers and content of a given URL without specifying the referer.
   *
   * @param url
   * 			The given URL.
   * @throws IllegalStateException
   * 			Whenever an IO-related problem occurs.
   * @effect
   * 			| get(url, getLastPage())
   * @see
   * 			{@link #get(String, String)}
   */
  public void get(String url) throws IllegalStateException {
    get(url, getLastPage());
  }

  /**
   * Post the given postdata to a given url with a given referer, and return the response of the server.
   *
   * @param url
   * 			The given URL.
   * @param postdata
   * 			The given postdata.
   * @param referer
   * 			The given referer.
   * @throws IllegalStateException
   * 			Whenever a IO-related problem occurs.
   * @post
   * 			new.getHtml() will return the response of the web server on this request.
   */
  public void post(String url, String postdata, String referer) throws IllegalStateException {
    try {
      URL url_ = new URL(url);
      HttpURLConnection conn = (HttpURLConnection) url_.openConnection(getProxy());

      conn.setRequestMethod("POST");
      conn.setAllowUserInteraction(false);
      conn.setDoOutput(true);
      conn.setInstanceFollowRedirects(false);

      conn.setRequestProperty("User-Agent", "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9) Gecko/2008061015 Firefox/3.0");
      conn.setRequestProperty("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
      conn.setRequestProperty("Accept-Language", "en-us,en;q=0.5");
      //conn.setRequestProperty("Accept-Encoding", "gzip,deflate");
      conn.setRequestProperty("Accept-Charset", "ISO-8859-1,utf-8;q=0.7,*;q=0.7");
      conn.setRequestProperty("Keep-Alive","300");
      conn.setRequestProperty("Connection", "keep-alive");
      conn.setRequestProperty("Referer", referer);
      if(!cookie.toString().isEmpty()) conn.setRequestProperty("Cookie", cookie.toString());
      conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
      conn.setRequestProperty("Content-Length", Integer.toString(postdata.length()));

      // Write postdata
      OutputStreamWriter wr = new OutputStreamWriter(conn.getOutputStream());
          wr.write(postdata);
          wr.flush();
          wr.close();

          // Get response-headers
          String headers = "";
          for(String key: conn.getHeaderFields().keySet())
          	headers += ((key != null)?key + ": ":"") + conn.getHeaderField(key) + "\n";

          // Get content
      String result = "";
      String line = null;
      BufferedReader d = new BufferedReader(new InputStreamReader(new DataInputStream(conn.getInputStream())));
      while ((line = d.readLine()) != null) result += line +"\n";
      d.close();

      cookie.setCookies(conn);
      setLastPage(url);

      setHtml(headers + "\n" + result);
    } catch (IOException e) {
      throw new IllegalStateException("An IOException occurred:\n"+e.getMessage());
    }
  }

  /**
   * A method to get the response of the web server after requesting the given URL without specifying the referer.
   *
   * @param url
   * 			The given URL.
   * @param postdata
   * 			The given postdata.
   * @throws IllegalStateException
   * 			Whenever a IO-related problem occurs.
   * @effect
   * 			| post(url, postdata, getLastPage())
   * @see
   * 			{@link #post(String, String, String)}
   */
  public void post(String url, String postdata) throws IllegalStateException {
    post(url, postdata, getLastPage());
  }

  /**
   * Set the value of the last page that was visited.
   *
   * @param lastpage
   * 			The new value of the last page.
   * @throws IllegalArgumentException
   * 			If the given page is null.
   * @post
   * 			| new.getLastPage().equals(lastpage);
   */
  public void setLastPage(String lastpage) {
    if(lastpage == null) throw new IllegalArgumentException("The given page must not be null.");
    this.lastpage = lastpage;
  }

  /**
   * Return the last page that was visited.
   */
  public String getLastPage() {
    return lastpage;
  }

  /**
   * Clear the last page that was visited.
   *
   * @effect	| setLastPage("");
   */
  public void clearLastPage() {
    setLastPage("");
  }

  /**
   * The page that was last visited.
   */
  private String lastpage = "";

  /**
   * Set a custom cookie for this HttpWrapper.
   *
   * @param cookie	The given cookie.
   * @post			| new.getCookie().equals(cookie);
   */
  public void setCookie(Cookie cookie) {
    if(cookie == null) throw new IllegalArgumentException("The given cookie must not be null.");
    this.cookie = cookie;
  }

  /**
   * Return a clone of the Cookie of this HttpWrapper.
   */
  public Cookie getCookie() {
    return cookie.clone();
  }

  /**
   * The cookies of this wrapper.
   * Be aware that the cookies are not stored per domain name, but per instance of a HttpWrapper.
   */
  private Cookie cookie;

  /**
   * Return the html content that this Wrapper has last retrieved from a request.
   */
  public String getHtml() {
    return this.html;
  }

  /**
   * Set the html content of this HttpWrapper.
   *
   * @param html
   * 			The new html content.
   */
  private void setHtml(String html) {
    this.html = html;
  }

  /**
   * The html content of the last page this HttpWrapper requested.
   */
  private String html;

  /**
   * Set a proxy for this HttpWrapper.
   *
   * @param host
   * 			The host of the proxy.
   * @param port
   * 			The port of the proxy.
   * @see
   * 			{@link #noProxy()} to disable the use of a proxy.
   */
  public void setProxy(String host, int port) {
    this.proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(host, port));
  }

  /**
   * Disable the use of a proxy for this HttpWrapper.
   */
  public void noProxy() {
    this.proxy = Proxy.NO_PROXY;
  }

  /**
   * Return the proxy of this HttpWrapper.
   */
  private Proxy getProxy() {
    return this.proxy;
  }

  /**
   * The proxy to be used with this wrapper.
   */
  private Proxy proxy;

}

Cookie.java

package honoki.web;

import java.io.Serializable;
import java.net.URLConnection;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.StringTokenizer;

/**
 * This class describes a cookie. It is designed to work with {@link honoki.web.HttpWrapper}.
 * A Cookie instance of this class does not differentiate between different domains,
 * so the user is expected to handle the cookies appropriately.
 *
 * @author Pieter Hiele
 * @version 1.1
 */
public class Cookie implements Serializable {

  private static final long serialVersionUID = 1L;

  /**
   * The default constructor.
   */
  public Cookie() { }

  /**
   * This constructor allows you to provide a textual cookie that will be stored.
   *
   * @param cookie
   * 			The cookie to be stored.
   */
  public Cookie(String cookie) {
    setCookies(cookie);
  }

  /**
   * Read the cookies from the given connection and save them in this Cookie.
   *
   * @param conn
   * 			The given connection to extract the cookies from.
   */
  public void setCookies(URLConnection conn) {
    int i=1;
    String hdrKey, hdrString, aCookie;
    while ((hdrKey = conn.getHeaderFieldKey(i)) != null) {
      if (hdrKey.equals("Set-Cookie")) {
        hdrString = conn.getHeaderField(i);
        StringTokenizer st = new StringTokenizer(hdrString, ",");
        while (st.hasMoreTokens()) {
          String s = st.nextToken();
          aCookie = (s.indexOf(";") > -1) ? s.substring(0, s.indexOf(";")) : s;
          int j = aCookie.indexOf("=");
          if (j > -1)	cookies.put(aCookie.substring(0, j), aCookie.substring(j + 1));
        }
      }
      i++;
    }
  }

  /**
   * Read the cookies from a string and save them in this Cookie.
   *
   * @param cookie
   * 		A cookie formatted as a string.
   */
  public void setCookies(String cookie) {
    String cookies[] = cookie.split("; ");
    String parts[];
    for(String c: cookies) {
      if(c.contains("=")) {
        parts = c.split("=",2);
        this.cookies.put(parts[0], parts[1]);
      }
    }
  }

  /**
   * Return a textual representation of this Cookie.
   */
  public String toString() {
    String result = "";
    Enumeration keys = cookies.keys();
    while (keys.hasMoreElements()) {
      String key = (String)keys.nextElement();
      result += key + "=" + cookies.get(key);
      if (keys.hasMoreElements()) result += "; ";
    }
    return result;
  }

  /**
   * Clear the current cookie of this wrapper.
   *
   * @post
   * 			| new.getCookies().equals("")
   */
  public void clearCookies() {
    cookies.clear();
  }

  /**
   * Remove the cookie with the given name.
   *
   * @param name
   * 		The name of the cookie to remove.
   */
  public void removeCookie(String name) {
    cookies.remove(name);
  }

  /**
   * The actual data structure to store the cookie parts in.
   */
  private Hashtable cookies = new Hashtable();

  /**
   * Returns a clone of this Cookie.
   */
  @Override
  public Cookie clone() {
    Cookie clone = new Cookie();
    clone.setCookies(this.toString());
    return clone;
  }

}