Building HTTP Service in C++ (Using Modern Tools)
Tags: cpp, tutorial, todayilearned, webdev
Published: 2020-12-05 21:50:12

Today I will show you how to build an HTTP service in C++ using modern tools. The point of this article is to show how easy (or hard) it is to setup an environment for cross-platform C++ development, and package the binary in one of the modern distribution formats, container. 

First, a disclaimer: 

> I am not a professional C++ developers, and any opinions shared in this tutorial should not be treated as absolute truth!

That said, since I am not profession C++ developer, this tutorial is suitable for "everyone-and-their-mother". The shell commands shown should work in most recent versions of Linux and OSX. Windows users can find themselves making some extra research, but overall the tools used are cross-platform.

## Requirements

First things first, install the following tools:

- [CMake](https://cmake.org/) - system designed to build cross-platform software.
- [Conan](https://conan.io/) - C/C++ package manager.
- [Make](https://www.gnu.org/software/make/) - tool controlling the generation of executables.
- [Docker](https://www.docker.com/) - set of tools for OS-level virtualization.

## What are we building?

The HTTP service and is mostly based on [this particular example](https://github.com/boostorg/beast/tree/develop/example/http/server/small) from Boost Beast library. I modified it, replacing plaintext responses with json, and adding Boost Log library.

## Setting up development tools

Start by creating a `src` directory in the root of your project, and putting [this C++ file](https://gist.githubusercontent.com/hi-artem/07e896d5d7b43d1cf63cf58e8665524b/raw/73d3148603c16e0505af52071b7658e6eda967b4/main.cpp) inside:

```sh
mkdir src
curl -o src/main.cpp https://gist.githubusercontent.com/hi-artem/07e896d5d7b43d1cf63cf58e8665524b/raw/73d3148603c16e0505af52071b7658e6eda967b4/main.cpp
```

Although Boost is one of the well supported libraries, I always struggle manually installing it on my development machine. This is where Conan comes into play. For people familiar with Node, think of Conan as NPM or Yarn for C/C++.

Create `conanfile.txt` with the following content:

```ini
[requires]
boost/1.74.0

[generators]
cmake
```

Next, configure CMake to let it know about location of the C++ source file, and enable Conan integration. It can be done by creating `CMakeLists.txt` looking like so:

```cmake
cmake_minimum_required(VERSION 3.10)

project(http-service VERSION 0.0.1)
set(CMAKE_CXX_STANDARD 14)

include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
conan_basic_setup()

add_executable(http-service src/main.cpp)
target_link_libraries(http-service ${CONAN_LIBS})
```

## Building executable

This is all you need to build and run the project locally! The following shell script contains all the required build commands:

```sh
#!/bin/bash

# remove all build folder if exists
rm -rf build

# create new build folder
mkdir build && cd build

# install dependencies
conan install ..

# generate build files
cmake ..

# generate executable
make
```

Once build succeded the service can be started by running:

```sh
./build/bin/http-service 0.0.0.0 8080
```

You should see something like:

```sh
[2020-12-05 13:17:27.928789] [0x000000011670fdc0] [info]    Service is listening on 0.0.0.0:8080
```

Use `curl` or web browser, to test that service is running. For example:

```sh
curl -i 0.0.0.0:8000/api/status

## Should return something similar to:
HTTP/1.1 200 OK
Connection: close
Content-Type: application/json
Content-Length: 60

{"status":"OK","request_count":"1","timestamp":"1607196274"}
```

## Containerizing service

The previous steps configured development environment, however modern stacks require application to be packaged as container so it can be run by one of the orchestrators, like Kubernetes or Nomad.

Since we are using Docker to containerize the service, let's start by creating `Dockerfile`:

```dockerfile
# Create image with all required build tools
FROM ubuntu:18.04 as build-tools

RUN apt-get update && apt-get install -y \
  build-essential \
  cmake \
  python3-pip \
  && rm -rf /var/lib/apt/lists/*

RUN pip3 install conan


# Build binary
FROM build-tools as builder

WORKDIR /usr/src/service

COPY . .

WORKDIR /usr/src/service/build

RUN conan install ..
RUN cmake ..
RUN make


# Copy binary to fresh ubuntu image
FROM ubuntu:18.04 as runner

RUN groupadd -r ubuntu && useradd --no-log-init -r -g ubuntu ubuntu
USER ubuntu:ubuntu

COPY --from=builder /usr/src/service/build/bin/http-service /usr/bin/http-service
EXPOSE 8080

CMD [ "http-service", "0.0.0.0", "8080" ]
```

The image is created using multi-stage build process: first, creating the image with all required tools to build the service; second, build the service; finally, copy the executable in fresh ubuntu image. The process is run as non-root user to avoid privileges escalation.

The last thing before the image build, create `.dockerignore` file, which serves similar purpose as `.gitignore`:

```dockerignore
build
.vscode
```

Finally, let's build the image. For folks no familiar with Docker, this commands will build and run the HTTP service:

```sh
docker build -t http-service .
docker run --rm --init -p 8080:8080 http-service
```

As previously, use `curl` or web browser to test that service is running:

```sh
curl -i localhost:8000/api/status

## Should return something similar to:
HTTP/1.1 200 OK
Connection: close
Content-Type: application/json
Content-Length: 60

{"status":"OK","request_count":"1","timestamp":"1607196274"}
```

At this point, the image can be tagged and pushed in a Docker registry, from where it can be pulled by a container orchestrator.

---

The purposes of this article was to reduce frustration associated with setting environment for C++ development. I hope you were able to follow. If not, the source code is available at [`github.com/hi-artem/http-service`](https://github.com/hi-artem/http-service). Let me know in comments if you have any questions.