Building a Rust Project that Uses reqwest on Project IDX


I’ve been working on a project that shows users their current public IP address and will eventually add some additional useful tools people can use. Yesterday I got stuck trying to make an external API call to https://ipgeolocation.io/ and after doing some research I decided to add the reqwest crate to my project. In Google’s IDX platform, this wasn’t as easy as I was expecting.

Lets talk about the problem and how I fixed it.

My Starting Point

The NIX package manager in IDX handles the installation of packages in its own very unique and repeatable way. I won’t get into it here, but you can read all about it HERE. I like it, but using the implementation in IDX can be cumbersome and I think still lacks some of the customization that NIX seems to depend on.

I bring that up because solving this problem required modifications to the packages I installed via NIX along with modifications to my Cargo.toml file.

Working State

I had a working project with a dev.nix file and a Cargo.toml file that looked like this.

name = "ip-reflector-service"
version = "0.1.4"
edition = "2021"
description = "Returns the public IP address of the endopint making the call to the service."
license = "BSD-2-Clause"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
axum = "0.7.4"
base64 = "0.21.7"
handlebars = { version = "5.1.1", features = ["dir_source"] }
serde = { version = "1.0.195", features = ["derive"] }
tokio = { version = "1.35.1", features = ["full"] }
tower-http = { version = "0.5.1", features = ["cors", "fs"] }

Cargo.toml

# To learn more about how to use Nix to configure your environment
# see: https://developers.google.com/idx/guides/customize-idx-env
{ pkgs, ... }: {
  channel = "stable-23.11"; # "stable-23.11" or "unstable"
  # Use https://search.nixos.org/packages to  find packages
  packages = [
    pkgs.cargo
    pkgs.rustc
    pkgs.stdenv.cc
    pkgs.cargo-watch
  ];
  # Sets environment variables in the workspace
  env = {
    RUST_SRC_PATH = "${pkgs.rustPlatform.rustLibSrc}";
  };
  # search for the extension on https://open-vsx.org/ and use "publisher.id"
  idx.extensions = [
    "rust-lang.rust-analyzer"
    "tamasfe.even-better-toml"
    "serayuzgur.crates"
    "vadimcn.vscode-lldb"
    "WakaTime.vscode-wakatime"
    "rangav.vscode-thunder-client"
    "ritwickdey.LiveServer"
  ];
}

dev.nix

The Problem

Cargo would build the package just fine at this point. However, as soon as I ran cargo add reqwest the build would fail with the following errors. (Apologies for the length)

Compiling openssl-sys v0.9.99
error: failed to run custom build command for `openssl-sys v0.9.99`

