Repository navigation
Expand file tree
/
Copy pathreader.go
More file actions
72 lines (68 loc) · 2.47 KB
/
Copy pathreader.go
File metadata and controls
72 lines (68 loc) · 2.47 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
package jsonlimit
import "io"
// Reader wraps an io.Reader and refuses to deliver more than max bytes in
// total, returning a *BytesLimitError instead of silently truncating.
//
// This bounds the input side, independently of and before anything in
// Limits: a request body can be capped by Reader before a single byte
// reaches Decode, the same way Limits.MaxDecodedSize bounds the decoded
// value built from whatever bytes did get through. The two are not
// redundant. A single giant string, for instance, must be fully read and
// unescaped before Decode's own Limits.MaxStringLength check can see its
// length (encoding/json's tokenizer hands over the whole decoded string in
// one call) — but Reader stops that same input mid-stream, at the byte
// level, well before it is fully buffered. See the README's benchmarks for
// the size of that difference.
//
// Reader is modeled on net/http.MaxBytesReader's read-ahead trick — it asks
// the underlying reader for one byte more than the remaining budget, so an
// input that lands exactly on the limit is not mistaken for one that
// overflows it — but has no dependency on net/http and returns jsonlimit's
// own error type.
type Reader struct {
r io.Reader
limit int64
remain int64
err error
}
// NewReader returns a Reader over r that refuses to deliver more than max
// bytes. A non-positive max disables the limit; Read then simply delegates
// to r.
func NewReader(r io.Reader, max int64) *Reader {
if max < 0 {
max = 0
}
return &Reader{r: r, limit: max, remain: max}
}
// Read implements io.Reader. Once the configured byte budget has been
// exhausted, Read returns a *BytesLimitError (wrapped in the sense of
// errors.As, though there is nothing further to unwrap) on every
// subsequent call; the underlying reader is never consulted again.
func (l *Reader) Read(p []byte) (int, error) {
if l.err != nil {
return 0, l.err
}
if l.limit <= 0 {
return l.r.Read(p)
}
if len(p) == 0 {
return 0, nil
}
// Ask for at most one byte more than the remaining budget: enough to
// tell whether the input stops exactly at the limit (fine) or keeps
// going past it (not fine), without over-reading arbitrarily far
// beyond the cap we exist to enforce.
if int64(len(p))-1 > l.remain {
p = p[:l.remain+1]
}
n, err := l.r.Read(p)
if int64(n) <= l.remain {
l.remain -= int64(n)
l.err = err
return n, err
}
n = int(l.remain)
l.remain = 0
l.err = &BytesLimitError{limit: l.limit, observed: l.limit}
return n, l.err
}