-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathhttp_helpers.go
More file actions
156 lines (138 loc) · 3.95 KB
/
Copy pathhttp_helpers.go
File metadata and controls
156 lines (138 loc) · 3.95 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
package main
import (
"errors"
"fmt"
"image"
"io"
"log"
"mime"
"mime/multipart"
"net/http"
"os"
"path/filepath"
"strings"
)
const (
maxUploadSize int64 = 20 << 20
maxPDFUploadSize int64 = 50 << 20
maxBatchUploadSize int64 = 100 << 20
maxFormMemory int64 = 8 << 20
maxImageDimension = 8000
maxImagePixels = 20_000_000
maxBatchFiles = 50
)
func parseMultipartForm(w http.ResponseWriter, r *http.Request, maxBytes int64) bool {
if r.Method != http.MethodPost {
w.Header().Set("Allow", http.MethodPost)
http.Error(w, "Método não permitido", http.StatusMethodNotAllowed)
return false
}
r.Body = http.MaxBytesReader(w, r.Body, maxBytes)
// #nosec G120 -- MaxBytesReader impõe o limite total antes do parser multipart.
if err := r.ParseMultipartForm(min(maxBytes, maxFormMemory)); err != nil {
var maxBytesErr *http.MaxBytesError
if errors.As(err, &maxBytesErr) {
http.Error(w, "Arquivo ou formulário excede o limite permitido", http.StatusRequestEntityTooLarge)
return false
}
http.Error(w, "Formulário inválido", http.StatusBadRequest)
return false
}
return true
}
func cleanupMultipartForm(r *http.Request) {
if r.MultipartForm == nil {
return
}
if err := r.MultipartForm.RemoveAll(); err != nil {
log.Printf("Erro ao remover arquivos temporários do upload: %v", err)
}
}
func decodeImage(file multipart.File) (image.Image, string, error) {
format, err := validateImageFile(file)
if err != nil {
return nil, "", err
}
img, decodedFormat, err := image.Decode(file)
if err != nil {
return nil, "", fmt.Errorf("decodificar imagem: %w", err)
}
if decodedFormat != "" {
format = decodedFormat
}
return img, format, nil
}
func validateImageFile(file multipart.File) (string, error) {
config, format, err := image.DecodeConfig(file)
if err != nil {
return "", fmt.Errorf("ler cabeçalho da imagem: %w", err)
}
if err := validateImageDimensions(config.Width, config.Height); err != nil {
return "", err
}
if _, err := file.Seek(0, io.SeekStart); err != nil {
return "", fmt.Errorf("reposicionar arquivo de imagem: %w", err)
}
return format, nil
}
func validateImageDimensions(width, height int) error {
if width <= 0 || height <= 0 {
return errors.New("dimensões de imagem inválidas")
}
if width > maxImageDimension || height > maxImageDimension {
return fmt.Errorf("dimensão máxima permitida é %dpx", maxImageDimension)
}
if int64(width)*int64(height) > maxImagePixels {
return fmt.Errorf("imagem excede o limite de %d megapixels", maxImagePixels/1_000_000)
}
return nil
}
func setDownloadHeaders(w http.ResponseWriter, contentType, filename string) {
w.Header().Set("Content-Type", contentType)
w.Header().Set("Content-Disposition", mime.FormatMediaType("attachment", map[string]string{
"filename": safeFilename(filename),
}))
}
func safeFilename(filename string) string {
filename = strings.ReplaceAll(filename, "\\", "/")
filename = filepath.Base(filename)
filename = strings.Map(func(r rune) rune {
if r < 0x20 || r == 0x7f {
return -1
}
return r
}, filename)
filename = strings.TrimSpace(filename)
if filename == "" || filename == "." {
return "download"
}
runes := []rune(filename)
if len(runes) > 120 {
filename = string(runes[:120])
}
return filename
}
func internalError(w http.ResponseWriter, publicMessage string, err error) {
log.Printf("%s: %v", publicMessage, err)
http.Error(w, publicMessage, http.StatusInternalServerError)
}
func serveDownloadFile(w http.ResponseWriter, file *os.File, contentType, filename string) error {
if _, err := file.Seek(0, io.SeekStart); err != nil {
return err
}
setDownloadHeaders(w, contentType, filename)
_, err := io.Copy(w, file)
return err
}
func saveToTempFile(src io.Reader, prefix string) (string, error) {
tmp, err := os.CreateTemp("", prefix)
if err != nil {
return "", err
}
defer tmp.Close()
if _, err := io.Copy(tmp, src); err != nil {
os.Remove(tmp.Name())
return "", err
}
return tmp.Name(), nil
}