Caused by:
  process didn't exit successfully: `/home/user/ip-reflector-service/target/debug/build/openssl-sys-8cd9a23bda120eef/build-script-main` (exit status: 101)
  --- stdout
  cargo:rerun-if-env-changed=X86_64_UNKNOWN_LINUX_GNU_OPENSSL_LIB_DIR
  X86_64_UNKNOWN_LINUX_GNU_OPENSSL_LIB_DIR unset
  cargo:rerun-if-env-changed=OPENSSL_LIB_DIR
  OPENSSL_LIB_DIR unset
  cargo:rerun-if-env-changed=X86_64_UNKNOWN_LINUX_GNU_OPENSSL_INCLUDE_DIR
  X86_64_UNKNOWN_LINUX_GNU_OPENSSL_INCLUDE_DIR unset
  cargo:rerun-if-env-changed=OPENSSL_INCLUDE_DIR
  OPENSSL_INCLUDE_DIR unset
  cargo:rerun-if-env-changed=X86_64_UNKNOWN_LINUX_GNU_OPENSSL_DIR
  X86_64_UNKNOWN_LINUX_GNU_OPENSSL_DIR unset
  cargo:rerun-if-env-changed=OPENSSL_DIR
  OPENSSL_DIR unset
  cargo:rerun-if-env-changed=OPENSSL_NO_PKG_CONFIG
  cargo:rerun-if-env-changed=PKG_CONFIG_x86_64-unknown-linux-gnu
  cargo:rerun-if-env-changed=PKG_CONFIG_x86_64_unknown_linux_gnu
  cargo:rerun-if-env-changed=HOST_PKG_CONFIG
  cargo:rerun-if-env-changed=PKG_CONFIG
  cargo:rerun-if-env-changed=OPENSSL_STATIC
  cargo:rerun-if-env-changed=OPENSSL_DYNAMIC
  cargo:rerun-if-env-changed=PKG_CONFIG_ALL_STATIC
  cargo:rerun-if-env-changed=PKG_CONFIG_ALL_DYNAMIC
  cargo:rerun-if-env-changed=PKG_CONFIG_PATH_x86_64-unknown-linux-gnu
  cargo:rerun-if-env-changed=PKG_CONFIG_PATH_x86_64_unknown_linux_gnu
  cargo:rerun-if-env-changed=HOST_PKG_CONFIG_PATH
  cargo:rerun-if-env-changed=PKG_CONFIG_PATH
  cargo:rerun-if-env-changed=PKG_CONFIG_LIBDIR_x86_64-unknown-linux-gnu
  cargo:rerun-if-env-changed=PKG_CONFIG_LIBDIR_x86_64_unknown_linux_gnu
  cargo:rerun-if-env-changed=HOST_PKG_CONFIG_LIBDIR
  cargo:rerun-if-env-changed=PKG_CONFIG_LIBDIR
  cargo:rerun-if-env-changed=PKG_CONFIG_SYSROOT_DIR_x86_64-unknown-linux-gnu
  cargo:rerun-if-env-changed=PKG_CONFIG_SYSROOT_DIR_x86_64_unknown_linux_gnu
  cargo:rerun-if-env-changed=HOST_PKG_CONFIG_SYSROOT_DIR
  cargo:rerun-if-env-changed=PKG_CONFIG_SYSROOT_DIR
  cargo:rerun-if-env-changed=OPENSSL_STATIC
  cargo:rerun-if-env-changed=OPENSSL_DYNAMIC
  cargo:rerun-if-env-changed=PKG_CONFIG_ALL_STATIC
  cargo:rerun-if-env-changed=PKG_CONFIG_ALL_DYNAMIC
  cargo:rerun-if-env-changed=PKG_CONFIG_PATH_x86_64-unknown-linux-gnu
  cargo:rerun-if-env-changed=PKG_CONFIG_PATH_x86_64_unknown_linux_gnu
  cargo:rerun-if-env-changed=HOST_PKG_CONFIG_PATH
  cargo:rerun-if-env-changed=PKG_CONFIG_PATH
  cargo:rerun-if-env-changed=PKG_CONFIG_LIBDIR_x86_64-unknown-linux-gnu
  cargo:rerun-if-env-changed=PKG_CONFIG_LIBDIR_x86_64_unknown_linux_gnu
  cargo:rerun-if-env-changed=HOST_PKG_CONFIG_LIBDIR
  cargo:rerun-if-env-changed=PKG_CONFIG_LIBDIR
  cargo:rerun-if-env-changed=PKG_CONFIG_SYSROOT_DIR_x86_64-unknown-linux-gnu
  cargo:rerun-if-env-changed=PKG_CONFIG_SYSROOT_DIR_x86_64_unknown_linux_gnu
  cargo:rerun-if-env-changed=HOST_PKG_CONFIG_SYSROOT_DIR
  cargo:rerun-if-env-changed=PKG_CONFIG_SYSROOT_DIR
  run pkg_config fail: Could not run `PKG_CONFIG_PATH=/usr/lib/pkgconfig PKG_CONFIG_ALLOW_SYSTEM_CFLAGS=1 pkg-config --libs --cflags openssl`
  The pkg-config command could not be found.

  Most likely, you need to install a pkg-config package for your OS.
  Try `apt install pkg-config`, or `yum install pkg-config`,
  or `pkg install pkg-config`, or `apk add pkgconfig` depending on your distribution.

  If you've already installed it, ensure the pkg-config command is one of the
  directories in the PATH environment variable.

  If you did not expect this build to link to a pre-installed system library,
  then check documentation of the openssl-sys crate for an option to
  build the library from source, or disable features or dependencies
  that require pkg-config.

  --- stderr
  thread 'main' panicked at /home/user/.cargo/registry/src/index.crates.io-6f17d22bba15001f/openssl-sys-0.9.99/build/find_normal.rs:190:5:


  Could not find directory of OpenSSL installation, and this `-sys` crate cannot
  proceed without this knowledge. If OpenSSL is installed and this crate had
  trouble finding it,  you can set the `OPENSSL_DIR` environment variable for the
  compilation process.

  Make sure you also have the development packages of openssl installed.
  For example, `libssl-dev` on Ubuntu or `openssl-devel` on Fedora.

  If you're in a situation where you think the directory *should* be found
  automatically, please open a bug at https://github.com/sfackler/rust-openssl
  and include information about your system as well as this message.

  $HOST = x86_64-unknown-linux-gnu
  $TARGET = x86_64-unknown-linux-gnu
  openssl-sys = 0.9.99


  It looks like you're compiling on Linux and also targeting Linux. Currently this
  requires the `pkg-config` utility to find OpenSSL but unfortunately `pkg-config`
  could not be found. If you have OpenSSL installed you can likely fix this by
  installing `pkg-config`.


  note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

