newspaint

Documenting Problems That Were Difficult To Find The Answer To

Creating a Reverse Proxy For National Rail’s Mobile Website

The Nation Rail mobile website, http://m.nationalrail.co.uk/, thinks it is smarter than you. If you try and access this link from your desktop browser you will be redirected to the full website: http://www.nationalrail.co.uk/.

I wanted to create an iframe on a personal webpage so that I could keep up-to-date with the next departing train times. Let’s say you want to know what trains are departing London Waterloo and stopping at Surbiton. You could visit the full website live departures page (http://ojp.nationalrail.co.uk/service/ldbboard/dep/WAT/SUR/To) but the page has more than you might want in a smaller iframe. The mobile site view is what you’d prefer at http://m.nationalrail.co.uk/pj/ldbboard/dep/WAT/SUR/To using the following embedded code:

<style type="text/css">
iframe.infoframe {
  float: right;
}
</style>



<script type="text/javascript">
function updatetrainframe() {
  var myFrame = document.getElementById("idtrainframe");
  myFrame.src = "http://m.nationalrail.co.uk/pj/ldbboard/dep/PAD/RDG/To";
}

function startrepeat( callback, interval ) {
  callback();
  setInterval( callback, interval );
}

startrepeat( updatetrainframe, 1000 * 60 * 5 ); // update every 5 minutes starting now
</script>

Of course the problem is that the National Rail website hijacks your request to the mobile website and plonks you on the full desktop version of the website – which is not what you wanted (this is incredibly bad practice and somebody at National Rail should be fired for this: if someone goes to the effort to deliberately type “m.” instead of “www.” then it is clear they want the mobile version of the site).

Creating a Reverse Proxy Using Apache

Creating a reverse proxy is easy using the Apache web server. Create a new virtual site and add the following to the configuration:

ProxyPass / http://m.nationalrail.co.uk/
ProxyPassReverse / http://m.nationalrail.co.uk/
RequestHeader set User-Agent "Mozilla/5.0 (Linux; U; Android 2.2; nl-nl; Desire_A8181 Build/FRF91) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1"

This will proxy-pass requests to your virtual site to the National Rail mobile website and substitute the user-agent for a mobile phone’s – fooling the National Rail website into serving the content you wanted all along.

Creating a Reverse Proxy Using Node.JS

Maybe you don’t have the convenience of running an Apache webserver – or perhaps you do but want to use a different method.

If you use node.js then the following script will proxy-pass requests to the National Rail mobile website:

var http = require('http');

var debugging = 0;

function deleteAnyCase( associativeArray, keyString ) {
    for ( var key in associativeArray ) {
        if ( key.toLowerCase() === keyString.toLowerCase() ) {
            delete associativeArray[ key ];
        }
    }
}

function httpUserRequest( userRequest, userResponse ) {
    if ( debugging )
        console.log( '  > request: ' + userRequest.url );

    // create options object required for proxy request we will make
    var options = {
        host: 'm.nationalrail.co.uk',
        port: 80,
    };
    options.path = userRequest.url;
    options.headers = userRequest.headers;
    options.method = userRequest.method;
    deleteAnyCase( options.headers, "user-agent" );

    // we must fool National Rail into thinking we are mobile device
    options.headers['user-agent'] = 'Mozilla/5.0 (Linux; U; Android 2.2; nl-nl; Desire_A8181 Build/FRF91) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1'
    options.headers.host = options.host; // important: site name but no port
    delete options.headers.cookie; // stop National Rail tracking us

    if ( debugging )
        console.log( '  >   options = %j', options );

    // make request to national rail, and route data between user and target
    var proxyRequest = http.request(
        options,
        function (proxyResponse) {
            if ( debugging )
                console.log( '  < response' );

            if ( debugging )
                console.log( '      headers = %j', proxyResponse.headers );

            // protect again National Rail trying to redirect to new host
            var newHeaders = proxyResponse.headers;
            if ( "location" in newHeaders ) {
                newHeaders["location"] = newHeaders["location"].replace(
                    "http://m.nationalrail.co.uk", ""
                );
            }

            userResponse.writeHead(
                proxyResponse.statusCode,
                newHeaders
            );

            // route incoming data from National Rail to user response
            proxyResponse.on(
                'data',
                function (chunk) {
                    if ( debugging )
                        console.log( '  < response data' );

                    userResponse.write( chunk );
                }
            );

            proxyResponse.on(
                'end',
                function() {
                    if ( debugging )
                        console.log( '  < response end' );

                    userResponse.end();
                }
            );
        }
    );

    proxyRequest.on( 'error', function(error) {
        console.error( '  problem with request: ' + error.message );
    } );

    // route outgoing user request to National Rail
    userRequest.on( 'data', function(chunk) {
        if ( debugging )
            console.log( '  > request data' );

        proxyRequest.write( chunk );
    } );
    userRequest.on( 'end', function() {
        if ( debugging )
            console.log( '  > request end' );

        proxyRequest.end();
    } );
}

function main() {
    var port = 5555; // default port if none on command line

    // check for any command line arguments
    for ( var argn = 2; argn < process.argv.length; argn++ ) {
        if ( process.argv[argn] === '-p' ) {
            port = parseInt( process.argv[argn + 1] );
            argn++;
            continue;
        }

        if ( process.argv[argn] === '-d' ) {
            debugging = 1;
            continue;
        }
    }

    if ( debugging ) {
        console.log( 'server listening on port ' + port );
    }

    // start HTTP server with custom request handler callback function
    http.createServer( httpUserRequest ).listen(port);
}

main();

This script checks the command line for a port argument (and optionally a debugging flag) and will listen on that port and proxy any requests to the National Rail mobile website. By default it attaches to locahost:5555 – so you can run this script and fetch the departures from London Waterloo to Surbiton using the URL http://localhost:5555/pj/ldbboard/dep/WAT/SUR/To.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: