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
COPY go.mod ./
RUN go mod download
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
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
WORKDIR /app
+29 -40
View File
@@ -4,21 +4,24 @@ import (
"crypto/rand"
"encoding/json"
"fmt"
"html/template"
"io"
"log"
"mime"
"net/http"
"os"
"path/filepath"
"strings"
"time"
"html/template"
)
var uploadDir string
const idLength = 8
const idCharset = "abcdefghijklmnopqrstuvwxyz0123456789"
var indexHTML *template.Template
func main() {
uploadDir = os.Getenv("UPLOAD_DIR")
if uploadDir == "" {
@@ -60,7 +63,7 @@ func handleRequest(w http.ResponseWriter, r *http.Request) {
}
if r.Method == "PUT" {
handleCurlUpload(w, r, start)
handleUpload(w, r, start)
return
}
@@ -69,11 +72,15 @@ func handleRequest(w http.ResponseWriter, r *http.Request) {
return
}
if r.Method == "HEAD" {
return
}
w.WriteHeader(http.StatusMethodNotAllowed)
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, "/")
if filename == "" {
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))
return
}
if !strings.HasPrefix(filePath, uploadDir) {
http.NotFound(w, r)
logRequest(r, 404, time.Since(start))
return
}
defer file.Close()
stat, err := file.Stat()
@@ -212,7 +225,7 @@ func handleDownload(w http.ResponseWriter, r *http.Request, start time.Time) {
func generateUniqueID() (string, error) {
const maxRetries = 100
for i := 0; i < maxRetries; i++ {
for range maxRetries {
id := generateID()
targetDir := filepath.Join(uploadDir, id)
@@ -249,11 +262,11 @@ func getHost(r *http.Request) string {
// Check standard Forwarded header (RFC 7239)
if forwarded := r.Header.Get("Forwarded"); forwarded != "" {
// Parse "Forwarded: for=...; host=example.com; proto=https"
parts := strings.Split(forwarded, ";")
for _, part := range parts {
parts := strings.SplitSeq(forwarded, ";")
for part := range parts {
part = strings.TrimSpace(part)
if strings.HasPrefix(part, "host=") {
return strings.TrimPrefix(part, "host=")
if host, found := strings.CutPrefix(part, "host="); found {
return host
}
}
}
@@ -267,29 +280,23 @@ func getHost(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 != "" {
return proto
}
// Check standard Forwarded header (RFC 7239)
if forwarded := r.Header.Get("Forwarded"); forwarded != "" {
// Parse "Forwarded: for=...; proto=https; host=..."
parts := strings.Split(forwarded, ";")
for _, part := range parts {
parts := strings.SplitSeq(forwarded, ";")
for part := range parts {
part = strings.TrimSpace(part)
if strings.HasPrefix(part, "proto=") {
return strings.TrimPrefix(part, "proto=")
if proto, found := strings.CutPrefix(part, "proto="); found {
return proto
}
}
}
// Check X-Forwarded-SSL (some proxies use this)
if ssl := r.Header.Get("X-Forwarded-SSL"); ssl == "on" {
return "https"
}
// Check if connection is TLS
if r.TLS != nil {
return "https"
}
@@ -298,28 +305,10 @@ func getProtocol(r *http.Request) string {
}
func detectContentType(filename string) string {
ext := strings.ToLower(filepath.Ext(filename))
types := map[string]string{
".txt": "text/plain",
".html": "text/html",
".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
ext := filepath.Ext(filename)
mimeType := mime.TypeByExtension(ext)
if mimeType == "" {
return mimeType
}
return "application/octet-stream"
}