How to use CURL in Vala
Tags: gtk, vala, curl, glib
Published: 2022-05-05 17:04:14

Hi everyone! Nowadays there are a lot of applications written in Vala that make HTTP requests and various libraries that can help with it. At the same time, there is a certain lack of information on how to make requests via curl. This article is aimed to cover the gap.

I've encountered this lack of info while working on the [Whaler](https://github.com/sdv43/whaler) app. The app calls Docker Engine API methods to manage Docker containers under the hood. 

At the start of the project, I faced a question: Which library should I use to make HTTP requests? There was an important requirement - the library had to support UNIX sockets.

There were three options:
- GLib.Socket
- libsoup-2.4
- libcurl

GLib.Socket required implementing a wrapper to work with the HTTP protocol. Libsoup-2.4 didn't support UNIX sockets (they became available starting from the 3.0 version). That’s why I skipped the first two and chose libcurl. It had Vala bindings and supported UNIX sockets.

## How to add libcurl to a project

First of all, check if libcurl is installed on your system. If not, make sure to install it.

Let’s have a look at a small demo project. 

Folder structure:
```
project/
    meson.build
    app.vala
    vapi/
        libcurl.vapi
        libcurl.deps
```

You can download libcurl.vapi and libcurl.deps on [List of Vala Bindings](https://wiki.gnome.org/Projects/Vala/ListOfBindings#A.22vala-extra-vapis.22_Git_Repository).

meson.build:
```meson
project('com.github.sdv43.vala-recipes.curl', 'vala', 'c', version: '1.0.0')

vapi_dir = meson.current_source_dir() / 'vapi'
add_project_arguments(['--vapidir', vapi_dir], language: 'vala')

executable(
    meson.project_name(),
    'app.vala',
    dependencies: [
        dependency('glib-2.0'),
        meson.get_compiler('vala').find_library('posix'),
        meson.get_compiler('vala').find_library('libcurl', dirs: vapi_dir),
        meson.get_compiler('c').find_library('libcurl'),
    ],
)
```

Now we can use CURL in the app.vala:
```vala
void main() {
    var curl = new Curl.EasyHandle();

    // set curl options

    var code = curl.perform();
    assert_true(code == Curl.Code.OK);
}
```

## Easy interface vs. Multi interface

CURL provides two different interfaces: Easy and Multi. The first one works synchronously and the second one, if I got it right, can make several requests simultaneously and works in asynchronous manner.

I chose to use the Easy interface because I found an [example](https://github.com/Richard-W/curl-vala) written in Vala. 

The last problem to be solved was the one with blocking the thread when calling `curl_perform`.

I decided to go with the first solution that came to mind. That is to call `curl_perform` in a separate thread by using GLib.Task. It prevents the main thread from blocking and allows the app to work correctly. Here is an example:

app.vala:
```vala
void main() {
    var curl = new Curl.EasyHandle();

    // set curl options

    var task = new Task (null, null, (source_obj, cl_task) => {
        var curl_code = (Curl.Code)cl_task.propagate_int ();
        assert_true(curl_code == Curl.Code.OK);
    });

    task.set_task_data (curl, null);

    task.run_in_thread ((cl_task, source_obj, curl, cancellable) => {
        unowned var cl_curl = (Curl.EasyHandle)curl;

        var code = cl_curl.perform ();
        cl_task.return_int (code);
    });
}
```

The final solution used in Whaler is available on Github: [HttpClient.vala](https://github.com/sdv43/whaler/blob/master/src/Utils/HttpClient.vala)

## Links

- Curl example from [Richard-W](https://github.com/Richard-W/curl-vala)
- [List of Vala Bindings](https://wiki.gnome.org/Projects/Vala/ListOfBindings#A.22vala-extra-vapis.22_Git_Repository)
- [CURL Manual](https://curl.se/libcurl/c/)

## Questions & What's next

I believe it is just one of many possible ways to make HTTP requests in Vala. So I would be happy to hear your thoughts:

- Are there any other HTTP libraries for Vala?
- Is it possible to create a solution without using GLib.Task? Can the Multi interface help with it?