feat: basic file handling
This commit is contained in:
115
http_handle.go
Normal file
115
http_handle.go
Normal file
@@ -0,0 +1,115 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"log/slog"
|
||||
"mime"
|
||||
"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 := mime.TypeByExtension(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
|
||||
}
|
||||
Reference in New Issue
Block a user