Use WinINet API for HTTP communication with Kotlin MPP and get rid of dependency on libcurl for Windows.
The story behind
We have decided to use Kotlin MPP for building our Localazy CLI tool for several reasons - we love Kotlin here at Localazy and our backend is mostly based on Kotlin, and we also love the idea of having a single codebase for Linux, Windows, macOS and JVM version.
You can read more information about how we built Localazy CLI with Kotlin MPP and fully automated its releases with Github Actions in a separate post.
Kotlin MPP + HTTP/HTTPS
One of the issues we had to face was how to implement HTTP communication. For the JVM version, we went with plain old HttpURLConnection and for native binaries, the ktor clients library seemed to be a viable option.
Everything worked well and during testing, the HTTP communication was flawless, but the ktor clients library introduced a dependency on libcurl. It’s not a big deal on Linux or macOS as you either already have libcurl installed or you can install it with a single line through APT, YUM or Homebrew.
On Windows, however, it was necessary to include almost 20 DLL files in the same directory where the CLI binary is placed. And that’s not the solution we were looking for.
Before we get to the technical solution, let’s talk about what we really needed. Localazy CLI only needed four HTTPS calls to verify your parameters, download metadata about the app for the validation step, upload your files, and download your files along with their metadata.
There were three GETs and one POST, and we needed to send custom headers for authorization and transfer of arbitrary data about the CLI version, etc.
No big deal. We only needed basic stuff and that gave us more possible solutions…
The first simple solution that came to our minds was to create an installer for the CLI app and install it as a normal Windows app and along the way, install also required DLLs. We immediately disliked this solution. That’s not the developer-friendly “get & use it” way and installers are enemies of automation.
Statically link libcurl to the binary
We rebuilt libcurl from source with all static mode enabled and with all external dependencies like OpenSSL disabled. Instead, we switched to Windows native subsystems. Also, we disabled support for all protocols except for HTTP. All these steps were about getting a statically linkable library that was as small as possible.
However, we had no luck. Linking was tricky because the ktor clients library was also trying to link against libcurl but not ours manually compiled one, and we ended up with a huge binary that still needed some dependencies.
While the ktor clients looked like a great solution initially, it showed up that for our simple requirements, it involved costs that we didn’t want to cover.
Switch to WinInet
Windows comes with its own native API for HTTP communication - WinINet. It’s relatively simple, and if the app uses it, there are no extra dependencies as WinInet is available directly in Windows.
I could not find any existing Kotlin MPP implementation of HTTP communication using WinInet API, so I started from scratch. Thankfully, Microsoft has pretty good documentation, and there are examples available in C/C++.
The source code based on what I wrote for Localazy CLI is available on Github. The aim wasn’t to implement all features but to create a fast solution for invoking GET and POST requests.
If you need more information about WinInet to add some features or improve my solution, visit Microsoft’s documentation for WinInet API.