Performing HTTP request in Pony
by Paweł Świątkowski
Recently I got interested in Pony. It is an actor-based and compiled to native code language which show a really good design. Basically, looks promising.
As this is not my first niche language, I know that there are many downsides to such ecosystems. One of them, probably most important, is lack of packages or tutorials for “common operations”. You have to do the hard work yourself, by compiling fragments of available documentation and digging through the source code. The result can be really rewarding, maybe even worth a blogpost? This one is born out of this: the need to perform HTTP request in Pony.
For Pony, there is an official example available. But I think it is a bit intimidating. It is “HTTP request done as right as possible”, but I did not want this. I wanted to perform it with as little code as possible and without using more complex language features. Here’s what I got:
There are quite a few things going on here, I’d like to go through them step by step. So, first of all, I import some packages using a
use keyword. Nothing fancy. Than I define
Main actor, which is a bit special – it’s like
main function in C (more precisely, its
create constructor, which we will talk about later). First complex thing is a
get_ssl_context function. Since I’m going to perform a
GET on Github API (which only supports HTTPS), I need this for the request to be done. The
cacert.pem file is taken from the
httpget.pony example linked above.
Then comes the constructor I mentioned above. You can tell it from
new keyword. Its only argument is Env, which is basically “the outside world”. Some variables are assigned and then a
try block is started. This is required because there are many things that might go wrong inside it and compiler won’t let me proceed without explicitly mentioning that it might happen. Note that this kind of block is also used in
get_ssl_context function, because I’m reading a file, which is also pretty unsafe stuff.
Inside the block, I create an instance of built-in
net/http, passing it fragment of
env and SSL context. Then I build a URL from a string. The apostrophe there is nothing special, it is just part of the variable name, denoting that it is somehow connected to the original
Here come a bit of Pony “magic” (quoted, because it’s not really magic; it’s just being explicit). First of all,
this~handle_response() it a partial application of
handle_response function. Using
recover ... end block creates an immutable reference to it. I also need to specify that it’s
val reference, because otherwise it won’t go with Payload creation in the line below.
Now I create a real payload of the request. The arguments are pretty obvious (I hope): a method, a url and a handler, which will be called asynchronously. The last part is important. In Pony, there are no blocking operations, so we have to have a handler here.
In the next line, the request if actually made by passing the payload to the client I defined before.
consume keyword is kind of like a destructive read, by which we let the compiler know that it won’t be ever used again (which also makes it concurrent-safe automatically).
The last part is to define an actual handler. This is simple. It is a behaviour (not a function, but behaviour is basically an asynchronous function) that takes two arguments: a request and a response. All I do is to print response status to stdout.
Now, after I compiled and run the code, the response written was
403, which is not quite what I expected. To solve the mystery, I had to print the response body too. So I changed my handler to:
After that, I saw this:
Request forbidden by administrative rules. Please make sure your request has a User-Agent header (http://developer.github.com/v3/#user-agent-required). Check https://developer.github.com for other possible causes.
So, it turns out that you can’t make requests to Github API without specifying
User-Agent header. This was easily fixed by adding a line to the constructor:
And now everything worked.
This was the simplest way I found to perform a HTTP request. After all it’s not as hard as it looked and I learned quite a few useful things about the language. I hope to explore it more in the future, for example with parsing JSON from the response and do something with it.