Using com.sun.net.httpserver

One of the things I needed for mp3d was an HTTP server. Sun added one to JDK6 – com.sun.net.httpserver – but the documentation's a bit sparse, there's no example code (and the snippet in the package description doesn't compile), and judging by the results of my web searches, it's not widely used.

Despite these obstacles, it was pretty easy to do everything I needed. Here are a few notes from my experiences.

Getting query parameters

If you're using HTML forms with the GET method, you'll need access to your query parameters. The obvious way to get them would be to call HttpExchange.getRequestURI and then call URI.getQuery on the result. Unfortunately, that's broken: URI's built-in decoder only copes with %xx escapes, and doesn't know that '+' needs to be translated to ' '. Bizarre.

What you need to do is use URLDecoder on the result of URI.getRawQuery instead.

(Either way you need to break the string into key-value pairs yourself.)

Getting form data

If, on the other hand, your HTML forms use the POST method, you'll need access to your form data. Here the obvious scheme works fine: just read the input stream. Again, I'm surprised there's no convenience method to help with this common task.

Setting headers in the HTTP response

If you want to set an HTTP header in your response, use the HttpExchange.getResponseHeaders method. That gives you a Headers object which is just a map. You can use Headers.put as if it were a plain old HashMap or whatever, but I preferred to be explicit and use Headers.add or Headers.set to make my exact intention clear.

(Getting the response headers object and calling a setter on that is pretty obvious when you know, but it took me longer to find than it should have done, in part because I wasn't sure the functionality even existed, but mainly because I was looking to start my journey with a setter, not a getter.)

Exception handling

Exceptions not caught by your HttpHandler implementations are reported, but in very spartan fashion, without stack traces. There's also no way to set an uncaught exception handler (as there is with AWT, say). You'll probably want to write your own code to deal with this. An obvious possibility is to write your own abstract class implementing HttpHandler with a "handle" method that catches and reports anything thrown by your class' new abstract method that does the actual handling of the request.

HTTP response codes

If you want constants for HTTP response codes so your code's not full of magic numbers such as 404 and 307, look at HttpURLConnection (from the standard java.net package). Then you can use, for example, the more obvious HttpURLConnection.HTTP_NOT_FOUND instead of 404.

GZIP content encoding

There's no built-in support for the GZIP content encoding, but it's pretty easy to do it yourself. Given an HttpExchange 't':

t.getResponseHeaders().set("Content-Encoding", "gzip");
t.sendResponseHeaders(HttpURLConnection.HTTP_OK, 0);
final GZIPOutputStream os = new GZIPOutputStream(t.getResponseBody());

You might want to check the request headers to ensure that the client can cope with this encoding, but I couldn't be bothered. As the Zen master said, a bug's not a bug until it inconveniences somebody.

How does HttpServer choose the appropriate HttpContext?

When you set up your HttpServer, you add "contexts", which are basically associations between a path and the HttpHandler that corresponds to that path. You can just have one that parses the path itself, if you like. The current implementation is an unsorted linked list, but that's fine for the kinds of things you'd sensibly use this code for.

Is it kosher?

You'll have noticed that the HTTP server packages are com.sun.* packages. Although they're mentioned in the Networking Features part of the JDK6 release notes, they're not indexed in the Java Platform API Specification.

The rules for com.sun.* packages aren't mentioned in the JDK6 documentation's Note About sun.* Packages.

As I understand it, com.sun.* differs from sun.* in that Sun supports the former but not the latter. In neither case are you guaranteed that it'll be available in other implementations. (For the record, Apple's Mac OS 10.5 JDK6 does include the com.sun.net.httpserver packages.)

Past experience suggests that a com.sun.* API in one JDK release won't necessarily be in the next, so you might not want to base your business on this API. But for a weekend hack that would be equally fine with an 80-line HTTP server using little more than a ServerSocket, it's a handy jump start.

Sample code?

Even I have better things to do than write you a nice little tutorial, but the mp3d source currently contains working examples of all the above.

I'm pretty sure I spent longer writing this post than I did working any of this stuff out or writing the code.