Code cleanup. Use go mime type detection

This commit is contained in:
Nils O. Selåsdal
2026-05-29 22:08:23 +02:00
parent 82d04ad7cd
commit ba371dcce5
2 changed files with 34 additions and 43 deletions
+4 -2
View File
@@ -3,7 +3,6 @@ FROM golang:1.26-alpine AS builder
WORKDIR /build WORKDIR /build
COPY go.mod ./ COPY go.mod ./
RUN go mod download
COPY index.html.tmpl main.go ./ COPY index.html.tmpl main.go ./
@@ -11,7 +10,10 @@ RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o beam .
FROM alpine:latest FROM alpine:latest
RUN adduser -D -u 1000 beam RUN apk add mailcap \ # for mime types
&& adduser -D -u 1000 beam \
&& mkdir /uploads \
&& chown beam /uploads
USER beam USER beam
WORKDIR /app WORKDIR /app
+29 -40
View File
@@ -4,21 +4,24 @@ import (
"crypto/rand" "crypto/rand"
"encoding/json" "encoding/json"
"fmt" "fmt"
"html/template"
"io" "io"
"log" "log"
"mime"
"net/http" "net/http"
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
"time" "time"
"html/template"
) )
var uploadDir string var uploadDir string
const idLength = 8 const idLength = 8
const idCharset = "abcdefghijklmnopqrstuvwxyz0123456789" const idCharset = "abcdefghijklmnopqrstuvwxyz0123456789"
var indexHTML *template.Template var indexHTML *template.Template
func main() { func main() {
uploadDir = os.Getenv("UPLOAD_DIR") uploadDir = os.Getenv("UPLOAD_DIR")
if uploadDir == "" { if uploadDir == "" {
@@ -60,7 +63,7 @@ func handleRequest(w http.ResponseWriter, r *http.Request) {
} }
if r.Method == "PUT" { if r.Method == "PUT" {
handleCurlUpload(w, r, start) handleUpload(w, r, start)
return return
} }
@@ -69,11 +72,15 @@ func handleRequest(w http.ResponseWriter, r *http.Request) {
return return
} }
if r.Method == "HEAD" {
return
}
w.WriteHeader(http.StatusMethodNotAllowed) w.WriteHeader(http.StatusMethodNotAllowed)
logRequest(r, 405, time.Since(start)) logRequest(r, 405, time.Since(start))
} }
func handleCurlUpload(w http.ResponseWriter, r *http.Request, start time.Time) { func handleUpload(w http.ResponseWriter, r *http.Request, start time.Time) {
filename := strings.TrimPrefix(r.URL.Path, "/") filename := strings.TrimPrefix(r.URL.Path, "/")
if filename == "" { if filename == "" {
http.Error(w, "Filename required", http.StatusBadRequest) http.Error(w, "Filename required", http.StatusBadRequest)
@@ -192,6 +199,12 @@ func handleDownload(w http.ResponseWriter, r *http.Request, start time.Time) {
logRequest(r, 404, time.Since(start)) logRequest(r, 404, time.Since(start))
return return
} }
if !strings.HasPrefix(filePath, uploadDir) {
http.NotFound(w, r)
logRequest(r, 404, time.Since(start))
return
}
defer file.Close() defer file.Close()
stat, err := file.Stat() stat, err := file.Stat()
@@ -212,7 +225,7 @@ func handleDownload(w http.ResponseWriter, r *http.Request, start time.Time) {
func generateUniqueID() (string, error) { func generateUniqueID() (string, error) {
const maxRetries = 100 const maxRetries = 100
for i := 0; i < maxRetries; i++ { for range maxRetries {
id := generateID() id := generateID()
targetDir := filepath.Join(uploadDir, id) targetDir := filepath.Join(uploadDir, id)
@@ -249,11 +262,11 @@ func getHost(r *http.Request) string {
// Check standard Forwarded header (RFC 7239) // Check standard Forwarded header (RFC 7239)
if forwarded := r.Header.Get("Forwarded"); forwarded != "" { if forwarded := r.Header.Get("Forwarded"); forwarded != "" {
// Parse "Forwarded: for=...; host=example.com; proto=https" // Parse "Forwarded: for=...; host=example.com; proto=https"
parts := strings.Split(forwarded, ";") parts := strings.SplitSeq(forwarded, ";")
for _, part := range parts { for part := range parts {
part = strings.TrimSpace(part) part = strings.TrimSpace(part)
if strings.HasPrefix(part, "host=") { if host, found := strings.CutPrefix(part, "host="); found {
return strings.TrimPrefix(part, "host=") return host
} }
} }
} }
@@ -267,29 +280,23 @@ func getHost(r *http.Request) string {
} }
func getProtocol(r *http.Request) string { func getProtocol(r *http.Request) string {
// Check X-Forwarded-Proto (common proxy header)
if proto := r.Header.Get("X-Forwarded-Proto"); proto != "" { if proto := r.Header.Get("X-Forwarded-Proto"); proto != "" {
return proto return proto
} }
// Check standard Forwarded header (RFC 7239)
if forwarded := r.Header.Get("Forwarded"); forwarded != "" { if forwarded := r.Header.Get("Forwarded"); forwarded != "" {
// Parse "Forwarded: for=...; proto=https; host=..." parts := strings.SplitSeq(forwarded, ";")
parts := strings.Split(forwarded, ";") for part := range parts {
for _, part := range parts {
part = strings.TrimSpace(part) part = strings.TrimSpace(part)
if strings.HasPrefix(part, "proto=") { if proto, found := strings.CutPrefix(part, "proto="); found {
return strings.TrimPrefix(part, "proto=") return proto
} }
} }
} }
// Check X-Forwarded-SSL (some proxies use this)
if ssl := r.Header.Get("X-Forwarded-SSL"); ssl == "on" { if ssl := r.Header.Get("X-Forwarded-SSL"); ssl == "on" {
return "https" return "https"
} }
// Check if connection is TLS
if r.TLS != nil { if r.TLS != nil {
return "https" return "https"
} }
@@ -298,28 +305,10 @@ func getProtocol(r *http.Request) string {
} }
func detectContentType(filename string) string { func detectContentType(filename string) string {
ext := strings.ToLower(filepath.Ext(filename)) ext := filepath.Ext(filename)
types := map[string]string{ mimeType := mime.TypeByExtension(ext)
".txt": "text/plain", if mimeType == "" {
".html": "text/html", return mimeType
".css": "text/css",
".js": "application/javascript",
".json": "application/json",
".xml": "application/xml",
".pdf": "application/pdf",
".zip": "application/zip",
".jpg": "image/jpeg",
".jpeg": "image/jpeg",
".png": "image/png",
".gif": "image/gif",
".svg": "image/svg+xml",
".mp3": "audio/mpeg",
".mp4": "video/mp4",
".webm": "video/webm",
}
if ct, ok := types[ext]; ok {
return ct
} }
return "application/octet-stream" return "application/octet-stream"
} }