Saturday, 8 March 2014

Modifying an HTTP Redirect Request with HttpClient 4.x

So I find myself doing a lot of HTTP work in Java, and in general, my client library of choice is the excellent HttpClient. This has changed a lot in version 4 (generally for the better) but as most of the web search results still point to 3.x version issues, it's difficult to find a solution to a problem.

One issue I've had a couple of times now, is handling redirects that are more complicated than the basic "resend the same request to the new location".

I was making a request to one system, which required an Authorization header. This system then redirected me to a file on Amazon S3.

There's a problem with this - Amazon S3 also uses an "Authorization" header, and it was attempting to read my header, rejecting and giving me a 400 Bad Request.

So how do we solves this and the general problem - how can we modify a request in the event of a redirect?

One option is to disable the automatic following of redirects and do all redirecting manually - but this is a lot of work, and it's so easy to have HttpClient handle the redirects for us.

Instead, HttpClient has a RedirectStrategy interface, whose implementing class DefaultRedirectStrategy is executed on every redirect. So, all that is needed, is to create and enable our own RedirectStrategy which strips out the Authorization header if the request is for S3.

To save time and effort, it's easier to just extended DefaultRedirectStrategy:

public class AmazonRedirectStrategy extends DefaultRedirectStrategy {

    @Override
    public HttpUriRequest getRedirect(HttpRequest request, HttpResponse response, HttpContext context) throws ProtocolException {

        Header[] locationHeaders = response.getHeaders("Location");
        if (locationHeaders.length >= 1) {
            if (locationHeaders[0].getValue().contains("s3.amazonaws.com")) {
                ((RequestWrapper)request).getOriginal().removeHeaders("Authorization");
            }
        }
        return super.getRedirect(request, response, context);
    }
}

You have to cast the original request into a RequestWrapper which according to the JavaDoc is:
"A wrapper class for HttpRequests that can be used to change properties of the current request without modifying the original object."

Then, you need to register this class with your HttpClient. With the latest version, you set this on the HttpClientBuilder whilst you're building the client. If you're using the older < 4.3 DefaultHttpClient, you can set this directly.

That's it! Using this method, you can handle a lot of custom redirect needs without having to perform all the redirecting manually.

I hope someone finds this useful, any questions, errors etc, please leave in the comments.

Will







Saturday, 25 January 2014

Compiling GTK+ 2 on Amazon Linux

It's the usual story, I need some small seemingly simple library installed - in this case UFRaw so I could get ImageMagick to convert raw camera images, and I end up spending hours trying to get a dependency built - this time it was GTK+ 2, which has a load of dependencies which themselves aren't too easy to build.

So I've had to do this a couple of times now, and each time, I completely forget how to do it and spend ages fumbling with the wrong libraries - hence the write up.

I install everything into /usr/local/. If you want to put it somewhere else just make sure to update the envars as well as the configure prefix.

Tip: You'll need root privileges to run "make install"

   1. Set up environment variables so pkg-config can find the libs we're installing.
export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig
export LD_LIBRARY_PATH=/usr/local/lib
   2. Install dependencies that are available in the yum repo.
yum install libffi-devel libjpeg-devel libpng-devel libtiff-devel freetype-devel fontconfig-devel pango-devel gcc gettext

   3.  Make a directory to hold all the sources
mkdir ~/gtk_sources

   4. Install glib 2.38. As of writing, this is the most recent version. ATK needs at least 2.31,and this isn't available in the repos.
cd ~/gtk_sources
wget http://ftp.gnome.org/pub/gnome/sources/glib/2.38/glib-2.38.2.tar.xz
tar -xJf glib-2.38.2.tar.xz
cd glib-2.38.2
./configure --prefix=/usr/local
make
make install

   5. Install ATK
cd ~/gtk_sources
wget http://ftp.gnome.org/pub/GNOME/sources/atk/2.10/atk-2.10.0.tar.xz
tar -xJf atk-2.10.0.tar.xz
cd atk-2.10.0
./configure --prefix=/usr/local
make
make install


   6. Install gdk-pixbuf
cd ~/gtk_sources
wget http://ftp.gnome.org/pub/GNOME/sources/gdk-pixbuf/2.29/gdk-pixbuf-2.29.3.tar.xz
tar -xJf gdk-pixbuf-2.29.3.tar.xz
cd gdk-pixbuf-2.29.3
./configure --prefix=/usr/local
make
make install

   7.  Install GTK+ 2. (2.24.22)
cd ~/gtk_sources
wget http://ftp.gnome.org/pub/gnome/sources/gtk+/2.24/gtk+-2.24.22.tar.xz
tar -xJf gtk+-2.24.22.tar.xz
cd gtk+-2.24.22
./configure --prefix=/usr/local
make
make install


Hopefully all works and GTK is installed.


I've tested this on a clean install of Amazon Linux at the time of posting so should be good, but if anyone notices a mistake please comment.