115 lines
2.3 KiB
Go
115 lines
2.3 KiB
Go
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)
|
|
|
|
conn.Close()
|
|
}
|
|
|
|
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("<DOCTYPE html><html><head><title>Hello World</title></head><body><h1>Hello World</h1></body></html>"), 0644)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|