DOC-01: upload-api & chunk-handling
Fortsetzbarer Server-seitiger Upload: upload_sessions (bytes_received, GREATEST-Update verhindert Rueckschritt bei erneut zugestellten Chunks), Staging via os.File.WriteAt (beliebige Chunk-Reihenfolge/-Wiederholung), MIME-/Groessen-Validierung vor jedem Byte. Complete() liest Klartext einmal via io.TeeReader fuer SHA-256 UND Verschluesselung gleichzeitig (Reihenfolge Hash->verschluesseln->ablegen eingehalten), legt Dokument+Revision transaktional an (neue Spalte file_revisions.wrapped_dek fuer FDN-09). Auf 192.168.1.131 verifiziert: Resume nach simuliertem Abbruch bei 50% liefert identische Endpruefsumme, 20 parallele Uploads ohne Kollision/ Datenverlust, Pruefsumme entspricht exakt dem Klartext, transaktionale Dokument+Revision-Anlage bestaetigt. Reale Skalierungs-Einschraenkung gefunden: echter 1-GiB-Durchlauf endete mit OOM (4GB-RAM-Testhost ohne Swap, FDN-03/FDN-09 puffern vollstaendig im Speicher statt zu streamen). Auf 300 MiB reduziert vollstaendig verifiziert (Upload/Verschluesselung/Ablage/Checksumme/Entschluesselungs-Round-Trip alles exakt) - Ressourcen-, keine Korrektheitsfrage, dokumentiert als Folgeticket-Kandidat statt stillschweigend uebergangen. Siehe dms/docs/DOC-01-PRUEFPROTOKOLL.md fuer alle Pruefungsergebnisse. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HhgFcLS8tYMhDJpP74C6AQ
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
bf7559e118
commit
9a374dd91e
@@ -0,0 +1,59 @@
|
||||
// Package upload implementiert DOC-01: fortsetzbare Server-seitige
|
||||
// Dateiaufnahme mit Größen-/MIME-Validierung und transaktionaler
|
||||
// Dokument+Revision-Anlage. Nutzt FDN-03 (Storage) und FDN-09
|
||||
// (Verschlüsselung) als bereits fertige Bausteine, dupliziert sie nicht.
|
||||
package upload
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// ErrFileTooLarge wird geliefert, wenn die angekündigte oder tatsächliche
|
||||
// Größe das konfigurierte Limit überschreitet (Schutz vor
|
||||
// Speicherbomben — siehe "Bekannte Fehler vermeiden" im Ticket).
|
||||
var ErrFileTooLarge = errors.New("upload: datei ueberschreitet das erlaubte groessenlimit")
|
||||
|
||||
// ErrDisallowedMimeType wird geliefert, wenn der MIME-Typ nicht auf der
|
||||
// Positivliste steht (Akzeptanzkriterium 2).
|
||||
var ErrDisallowedMimeType = errors.New("upload: dateityp ist nicht erlaubt")
|
||||
|
||||
// Validator prüft Größe und MIME-Typ VOR jedem Byte, das tatsächlich
|
||||
// entgegengenommen wird — die Prüfung selbst braucht keinen Datei-Inhalt,
|
||||
// nur die vom Client angekündigten Metadaten.
|
||||
type Validator struct {
|
||||
maxSizeBytes int64
|
||||
allowedMimeTypes map[string]bool
|
||||
}
|
||||
|
||||
// NewValidator erzeugt einen Validator. maxSizeBytes<=0 bedeutet kein
|
||||
// Limit (bewusst explizit statt eines "magischen" Default — siehe Ticket-
|
||||
// Vorgabe "kein Start ohne Konfiguration" NUR für die Voreinstellung
|
||||
// selbst, nicht für sicherheitsrelevante Limits).
|
||||
func NewValidator(maxSizeBytes int64, allowedMimeTypes []string) *Validator {
|
||||
allowed := make(map[string]bool, len(allowedMimeTypes))
|
||||
for _, m := range allowedMimeTypes {
|
||||
allowed[m] = true
|
||||
}
|
||||
return &Validator{maxSizeBytes: maxSizeBytes, allowedMimeTypes: allowed}
|
||||
}
|
||||
|
||||
func (v *Validator) ValidateMimeType(mimeType string) error {
|
||||
if len(v.allowedMimeTypes) == 0 {
|
||||
return nil
|
||||
}
|
||||
if !v.allowedMimeTypes[mimeType] {
|
||||
return fmt.Errorf("%w: %q", ErrDisallowedMimeType, mimeType)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *Validator) ValidateSize(sizeBytes int64) error {
|
||||
if v.maxSizeBytes <= 0 {
|
||||
return nil
|
||||
}
|
||||
if sizeBytes > v.maxSizeBytes {
|
||||
return fmt.Errorf("%w: %d bytes > limit %d bytes", ErrFileTooLarge, sizeBytes, v.maxSizeBytes)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user