commit d0bed89029ea8eb1c32d8e2720ecb59d24ec49f8 Author: Kevin Midboe Date: Thu Nov 7 10:54:06 2024 +0100 Init commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..254defd --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +server diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..8727d26 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,11 @@ +# Use the scratch base image +FROM scratch + +WORKDIR /app +COPY server . + +# Expose port 80 +EXPOSE 80 + +# Run the binary +CMD ["/app/server"] diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..6f6325a --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Kevin Midbøe + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..c1ba814 --- /dev/null +++ b/Makefile @@ -0,0 +1,41 @@ +.PHONY: all docker-build run clean + +# Variables for C compilation +CC = gcc +CFLAGS = -Os -fdata-sections -ffunction-sections -march=native -Wall -Wextra +TARGET = server + +# Variables for Docker Buildx +IMAGE_NAME = kevinmidboe/http-200 +VERSION = latest +PLATFORMS = linux/amd64,linux/arm64,linux/arm/v7 +DATE=$(shell date +%Y.%m.%d) + +# Default rule to compile the C program and build Docker image +all: $(TARGET) + +run: + ./server + +# Rule to compile the C executable +$(TARGET): server.c + $(CC) $(CFLAGS) -o $(TARGET) server.c + +# Docker Buildx setup +docker-setup: $(TARGET) + docker buildx create --use --name multiarch_builder || true + docker buildx inspect --bootstrap + +# Build multi-platform Docker image without pushing to registry +docker-build: docker-setup + docker buildx build --platform $(PLATFORMS) -t $(IMAGE_NAME):$(VERSION) -t $(IMAGE_NAME):$(DATE) . + +# Build multi-platform Docker image and push to registry +docker-publish: docker-setup + docker buildx build --platform $(PLATFORMS) -t $(IMAGE_NAME):$(VERSION) -t $(IMAGE_NAME):$(DATE) --push . + +# Clean up Buildx builder and compiled binary +clean: + rm -f $(TARGET) + docker buildx rm multiarch_builder || true + diff --git a/README.md b/README.md new file mode 100644 index 0000000..540984e --- /dev/null +++ b/README.md @@ -0,0 +1,41 @@ +# http-200 + +Simple http C server that always responds 200 to all http requests. Resulting in docker image size smaller than 40 kB (or 3 kB compressed). + +## build + +```bash +gcc -o server server.c +``` + +or using make + +```bash +make +``` + +## run + +```bash +./server +``` + +or + +```bash +make run +``` + +## docker + +build using docker with + +```bash +docker build -t http-200 . +``` + +publish (multi-arch) using + +```bash +make docker-publish +``` diff --git a/server.c b/server.c new file mode 100644 index 0000000..b82007e --- /dev/null +++ b/server.c @@ -0,0 +1,91 @@ +#include +#include +#include +#include +#include +#include +#include +#include // For inet_ntop + +int server_fd; + +void handle_signal(int signal) { + printf("\nReceived signal %d, shutting down server...\n", signal); + fflush(stdout); // Ensure output is flushed immediately + if (server_fd >= 0) { + close(server_fd); + } + exit(0); // Graceful exit after closing the socket +} + +int main() { + struct sockaddr_in address; + struct sockaddr_in client_address; + int client_fd; + int addrlen = sizeof(address); + int port = 80; + + // Disable stdout buffering + setbuf(stdout, NULL); // Disables buffering, output is immediate + + // Setup signal handler for graceful shutdown + struct sigaction sa; + sa.sa_handler = handle_signal; + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + sigaction(SIGINT, &sa, NULL); + sigaction(SIGTERM, &sa, NULL); + + // Create socket + server_fd = socket(AF_INET, SOCK_STREAM, 0); + if (server_fd == 0) { + perror("Socket failed"); + return 1; + } + + // Configure the server address and port + address.sin_family = AF_INET; + address.sin_addr.s_addr = INADDR_ANY; + address.sin_port = htons(port); + + // Bind the socket to the port + if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) { + perror("Bind failed"); + return 1; + } + + // Start listening for incoming connections + if (listen(server_fd, 1) < 0) { + perror("Listen failed"); + return 1; + } + + // Output immediately after listening starts + printf("Server listening on port %i...\n", port); + fflush(stdout); // Ensure output is flushed immediately + + while (1) { + // Accept client connections + client_fd = accept(server_fd, (struct sockaddr *)&client_address, (socklen_t *)&addrlen); + if (client_fd < 0) { + perror("Accept failed"); + continue; + } + + // Get the client's IP address + char client_ip[INET_ADDRSTRLEN]; + if (inet_ntop(AF_INET, &client_address.sin_addr, client_ip, sizeof(client_ip)) == NULL) { + perror("inet_ntop failed"); + } else { + printf("Connection from IP: %s\n", client_ip); // Log the client IP + } + + // Send a simple HTTP 200 OK response + char response[] = "HTTP/1.1 200 OK\r\nContent-Length: 0\r\nserver: http-200\r\n\r\n"; + send(client_fd, response, strlen(response), 0); + close(client_fd); // Close the client connection after sending the response + } + + return 0; +} +