package main import ( "bufio" "errors" "fmt" "io" "io/fs" "log/slog" "net" "os" "path" "strings" "tilok.dev/go-http-server/config" rh "tilok.dev/go-http-server/response_helper" ) func HandleHTTPRequest(headers Headers, conn net.Conn) { if strings.Contains(headers.Uri, "..") { conn.Write([]byte("HTTP/1.1 403 Fuck you\r\n\r\n")) conn.Close() return } config := config.GetConfig() _, err := os.Stat(config.Htdocs) if err != nil { if errors.Is(err, fs.ErrNotExist) { CreateDefaultHtdocs(config.Htdocs) } else { slog.Error("Failed to stat directory", "error", err) panic("Failed to stat directory") } } desiredPath := path.Join(config.Htdocs, headers.Uri) if headers.Uri == "/" { desiredPath = path.Join(config.Htdocs, "index.html") } file, err := os.Open(desiredPath) if err != nil { if errors.Is(err, fs.ErrNotExist) { rh.RespondWithStatusCode(404, conn) return } if errors.Is(err, fs.ErrPermission) { rh.RespondWithStatusCode(403, conn) return } slog.Error("Unhandled error case", "err", err) rh.RespondWithStatusCode(500, conn) return } defer file.Close() sendFile(file, conn) } func sendFile(file *os.File, conn net.Conn) { reader := bufio.NewReader(file) defer file.Close() fileStat, err := file.Stat() if err != nil { slog.Error("Failed to stat file", "error", err) rh.RespondWithStatusCode(500, conn) return } parts := strings.Split(fileStat.Name(), ".") ext := "." + parts[len(parts)-1] mime_type := rh.ExtensionToMimetype[ext] length := fileStat.Size() if mime_type == "" { mime_type = "application/octet-stream" } header := fmt.Sprintf("HTTP/1.1 200 OK\r\nContent-Type: %s\r\nContent-Length: %d\r\nServer: Tilo's Go HTTP Server\r\n\r\n", mime_type, length) conn.Write([]byte(header)) buf := make([]byte, 4096) for { n, err := reader.Read(buf) if err != nil { if err == io.EOF { break } slog.Error("Failed to read file", "error", err) rh.RespondWithStatusCode(500, conn) return } conn.Write(buf[:n]) } } func CreateDefaultHtdocs(dirPath string) error { err := os.MkdirAll(dirPath, 0755) if err != nil { return err } err = os.WriteFile(path.Join(dirPath, "index.html"), []byte("Hello World

Hello World

"), 0644) if err != nil { return err } return nil }