Code cleanup. Use go mime type detection
This commit is contained in:
+4
-2
@@ -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
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user