Skip to content

Commit

Permalink
Switch to zig. Closes #1
Browse files Browse the repository at this point in the history
  • Loading branch information
kljensen committed Sep 23, 2024
1 parent eca550b commit 2831c36
Show file tree
Hide file tree
Showing 8 changed files with 73 additions and 251 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
/target
server.o
server
166 changes: 0 additions & 166 deletions Cargo.lock

This file was deleted.

16 changes: 0 additions & 16 deletions Cargo.toml

This file was deleted.

26 changes: 7 additions & 19 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,35 +1,23 @@
# Use a Rust image based on Alpine
FROM rust:1.81.0-alpine3.20 as builder
FROM alpine:3.20.3 as builder

Check warning on line 1 in Dockerfile

View workflow job for this annotation

GitHub Actions / build-and-push-image

The 'as' keyword should match the case of the 'from' keyword

FromAsCasing: 'as' and 'FROM' keywords' casing do not match More info: https://docs.docker.com/go/dockerfile/rule/from-as-casing/

# Install build dependencies
RUN apk add --no-cache musl-dev
RUN apk add --no-cache zig upx

# Set the working directory
WORKDIR /usr/src/app

# Copy the entire project
COPY . .

# Determine the system architecture and set the appropriate Rust target
RUN arch=$(uname -m) && \
case "$arch" in \
"x86_64") echo "x86_64-unknown-linux-musl" > /tmp/target ;; \
"aarch64") echo "aarch64-unknown-linux-musl" > /tmp/target ;; \
*) echo "Unsupported architecture: $arch" && exit 1 ;; \
esac

# Add the target to rustup and build the project
RUN rustup target add $(cat /tmp/target) && \
cargo build --release --target $(cat /tmp/target)

# Copy the binary to a known location
RUN cp target/$(cat /tmp/target)/release/hello-world-http /usr/local/bin/hello-world-http
RUN \
zig build-exe -lc -static -O ReleaseSmall ./server.zig && \
upx -9 ./server

# Start a new stage for a minimal runtime container
FROM scratch

# Copy the built executable from the builder stage
COPY --from=builder /usr/local/bin/hello-world-http /hello-world-http
COPY --from=builder /usr/src/app/server /server

# Set the startup command
CMD ["/hello-world-http"]
CMD ["/server"]
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@

all:
zig build-exe -lc -static -O ReleaseSmall ./server.zig
32 changes: 23 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,34 +1,48 @@
# Hello world http
# Tiny Hello World HTTP

This is a simple hello world http server written in Rust.
I wrote this to use as a very tiny Docker image for testing
HTTP services. This image is a bit smaller than 600kb.
The goal of this project is to be the _smallest possible_ Docker
image for testing HTTP services. The server responds to all HTTP
requests with a simple "Hello, World!" As of v0.5.0, the Docker
container is just 22.8kB.

## Running with Docker

To run this with Docker, do something like

```sh
docker run -e HOST=0.0.0.0 -p 8000:8000 ghcr.io/kljensen/hello-world-http:latest
docker run -e HOST=0.0.0.0 -e PORT=8000 -p 8000:8000 --init ghcr.io/kljensen/hello-world-http:latest
```

The `HOST` will tell the server to listen for outisde
requests. The `-p 8000:8000` will map the container's
port 8000 to the host's port 8000.
Notice:

- `HOST` and `PORT` are _required_.
- `HOST` must be in dotted decimal, like `0.0.0.0`
- If `HOST` is something other than `0.0.0.0`, your
container will likely not respond to external requests.
- `PORT` is the port on which the server will listen
inside the container. If you're forwarding from the
host to the container, obviously this needs to match
the port you publish with `-p`. See [the Docker documentation](https://docs.docker.com/engine/network/#published-ports).
- The `--init` flag is optional, but it's a good idea to
use it. It ensures that the server process is stopped
properly when Docker gets a `SIGTERM` signal. (For example,
this will make `docker run` handle `Ctrl-C` properly.)

## Running with Docker Compose

To run this with Docker Compose, you should have a
To run this with Docker Compose, you should have a
`docker-compose.yml` file that looks something like

```yaml
services:
hello-world:
image: ghcr.io/kljensen/hello-world-http:latest
init: true
ports:
- "8000:8000"
environment:
- HOST=0.0.0.0
- PORT=8000
```
Then you can run it with
Expand Down
38 changes: 38 additions & 0 deletions server.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
const std = @import("std");
const net = std.net;
const print = std.debug.print;

pub fn main() !void {
const allocator = std.heap.c_allocator;

// Get the HOST environment variable
const host = try std.process.getEnvVarOwned(allocator, "HOST");
defer allocator.free(host);

// Get the PORT environment variable
const port_str = try std.process.getEnvVarOwned(allocator, "PORT");
defer allocator.free(port_str);

// Parse the port string into a u16 integer
const port_number = try std.fmt.parseInt(u16, port_str, 10);

// Parse the IP address and port
const addr = try net.Address.parseIp(host, port_number);

var server = try addr.listen(.{ .reuse_port = true });
defer server.deinit();

print("Server Listening on {s}:{}\n", .{ host, port_number });

const response = "HTTP/1.1 200 OK\r\n" ++ "Content-Length: 11\r\n" ++ "Content-Type: text/plain\r\n" ++ "Connection: close\r\n" ++ "\r\n" ++ "hello world";

while (server.accept()) |client| {
defer client.stream.close();

print("Connection received from {}\n", .{client.address});

_ = try client.stream.write(response);
} else |err| {
print("Error accepting connection: {}\n", .{err});
}
}
40 changes: 0 additions & 40 deletions src/main.rs

This file was deleted.

0 comments on commit 2831c36

Please sign in to comment.