So many errors

So if you look through those error messages you’ll see that while trying to build the openssl-sys crate, cargo is unable to find information about the install location for OpenSSL and supporting libs. Now I played with this for a while before coming to a solution that worked for me. I tried adding the pkg-config package with no luck, adding the OpenSSL package by itself didn’t help either, and I just decided not to track down and hard code the OPENSSL_DIR variables it was looking for because I figured it would break the build in Cloud Build.

I was just having a heck of a time getting cargo to use the system install of OpenSSL. So to fix this, I figured out I can just have cargo build the openssl-src crate and we can link to that when we need to build the reqwest crate.

How I fixed it

In order to get the openssl-src crate to compile I had to first update my dev.nix file so that build packages would be present in my developer environment. After that I just updated my Cargo.toml file to use the crates I needed and the build worked!

dev.nix

To make sure I had all of the build dependencies required for the openssl-crate, I had to add three packages.

  • pkgs.openssl
  • pkgs.perl
  • pkgs.gnumake

Once I added these to my dev.nix file, I rebuild my dev environment and was then ready to update my Cargo.toml file. The final version of my dev.nix file is below.

# To learn more about how to use Nix to configure your environment
# see: https://developers.google.com/idx/guides/customize-idx-env
{ pkgs, ... }: {
  channel = "stable-23.11"; # "stable-23.11" or "unstable"
  # Use https://search.nixos.org/packages to  find packages
  packages = [
    pkgs.cargo
    pkgs.rustc
    pkgs.stdenv.cc
    pkgs.cargo-watch
    pkgs.openssl # <-- Added
    pkgs.perl # <-- Added
    pkgs.gnumake # <-- Added
  ];
  # Sets environment variables in the workspace
  env = {
    RUST_SRC_PATH = "${pkgs.rustPlatform.rustLibSrc}";
  };
  # search for the extension on https://open-vsx.org/ and use "publisher.id"
  idx.extensions = [
    "rust-lang.rust-analyzer"
    "tamasfe.even-better-toml"
    "serayuzgur.crates"
    "vadimcn.vscode-lldb"
    "WakaTime.vscode-wakatime"
    "rangav.vscode-thunder-client"
    "ritwickdey.LiveServer"
  ];
}

Completed dev.nix file

Cargo.toml

After my rebuild I added openssl and reqwest to my Cargo.toml file. When you add the openssl crate make sure that you enable the vendored cargo feature. Otherwise this won’t build the openssl-src crate, which is the entire reason we’re doing it this way.

My final Cargo.toml file ended up looking like this. (The json feature isn’t required for this to work. I just needed it for my project.)

[package]
name = "ip-reflector-service"
version = "0.1.4"
edition = "2021"
description = "Returns the public IP address of the endopint making the call to the service."
license = "BSD-2-Clause"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
axum = "0.7.4"
base64 = "0.21.7"
handlebars = { version = "5.1.1", features = ["dir_source"] }
openssl = { version = "0.10.63", features = ["vendored"] } # <-- Added
reqwest = { version = "0.11.23", features = ["json"] } # <-- Added
serde = { version = "1.0.195", features = ["derive"] }
tokio = { version = "1.35.1", features = ["full"] }
tower-http = { version = "0.5.1", features = ["cors", "fs"] }

Completed Cargo.toml file

Conclusion

With a couple of changes to my dev.nix and Cargo.toml file I was able to get back to my project! A lot of this information was pulled together from different sources. I didn’t find any documentation that perfectly fit my situation, so hopefully it’ll help you out too!

P.S. I did test this with my existing Cloud Build pipeline and although it did build without issues, the build time wend up by 4 minutes. If you’re working on something that already has a long build time, this may not be the best solution for you.