-
Notifications
You must be signed in to change notification settings - Fork 1
/
scrubber.go
109 lines (95 loc) · 2.6 KB
/
scrubber.go
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
// Package metascrubber provides a streaming metadata remover
package metascrubber
import (
"bytes"
"errors"
"io"
"io/ioutil"
"net/http"
)
// GetScrubber returns a reader which has metadata removed from its contents
func GetScrubber(reader io.Reader) (io.Reader, error) {
// net/http.sniffLen = 512
contentTypeHead := make([]byte, 512)
n, err := io.ReadFull(reader, contentTypeHead)
if err != nil {
if errors.Is(err, io.EOF) || errors.Is(err, io.ErrUnexpectedEOF) {
return bytes.NewReader(contentTypeHead[:n]), nil
}
return nil, err
}
mimeType := http.DetectContentType(contentTypeHead)
// Reset the reader to contain the whole stream again
reader = io.MultiReader(bytes.NewReader(contentTypeHead), reader)
if mimeType == "image/png" {
return &metaScrubber{
sr: &pngSegmentReader{
reader: reader,
},
segmentData: bytes.NewReader([]byte{}),
}, nil
} else if mimeType == "image/jpeg" {
return &metaScrubber{
sr: &jpegSegmentReader{
reader: reader,
},
segmentData: bytes.NewReader([]byte{}),
}, nil
}
return reader, nil
}
// MalformedDataError represents an error parsing the underlying file format
// This could be an upstream reader issue, so consider checking it if needed
type MalformedDataError struct {
Message string
Err error
}
func (e *MalformedDataError) Unwrap() error { return e.Err }
func (e *MalformedDataError) Error() string {
message := e.Message
if e.Err != nil {
message += ": " + e.Err.Error()
}
return message
}
type segmentReader interface {
// Returns io.EOF when no more segments exist.
// The returned reader MUST be exhausted/read until EOF for further calls to nextSegment to be valid
nextSegment() (r io.Reader, isMetadata bool, err error)
}
type metaScrubber struct {
sr segmentReader
segmentData io.Reader
}
func (ms *metaScrubber) Read(p []byte) (n int, err error) {
var (
m int
isMeta bool
)
// Need to exhaust segmentData by calling read until EOF, n >= len(p), or other error
for err == nil {
m, err = ms.segmentData.Read(p[n:])
n += m
if (err != nil && !errors.Is(err, io.EOF)) || n >= len(p) {
return
}
}
for ms.segmentData, isMeta, err = ms.sr.nextSegment(); err == nil; ms.segmentData, isMeta, err = ms.sr.nextSegment() {
if isMeta {
if _, err = io.Copy(ioutil.Discard, ms.segmentData); err != nil {
err = &MalformedDataError{"unable to read and discard metadata segment", err}
return
}
continue
}
// Same as above
for err == nil {
m, err = ms.segmentData.Read(p[n:])
n += m
if (err != nil && !errors.Is(err, io.EOF)) || n >= len(p) {
return
}
}
}
return
}