-
Notifications
You must be signed in to change notification settings - Fork 7
/
parser.go
151 lines (122 loc) · 3.01 KB
/
parser.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
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
package baraka
import (
"bytes"
"errors"
"fmt"
"io"
"mime"
"net/http"
)
const (
defaultMaxFileSize = 10 << 20
defaultMaxParseCount = 20
defaultMaxFileCount = 0
)
var (
ErrMaxFileCountExceeded = errors.New("max file count to save exceeded")
ErrExtensionNotFound = errors.New("can't detect file's extension")
)
// Parser contains parsing options and interfaces to do some other actions
type Parser struct {
Options ParserOptions
Filter Filter
Inspector Inspector
}
// ParserOptions contains parser's options about parsing.
type ParserOptions struct {
MaxFileSize int
MaxFileCount int
MaxParseCount int
}
// NewParser creates a new Parser.
func NewParser(options ParserOptions) *Parser {
return &Parser{
Options: options,
}
}
// DefaultParser creates a new parser with the default settings
func DefaultParser() *Parser {
options := ParserOptions{
MaxFileSize: defaultMaxFileSize,
MaxFileCount: defaultMaxFileCount,
MaxParseCount: defaultMaxParseCount,
}
return NewParser(options)
}
// SetFilter sets the filter of the parser
func (parser *Parser) SetFilter(filter Filter) *Parser {
parser.Filter = filter
return parser
}
// SetInspector sets the inspector of the parser
func (parser *Parser) SetInspector(inspector Inspector) *Parser {
parser.Inspector = inspector
return parser
}
// Parse parses the http request with the multipart.Reader.
// reads parts inside the loop which iterates up to MaxParseCount at most.
// creates a []byte (buf) which gonna contain the part data.
func (parser *Parser) Parse(r *http.Request) (*Request, error) {
reader, err := r.MultipartReader()
if err != nil {
return nil, err
}
parts := make(map[string][]*Part)
request := NewRequest(parts)
process:
for parseCount := 0; parseCount < parser.Options.MaxParseCount; parseCount++ {
part, err := reader.NextPart()
if err != nil {
if err == io.EOF {
break
}
return nil, err
}
data := bytes.NewBuffer(nil)
for {
if data.Len() > parser.Options.MaxFileSize {
continue process
}
buf := make([]byte, 1024)
n, err := part.Read(buf)
if err != nil {
if err == io.EOF {
buf = buf[:n]
data.Write(buf)
break
}
return nil, err
}
data.Write(buf)
}
part.Close()
p := Part{
Name: part.FileName(),
Headers: part.Header,
Size: data.Len(),
Content: data.Bytes(),
}
if parser.Inspector != nil {
contentType := parser.Inspector.Inspect(data.Bytes())
extensions, err := mime.ExtensionsByType(contentType)
if err != nil {
return nil, err
}
if len(extensions) == 0 {
return nil, fmt.Errorf("%w, filename: %s", ErrExtensionNotFound, part.FileName())
}
p.Extension = extensions[0]
}
if parser.Filter != nil {
ok := parser.Filter.Filter(&p)
if !ok {
continue
}
}
request.parts[part.FormName()] = append(request.parts[part.FormName()], &p)
if len := len(request.parts); len == parser.Options.MaxFileCount && len != 0 {
return nil, ErrMaxFileCountExceeded
}
}
return request, nil
}