Making a Basic HTTP Request in Eiffel (and a rant)

By Loren Segal on September 09, 2008 at 919:957:346 PM

I originally just wanted to post my findings on implementing an HTTP client to serve as an example to others learning Eiffel, but I realized that the importance of the following example would be lost on many people without a little background on the subject and felt like an explanation in the form of a rant should also be included. Therefore, if you’re just looking for the example, scroll down to the later sections and take a look. Otherwise, keep reading.

Don’t know Eiffel? Start here

This year I learn Eiffel, a very unique language that’s frankly ahead of its time. Everything in the language comes nearly straight out of a Software Engineering textbook and implements many of the best practices and theoretical “right ideas”. Central to the language is the paradigm of “Design by Contract“, the concept that the public interface (API) of your modules are essentially your bible, the central core of your designs, and like with any contract, the behaviour of your published features should be upheld. Doing this upholding is essentially what we call “verifiability”. As mentioned above Eiffel does this in a very “engineering” like manner with pre and post-conditions that can come right out of the use case stories from your requirements analysis phase. In the end, your API essentially becomes not a class with members, but a class with “features” (an actual keyword in the language itself, directly analogous to the actual engineering principle of features and functional requirements) that your product supports, much like a car would have certain features that are all guaranteed to exist and function properly (like brakes!).

I also just want to point out how awesome the EiffelStudio IDE is. Not only does it compile to C or MSIL (.NET), but it has great support for refactoring and even software metrics, the latter being something I’ve never even seen before in a standard IDE. Example: find me all the obsolete (deprecated) features (methods/attributes) in my system:

Really cool stuff. I’m not sure it’s worth $6000, but it does explain a little about the pricing scheme.

The rant

Unfortunately, I so far have two gripes. Firstly, it looks as though the people behind Eiffel are targeting the “large enterprise” market heavily (how do I know? Their official IDE is damn expensive for commercial use). Given the features of the language, this makes perfect business sense in the short term, though niching the language could be detrimental to its ability to influence the computer science industry as a whole which could end up being a bad business decision in the long run, where languages can outlive certain “enterprises” these days. The second gripe brings me to my post:

Eiffel libraries seem to be very poorly documented, and their API’s are… well, crap.

I wanted to practice my Eiffel by writing a simple client that would pull some data from a web resource. The class I had to deal with was the HTTP_PROTOCOL class (yes, classes are uppercased in Eiffel, deal with it). I can only assume through a long amount of scouring that this is the highest level HTTP class that EiffelNet (Eiffel’s standard networking library) has. Look at that API and try to tell me its usage seems obvious (queries are methods or attributes that return values, commands are methods that don’t). You can’t. Ignoring the fact that it doesn’t even seem to implement any method beyond “GET”, the API is completely unobvious and the “feature” documentation surely doesn’t help (the doc. for initiate_transfer is: “Initiate transfer.”, I kid you not). Did you know you needed to call three methods before you can read data? You probably didn’t. Can you figure out what they are? As an author of a documentation tool, this is something that seriously bugs me.

Note that none of my problems are with the language. The concepts the language puts forward are exceptionally useful. The real problem here is library documentation, support, and frankly, poorly implemented standard libraries. It’s very disappointing.

A Simple HTTP GET Request in Eiffel (Code)

With sparse online resources and very few examples of net code using the above class, I wound up spending some (read: too much) time figuring it out myself. The (read: my) solution is to subclass the HTTP_PROTOCOL class of above. Let’s call our class HTTP_CLIENT. Here’s what the code actually looks like (I’ll explain what I did below in detail):

In a file http_client.e:

class
	HTTP_CLIENT

inherit
	HTTP_PROTOCOL
		export
			{ANY}
				headers,
				content_length,
				get_headers,
				get_content_length
		redefine
			check_error
		end

create
	make

feature -- HTTP requests

	get: STRING_8
		require
			not_open: not is_open
			not_initiated: not transfer_initiated
		do
			set_read_mode
			open
			initiate_transfer

			from
				Result := ""
			until
				not is_packet_pending or not is_readable
			loop
				read
				Result.append (last_packet)
			end

			close
		ensure
			no_packets_left: not is_packet_pending
			closed: not is_open
		end

feature {NONE} -- Implementation

	check_error do end

end

Code Explanation

This won’t be a tutorial on the syntax of Eiffel, there are two articles that should get you up to speed on what the code I wrote means. I do, however, want to point out some of nuances of why some of the lines I wrote need to be there.

1. Re-exposing the API

The first thing you’ll see in the code is that a bunch of headers and content-length features are exposed to ANY (that means “public to all” in Eiffel). Note that this functionality is part of Eiffel’s very extensive feature adaptation support (see the second tutorial above). When I was looking through the API trying to get things working, I realized that these methods were implemented, but made private. Why? This seems to me like some obvious bad API design. Accessing data such as response headers (and content length) seem like obvious requirements of any class implementing an HTTP protocol, so I made them public. They’re not actually used in my example app, but I might want to use them in the future. So might you, O’ user of my example.

2. Implementing the missing get feature

Obviously this is the heart of the new class. Oddly enough, the HTTP_PROTOCOL class did not implement a proper get feature that returned data from a request. Instead, the client of the class must manually read from the socket at the low level— more bad design. Well, that’s basically what this feature (must get used to saying that) does. You might notice the require and ensure statements. These are our pre and post conditions discussed above and handle the verification that our requirements have been met. As you may have guessed, this means “testing code” in plain old English. It’s nice to have test assertions alongside the code body, and this example shows how elegant these assertions can be. Note that the part before the colon (and the colon itself) is an optional open-ended label to name the specific assertion for debugging purposes. By the way, with Eiffel we can choose to ignore this in a production compilation of the system for efficiency.

3. Redefining check_error feature

This was an optional change, but this class is implemented to throw an error when it does not read an HTTP OK from the headers, essentially raising an error for any 404, 403, 503, etc. reply, and raising it with the same error code (symbolically called file_not_found). This wouldn’t be so bad (ignoring the lack of error code distinction) except for the fact that because of the pre/post condition verification done in the read feature, it’s impossible for this object to now read the body data from the socket (unless I’m wrong about this). Again, bad API design since an error code doesn’t mean the page body is useless to you, especially not if what you’re implementing is a web browser. The solution here is to use the <b>redefine</b> functionality (feature adaptation) to override the feature and basically make it do nothing. At least that’s a good enough thing for it to do for now.

Using the Class

Usage of the class is just a call on get after constructing the object with a URL. It can be exemplified by the following application code:

class APPLICATION create make
feature
	make
		local
			http: HTTP_CLIENT
			url: HTTP_URL
		do
			create url.make ("http://gnuu.org/2008/09/19/a-basic-http-req-in-eiffel-and-rant")
			create http.make (url)
			print (http.get + "%N")
		end
end

Hopefully you found this example useful. And hopefully someone can point out that I’m wrong about everything I said regarding Eiffel’s API/docs and point me to some proper API documentation and perhaps some updated (standard or widely used) libraries that have proper implementations for ubiquitous protocols like HTTP. I’m a little disappointed with Eiffel on this count and I would really love to be proven wrong about everything I said here. Really.

Questions? Comments? Follow me on Twitter (@lsegal) or email me.