From 795c7eccb8d34ce4150b7dc7a7f4822a3ba1db0b Mon Sep 17 00:00:00 2001 From: Tilo K Date: Sun, 15 Feb 2026 15:22:10 +0100 Subject: [PATCH] Inital commit --- go.mod | 3 ++ main.go | 43 ++++++++++++++++++ readheaders.go | 71 ++++++++++++++++++++++++++++++ response_helper/response_helper.go | 14 ++++++ 4 files changed, 131 insertions(+) create mode 100644 go.mod create mode 100644 main.go create mode 100644 readheaders.go create mode 100644 response_helper/response_helper.go diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..4c302fa --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module tilok.dev/go-http-server + +go 1.25.0 diff --git a/main.go b/main.go new file mode 100644 index 0000000..5c10d62 --- /dev/null +++ b/main.go @@ -0,0 +1,43 @@ +package main + +import ( + "bufio" + "log/slog" + "net" + + rh "tilok.dev/go-http-server/response_helper" +) + +func main() { + //TODO: Make interface configurable + interf := "127.0.0.1:8080" + listener, err := net.Listen("tcp", interf) + if err != nil { + slog.Error("Could not create listener", "err", err.Error()) + } + + defer listener.Close() + + for { + conn, err := listener.Accept() + if err != nil { + slog.Error("Could not accept connection", "err", err.Error()) + continue + } + + go handleConnection(conn) + } +} + +func handleConnection(conn net.Conn) { + defer conn.Close() + reader := bufio.NewReader(conn) + headers := readHeaders(reader) + if headers == nil { + rh.RespondWithStatusCode(400, conn) + return + } + + slog.Info("Received request", "method", headers.Method, "uri", headers.Uri, "proto", headers.Proto, "client", conn.RemoteAddr().String()) + headers.debugPrintHeaders() +} diff --git a/readheaders.go b/readheaders.go new file mode 100644 index 0000000..e518e61 --- /dev/null +++ b/readheaders.go @@ -0,0 +1,71 @@ +package main + +import ( + "bufio" + "fmt" + "log/slog" + "strings" +) + +type Headers struct { + Method string + Uri string + Proto string + + KV map[string]string +} + +func readHeaders(reader *bufio.Reader) *Headers { + headers := Headers{} + kvHeaders := make(map[string]string) + versionLine, err := reader.ReadString('\n') + if err != nil { + slog.Error("Could not read version line", "err", err.Error()) + return nil + } + + method, uri, proto := parseRequestLine(versionLine) + if method == "" || uri == "" || proto == "" { + slog.Error("Invalid request line", "line", versionLine) + return nil + } + + headers.Method = method + headers.Uri = uri + headers.Proto = proto + + headerStr, err := reader.ReadString('\n') + if err != nil { + slog.Error("Could not read headers", "err", err.Error()) + return nil + } + + for strings.TrimSpace(headerStr) != "" { + header := strings.SplitN(headerStr, ":", 2) + if len(header) == 2 { + kvHeaders[strings.TrimSpace(header[0])] = strings.TrimSpace(header[1]) + } + headerStr, err = reader.ReadString('\n') + if err != nil { + slog.Error("Could not read headers", "err", err.Error()) + return nil + } + } + headers.KV = kvHeaders + return &headers +} + +func (m *Headers) debugPrintHeaders() { + for key, value := range m.KV { + fmt.Printf("%s: %s\n", key, value) + } +} + +func parseRequestLine(versionLine string) (string, string, string) { + cleanLine := strings.TrimSpace(versionLine) + parts := strings.Split(cleanLine, " ") + if len(parts) != 3 { + return "", "", "" + } + return parts[0], parts[1], parts[2] +} diff --git a/response_helper/response_helper.go b/response_helper/response_helper.go new file mode 100644 index 0000000..14c97e7 --- /dev/null +++ b/response_helper/response_helper.go @@ -0,0 +1,14 @@ +package response_helper + +import ( + "fmt" + "net" + "net/http" +) + +func RespondWithStatusCode(statusCode int, conn net.Conn) { + conn.Write([]byte(fmt.Sprintf("HTTP/1.1 %d %s\r\n", statusCode, http.StatusText(statusCode)))) + conn.Write([]byte("Content-Type: text/plain\r\n")) + conn.Write([]byte("Content-Length: 0\r\n")) + conn.Write([]byte("\r\n")) +}