Codebase list golang-github-anacrolix-missinggo / 7641950
New upstream version 2.1.0 Drew Parsons 5 years ago
182 changed file(s) with 9388 addition(s) and 0 deletion(s). Raw diff Collapse all Expand all
0 The MIT License (MIT)
1
2 Copyright (c) 2015 Matt Joiner
3
4 Permission is hereby granted, free of charge, to any person obtaining a copy
5 of this software and associated documentation files (the "Software"), to deal
6 in the Software without restriction, including without limitation the rights
7 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 copies of the Software, and to permit persons to whom the Software is
9 furnished to do so, subject to the following conditions:
10
11 The above copyright notice and this permission notice shall be included in
12 all copies or substantial portions of the Software.
13
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 THE SOFTWARE.
0 # missinggo
1 [![GoDoc](https://godoc.org/github.com/anacrolix/missinggo?status.svg)](https://godoc.org/github.com/anacrolix/missinggo)
2
3 Stuff that supplements Go's stdlib, or isn't significant enough to be in its own repo.
0 package missinggo
1
2 import (
3 "net"
4 "strconv"
5 )
6
7 // Extracts the port as an integer from an address string.
8 func AddrPort(addr net.Addr) int {
9 switch raw := addr.(type) {
10 case *net.UDPAddr:
11 return raw.Port
12 case *net.TCPAddr:
13 return raw.Port
14 default:
15 _, port, err := net.SplitHostPort(addr.String())
16 if err != nil {
17 panic(err)
18 }
19 i64, err := strconv.ParseInt(port, 0, 0)
20 if err != nil {
21 panic(err)
22 }
23 return int(i64)
24 }
25 }
26
27 func AddrIP(addr net.Addr) net.IP {
28 if addr == nil {
29 return nil
30 }
31 switch raw := addr.(type) {
32 case *net.UDPAddr:
33 return raw.IP
34 case *net.TCPAddr:
35 return raw.IP
36 default:
37 host, _, err := net.SplitHostPort(addr.String())
38 if err != nil {
39 panic(err)
40 }
41 return net.ParseIP(host)
42 }
43 }
0 package missinggo
1
2 import (
3 "os"
4 "time"
5 )
6
7 // Extracts the access time from the FileInfo internals.
8 func FileInfoAccessTime(fi os.FileInfo) time.Time {
9 return fileInfoAccessTime(fi)
10 }
0 // +build linux dragonfly openbsd solaris
1
2 package missinggo
3
4 import (
5 "os"
6 "syscall"
7 "time"
8 )
9
10 func fileInfoAccessTime(fi os.FileInfo) time.Time {
11 ts := fi.Sys().(*syscall.Stat_t).Atim
12 return time.Unix(int64(ts.Sec), int64(ts.Nsec))
13 }
0 // +build darwin freebsd netbsd
1
2 package missinggo
3
4 import (
5 "os"
6 "syscall"
7 "time"
8 )
9
10 func fileInfoAccessTime(fi os.FileInfo) time.Time {
11 ts := fi.Sys().(*syscall.Stat_t).Atimespec
12 return time.Unix(int64(ts.Sec), int64(ts.Nsec))
13 }
0 package missinggo
1
2 import (
3 "os"
4 "syscall"
5 "time"
6 )
7
8 func fileInfoAccessTime(fi os.FileInfo) time.Time {
9 sec := fi.Sys().(*syscall.Dir).Atime
10 return time.Unix(int64(sec), 0)
11 }
0 package missinggo
1
2 import (
3 "io/ioutil"
4 "os"
5 "testing"
6
7 "github.com/stretchr/testify/assert"
8 "github.com/stretchr/testify/require"
9 )
10
11 func TestFileInfoAccessTime(t *testing.T) {
12 f, err := ioutil.TempFile("", "")
13 require.NoError(t, err)
14 assert.NoError(t, f.Close())
15 name := f.Name()
16 t.Log(name)
17 defer func() {
18 err := os.Remove(name)
19 if err != nil {
20 t.Log(err)
21 }
22 }()
23 fi, err := os.Stat(name)
24 require.NoError(t, err)
25 t.Log(FileInfoAccessTime(fi))
26 }
0 package missinggo
1
2 import (
3 "os"
4 "syscall"
5 "time"
6 )
7
8 func fileInfoAccessTime(fi os.FileInfo) time.Time {
9 ts := fi.Sys().(*syscall.Win32FileAttributeData).LastAccessTime
10 return time.Unix(0, int64(ts.Nanoseconds()))
11 }
0 // Package bitmap provides a []bool/bitmap implementation with standardized
1 // iteration. Bitmaps are the equivalent of []bool, with improved compression
2 // for runs of similar values, and faster operations on ranges and the like.
3 package bitmap
4
5 import (
6 "math"
7
8 "github.com/RoaringBitmap/roaring"
9
10 "github.com/anacrolix/missinggo/iter"
11 )
12
13 const MaxInt = -1
14
15 type BitIndex = int
16
17 type Interface interface {
18 Len() int
19 }
20
21 // Bitmaps store the existence of values in [0,math.MaxUint32] more
22 // efficiently than []bool. The empty value starts with no bits set.
23 type Bitmap struct {
24 RB *roaring.Bitmap
25 }
26
27 var ToEnd int = -1
28
29 // The number of set bits in the bitmap. Also known as cardinality.
30 func (me *Bitmap) Len() int {
31 if me.RB == nil {
32 return 0
33 }
34 return int(me.RB.GetCardinality())
35 }
36
37 func (me Bitmap) ToSortedSlice() (ret []int) {
38 if me.RB == nil {
39 return
40 }
41 for _, ui32 := range me.RB.ToArray() {
42 ret = append(ret, int(int32(ui32)))
43 }
44 return
45 }
46
47 func (me *Bitmap) lazyRB() *roaring.Bitmap {
48 if me.RB == nil {
49 me.RB = roaring.NewBitmap()
50 }
51 return me.RB
52 }
53
54 func (me Bitmap) Iter(cb iter.Callback) {
55 me.IterTyped(func(i int) bool {
56 return cb(i)
57 })
58 }
59
60 // Returns true if all values were traversed without early termination.
61 func (me Bitmap) IterTyped(f func(int) bool) bool {
62 if me.RB == nil {
63 return true
64 }
65 it := me.RB.Iterator()
66 for it.HasNext() {
67 if !f(int(it.Next())) {
68 return false
69 }
70 }
71 return true
72 }
73
74 func checkInt(i BitIndex) {
75 if i < math.MinInt32 || i > math.MaxInt32 {
76 panic("out of bounds")
77 }
78 }
79
80 func (me *Bitmap) Add(is ...BitIndex) {
81 rb := me.lazyRB()
82 for _, i := range is {
83 checkInt(i)
84 rb.AddInt(i)
85 }
86 }
87
88 func (me *Bitmap) AddRange(begin, end BitIndex) {
89 if begin >= end {
90 return
91 }
92 me.lazyRB().AddRange(uint64(begin), uint64(end))
93 }
94
95 func (me *Bitmap) Remove(i BitIndex) bool {
96 if me.RB == nil {
97 return false
98 }
99 return me.RB.CheckedRemove(uint32(i))
100 }
101
102 func (me *Bitmap) Union(other Bitmap) {
103 me.lazyRB().Or(other.lazyRB())
104 }
105
106 func (me *Bitmap) Contains(i int) bool {
107 if me.RB == nil {
108 return false
109 }
110 return me.RB.Contains(uint32(i))
111 }
112
113 func (me *Bitmap) Sub(other Bitmap) {
114 if other.RB == nil {
115 return
116 }
117 if me.RB == nil {
118 return
119 }
120 me.RB.AndNot(other.RB)
121 }
122
123 func (me *Bitmap) Clear() {
124 if me.RB == nil {
125 return
126 }
127 me.RB.Clear()
128 }
129
130 func (me Bitmap) Copy() (ret Bitmap) {
131 ret = me
132 if ret.RB != nil {
133 ret.RB = ret.RB.Clone()
134 }
135 return
136 }
137
138 func (me *Bitmap) FlipRange(begin, end BitIndex) {
139 me.lazyRB().FlipInt(begin, end)
140 }
141
142 func (me *Bitmap) Get(bit BitIndex) bool {
143 return me.RB != nil && me.RB.ContainsInt(bit)
144 }
145
146 func (me *Bitmap) Set(bit BitIndex, value bool) {
147 if value {
148 me.lazyRB().AddInt(bit)
149 } else {
150 if me.RB != nil {
151 me.RB.Remove(uint32(bit))
152 }
153 }
154 }
155
156 func (me *Bitmap) RemoveRange(begin, end BitIndex) *Bitmap {
157 if me.RB == nil {
158 return me
159 }
160 rangeEnd := uint64(end)
161 if end == ToEnd {
162 rangeEnd = 0x100000000
163 }
164 me.RB.RemoveRange(uint64(begin), rangeEnd)
165 return me
166 }
167
168 func (me Bitmap) IsEmpty() bool {
169 return me.RB == nil || me.RB.IsEmpty()
170 }
0 package bitmap
1
2 import (
3 "math"
4 "testing"
5
6 "github.com/RoaringBitmap/roaring"
7 "github.com/stretchr/testify/assert"
8 "github.com/stretchr/testify/require"
9
10 "github.com/anacrolix/missinggo/iter"
11 "github.com/anacrolix/missinggo/slices"
12 )
13
14 func TestEmptyBitmap(t *testing.T) {
15 var bm Bitmap
16 assert.False(t, bm.Contains(0))
17 bm.Remove(0)
18 it := iter.NewIterator(&bm)
19 assert.Panics(t, func() { it.Value() })
20 assert.False(t, it.Next())
21 }
22
23 func bitmapSlice(bm *Bitmap) (ret []int) {
24 sl := iter.IterableAsSlice(bm)
25 slices.MakeInto(&ret, sl)
26 return
27 }
28
29 func TestSimpleBitmap(t *testing.T) {
30 bm := new(Bitmap)
31 assert.EqualValues(t, []int(nil), bitmapSlice(bm))
32 bm.Add(0)
33 assert.True(t, bm.Contains(0))
34 assert.False(t, bm.Contains(1))
35 assert.EqualValues(t, 1, bm.Len())
36 bm.Add(3)
37 assert.True(t, bm.Contains(0))
38 assert.True(t, bm.Contains(3))
39 assert.EqualValues(t, []int{0, 3}, bitmapSlice(bm))
40 assert.EqualValues(t, 2, bm.Len())
41 bm.Remove(0)
42 assert.EqualValues(t, []int{3}, bitmapSlice(bm))
43 assert.EqualValues(t, 1, bm.Len())
44 }
45
46 func TestSub(t *testing.T) {
47 var left, right Bitmap
48 left.Add(2, 5, 4)
49 right.Add(3, 2, 6)
50 assert.Equal(t, []int{4, 5}, Sub(left, right).ToSortedSlice())
51 assert.Equal(t, []int{3, 6}, Sub(right, left).ToSortedSlice())
52 }
53
54 func TestSubUninited(t *testing.T) {
55 var left, right Bitmap
56 assert.EqualValues(t, []int(nil), Sub(left, right).ToSortedSlice())
57 }
58
59 func TestAddRange(t *testing.T) {
60 var bm Bitmap
61 bm.AddRange(21, 26)
62 bm.AddRange(9, 14)
63 bm.AddRange(11, 16)
64 bm.Remove(12)
65 assert.EqualValues(t, []int{9, 10, 11, 13, 14, 15, 21, 22, 23, 24, 25}, bm.ToSortedSlice())
66 assert.EqualValues(t, 11, bm.Len())
67 bm.Clear()
68 bm.AddRange(3, 7)
69 bm.AddRange(0, 3)
70 bm.AddRange(2, 4)
71 bm.Remove(3)
72 assert.EqualValues(t, []int{0, 1, 2, 4, 5, 6}, bm.ToSortedSlice())
73 assert.EqualValues(t, 6, bm.Len())
74 }
75
76 func TestRemoveRange(t *testing.T) {
77 var bm Bitmap
78 bm.AddRange(3, 12)
79 assert.EqualValues(t, 9, bm.Len())
80 bm.RemoveRange(14, -1)
81 assert.EqualValues(t, 9, bm.Len())
82 bm.RemoveRange(2, 5)
83 assert.EqualValues(t, 7, bm.Len())
84 bm.RemoveRange(10, -1)
85 assert.EqualValues(t, 5, bm.Len())
86 }
87
88 func TestLimits(t *testing.T) {
89 var bm Bitmap
90 assert.Panics(t, func() { bm.Add(math.MaxInt64) })
91 bm.Add(-1)
92 assert.EqualValues(t, 1, bm.Len())
93 assert.EqualValues(t, []int{MaxInt}, bm.ToSortedSlice())
94 }
95
96 func TestRoaringRangeEnd(t *testing.T) {
97 r := roaring.New()
98 r.Add(roaring.MaxUint32)
99 require.EqualValues(t, 1, r.GetCardinality())
100 r.RemoveRange(0, roaring.MaxUint32)
101 assert.EqualValues(t, 1, r.GetCardinality())
102 r.RemoveRange(0, math.MaxUint64)
103 assert.EqualValues(t, 0, r.GetCardinality())
104 }
0 package bitmap
1
2 import "github.com/RoaringBitmap/roaring"
3
4 func Sub(left, right Bitmap) Bitmap {
5 return Bitmap{
6 RB: roaring.AndNot(left.lazyRB(), right.lazyRB()),
7 }
8 }
9
10 func Flip(bm Bitmap, start, end int) Bitmap {
11 return Bitmap{
12 RB: roaring.FlipInt(bm.lazyRB(), start, end),
13 }
14 }
0 package bitmap
1
2 import "github.com/RoaringBitmap/roaring"
3
4 type Iter struct {
5 ii roaring.IntIterable
6 }
7
8 func (me *Iter) Next() bool {
9 if me == nil {
10 return false
11 }
12 return me.ii.HasNext()
13 }
14
15 func (me *Iter) Value() interface{} {
16 return me.ValueInt()
17 }
18
19 func (me *Iter) ValueInt() int {
20 return int(me.ii.Next())
21 }
22
23 func (me *Iter) Stop() {}
0 package cache
1
2 import (
3 "fmt"
4 "log"
5 "sync"
6
7 humanize "github.com/dustin/go-humanize"
8 )
9
10 type Key = string
11
12 type Cache struct {
13 mu sync.Mutex
14 filled int64
15 Policy Policy
16 Items map[Key]ItemMeta
17 }
18
19 type ItemMeta struct {
20 Size int64
21 CanEvict bool
22 Usage
23 }
24
25 type Item struct {
26 Key
27 ItemMeta
28 }
29
30 func (me *Cache) Remove(k Key) {
31 me.mu.Lock()
32 i := me.Items[k]
33 me.filled -= i.Size
34 delete(me.Items, k)
35 me.Policy.Forget(k)
36 me.mu.Unlock()
37 }
38
39 func (me *Cache) Update(i Item) {
40 me.mu.Lock()
41 m := me.Items[i.Key]
42 me.filled -= m.Size
43 me.filled += i.Size
44 if me.Items == nil {
45 me.Items = make(map[Key]ItemMeta)
46 }
47 me.Items[i.Key] = i.ItemMeta
48 if i.CanEvict {
49 me.Policy.Update(i.Key, i.Usage)
50 } else {
51 me.Policy.Forget(i.Key)
52 }
53 me.mu.Unlock()
54 }
55
56 func (me *Cache) logState() {
57 log.Print(me)
58 }
59
60 func (me *Cache) String() string {
61 me.mu.Lock()
62 defer me.mu.Unlock()
63 return fmt.Sprintf(
64 "%p: %d items, %v bytes used, lru: %s",
65 me, len(me.Items), humanize.Bytes(uint64(me.filled)),
66 func() string {
67 k, ok := me.Policy.Candidate()
68 if ok {
69 i := me.Items[k]
70 return fmt.Sprintf(
71 "%q (%v: %v)",
72 k, humanize.Bytes(uint64(i.Size)), i.Usage)
73 }
74 return "none"
75 }(),
76 )
77 }
78
79 func (me *Cache) Used() int64 {
80 return me.filled
81 }
82
83 func (me *Cache) NumItems() int {
84 return len(me.Items)
85 }
86
87 func (me *Cache) Clear() {
88 for k := range me.Items {
89 delete(me.Items, k)
90 me.Policy.Forget(k)
91 }
92 me.Items = nil
93 me.filled = 0
94 }
95
96 func (me *Cache) Filled() int64 {
97 me.mu.Lock()
98 defer me.mu.Unlock()
99 return me.filled
100 }
101
102 func (me *Cache) Candidate() (Item, bool) {
103 me.mu.Lock()
104 defer me.mu.Unlock()
105 k, ok := me.Policy.Candidate()
106 return Item{
107 Key: k,
108 ItemMeta: me.Items[k],
109 }, ok
110 }
0 package cache
1
2 import (
3 "sync"
4
5 "github.com/anacrolix/missinggo"
6 "github.com/anacrolix/missinggo/orderedmap"
7 )
8
9 type LruPolicy struct {
10 mu sync.RWMutex
11 sorted orderedmap.OrderedMap
12 keys map[Key]Usage
13 }
14
15 type lruItem struct {
16 Key
17 Usage
18 }
19
20 var _ Policy = (*LruPolicy)(nil)
21
22 func (me *LruPolicy) Candidate() (k Key, ok bool) {
23 me.mu.RLock()
24 defer me.mu.RUnlock()
25 if me.sorted == nil {
26 return
27 }
28 me.sorted.Iter(func(i interface{}) bool {
29 k = i.(lruItem).Key
30 ok = true
31 return false
32 })
33 return
34 }
35
36 func (me *LruPolicy) Forget(k Key) {
37 me.mu.Lock()
38 defer me.mu.Unlock()
39 u, ok := me.keys[k]
40 if !ok {
41 return
42 }
43 me.sorted.Unset(lruItem{k, u})
44 delete(me.keys, k)
45 }
46
47 func (me *LruPolicy) NumItems() int {
48 return len(me.keys)
49 }
50
51 func (me *LruPolicy) Update(k Key, u Usage) {
52 me.mu.Lock()
53 defer me.mu.Unlock()
54 if me.sorted == nil {
55 me.sorted = orderedmap.New(func(l, r interface{}) bool {
56 _l := l.(lruItem)
57 _r := r.(lruItem)
58 var ml missinggo.MultiLess
59 ml.NextBool(_l.Usage.Less(_r.Usage), _r.Usage.Less(_l.Usage))
60 ml.StrictNext(_l.Key == _r.Key, _l.Key < _r.Key)
61 return ml.Less()
62 })
63 }
64 if u, ok := me.keys[k]; ok {
65 me.sorted.Unset(lruItem{k, u})
66 }
67 me.sorted.Set(lruItem{k, u}, struct{}{})
68 if me.keys == nil {
69 me.keys = make(map[Key]Usage)
70 }
71 me.keys[k] = u
72 }
0 package cache
1
2 type Usage interface {
3 Less(Usage) bool
4 }
5
6 type Policy interface {
7 Candidate() (Key, bool)
8 Update(Key, Usage)
9 Forget(Key)
10 NumItems() int
11 }
0 package missinggo
1
2 import (
3 "crypto/tls"
4 "log"
5 "os"
6 "path/filepath"
7 "strings"
8 )
9
10 func LoadCertificateDir(dir string) (certs []tls.Certificate, err error) {
11 d, err := os.Open(dir)
12 if err != nil {
13 return
14 }
15 defer d.Close()
16 const defaultPEMFile = "default.pem"
17 if p := filepath.Join(dir, defaultPEMFile); FilePathExists(p) {
18 cert, err := tls.LoadX509KeyPair(p, p)
19 if err == nil {
20 certs = append(certs, cert)
21 } else {
22 log.Printf("error loading default certicate: %s", err)
23 }
24 }
25 files, err := d.Readdir(-1)
26 if err != nil {
27 return
28 }
29 for _, f := range files {
30 if f.Name() == defaultPEMFile {
31 continue
32 }
33 if !strings.HasSuffix(f.Name(), ".pem") {
34 continue
35 }
36 p := filepath.Join(dir, f.Name())
37 cert, err := tls.LoadX509KeyPair(p, p)
38 if err != nil {
39 log.Printf("error loading key pair from %q: %s", p, err)
40 continue
41 }
42 certs = append(certs, cert)
43 }
44 return
45 }
0 package missinggo
1
2 import "sync"
3
4 type ChanCond struct {
5 mu sync.Mutex
6 ch chan struct{}
7 }
8
9 func (me *ChanCond) Wait() <-chan struct{} {
10 me.mu.Lock()
11 defer me.mu.Unlock()
12 if me.ch == nil {
13 me.ch = make(chan struct{})
14 }
15 return me.ch
16 }
17
18 func (me *ChanCond) Signal() {
19 me.mu.Lock()
20 defer me.mu.Unlock()
21 select {
22 case me.ch <- struct{}{}:
23 default:
24 }
25 }
26
27 func (me *ChanCond) Broadcast() {
28 me.mu.Lock()
29 defer me.mu.Unlock()
30 if me.ch == nil {
31 return
32 }
33 close(me.ch)
34 me.ch = nil
35 }
0 package chans
1
2 import (
3 "reflect"
4 )
5
6 // Receives from any channel until it's closed.
7 func Drain(ch interface{}) {
8 chValue := reflect.ValueOf(ch)
9 for {
10 _, ok := chValue.Recv()
11 if !ok {
12 break
13 }
14 }
15 }
0 package main
1
2 import (
3 "crypto/tls"
4 "fmt"
5 "io"
6 "log"
7 "net/http"
8 "os"
9 "regexp"
10 "strconv"
11
12 _ "github.com/anacrolix/envpprof"
13 "github.com/anacrolix/tagflag"
14 "github.com/dustin/go-humanize"
15
16 "github.com/anacrolix/missinggo"
17 "github.com/anacrolix/missinggo/filecache"
18 )
19
20 var c *filecache.Cache
21
22 func handleNewData(w http.ResponseWriter, path string, offset int64, r io.Reader) (served bool) {
23 f, err := c.OpenFile(path, os.O_CREATE|os.O_WRONLY)
24 if err != nil {
25 log.Print(err)
26 http.Error(w, "couldn't open file", http.StatusInternalServerError)
27 return true
28 }
29 defer f.Close()
30 f.Seek(offset, os.SEEK_SET)
31 _, err = io.Copy(f, r)
32 if err != nil {
33 log.Print(err)
34 c.Remove(path)
35 http.Error(w, "didn't complete", http.StatusInternalServerError)
36 return true
37 }
38 return
39 }
40
41 // Parses out the first byte from a Content-Range header. Returns 0 if it
42 // isn't found, which is what is implied if there is no header.
43 func parseContentRangeFirstByte(s string) int64 {
44 matches := regexp.MustCompile(`(\d+)-`).FindStringSubmatch(s)
45 if matches == nil {
46 return 0
47 }
48 ret, _ := strconv.ParseInt(matches[1], 0, 64)
49 return ret
50 }
51
52 func handleDelete(w http.ResponseWriter, path string) {
53 err := c.Remove(path)
54 if err != nil {
55 log.Print(err)
56 http.Error(w, "didn't work", http.StatusInternalServerError)
57 return
58 }
59 }
60
61 func main() {
62 log.SetFlags(log.Flags() | log.Lshortfile)
63 args := struct {
64 Capacity tagflag.Bytes `short:"c"`
65 Addr string
66 }{
67 Capacity: -1,
68 Addr: "localhost:2076",
69 }
70 tagflag.Parse(&args)
71 root, err := os.Getwd()
72 if err != nil {
73 log.Fatal(err)
74 }
75 log.Printf("cache root at %q", root)
76 c, err = filecache.NewCache(root)
77 if err != nil {
78 log.Fatalf("error creating cache: %s", err)
79 }
80 if args.Capacity < 0 {
81 log.Printf("no capacity set, no evictions will occur")
82 } else {
83 c.SetCapacity(args.Capacity.Int64())
84 log.Printf("setting capacity to %s bytes", humanize.Comma(args.Capacity.Int64()))
85 }
86 http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
87 p := r.URL.Path[1:]
88 switch r.Method {
89 case "DELETE":
90 log.Printf("%s %s", r.Method, r.RequestURI)
91 handleDelete(w, p)
92 return
93 case "PUT", "PATCH", "POST":
94 contentRange := r.Header.Get("Content-Range")
95 firstByte := parseContentRangeFirstByte(contentRange)
96 log.Printf("%s (%d-) %s", r.Method, firstByte, r.RequestURI)
97 handleNewData(w, p, firstByte, r.Body)
98 return
99 }
100 log.Printf("%s %s %s", r.Method, r.Header.Get("Range"), r.RequestURI)
101 f, err := c.OpenFile(p, os.O_RDONLY)
102 if os.IsNotExist(err) {
103 http.NotFound(w, r)
104 return
105 }
106 if err != nil {
107 log.Printf("couldn't open requested file: %s", err)
108 http.Error(w, "couldn't open file", http.StatusInternalServerError)
109 return
110 }
111 defer func() {
112 go f.Close()
113 }()
114 info, _ := f.Stat()
115 w.Header().Set("Content-Range", fmt.Sprintf("*/%d", info.Size()))
116 http.ServeContent(w, r, p, info.ModTime(), f)
117 })
118 http.HandleFunc("/status", func(w http.ResponseWriter, r *http.Request) {
119 info := c.Info()
120 fmt.Fprintf(w, "Capacity: %d\n", info.Capacity)
121 fmt.Fprintf(w, "Current Size: %d\n", info.Filled)
122 fmt.Fprintf(w, "Item Count: %d\n", info.NumItems)
123 })
124 http.HandleFunc("/lru", func(w http.ResponseWriter, r *http.Request) {
125 c.WalkItems(func(item filecache.ItemInfo) {
126 fmt.Fprintf(w, "%s\t%d\t%s\n", item.Accessed, item.Size, item.Path)
127 })
128 })
129 cert, err := missinggo.NewSelfSignedCertificate()
130 if err != nil {
131 log.Fatal(err)
132 }
133 srv := http.Server{
134 Addr: args.Addr,
135 TLSConfig: &tls.Config{
136 Certificates: []tls.Certificate{cert},
137 },
138 }
139 log.Fatal(srv.ListenAndServeTLS("", ""))
140 }
0 package main
1
2 import (
3 "testing"
4 )
5
6 func TestFailedPutDeletesFile(t *testing.T) {
7 // TODO
8 }
0 package main
1
2 import (
3 "encoding/base32"
4 "encoding/hex"
5 "fmt"
6 "io"
7 "io/ioutil"
8 "net/url"
9 "os"
10 "strings"
11 )
12
13 func escape(encoding string) {
14 switch {
15 case strings.HasPrefix("query", encoding):
16 b, err := ioutil.ReadAll(os.Stdin)
17 if err != nil {
18 fmt.Fprintln(os.Stderr, err)
19 os.Exit(1)
20 }
21 os.Stdout.Write([]byte(url.QueryEscape(string(b))))
22 case strings.HasPrefix("hex", encoding):
23 b, err := ioutil.ReadAll(os.Stdin)
24 if err != nil {
25 fmt.Fprintln(os.Stderr, err)
26 os.Exit(1)
27 }
28 os.Stdout.Write([]byte(hex.EncodeToString(b)))
29 default:
30 fmt.Fprintf(os.Stderr, "unknown escape encoding: %q\n", encoding)
31 os.Exit(2)
32 }
33 }
34
35 func unescape(encoding string) {
36 switch {
37 case strings.HasPrefix("query", encoding):
38 b, err := ioutil.ReadAll(os.Stdin)
39 if err != nil {
40 fmt.Fprintln(os.Stderr, err)
41 os.Exit(1)
42 }
43 s, err := url.QueryUnescape(string(b))
44 if err != nil {
45 fmt.Fprintln(os.Stderr, err)
46 os.Exit(1)
47 }
48 os.Stdout.Write([]byte(s))
49 case strings.HasPrefix("b32", encoding):
50 d := base32.NewDecoder(base32.StdEncoding, os.Stdin)
51 io.Copy(os.Stdout, d)
52 default:
53 fmt.Fprintf(os.Stderr, "unknown unescape encoding: %q\n", encoding)
54 }
55 }
56
57 func main() {
58 if len(os.Args) != 3 {
59 fmt.Fprintf(os.Stderr, "expected two arguments: <mode> <encoding>: got %d\n", len(os.Args)-1)
60 os.Exit(2)
61 }
62 mode := os.Args[1]
63 switch {
64 case strings.HasPrefix("escape", mode):
65 escape(os.Args[2])
66 case strings.HasPrefix("unescape", mode) || strings.HasPrefix("decode", mode):
67 unescape(os.Args[2])
68 default:
69 fmt.Fprintf(os.Stderr, "unknown mode: %q\n", mode)
70 os.Exit(2)
71 }
72 }
0 package main
1
2 import (
3 "fmt"
4 "os"
5 )
6
7 func main() {
8 for _, v := range os.Environ() {
9 fmt.Printf("%s\n", v)
10 }
11 }
0 package main
1
2 import (
3 "log"
4 "net"
5 "net/http"
6 "os"
7
8 "github.com/anacrolix/tagflag"
9 )
10
11 func setIfGetHeader(w http.ResponseWriter, r *http.Request, set, get string) {
12 h := r.Header.Get(get)
13 if h == "" {
14 return
15 }
16 w.Header().Set(set, h)
17 }
18
19 func allowCORS(h http.Handler) http.Handler {
20 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
21 if origin := r.Header.Get("Origin"); origin != "" {
22 w.Header().Set("Access-Control-Allow-Origin", origin)
23 w.Header().Set("Access-Control-Allow-Credentials", "true")
24 setIfGetHeader(w, r, "Access-Control-Allow-Methods", "Access-Control-Request-Method")
25 setIfGetHeader(w, r, "Access-Control-Allow-Headers", "Access-Control-Request-Headers")
26 w.Header().Set("Access-Control-Expose-Headers", "Content-Type")
27 }
28 h.ServeHTTP(w, r)
29 })
30 }
31
32 func main() {
33 var flags = struct {
34 Addr string
35 }{
36 Addr: "localhost:8080",
37 }
38 tagflag.Parse(&flags)
39 dir, err := os.Getwd()
40 if err != nil {
41 log.Fatal(err)
42 }
43 l, err := net.Listen("tcp", flags.Addr)
44 if err != nil {
45 log.Fatal(err)
46 }
47 defer l.Close()
48 addr := l.Addr()
49 log.Printf("serving %q at %s", dir, addr)
50 log.Fatal(http.Serve(l, allowCORS(http.FileServer(http.Dir(dir)))))
51 }
0 package main
1
2 func main() {}
0 package main
1
2 import (
3 "fmt"
4 "net/url"
5 "os"
6 )
7
8 func main() {
9 fmt.Println(url.QueryEscape(os.Args[1]))
10 }
0 package main
1
2 import (
3 "fmt"
4 "net/url"
5 "os"
6 )
7
8 func main() {
9 fmt.Println(url.QueryUnescape(os.Args[1]))
10 }
0 package conntrack
1
2 import "expvar"
3
4 type Protocol = string
5
6 type Endpoint = string
7
8 type Entry struct {
9 Protocol
10 LocalAddr Endpoint
11 RemoteAddr Endpoint
12 }
13
14 var expvars = expvar.NewMap("conntrack")
0 package conntrack
1
2 import (
3 "context"
4 "math"
5 "strconv"
6 "sync"
7 "testing"
8 "time"
9
10 "github.com/stretchr/testify/assert"
11
12 _ "github.com/anacrolix/envpprof"
13 "github.com/bradfitz/iter"
14 )
15
16 func entry(id int) Entry {
17 return Entry{"", "", strconv.FormatInt(int64(id), 10)}
18 }
19
20 func TestWaitingForSameEntry(t *testing.T) {
21 i := NewInstance()
22 i.SetMaxEntries(1)
23 i.Timeout = func(Entry) time.Duration {
24 return 0
25 }
26 e1h1 := i.WaitDefault(context.Background(), entry(1))
27 gotE2s := make(chan struct{})
28 for range iter.N(2) {
29 go func() {
30 i.WaitDefault(context.Background(), entry(2))
31 gotE2s <- struct{}{}
32 }()
33 }
34 gotE1 := make(chan struct{})
35 var e1h2 *EntryHandle
36 go func() {
37 e1h2 = i.WaitDefault(context.Background(), entry(1))
38 gotE1 <- struct{}{}
39 }()
40 select {
41 case <-gotE1:
42 case <-gotE2s:
43 t.FailNow()
44 }
45 go e1h1.Done()
46 go e1h2.Done()
47 <-gotE2s
48 <-gotE2s
49 }
50
51 func TestInstanceSetNoMaxEntries(t *testing.T) {
52 i := NewInstance()
53 i.SetMaxEntries(0)
54 var wg sync.WaitGroup
55 wait := func(e Entry, p priority) {
56 i.Wait(context.Background(), e, "", p)
57 wg.Done()
58 }
59 for _, e := range []Entry{entry(0), entry(1)} {
60 for _, p := range []priority{math.MinInt32, math.MaxInt32} {
61 wg.Add(1)
62 go wait(e, p)
63 }
64 }
65 waitForNumWaiters := func(num int) {
66 i.mu.Lock()
67 for len(i.waiters) != num {
68 i.numWaitersChanged.Wait()
69 }
70 i.mu.Unlock()
71 }
72 waitForNumWaiters(4)
73 i.SetNoMaxEntries()
74 waitForNumWaiters(0)
75 wg.Wait()
76 }
77
78 func TestWaitReturnsNilContextCompleted(t *testing.T) {
79 i := NewInstance()
80 i.SetMaxEntries(0)
81 ctx, cancel := context.WithCancel(context.Background())
82 cancel()
83 assert.Nil(t, i.WaitDefault(ctx, entry(0)))
84 ctx, cancel = context.WithTimeout(context.Background(), 10*time.Millisecond)
85 assert.Nil(t, i.WaitDefault(ctx, entry(1)))
86 cancel()
87 }
88
89 func TestWaitContextCanceledButRoomForEntry(t *testing.T) {
90 i := NewInstance()
91 i.SetMaxEntries(1)
92 ctx, cancel := context.WithCancel(context.Background())
93 go cancel()
94 eh := i.WaitDefault(ctx, entry(0))
95 if eh == nil {
96 assert.Error(t, ctx.Err())
97 } else {
98 eh.Done()
99 }
100 }
101
102 func TestUnlimitedInstance(t *testing.T) {
103 i := NewInstance()
104 i.SetNoMaxEntries()
105 i.Timeout = func(Entry) time.Duration { return 0 }
106 eh := i.WaitDefault(context.Background(), entry(0))
107 assert.NotNil(t, eh)
108 i.mu.Lock()
109 assert.Len(t, i.entries[eh.e], 1)
110 i.mu.Unlock()
111 eh.Done()
112 i.mu.Lock()
113 assert.Nil(t, i.entries[eh.e])
114 i.mu.Unlock()
115 }
116
117 func TestUnlimitedInstanceContextCanceled(t *testing.T) {
118 i := NewInstance()
119 i.SetNoMaxEntries()
120 i.Timeout = func(Entry) time.Duration { return 0 }
121 ctx, cancel := context.WithCancel(context.Background())
122 cancel()
123 eh := i.WaitDefault(ctx, entry(0))
124 assert.NotNil(t, eh)
125 i.mu.Lock()
126 assert.Len(t, i.entries[eh.e], 1)
127 i.mu.Unlock()
128 eh.Done()
129 i.mu.Lock()
130 assert.Nil(t, i.entries[eh.e])
131 i.mu.Unlock()
132 }
133
134 func TestContextCancelledWhileWaiting(t *testing.T) {
135 i := NewInstance()
136 i.SetMaxEntries(0)
137 ctx, cancel := context.WithCancel(context.Background())
138 i.mu.Lock()
139 assert.Len(t, i.waiters, 0)
140 i.mu.Unlock()
141 waitReturned := make(chan struct{})
142 go func() {
143 eh := i.WaitDefault(ctx, entry(0))
144 assert.Nil(t, eh)
145 close(waitReturned)
146 }()
147 for {
148 i.mu.Lock()
149 if len(i.waiters) == 1 {
150 i.mu.Unlock()
151 break
152 }
153 i.mu.Unlock()
154 time.Sleep(time.Millisecond)
155 }
156 cancel()
157 <-waitReturned
158 assert.Len(t, i.entries, 0)
159 assert.Len(t, i.waiters, 0)
160 }
161
162 func TestRaceWakeAndContextCompletion(t *testing.T) {
163 i := NewInstance()
164 i.SetMaxEntries(1)
165 eh0 := i.WaitDefault(context.Background(), entry(0))
166 ctx, cancel := context.WithCancel(context.Background())
167 waitReturned := make(chan struct{})
168 go func() {
169 eh1 := i.WaitDefault(ctx, entry(1))
170 if eh1 != nil {
171 eh1.Forget()
172 }
173 close(waitReturned)
174 }()
175 go cancel()
176 go eh0.Forget()
177 <-waitReturned
178 cancel()
179 eh0.Forget()
180 i.mu.Lock()
181 assert.Len(t, i.entries, 0)
182 assert.Len(t, i.waiters, 0)
183 i.mu.Unlock()
184 }
0 package conntrack
1
2 import (
3 "sync"
4 "time"
5 )
6
7 type EntryHandle struct {
8 reason string
9 e Entry
10 priority priority
11 i *Instance
12 expires time.Time
13 created time.Time
14 wake sync.Mutex
15 }
16
17 func (eh *EntryHandle) Done() {
18 expvars.Add("entry handles done", 1)
19 timeout := eh.timeout()
20 eh.expires = time.Now().Add(timeout)
21 if timeout <= 0 {
22 eh.remove()
23 } else {
24 time.AfterFunc(eh.timeout(), eh.remove)
25 }
26 }
27
28 func (eh *EntryHandle) Forget() {
29 expvars.Add("entry handles forgotten", 1)
30 eh.remove()
31 }
32
33 func (eh *EntryHandle) remove() {
34 eh.i.remove(eh)
35 }
36
37 func (eh *EntryHandle) timeout() time.Duration {
38 return eh.i.Timeout(eh.e)
39 }
0 package conntrack
1
2 import (
3 "context"
4 "fmt"
5 "io"
6 "sync"
7 "text/tabwriter"
8 "time"
9
10 "github.com/anacrolix/missinggo/orderedmap"
11 )
12
13 type reason = string
14
15 type Instance struct {
16 maxEntries int
17 noMaxEntries bool
18 Timeout func(Entry) time.Duration
19
20 mu sync.Mutex
21 // Occupied slots
22 entries map[Entry]handles
23
24 // priority to entryHandleSet, ordered by priority ascending
25 waitersByPriority orderedmap.OrderedMap
26 waitersByReason map[reason]entryHandleSet
27 waitersByEntry map[Entry]entryHandleSet
28 waiters entryHandleSet
29 numWaitersChanged sync.Cond
30 }
31
32 type (
33 entryHandleSet = map[*EntryHandle]struct{}
34 waitersByPriorityValue = entryHandleSet
35 priority int
36 handles = map[*EntryHandle]struct{}
37 )
38
39 func NewInstance() *Instance {
40 i := &Instance{
41 // A quarter of the commonly quoted absolute max on a Linux system.
42 maxEntries: 1 << 14,
43 Timeout: func(e Entry) time.Duration {
44 // udp is the main offender, and the default is allegedly 30s.
45 return 30 * time.Second
46 },
47 entries: make(map[Entry]handles),
48 waitersByPriority: orderedmap.New(func(_l, _r interface{}) bool {
49 return _l.(priority) > _r.(priority)
50 }),
51 waitersByReason: make(map[reason]entryHandleSet),
52 waitersByEntry: make(map[Entry]entryHandleSet),
53 waiters: make(entryHandleSet),
54 }
55 i.numWaitersChanged.L = &i.mu
56 return i
57 }
58
59 func (i *Instance) SetNoMaxEntries() {
60 i.mu.Lock()
61 defer i.mu.Unlock()
62 i.noMaxEntries = true
63 i.wakeAll()
64 }
65
66 func (i *Instance) SetMaxEntries(max int) {
67 i.mu.Lock()
68 defer i.mu.Unlock()
69 i.noMaxEntries = false
70 prev := i.maxEntries
71 i.maxEntries = max
72 for j := prev; j < max; j++ {
73 i.wakeOne()
74 }
75 }
76
77 func (i *Instance) remove(eh *EntryHandle) {
78 i.mu.Lock()
79 defer i.mu.Unlock()
80 hs := i.entries[eh.e]
81 delete(hs, eh)
82 if len(hs) == 0 {
83 delete(i.entries, eh.e)
84 i.wakeOne()
85 }
86 }
87
88 // Wakes all waiters.
89 func (i *Instance) wakeAll() {
90 for len(i.waiters) != 0 {
91 i.wakeOne()
92 }
93 }
94
95 // Wakes the highest priority waiter.
96 func (i *Instance) wakeOne() {
97 i.waitersByPriority.Iter(func(key interface{}) bool {
98 value := i.waitersByPriority.Get(key).(entryHandleSet)
99 for eh := range value {
100 i.wakeEntry(eh.e)
101 break
102 }
103 return false
104 })
105 }
106
107 func (i *Instance) deleteWaiter(eh *EntryHandle) {
108 delete(i.waiters, eh)
109 p := i.waitersByPriority.Get(eh.priority).(entryHandleSet)
110 delete(p, eh)
111 if len(p) == 0 {
112 i.waitersByPriority.Unset(eh.priority)
113 }
114 r := i.waitersByReason[eh.reason]
115 delete(r, eh)
116 if len(r) == 0 {
117 delete(i.waitersByReason, eh.reason)
118 }
119 e := i.waitersByEntry[eh.e]
120 delete(e, eh)
121 if len(e) == 0 {
122 delete(i.waitersByEntry, eh.e)
123 }
124 i.numWaitersChanged.Broadcast()
125 }
126
127 func (i *Instance) addWaiter(eh *EntryHandle) {
128 p, ok := i.waitersByPriority.GetOk(eh.priority)
129 if ok {
130 p.(entryHandleSet)[eh] = struct{}{}
131 } else {
132 i.waitersByPriority.Set(eh.priority, entryHandleSet{eh: struct{}{}})
133 }
134 if r := i.waitersByReason[eh.reason]; r == nil {
135 i.waitersByReason[eh.reason] = entryHandleSet{eh: struct{}{}}
136 } else {
137 r[eh] = struct{}{}
138 }
139 if e := i.waitersByEntry[eh.e]; e == nil {
140 i.waitersByEntry[eh.e] = entryHandleSet{eh: struct{}{}}
141 } else {
142 e[eh] = struct{}{}
143 }
144 i.waiters[eh] = struct{}{}
145 i.numWaitersChanged.Broadcast()
146 }
147
148 // Wakes all waiters on an entry. Note that the entry is also woken
149 // immediately, the waiters are all let through.
150 func (i *Instance) wakeEntry(e Entry) {
151 if _, ok := i.entries[e]; ok {
152 panic(e)
153 }
154 i.entries[e] = make(handles, len(i.waitersByEntry[e]))
155 for eh := range i.waitersByEntry[e] {
156 i.entries[e][eh] = struct{}{}
157 i.deleteWaiter(eh)
158 eh.wake.Unlock()
159 }
160 if i.waitersByEntry[e] != nil {
161 panic(i.waitersByEntry[e])
162 }
163 }
164
165 func (i *Instance) WaitDefault(ctx context.Context, e Entry) *EntryHandle {
166 return i.Wait(ctx, e, "", 0)
167 }
168
169 // Nil returns are due to context completion.
170 func (i *Instance) Wait(ctx context.Context, e Entry, reason string, p priority) (eh *EntryHandle) {
171 eh = &EntryHandle{
172 reason: reason,
173 e: e,
174 i: i,
175 priority: p,
176 created: time.Now(),
177 }
178 i.mu.Lock()
179 hs, ok := i.entries[eh.e]
180 if ok {
181 hs[eh] = struct{}{}
182 i.mu.Unlock()
183 expvars.Add("waits for existing entry", 1)
184 return
185 }
186 if i.noMaxEntries || len(i.entries) < i.maxEntries {
187 i.entries[eh.e] = handles{
188 eh: struct{}{},
189 }
190 i.mu.Unlock()
191 expvars.Add("waits with space in table", 1)
192 return
193 }
194 // Lock the mutex, so that a following Lock will block until it's unlocked by a wake event.
195 eh.wake.Lock()
196 i.addWaiter(eh)
197 i.mu.Unlock()
198 expvars.Add("waits that blocked", 1)
199 ctx, cancel := context.WithCancel(ctx)
200 defer cancel()
201 go func() {
202 <-ctx.Done()
203 i.mu.Lock()
204 if _, ok := i.waiters[eh]; ok {
205 i.deleteWaiter(eh)
206 eh.wake.Unlock()
207 }
208 i.mu.Unlock()
209 }()
210 // Blocks until woken by an Unlock.
211 eh.wake.Lock()
212 i.mu.Lock()
213 if _, ok := i.entries[eh.e][eh]; !ok {
214 eh = nil
215 }
216 i.mu.Unlock()
217 return
218 }
219
220 func (i *Instance) PrintStatus(w io.Writer) {
221 tw := tabwriter.NewWriter(w, 0, 0, 2, ' ', 0)
222 i.mu.Lock()
223 fmt.Fprintf(w, "num entries: %d\n", len(i.entries))
224 fmt.Fprintln(w)
225 fmt.Fprintf(w, "%d waiters:\n", len(i.waiters))
226 fmt.Fprintf(tw, "num\treason\n")
227 for r, ws := range i.waitersByReason {
228 fmt.Fprintf(tw, "%d\t%q\n", len(ws), r)
229 }
230 tw.Flush()
231 fmt.Fprintln(w)
232 fmt.Fprintln(w, "handles:")
233 fmt.Fprintf(tw, "protocol\tlocal\tremote\treason\texpires\tcreated\n")
234 for e, hs := range i.entries {
235 for h := range hs {
236 fmt.Fprintf(tw,
237 "%q\t%q\t%q\t%q\t%s\t%v ago\n",
238 e.Protocol, e.LocalAddr, e.RemoteAddr, h.reason,
239 func() interface{} {
240 if h.expires.IsZero() {
241 return "not done"
242 } else {
243 return time.Until(h.expires)
244 }
245 }(),
246 time.Since(h.created),
247 )
248 }
249 }
250 i.mu.Unlock()
251 tw.Flush()
252 }
0 package xheap
1
2 import (
3 "container/heap"
4 "sort"
5 )
6
7 type pushPopper interface {
8 Push(interface{})
9 Pop() interface{}
10 }
11
12 func Flipped(h heap.Interface) heap.Interface {
13 return struct {
14 sort.Interface
15 pushPopper
16 }{
17 sort.Reverse(h),
18 h,
19 }
20 }
21
22 // type top struct {
23 // k int
24 // heap.Interface
25 // }
26
27 // func (me top) Push(x interface{}) {
28 // heap.Push(me.Interface, x)
29 // if me.Len() > me.k {
30 // heap.Pop(me)
31 // }
32 // }
33
34 // func Bounded(k int, h heap.Interface) heap.Interface {
35 // return top{k, Flipped(h)}
36 // }
37
38 type slice struct {
39 Slice *[]interface{}
40 Lesser func(l, r interface{}) bool
41 }
42
43 func (me slice) Len() int { return len(*me.Slice) }
44
45 func (me slice) Less(i, j int) bool {
46 return me.Lesser((*me.Slice)[i], (*me.Slice)[j])
47 }
48
49 func (me slice) Pop() (ret interface{}) {
50 i := me.Len() - 1
51 ret = (*me.Slice)[i]
52 *me.Slice = (*me.Slice)[:i]
53 return
54 }
55
56 func (me slice) Push(x interface{}) {
57 *me.Slice = append(*me.Slice, x)
58 }
59
60 func (me slice) Swap(i, j int) {
61 sl := *me.Slice
62 sl[i], sl[j] = sl[j], sl[i]
63 }
64
65 func Slice(sl *[]interface{}, lesser func(l, r interface{}) bool) heap.Interface {
66 return slice{sl, lesser}
67 }
0 package missinggo
1
2 import (
3 "fmt"
4 "reflect"
5 )
6
7 // Copy elements from src to dst. Panics if the length of src and dst are
8 // different.
9 func CopyExact(dest interface{}, src interface{}) {
10 dV := reflect.ValueOf(dest)
11 sV := reflect.ValueOf(src)
12 if dV.Kind() == reflect.Ptr {
13 dV = dV.Elem()
14 }
15 if dV.Kind() == reflect.Array && !dV.CanAddr() {
16 panic(fmt.Sprintf("dest not addressable: %T", dest))
17 }
18 if sV.Kind() == reflect.Ptr {
19 sV = sV.Elem()
20 }
21 if sV.Kind() == reflect.String {
22 sV = sV.Convert(reflect.SliceOf(dV.Type().Elem()))
23 }
24 if !sV.IsValid() {
25 panic("invalid source, probably nil")
26 }
27 if dV.Len() != sV.Len() {
28 panic(fmt.Sprintf("dest len (%d) != src len (%d)", dV.Len(), sV.Len()))
29 }
30 if dV.Len() != reflect.Copy(dV, sV) {
31 panic("dammit")
32 }
33 }
0 package missinggo
1
2 import (
3 "bytes"
4 "strings"
5 "testing"
6 )
7
8 func TestCopyToArray(t *testing.T) {
9 var arr [3]byte
10 bb := []byte{1, 2, 3}
11 CopyExact(&arr, bb)
12 if !bytes.Equal(arr[:], bb) {
13 t.FailNow()
14 }
15 }
16
17 func TestCopyToSlicedArray(t *testing.T) {
18 var arr [5]byte
19 CopyExact(arr[:], "hello")
20 if !bytes.Equal(arr[:], []byte("hello")) {
21 t.FailNow()
22 }
23 }
24
25 func TestCopyDestNotAddr(t *testing.T) {
26 defer func() {
27 r := recover()
28 if r == nil {
29 t.FailNow()
30 }
31 t.Log(r)
32 }()
33 var arr [3]byte
34 CopyExact(arr, "nope")
35 }
36
37 func TestCopyLenMismatch(t *testing.T) {
38 defer func() {
39 r := recover()
40 if r == nil {
41 t.FailNow()
42 }
43 t.Log(r)
44 }()
45 CopyExact(make([]byte, 2), "abc")
46 }
47
48 func TestCopySrcString(t *testing.T) {
49 dest := make([]byte, 3)
50 CopyExact(dest, "lol")
51 if string(dest) != "lol" {
52 t.FailNow()
53 }
54 func() {
55 defer func() {
56 r := recover()
57 if r == nil {
58 t.FailNow()
59 }
60 }()
61 CopyExact(dest, "rofl")
62 }()
63 var arr [5]byte
64 CopyExact(&arr, interface{}("hello"))
65 if string(arr[:]) != "hello" {
66 t.FailNow()
67 }
68 }
69
70 func TestCopySrcNilInterface(t *testing.T) {
71 var arr [3]byte
72 defer func() {
73 r := recover().(string)
74 if !strings.Contains(r, "invalid source") {
75 t.FailNow()
76 }
77 }()
78 CopyExact(&arr, nil)
79 }
80
81 func TestCopySrcPtr(t *testing.T) {
82 var bigDst [1024]byte
83 var bigSrc [1024]byte = [1024]byte{'h', 'i'}
84 CopyExact(&bigDst, &bigSrc)
85 if !bytes.Equal(bigDst[:], bigSrc[:]) {
86 t.FailNow()
87 }
88 }
0 package missinggo
1
2 import (
3 "fmt"
4 "os"
5 )
6
7 func Unchomp(s string) string {
8 if len(s) > 0 && s[len(s)-1] == '\n' {
9 return s
10 }
11 return s + "\n"
12 }
13
14 func Fatal(msg interface{}) {
15 os.Stderr.WriteString(Unchomp(fmt.Sprint(msg)))
16 os.Exit(1)
17 }
0 package ctrlflow
1
2 import (
3 "fmt"
4 )
5
6 type valueWrapper struct {
7 value interface{}
8 }
9
10 func (me valueWrapper) String() string {
11 return fmt.Sprint(me.value)
12 }
13
14 func Panic(val interface{}) {
15 panic(valueWrapper{val})
16 }
17
18 func Recover(handler func(interface{}) bool) {
19 r := recover()
20 if r == nil {
21 return
22 }
23 if vw, ok := r.(valueWrapper); ok {
24 if handler(vw.value) {
25 return
26 }
27 }
28 panic(r)
29 }
0 // Package missinggo contains miscellaneous helpers used in many of anacrolix'
1 // projects.
2 package missinggo
0 package docopt
1
2 import (
3 "fmt"
4 "os"
5
6 "github.com/docopt/docopt-go"
7 )
8
9 func Parse(doc string) (opts map[string]interface{}) {
10 opts, err := docopt.Parse(doc, nil, true, "1.2.3", false, false)
11 if ue, ok := err.(*docopt.UserError); ok {
12 if ue.Error() != "" {
13 fmt.Fprintf(os.Stderr, "\n%s\n", ue)
14 }
15 os.Exit(2)
16 }
17 if err != nil {
18 fmt.Fprintf(os.Stderr, "error parsing docopt: %#v\n", err)
19 os.Exit(1)
20 }
21 return
22 }
0 package missinggo
1
2 import "reflect"
3
4 func IsZeroValue(i interface{}) bool {
5 return IsEmptyValue(reflect.ValueOf(i))
6 }
7
8 // Returns whether the value represents the empty value for its type. Used for
9 // example to determine if complex types satisfy the common "omitempty" tag
10 // option for marshalling. Taken from
11 // http://stackoverflow.com/a/23555352/149482.
12 func IsEmptyValue(v reflect.Value) bool {
13 switch v.Kind() {
14 case reflect.Func, reflect.Map, reflect.Slice:
15 return v.IsNil()
16 case reflect.Array:
17 z := true
18 for i := 0; i < v.Len(); i++ {
19 z = z && IsEmptyValue(v.Index(i))
20 }
21 return z
22 case reflect.Struct:
23 z := true
24 for i := 0; i < v.NumField(); i++ {
25 z = z && IsEmptyValue(v.Field(i))
26 }
27 return z
28 }
29 // Compare other types directly:
30 z := reflect.Zero(v.Type())
31 return v.Interface() == z.Interface()
32 }
0 package missinggo
1
2 import (
3 "reflect"
4 "testing"
5
6 "github.com/stretchr/testify/assert"
7 )
8
9 func TestEmptyValue(t *testing.T) {
10 assert.True(t, IsEmptyValue(reflect.ValueOf(false)))
11 assert.False(t, IsEmptyValue(reflect.ValueOf(true)))
12 }
0 package missinggo
1
2 // An interface for "encoding/base64".Encoder
3 type Encoding interface {
4 EncodeToString([]byte) string
5 DecodeString(string) ([]byte, error)
6 }
7
8 // An encoding that does nothing.
9 type IdentityEncoding struct{}
10
11 var _ Encoding = IdentityEncoding{}
12
13 func (IdentityEncoding) EncodeToString(b []byte) string { return string(b) }
14 func (IdentityEncoding) DecodeString(s string) ([]byte, error) { return []byte(s), nil }
0 package missinggo
1
2 import "sync"
3
4 // Events are boolean flags that provide a channel that's closed when true.
5 // This could go in the sync package, but that's more of a debug wrapper on
6 // the standard library sync.
7 type Event struct {
8 ch chan struct{}
9 closed bool
10 }
11
12 func (me *Event) LockedChan(lock sync.Locker) <-chan struct{} {
13 lock.Lock()
14 ch := me.C()
15 lock.Unlock()
16 return ch
17 }
18
19 // Returns a chan that is closed when the event is true.
20 func (me *Event) C() <-chan struct{} {
21 if me.ch == nil {
22 me.ch = make(chan struct{})
23 }
24 return me.ch
25 }
26
27 // TODO: Merge into Set.
28 func (me *Event) Clear() {
29 if me.closed {
30 me.ch = nil
31 me.closed = false
32 }
33 }
34
35 // Set the event to true/on.
36 func (me *Event) Set() (first bool) {
37 if me.closed {
38 return false
39 }
40 if me.ch == nil {
41 me.ch = make(chan struct{})
42 }
43 close(me.ch)
44 me.closed = true
45 return true
46 }
47
48 // TODO: Change to Get.
49 func (me *Event) IsSet() bool {
50 return me.closed
51 }
52
53 func (me *Event) Wait() {
54 <-me.C()
55 }
56
57 // TODO: Merge into Set.
58 func (me *Event) SetBool(b bool) {
59 if b {
60 me.Set()
61 } else {
62 me.Clear()
63 }
64 }
0 package missinggo
1
2 import "sync"
3
4 type SynchronizedEvent struct {
5 mu sync.Mutex
6 e Event
7 }
8
9 func (me *SynchronizedEvent) Set() {
10 me.mu.Lock()
11 me.e.Set()
12 me.mu.Unlock()
13 }
14
15 func (me *SynchronizedEvent) Clear() {
16 me.mu.Lock()
17 me.e.Clear()
18 me.mu.Unlock()
19 }
20
21 func (me *SynchronizedEvent) C() <-chan struct{} {
22 me.mu.Lock()
23 defer me.mu.Unlock()
24 return me.e.C()
25 }
0 package missinggo
1
2 import (
3 "testing"
4
5 "github.com/stretchr/testify/assert"
6 )
7
8 func TestSetEvent(t *testing.T) {
9 var e Event
10 e.Set()
11 }
12
13 func TestEventIsSet(t *testing.T) {
14 var e Event
15 assert.False(t, e.IsSet())
16 e.Set()
17 assert.True(t, e.IsSet())
18 }
0 package expect // import "github.com/anacrolix/missinggo/expect"
1
2 import (
3 "database/sql"
4 "fmt"
5 "reflect"
6 )
7
8 func Nil(x interface{}) {
9 if x != nil {
10 panic(fmt.Sprintf("expected nil; got %v", x))
11 }
12 }
13
14 func NotNil(x interface{}) {
15 if x == nil {
16 panic(x)
17 }
18 }
19
20 func Equal(x, y interface{}) {
21 if x == y {
22 return
23 }
24 yAsXType := reflect.ValueOf(y).Convert(reflect.TypeOf(x)).Interface()
25 if !reflect.DeepEqual(x, yAsXType) {
26 panic(fmt.Sprintf("%v != %v", x, y))
27 }
28 }
29
30 func StrictlyEqual(x, y interface{}) {
31 if x != y {
32 panic(fmt.Sprintf("%s != %s", x, y))
33 }
34 }
35
36 func OneRowAffected(r sql.Result) {
37 count, err := r.RowsAffected()
38 Nil(err)
39 if count != 1 {
40 panic(count)
41 }
42 }
43
44 func True(b bool) {
45 if !b {
46 panic(b)
47 }
48 }
49
50 var Ok = True
51
52 func False(b bool) {
53 if b {
54 panic(b)
55 }
56 }
57
58 func Zero(x interface{}) {
59 if x != reflect.Zero(reflect.TypeOf(x)).Interface() {
60 panic(x)
61 }
62 }
0 package expect
1
2 import (
3 "testing"
4
5 "github.com/stretchr/testify/assert"
6 )
7
8 func TestEqualDifferentIntTypes(t *testing.T) {
9 var a int = 1
10 var b int64 = 1
11 assert.EqualValues(t, a, b)
12 assert.NotEqual(t, a, b)
13 assert.NotPanics(t, func() { Equal(a, b) })
14 assert.Panics(t, func() { StrictlyEqual(a, b) })
15 }
0 package missinggo
1
2 import (
3 "bytes"
4 "expvar"
5 "fmt"
6 )
7
8 type IndentMap struct {
9 expvar.Map
10 }
11
12 var _ expvar.Var = (*IndentMap)(nil)
13
14 func NewExpvarIndentMap(name string) *IndentMap {
15 v := new(IndentMap)
16 v.Init()
17 expvar.Publish(name, v)
18 return v
19 }
20
21 func (v *IndentMap) String() string {
22 var b bytes.Buffer
23 fmt.Fprintf(&b, "{")
24 first := true
25 v.Do(func(kv expvar.KeyValue) {
26 if !first {
27 fmt.Fprintf(&b, ",")
28 }
29 fmt.Fprintf(&b, "\n\t%q: %v", kv.Key, kv.Value)
30 first = false
31 })
32 fmt.Fprintf(&b, "}")
33 return b.String()
34 }
0 package filecache
1
2 import (
3 "errors"
4 "log"
5 "os"
6 "path"
7 "path/filepath"
8 "sync"
9 "time"
10
11 "github.com/anacrolix/missinggo/pproffd"
12 "github.com/anacrolix/missinggo/resource"
13 )
14
15 const (
16 dirPerm = 0755
17 filePerm = 0644
18 )
19
20 type Cache struct {
21 root string
22 mu sync.Mutex
23 capacity int64
24 filled int64
25 policy Policy
26 items map[key]itemState
27 }
28
29 type CacheInfo struct {
30 Capacity int64
31 Filled int64
32 NumItems int
33 }
34
35 type ItemInfo struct {
36 Path key
37 Accessed time.Time
38 Size int64
39 }
40
41 // Calls the function for every item known to be in the cache.
42 func (me *Cache) WalkItems(cb func(ItemInfo)) {
43 me.mu.Lock()
44 defer me.mu.Unlock()
45 for k, ii := range me.items {
46 cb(ItemInfo{
47 Path: k,
48 Accessed: ii.Accessed,
49 Size: ii.Size,
50 })
51 }
52 }
53
54 func (me *Cache) Info() (ret CacheInfo) {
55 me.mu.Lock()
56 defer me.mu.Unlock()
57 ret.Capacity = me.capacity
58 ret.Filled = me.filled
59 ret.NumItems = len(me.items)
60 return
61 }
62
63 // Setting a negative capacity means unlimited.
64 func (me *Cache) SetCapacity(capacity int64) {
65 me.mu.Lock()
66 defer me.mu.Unlock()
67 me.capacity = capacity
68 }
69
70 func NewCache(root string) (ret *Cache, err error) {
71 root, err = filepath.Abs(root)
72 ret = &Cache{
73 root: root,
74 capacity: -1, // unlimited
75 }
76 ret.mu.Lock()
77 go func() {
78 defer ret.mu.Unlock()
79 ret.rescan()
80 }()
81 return
82 }
83
84 // An empty return path is an error.
85 func sanitizePath(p string) (ret key) {
86 if p == "" {
87 return
88 }
89 ret = key(path.Clean("/" + p))
90 if ret[0] == '/' {
91 ret = ret[1:]
92 }
93 return
94 }
95
96 // Leaf is a descendent of root.
97 func pruneEmptyDirs(root string, leaf string) (err error) {
98 rootInfo, err := os.Stat(root)
99 if err != nil {
100 return
101 }
102 for {
103 var leafInfo os.FileInfo
104 leafInfo, err = os.Stat(leaf)
105 if os.IsNotExist(err) {
106 goto parent
107 }
108 if err != nil {
109 return
110 }
111 if !leafInfo.IsDir() {
112 return
113 }
114 if os.SameFile(rootInfo, leafInfo) {
115 return
116 }
117 if os.Remove(leaf) != nil {
118 return
119 }
120 parent:
121 leaf = filepath.Dir(leaf)
122 }
123 }
124
125 func (me *Cache) Remove(path string) error {
126 me.mu.Lock()
127 defer me.mu.Unlock()
128 return me.remove(sanitizePath(path))
129 }
130
131 var (
132 ErrBadPath = errors.New("bad path")
133 ErrIsDir = errors.New("is directory")
134 )
135
136 func (me *Cache) StatFile(path string) (os.FileInfo, error) {
137 return os.Stat(me.realpath(sanitizePath(path)))
138 }
139
140 func (me *Cache) OpenFile(path string, flag int) (ret *File, err error) {
141 key := sanitizePath(path)
142 if key == "" {
143 err = ErrIsDir
144 return
145 }
146 f, err := os.OpenFile(me.realpath(key), flag, filePerm)
147 if flag&os.O_CREATE != 0 && os.IsNotExist(err) {
148 // Ensure intermediate directories and try again.
149 dirErr := os.MkdirAll(filepath.Dir(me.realpath(key)), dirPerm)
150 f, err = os.OpenFile(me.realpath(key), flag, filePerm)
151 if dirErr != nil && os.IsNotExist(err) {
152 return nil, dirErr
153 }
154 if err != nil {
155 go me.pruneEmptyDirs(key)
156 }
157 }
158 if err != nil {
159 return
160 }
161 ret = &File{
162 path: key,
163 f: pproffd.WrapOSFile(f),
164 onRead: func(n int) {
165 me.mu.Lock()
166 defer me.mu.Unlock()
167 me.updateItem(key, func(i *itemState, ok bool) bool {
168 i.Accessed = time.Now()
169 return ok
170 })
171 },
172 afterWrite: func(endOff int64) {
173 me.mu.Lock()
174 defer me.mu.Unlock()
175 me.updateItem(key, func(i *itemState, ok bool) bool {
176 i.Accessed = time.Now()
177 if endOff > i.Size {
178 i.Size = endOff
179 }
180 return ok
181 })
182 },
183 }
184 me.mu.Lock()
185 defer me.mu.Unlock()
186 me.updateItem(key, func(i *itemState, ok bool) bool {
187 if !ok {
188 *i, ok = me.statKey(key)
189 }
190 i.Accessed = time.Now()
191 return ok
192 })
193 return
194 }
195
196 func (me *Cache) rescan() {
197 me.filled = 0
198 me.policy = new(lru)
199 me.items = make(map[key]itemState)
200 err := filepath.Walk(me.root, func(path string, info os.FileInfo, err error) error {
201 if os.IsNotExist(err) {
202 return nil
203 }
204 if err != nil {
205 return err
206 }
207 if info.IsDir() {
208 return nil
209 }
210 path, err = filepath.Rel(me.root, path)
211 if err != nil {
212 log.Print(err)
213 return nil
214 }
215 key := sanitizePath(path)
216 me.updateItem(key, func(i *itemState, ok bool) bool {
217 if ok {
218 panic("scanned duplicate items")
219 }
220 *i, ok = me.statKey(key)
221 return ok
222 })
223 return nil
224 })
225 if err != nil {
226 panic(err)
227 }
228 }
229
230 func (me *Cache) statKey(k key) (i itemState, ok bool) {
231 fi, err := os.Stat(me.realpath(k))
232 if os.IsNotExist(err) {
233 return
234 }
235 if err != nil {
236 panic(err)
237 }
238 i.FromOSFileInfo(fi)
239 ok = true
240 return
241 }
242
243 func (me *Cache) updateItem(k key, u func(*itemState, bool) bool) {
244 ii, ok := me.items[k]
245 me.filled -= ii.Size
246 if u(&ii, ok) {
247 me.filled += ii.Size
248 me.policy.Used(k, ii.Accessed)
249 me.items[k] = ii
250 } else {
251 me.policy.Forget(k)
252 delete(me.items, k)
253 }
254 me.trimToCapacity()
255 }
256
257 func (me *Cache) realpath(path key) string {
258 return filepath.Join(me.root, filepath.FromSlash(string(path)))
259 }
260
261 func (me *Cache) TrimToCapacity() {
262 me.mu.Lock()
263 defer me.mu.Unlock()
264 me.trimToCapacity()
265 }
266
267 func (me *Cache) pruneEmptyDirs(path key) {
268 pruneEmptyDirs(me.root, me.realpath(path))
269 }
270
271 func (me *Cache) remove(path key) error {
272 err := os.Remove(me.realpath(path))
273 if os.IsNotExist(err) {
274 err = nil
275 }
276 if err != nil {
277 return err
278 }
279 me.pruneEmptyDirs(path)
280 me.updateItem(path, func(*itemState, bool) bool {
281 return false
282 })
283 return nil
284 }
285
286 func (me *Cache) trimToCapacity() {
287 if me.capacity < 0 {
288 return
289 }
290 for me.filled > me.capacity {
291 me.remove(me.policy.Choose().(key))
292 }
293 }
294
295 // TODO: Do I need this?
296 func (me *Cache) pathInfo(p string) itemState {
297 return me.items[sanitizePath(p)]
298 }
299
300 func (me *Cache) Rename(from, to string) (err error) {
301 _from := sanitizePath(from)
302 _to := sanitizePath(to)
303 me.mu.Lock()
304 defer me.mu.Unlock()
305 err = os.MkdirAll(filepath.Dir(me.realpath(_to)), dirPerm)
306 if err != nil {
307 return
308 }
309 err = os.Rename(me.realpath(_from), me.realpath(_to))
310 if err != nil {
311 return
312 }
313 // We can do a dance here to copy the state from the old item, but lets
314 // just stat the new item for now.
315 me.updateItem(_from, func(i *itemState, ok bool) bool {
316 return false
317 })
318 me.updateItem(_to, func(i *itemState, ok bool) bool {
319 *i, ok = me.statKey(_to)
320 return ok
321 })
322 return
323 }
324
325 func (me *Cache) Stat(path string) (os.FileInfo, error) {
326 return os.Stat(me.realpath(sanitizePath(path)))
327 }
328
329 func (me *Cache) AsResourceProvider() resource.Provider {
330 return &uniformResourceProvider{me}
331 }
0 package filecache
1
2 import (
3 "io"
4 "io/ioutil"
5 "os"
6 "path/filepath"
7 "testing"
8
9 "github.com/bradfitz/iter"
10 "github.com/stretchr/testify/assert"
11 "github.com/stretchr/testify/require"
12
13 "github.com/anacrolix/missinggo"
14 )
15
16 func TestCache(t *testing.T) {
17 td, err := ioutil.TempDir("", "gotest")
18 require.NoError(t, err)
19 defer os.RemoveAll(td)
20
21 c, err := NewCache(filepath.Join(td, "cache"))
22 require.NoError(t, err)
23 assert.EqualValues(t, CacheInfo{
24 Filled: 0,
25 Capacity: -1,
26 NumItems: 0,
27 }, c.Info())
28
29 c.WalkItems(func(i ItemInfo) {})
30
31 _, err = c.OpenFile("/", os.O_CREATE)
32 assert.NotNil(t, err)
33
34 _, err = c.OpenFile("", os.O_CREATE)
35 assert.NotNil(t, err)
36
37 c.WalkItems(func(i ItemInfo) {})
38
39 require.Equal(t, CacheInfo{
40 Filled: 0,
41 Capacity: -1,
42 NumItems: 0,
43 }, c.Info())
44
45 _, err = c.OpenFile("notexist", 0)
46 assert.True(t, os.IsNotExist(err), err)
47
48 _, err = c.OpenFile("/notexist", 0)
49 assert.True(t, os.IsNotExist(err), err)
50
51 _, err = c.OpenFile("/dir/notexist", 0)
52 assert.True(t, os.IsNotExist(err), err)
53
54 f, err := c.OpenFile("dir/blah", os.O_CREATE)
55 require.NoError(t, err)
56 defer f.Close()
57 require.Equal(t, CacheInfo{
58 Filled: 0,
59 Capacity: -1,
60 NumItems: 1,
61 }, c.Info())
62
63 c.WalkItems(func(i ItemInfo) {})
64
65 assert.True(t, missinggo.FilePathExists(filepath.Join(td, filepath.FromSlash("cache/dir/blah"))))
66 assert.True(t, missinggo.FilePathExists(filepath.Join(td, filepath.FromSlash("cache/dir/"))))
67 assert.Equal(t, 1, c.Info().NumItems)
68
69 c.Remove("dir/blah")
70 assert.False(t, missinggo.FilePathExists(filepath.Join(td, filepath.FromSlash("cache/dir/blah"))))
71 assert.False(t, missinggo.FilePathExists(filepath.Join(td, filepath.FromSlash("cache/dir/"))))
72 _, err = f.ReadAt(nil, 0)
73 assert.NotEqual(t, io.EOF, err)
74
75 a, err := c.OpenFile("/a", os.O_CREATE|os.O_WRONLY)
76 defer a.Close()
77 require.NoError(t, err)
78 b, err := c.OpenFile("b", os.O_CREATE|os.O_WRONLY)
79 defer b.Close()
80 require.NoError(t, err)
81 c.mu.Lock()
82 assert.False(t, c.pathInfo("a").Accessed.After(c.pathInfo("b").Accessed))
83 c.mu.Unlock()
84 n, err := a.WriteAt([]byte("hello"), 0)
85 assert.NoError(t, err)
86 assert.EqualValues(t, 5, n)
87 assert.EqualValues(t, CacheInfo{
88 Filled: 5,
89 Capacity: -1,
90 NumItems: 2,
91 }, c.Info())
92 assert.False(t, c.pathInfo("b").Accessed.After(c.pathInfo("a").Accessed))
93
94 // Reopen a, to check that the info values remain correct.
95 assert.NoError(t, a.Close())
96 a, err = c.OpenFile("a", 0)
97 require.NoError(t, err)
98 require.EqualValues(t, CacheInfo{
99 Filled: 5,
100 Capacity: -1,
101 NumItems: 2,
102 }, c.Info())
103
104 c.SetCapacity(5)
105 require.EqualValues(t, CacheInfo{
106 Filled: 5,
107 Capacity: 5,
108 NumItems: 2,
109 }, c.Info())
110
111 n, err = a.WriteAt([]byte(" world"), 5)
112 assert.Error(t, err)
113 n, err = b.WriteAt([]byte("boom!"), 0)
114 // "a" and "b" have been evicted.
115 require.NoError(t, err)
116 require.EqualValues(t, 5, n)
117 require.EqualValues(t, CacheInfo{
118 Filled: 5,
119 Capacity: 5,
120 NumItems: 1,
121 }, c.Info())
122 }
123
124 func TestSanitizePath(t *testing.T) {
125 assert.EqualValues(t, "", sanitizePath("////"))
126 assert.EqualValues(t, "", sanitizePath("/../.."))
127 assert.EqualValues(t, "a", sanitizePath("/a//b/.."))
128 assert.EqualValues(t, "a", sanitizePath("../a"))
129 assert.EqualValues(t, "a", sanitizePath("./a"))
130 }
131
132 func BenchmarkCacheOpenFile(t *testing.B) {
133 td, err := ioutil.TempDir("", "")
134 require.NoError(t, err)
135 defer os.RemoveAll(td)
136 c, err := NewCache(td)
137 for range iter.N(t.N) {
138 func() {
139 f, err := c.OpenFile("a", os.O_CREATE|os.O_RDWR)
140 require.NoError(t, err)
141 assert.NoError(t, f.Close())
142 }()
143 }
144 }
145
146 func TestFileReadWrite(t *testing.T) {
147 td, err := ioutil.TempDir("", "")
148 require.NoError(t, err)
149 defer os.RemoveAll(td)
150
151 c, err := NewCache(td)
152 require.NoError(t, err)
153
154 a, err := c.OpenFile("a", os.O_CREATE|os.O_EXCL|os.O_RDWR)
155 require.NoError(t, err)
156 defer a.Close()
157
158 for off, c := range []byte("herp") {
159 n, err := a.WriteAt([]byte{c}, int64(off))
160 assert.NoError(t, err)
161 require.EqualValues(t, 1, n)
162 }
163 for off, c := range []byte("herp") {
164 var b [1]byte
165 n, err := a.ReadAt(b[:], int64(off))
166 require.EqualValues(t, 1, n)
167 require.NoError(t, err)
168 assert.EqualValues(t, []byte{c}, b[:])
169 }
170
171 }
0 package filecache
1
2 import (
3 "errors"
4 "os"
5 "sync"
6
7 "github.com/anacrolix/missinggo/pproffd"
8 )
9
10 type File struct {
11 path key
12 f pproffd.OSFile
13 afterWrite func(endOff int64)
14 onRead func(n int)
15 mu sync.Mutex
16 offset int64
17 }
18
19 func (me *File) Seek(offset int64, whence int) (ret int64, err error) {
20 ret, err = me.f.Seek(offset, whence)
21 if err != nil {
22 return
23 }
24 me.offset = ret
25 return
26 }
27
28 var (
29 ErrFileTooLarge = errors.New("file too large for cache")
30 ErrFileDisappeared = errors.New("file disappeared")
31 )
32
33 func (me *File) Write(b []byte) (n int, err error) {
34 n, err = me.f.Write(b)
35 me.offset += int64(n)
36 me.afterWrite(me.offset)
37 return
38 }
39
40 func (me *File) WriteAt(b []byte, off int64) (n int, err error) {
41 n, err = me.f.WriteAt(b, off)
42 me.afterWrite(off + int64(n))
43 return
44 }
45
46 func (me *File) Close() error {
47 return me.f.Close()
48 }
49
50 func (me *File) Stat() (os.FileInfo, error) {
51 return me.f.Stat()
52 }
53
54 func (me *File) Read(b []byte) (n int, err error) {
55 n, err = me.f.Read(b)
56 me.onRead(n)
57 return
58 }
59
60 func (me *File) ReadAt(b []byte, off int64) (n int, err error) {
61 n, err = me.f.ReadAt(b, off)
62 me.onRead(n)
63 return
64 }
0 package filecache
1
2 import (
3 "os"
4 "time"
5
6 "github.com/anacrolix/missinggo"
7 )
8
9 type itemState struct {
10 Accessed time.Time
11 Size int64
12 }
13
14 func (i *itemState) FromOSFileInfo(fi os.FileInfo) {
15 i.Size = fi.Size()
16 i.Accessed = missinggo.FileInfoAccessTime(fi)
17 if fi.ModTime().After(i.Accessed) {
18 i.Accessed = fi.ModTime()
19 }
20 }
0 package filecache
1
2 type key string
3
4 func (me key) Before(other policyItemKey) bool {
5 return me < other.(key)
6 }
0 package filecache
1
2 import (
3 "time"
4
5 "github.com/anacrolix/missinggo/orderedmap"
6 )
7
8 type lru struct {
9 o orderedmap.OrderedMap
10 oKeys map[policyItemKey]lruKey
11 }
12
13 type lruKey struct {
14 item policyItemKey
15 used time.Time
16 }
17
18 func (me lruKey) Before(other lruKey) bool {
19 if me.used.Equal(other.used) {
20 return me.item.Before(other.item)
21 }
22 return me.used.Before(other.used)
23 }
24
25 var _ Policy = (*lru)(nil)
26
27 func (me *lru) Choose() (ret policyItemKey) {
28 any := false
29 me.o.Iter(func(i interface{}) bool {
30 ret = i.(lruKey).item
31 any = true
32 return false
33 })
34 if !any {
35 panic("cache empty")
36 }
37 return
38 }
39
40 func (me *lru) Used(k policyItemKey, at time.Time) {
41 if me.o == nil {
42 me.o = orderedmap.NewGoogleBTree(func(l, r interface{}) bool {
43 return l.(lruKey).Before(r.(lruKey))
44 })
45 } else {
46 me.o.Unset(me.oKeys[k])
47 }
48 lk := lruKey{k, at}
49 me.o.Set(lk, lk)
50 if me.oKeys == nil {
51 me.oKeys = make(map[policyItemKey]lruKey)
52 }
53 me.oKeys[k] = lk
54 }
55
56 func (me *lru) Forget(k policyItemKey) {
57 if me.o != nil {
58 me.o.Unset(me.oKeys[k])
59 }
60 delete(me.oKeys, k)
61 }
62
63 func (me *lru) NumItems() int {
64 return len(me.oKeys)
65 }
0 package filecache
1
2 import "testing"
3
4 func TestLRU(t *testing.T) {
5 testPolicy(t, &lru{})
6 }
0 package filecache
1
2 import (
3 "testing"
4 "time"
5
6 "github.com/stretchr/testify/assert"
7 )
8
9 func TestLruDuplicateAccessTimes(t *testing.T) {
10 var li Policy = new(lru)
11 now := time.Now()
12 li.Used(key("a"), now)
13 li.Used(key("b"), now)
14 assert.EqualValues(t, 2, li.NumItems())
15 }
0 package filecache
1
2 import "time"
3
4 type policyItemKey interface {
5 Before(policyItemKey) bool
6 }
7
8 type Policy interface {
9 Choose() policyItemKey
10 Used(k policyItemKey, at time.Time)
11 Forget(k policyItemKey)
12 NumItems() int
13 }
0 package filecache
1
2 import (
3 "testing"
4 "time"
5
6 "github.com/stretchr/testify/assert"
7 )
8
9 func testChooseForgottenKey(t *testing.T, p Policy) {
10 assert.Equal(t, 0, p.NumItems())
11 assert.Panics(t, func() { p.Choose() })
12 p.Used(key("a"), time.Now())
13 assert.Equal(t, 1, p.NumItems())
14 p.Used(key("a"), time.Now().Add(1))
15 assert.Equal(t, 1, p.NumItems())
16 p.Forget(key("a"))
17 assert.Equal(t, 0, p.NumItems())
18 assert.Panics(t, func() { p.Choose() })
19 }
20
21 func testPolicy(t *testing.T, p Policy) {
22 testChooseForgottenKey(t, p)
23 }
0 package filecache
1
2 import (
3 "io"
4 "os"
5
6 "github.com/anacrolix/missinggo/resource"
7 )
8
9 type uniformResourceProvider struct {
10 *Cache
11 }
12
13 var _ resource.Provider = &uniformResourceProvider{}
14
15 func (me *uniformResourceProvider) NewInstance(loc string) (resource.Instance, error) {
16 return &uniformResource{me.Cache, loc}, nil
17 }
18
19 type uniformResource struct {
20 Cache *Cache
21 Location string
22 }
23
24 func (me *uniformResource) Get() (io.ReadCloser, error) {
25 return me.Cache.OpenFile(me.Location, os.O_RDONLY)
26 }
27
28 func (me *uniformResource) Put(r io.Reader) (err error) {
29 f, err := me.Cache.OpenFile(me.Location, os.O_WRONLY|os.O_CREATE|os.O_TRUNC)
30 if err != nil {
31 return
32 }
33 defer f.Close()
34 _, err = io.Copy(f, r)
35 return
36 }
37
38 func (me *uniformResource) ReadAt(b []byte, off int64) (n int, err error) {
39 f, err := me.Cache.OpenFile(me.Location, os.O_RDONLY)
40 if err != nil {
41 return
42 }
43 defer f.Close()
44 return f.ReadAt(b, off)
45 }
46
47 func (me *uniformResource) WriteAt(b []byte, off int64) (n int, err error) {
48 f, err := me.Cache.OpenFile(me.Location, os.O_CREATE|os.O_WRONLY)
49 if err != nil {
50 return
51 }
52 defer f.Close()
53 return f.WriteAt(b, off)
54 }
55
56 func (me *uniformResource) Stat() (fi os.FileInfo, err error) {
57 return me.Cache.Stat(me.Location)
58 }
59
60 func (me *uniformResource) Delete() error {
61 return me.Cache.Remove(me.Location)
62 }
0 package missinggo
1
2 import "sync"
3
4 // Flag represents a boolean value, that signals sync.Cond's when it changes.
5 // It's not concurrent safe by intention.
6 type Flag struct {
7 Conds map[*sync.Cond]struct{}
8 value bool
9 }
10
11 func (me *Flag) Set(value bool) {
12 if value != me.value {
13 me.broadcastChange()
14 }
15 me.value = value
16 }
17
18 func (me *Flag) Get() bool {
19 return me.value
20 }
21
22 func (me *Flag) broadcastChange() {
23 for cond := range me.Conds {
24 cond.Broadcast()
25 }
26 }
27
28 func (me *Flag) addCond(c *sync.Cond) {
29 if me.Conds == nil {
30 me.Conds = make(map[*sync.Cond]struct{})
31 }
32 me.Conds[c] = struct{}{}
33 }
34
35 // Adds the sync.Cond to all the given Flag's.
36 func AddCondToFlags(cond *sync.Cond, flags ...*Flag) {
37 for _, f := range flags {
38 f.addCond(cond)
39 }
40 }
0 package futures
1
2 import "time"
3
4 func timeoutFuture(timeout time.Duration) *F {
5 return Start(func() (interface{}, error) {
6 time.Sleep(timeout)
7 return nil, nil
8 })
9 }
10
11 type Delayed struct {
12 Delay time.Duration
13 Fs []*F
14 }
0 package futures
1
2 import (
3 "context"
4 "fmt"
5 "testing"
6 "time"
7
8 "github.com/bradfitz/iter"
9 "github.com/stretchr/testify/assert"
10 )
11
12 // Delay unit, high enough that system slowness doesn't affect timing, but low
13 // enough to ensure tests are fast.
14 const u = 20 * time.Millisecond
15
16 func TestAsCompletedDelayed(t *testing.T) {
17 t.Parallel()
18 var fs []*F
19 s := time.Now()
20 for i := range iter.N(10) {
21 f := timeoutFuture(time.Duration(i) * u)
22 f.SetName(fmt.Sprintf("%d", i))
23 fs = append(fs, f)
24 }
25 as := AsCompletedDelayed(
26 context.Background(),
27 []*F{fs[0], fs[2]},
28 []Delayed{
29 {u, []*F{fs[1]}},
30 {3 * u, []*F{fs[0]}},
31 },
32 )
33 a := func(f, when time.Duration) {
34 t.Helper()
35 assert.Equal(t, fs[f], <-as)
36 if time.Since(s) < when*u {
37 t.Errorf("%d completed too soon", f)
38 }
39 if time.Since(s) >= (when+1)*u {
40 t.Errorf("%d completed too late", f)
41 }
42 }
43 a(0, 0)
44 a(1, 1)
45 a(2, 2)
46 a(0, 2)
47 _, ok := <-as
48 assert.False(t, ok)
49 assert.True(t, time.Since(s) < 4*u)
50 }
51
52 func TestAsCompletedDelayedContextCanceled(t *testing.T) {
53 t.Parallel()
54 var fs []*F
55 s := time.Now()
56 for i := range iter.N(10) {
57 f := timeoutFuture(time.Duration(i) * u)
58 f.SetName(fmt.Sprintf("%d", i))
59 fs = append(fs, f)
60 }
61 ctx, cancel := context.WithCancel(context.Background())
62 as := AsCompletedDelayed(
63 ctx,
64 []*F{fs[0], fs[2]},
65 []Delayed{
66 {u, []*F{fs[1]}},
67 {3 * u, []*F{fs[0]}},
68 },
69 )
70 a := func(f, when time.Duration) {
71 t.Helper()
72 assert.Equal(t, fs[f], <-as)
73 if time.Since(s) < when*u {
74 t.Errorf("%d completed too soon", f)
75 }
76 if time.Since(s) >= (when+1)*u {
77 t.Errorf("%d completed too late", f)
78 }
79 }
80 a(0, 0)
81 cancel()
82 _, ok := <-as
83 assert.False(t, ok)
84 assert.True(t, time.Since(s) < 1*u)
85 }
0 package futures
1
2 import (
3 "context"
4 "sync"
5 "time"
6
7 "github.com/bradfitz/iter"
8
9 "github.com/anacrolix/missinggo/slices"
10 )
11
12 // Sends each future as it completes on the returned chan, closing it when
13 // everything has been sent.
14 func AsCompleted(fs ...*F) <-chan *F {
15 ret := make(chan *F, len(fs))
16 var wg sync.WaitGroup
17 for _, f := range fs {
18 wg.Add(1)
19 go func(f *F) {
20 defer wg.Done()
21 <-f.Done()
22 ret <- f
23 }(f)
24 }
25 go func() {
26 wg.Wait()
27 close(ret)
28 }()
29 return ret
30 }
31
32 // Additional state maintained for each delayed element.
33 type delayedState struct {
34 timeout *F
35 added bool
36 }
37
38 // Returns futures as they complete. Delayed futures are not released until
39 // their timeout has passed, or all prior delayed futures, and the initial set
40 // have completed. One use case is to prefer the value in some futures over
41 // others, such as hitting several origin servers where some are better
42 // informed than others.
43 func AsCompletedDelayed(ctx context.Context, initial []*F, delayed []Delayed) <-chan *F {
44 ret := make(chan *F, func() int {
45 l := len(initial)
46 for _, d := range delayed {
47 l += len(d.Fs)
48 }
49 return l
50 }())
51 go func() {
52 defer close(ret)
53 var (
54 dss []delayedState
55 timeouts = map[*F]struct{}{} // Pending timeouts
56 )
57 for i := range delayed {
58 func(i int) {
59 f := Start(func() (interface{}, error) {
60 select {
61 case <-time.After(delayed[i].Delay):
62 return i, nil
63 case <-ctx.Done():
64 return nil, ctx.Err()
65 }
66 })
67 timeouts[f] = struct{}{}
68 dss = append(dss, delayedState{timeout: f})
69 }(i)
70 }
71 // Number of pending sends for a future.
72 results := map[*F]int{}
73 for _, f := range initial {
74 results[f]++
75 }
76 start:
77 // A slice of futures we want to send when they complete.
78 resultsSlice := func() (ret []*F) {
79 for f, left := range results {
80 for range iter.N(left) {
81 ret = append(ret, f)
82 }
83 }
84 return
85 }()
86 if len(resultsSlice) == 0 {
87 for i, ds := range dss {
88 if ds.added {
89 continue
90 }
91 // Add this delayed block prematurely.
92 delete(timeouts, ds.timeout)
93 for _, f := range delayed[i].Fs {
94 results[f]++
95 }
96 dss[i].added = true
97 // We need to recompute the results slice.
98 goto start
99 }
100 }
101 as := AsCompleted(append(
102 resultsSlice,
103 slices.FromMapKeys(timeouts).([]*F)...,
104 )...)
105 for {
106 select {
107 case <-ctx.Done():
108 return
109 case f, ok := <-as:
110 if !ok {
111 return
112 }
113 if _, ok := timeouts[f]; ok {
114 if ctx.Err() != nil {
115 break
116 }
117 i := f.MustResult().(int)
118 for _, f := range delayed[i].Fs {
119 results[f]++
120 }
121 delete(timeouts, f)
122 dss[i].added = true
123 goto start
124 }
125 select {
126 case ret <- f:
127 results[f]--
128 if results[f] == 0 {
129 delete(results, f)
130 }
131 if len(results) == 0 {
132 goto start
133 }
134 case <-ctx.Done():
135 return
136 }
137 }
138 }
139 }()
140 return ret
141 }
0 package futures
1
2 import (
3 "fmt"
4 "reflect"
5 "sync"
6 )
7
8 func Start(fn func() (interface{}, error)) *F {
9 f := &F{
10 done: make(chan struct{}),
11 }
12 go func() {
13 f.setResult(fn())
14 }()
15 return f
16 }
17
18 func StartNoError(fn func() interface{}) *F {
19 return Start(func() (interface{}, error) {
20 return fn(), nil
21 })
22 }
23
24 type F struct {
25 name string
26 mu sync.Mutex
27 result interface{}
28 err error
29 done chan struct{}
30 }
31
32 func (f *F) String() string {
33 if f.name != "" {
34 return f.name
35 }
36 return fmt.Sprintf("future %p", f)
37 }
38
39 func (f *F) SetName(s string) {
40 f.name = s
41 }
42
43 func (f *F) Err() error {
44 <-f.done
45 return f.err
46 }
47
48 // TODO: Just return value.
49 func (f *F) Result() (interface{}, error) {
50 <-f.done
51 f.mu.Lock()
52 defer f.mu.Unlock()
53 return f.result, f.err
54 }
55
56 func (f *F) MustResult() interface{} {
57 val, err := f.Result()
58 if err != nil {
59 panic(err)
60 }
61 return val
62 }
63
64 func (f *F) Done() <-chan struct{} {
65 return f.done
66 }
67
68 func (f *F) setResult(result interface{}, err error) {
69 f.mu.Lock()
70 defer f.mu.Unlock()
71 f.result = result
72 f.err = err
73 close(f.done)
74 }
75
76 func (f *F) ScanResult(res interface{}) error {
77 _res, err := f.Result()
78 reflect.ValueOf(res).Elem().Set(reflect.ValueOf(_res))
79 return err
80 }
0 module github.com/anacrolix/missinggo/v2
1
2 require (
3 github.com/RoaringBitmap/roaring v0.4.17
4 github.com/anacrolix/envpprof v1.0.0
5 github.com/anacrolix/missinggo v1.1.0
6 github.com/anacrolix/tagflag v1.0.0
7 github.com/bradfitz/iter v0.0.0-20190303215204-33e6a9893b0c
8 github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815
9 github.com/dustin/go-humanize v1.0.0
10 github.com/glycerine/goconvey v0.0.0-20190315024820-982ee783a72e // indirect
11 github.com/google/btree v1.0.0
12 github.com/gopherjs/gopherjs v0.0.0-20190309154008-847fc94819f9 // indirect
13 github.com/huandu/xstrings v1.2.0
14 github.com/pkg/errors v0.8.1 // indirect
15 github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46
16 github.com/smartystreets/assertions v0.0.0-20190215210624-980c5ac6f3ac // indirect
17 github.com/stretchr/testify v1.3.0
18 go.opencensus.io v0.20.2
19 )
0 cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
1 cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
2 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
3 github.com/RoaringBitmap/roaring v0.4.7 h1:eGUudvFzvF7Kxh7JjYvXfI1f7l22/2duFby7r5+d4oc=
4 github.com/RoaringBitmap/roaring v0.4.7/go.mod h1:8khRDP4HmeXns4xIj9oGrKSz7XTQiJx2zgh7AcNke4w=
5 github.com/RoaringBitmap/roaring v0.4.17 h1:oCYFIFEMSQZrLHpywH7919esI1VSrQZ0pJXkZPGIJ78=
6 github.com/RoaringBitmap/roaring v0.4.17/go.mod h1:D3qVegWTmfCaX4Bl5CrBE9hfrSrrXIr8KVNvRsDi1NI=
7 github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
8 github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
9 github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
10 github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
11 github.com/anacrolix/envpprof v0.0.0-20180404065416-323002cec2fa h1:xCaATLKmn39QqLs3tUZYr6eKvezJV+FYvVOLTklxK6U=
12 github.com/anacrolix/envpprof v0.0.0-20180404065416-323002cec2fa/go.mod h1:KgHhUaQMc8cC0+cEflSgCFNFbKwi5h54gqtVn8yhP7c=
13 github.com/anacrolix/envpprof v1.0.0 h1:AwZ+mBP4rQ5f7JSsrsN3h7M2xDW/xSE66IPVOqlnuUc=
14 github.com/anacrolix/envpprof v1.0.0/go.mod h1:KgHhUaQMc8cC0+cEflSgCFNFbKwi5h54gqtVn8yhP7c=
15 github.com/anacrolix/missinggo v1.1.0 h1:0lZbaNa6zTR1bELAIzCNmRGAtkHuLDPJqTiTtXoAIx8=
16 github.com/anacrolix/missinggo v1.1.0/go.mod h1:MBJu3Sk/k3ZfGYcS7z18gwfu72Ey/xopPFJJbTi5yIo=
17 github.com/anacrolix/tagflag v0.0.0-20180109131632-2146c8d41bf0 h1:xcd2GmlPWBsGNjdbwriHXvJJtagl1AnbjTPhJTksJDQ=
18 github.com/anacrolix/tagflag v0.0.0-20180109131632-2146c8d41bf0/go.mod h1:1m2U/K6ZT+JZG0+bdMK6qauP49QT4wE5pmhJXOKKCHw=
19 github.com/anacrolix/tagflag v1.0.0 h1:NoxBVyke6iEtXfSY/n3lY3jNCBjQDu7aTvwHJxNLJAQ=
20 github.com/anacrolix/tagflag v1.0.0/go.mod h1:1m2U/K6ZT+JZG0+bdMK6qauP49QT4wE5pmhJXOKKCHw=
21 github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
22 github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
23 github.com/bradfitz/iter v0.0.0-20140124041915-454541ec3da2 h1:1B/+1BcRhOMG1KH/YhNIU8OppSWk5d/NGyfRla88CuY=
24 github.com/bradfitz/iter v0.0.0-20140124041915-454541ec3da2/go.mod h1:PyRFw1Lt2wKX4ZVSQ2mk+PeDa1rxyObEDlApuIsUKuo=
25 github.com/bradfitz/iter v0.0.0-20190303215204-33e6a9893b0c h1:FUUopH4brHNO2kJoNN3pV+OBEYmgraLT/KHZrMM69r0=
26 github.com/bradfitz/iter v0.0.0-20190303215204-33e6a9893b0c/go.mod h1:PyRFw1Lt2wKX4ZVSQ2mk+PeDa1rxyObEDlApuIsUKuo=
27 github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
28 github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
29 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
30 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
31 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
32 github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815 h1:bWDMxwH3px2JBh6AyO7hdCn/PkvCZXii8TGj7sbtEbQ=
33 github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
34 github.com/dustin/go-humanize v0.0.0-20180421182945-02af3965c54e h1:Fw7ZmgiklsLh5EQWyHh1sumKSCG1+yjEctIpGKib87s=
35 github.com/dustin/go-humanize v0.0.0-20180421182945-02af3965c54e/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
36 github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
37 github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
38 github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
39 github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
40 github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
41 github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
42 github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd h1:r04MMPyLHj/QwZuMJ5+7tJcBr1AQjpiAK/rZWRrQT7o=
43 github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE=
44 github.com/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2 h1:Ujru1hufTHVb++eG6OuNDKMxZnGIvF6o/u8q/8h2+I4=
45 github.com/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE=
46 github.com/glycerine/goconvey v0.0.0-20180728074245-46e3a41ad493 h1:OTanQnFt0bi5iLFSdbEVA/idR6Q2WhCm+deb7ir2CcM=
47 github.com/glycerine/goconvey v0.0.0-20180728074245-46e3a41ad493/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24=
48 github.com/glycerine/goconvey v0.0.0-20190315024820-982ee783a72e h1:SiEs4J3BKVIeaWrH3tKaz3QLZhJ68iJ/A4xrzIoE5+Y=
49 github.com/glycerine/goconvey v0.0.0-20190315024820-982ee783a72e/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24=
50 github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
51 github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
52 github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
53 github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
54 github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
55 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
56 github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
57 github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
58 github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w=
59 github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
60 github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
61 github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
62 github.com/google/btree v0.0.0-20180124185431-e89373fe6b4a h1:ZJu5NB1Bk5ms4vw0Xu4i+jD32SE9jQXyfnOvwhHqlT0=
63 github.com/google/btree v0.0.0-20180124185431-e89373fe6b4a/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
64 github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
65 github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
66 github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
67 github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
68 github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e h1:JKmoR8x90Iww1ks85zJ1lfDGgIiMDuIptTOhJq+zKyg=
69 github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
70 github.com/gopherjs/gopherjs v0.0.0-20190309154008-847fc94819f9 h1:Z0f701LpR4dqO92bP6TnIe3ZURClzJtBhds8R8u1HBE=
71 github.com/gopherjs/gopherjs v0.0.0-20190309154008-847fc94819f9/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
72 github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
73 github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
74 github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo=
75 github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
76 github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
77 github.com/huandu/xstrings v1.0.0 h1:pO2K/gKgKaat5LdpAhxhluX2GPQMaI3W5FUz/I/UnWk=
78 github.com/huandu/xstrings v1.0.0/go.mod h1:4qWG/gcEcfX4z/mBDHJ++3ReCw9ibxbsNJbcucJdbSo=
79 github.com/huandu/xstrings v1.2.0 h1:yPeWdRnmynF7p+lLYz0H2tthW9lqhMJrQV/U7yy4wX0=
80 github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63UyNX5k4=
81 github.com/jtolds/gls v4.2.1+incompatible h1:fSuqC+Gmlu6l/ZYAoZzx2pyucC8Xza35fpRVWLVmUEE=
82 github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
83 github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
84 github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
85 github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
86 github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
87 github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
88 github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
89 github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
90 github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae h1:VeRdUYdCw49yizlSbMEn2SZ+gT+3IUKx8BqxyQdz+BY=
91 github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg=
92 github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
93 github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
94 github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
95 github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
96 github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
97 github.com/philhofer/fwd v1.0.0 h1:UbZqGr5Y38ApvM/V/jEljVxwocdweyH+vmYvRPBnbqQ=
98 github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
99 github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
100 github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
101 github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
102 github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
103 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
104 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
105 github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
106 github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
107 github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
108 github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
109 github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
110 github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
111 github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
112 github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
113 github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46 h1:GHRpF1pTW19a8tTFrMLUcfWwyC0pnifVo2ClaLq+hP8=
114 github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46/go.mod h1:uAQ5PCi+MFsC7HjREoAz1BU+Mq60+05gifQSsHSDG/8=
115 github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
116 github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
117 github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
118 github.com/smartystreets/assertions v0.0.0-20190215210624-980c5ac6f3ac h1:wbW+Bybf9pXxnCFAOWZTqkRjAc7rAIwo2e1ArUhiHxg=
119 github.com/smartystreets/assertions v0.0.0-20190215210624-980c5ac6f3ac/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
120 github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c h1:Ho+uVpkel/udgjbwB5Lktg9BtvJSh2DT0Hi6LPSyI2w=
121 github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s=
122 github.com/smartystreets/goconvey v0.0.0-20190306220146-200a235640ff h1:86HlEv0yBCry9syNuylzqznKXDK11p6D0DT596yNMys=
123 github.com/smartystreets/goconvey v0.0.0-20190306220146-200a235640ff/go.mod h1:KSQcGKpxUMHk3nbYzs/tIBAM2iDooCn0BmttHOJEbLs=
124 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
125 github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
126 github.com/stretchr/testify v1.2.1 h1:52QO5WkIUcHGIR7EnGagH88x1bUzqGXTC5/1bDTUQ7U=
127 github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
128 github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
129 github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
130 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
131 github.com/tinylib/msgp v1.0.2 h1:DfdQrzQa7Yh2es9SuLkixqxuXS2SxsdYn0KbdrOGWD8=
132 github.com/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
133 github.com/tinylib/msgp v1.1.0 h1:9fQd+ICuRIu/ue4vxJZu6/LzxN0HwMds2nq/0cFvxHU=
134 github.com/tinylib/msgp v1.1.0/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
135 github.com/willf/bitset v1.1.9 h1:GBtFynGY9ZWZmEC9sWuu41/7VBXPFCOAbCbqTflOg9c=
136 github.com/willf/bitset v1.1.9/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
137 github.com/willf/bitset v1.1.10 h1:NotGKqX0KwQ72NUzqrjZq5ipPNDQex9lo3WpaS8L2sc=
138 github.com/willf/bitset v1.1.10/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
139 go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
140 go.opencensus.io v0.20.2 h1:NAfh7zF0/3/HqtMvJNZ/RFrSlCE6ZTlHmKfhL/Dm1Jk=
141 go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
142 golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
143 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
144 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
145 golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
146 golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
147 golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
148 golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
149 golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
150 golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
151 golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
152 golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
153 golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
154 golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
155 golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
156 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
157 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
158 golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
159 golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
160 golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
161 golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
162 golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
163 golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
164 golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
165 golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
166 golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
167 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
168 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
169 golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
170 golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
171 golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
172 golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
173 google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
174 google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
175 google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
176 google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
177 google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
178 google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
179 google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
180 gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
181 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
182 gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
183 gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
184 gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
185 honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
186 honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
0 package missinggo
1
2 import (
3 "net"
4 "strconv"
5 "strings"
6 )
7
8 // Represents a split host port.
9 type HostMaybePort struct {
10 Host string // Just the host, with no port.
11 Port int // The port if NoPort is false.
12 NoPort bool // Whether a port is specified.
13 Err error // The error returned from net.SplitHostPort.
14 }
15
16 func (me *HostMaybePort) String() string {
17 if me.NoPort {
18 return me.Host
19 }
20 return net.JoinHostPort(me.Host, strconv.FormatInt(int64(me.Port), 10))
21 }
22
23 // Parse a "hostport" string, a concept that floats around the stdlib a lot
24 // and is painful to work with. If no port is present, what's usually present
25 // is just the host.
26 func SplitHostMaybePort(hostport string) HostMaybePort {
27 host, portStr, err := net.SplitHostPort(hostport)
28 if err != nil {
29 if strings.Contains(err.Error(), "missing port") {
30 return HostMaybePort{
31 Host: hostport,
32 NoPort: true,
33 }
34 }
35 return HostMaybePort{
36 Err: err,
37 }
38 }
39 portI64, err := strconv.ParseInt(portStr, 0, 0)
40 if err != nil {
41 return HostMaybePort{
42 Host: host,
43 Port: -1,
44 Err: err,
45 }
46 }
47 return HostMaybePort{
48 Host: host,
49 Port: int(portI64),
50 }
51 }
0 package missinggo
1
2 import (
3 "testing"
4
5 "github.com/stretchr/testify/assert"
6 )
7
8 func TestSplitHostMaybePortNoPort(t *testing.T) {
9 hmp := SplitHostMaybePort("some.domain")
10 assert.Equal(t, "some.domain", hmp.Host)
11 assert.True(t, hmp.NoPort)
12 assert.NoError(t, hmp.Err)
13 }
14
15 func TestSplitHostMaybePortPort(t *testing.T) {
16 hmp := SplitHostMaybePort("some.domain:123")
17 assert.Equal(t, "some.domain", hmp.Host)
18 assert.Equal(t, 123, hmp.Port)
19 assert.False(t, hmp.NoPort)
20 assert.NoError(t, hmp.Err)
21 }
22
23 func TestSplitHostMaybePortBadPort(t *testing.T) {
24 hmp := SplitHostMaybePort("some.domain:wat")
25 assert.Equal(t, "some.domain", hmp.Host)
26 assert.Equal(t, -1, hmp.Port)
27 assert.False(t, hmp.NoPort)
28 assert.Error(t, hmp.Err)
29 }
0 package missinggo
1
2 import (
3 "net"
4 "strconv"
5 )
6
7 func ParseHostPort(hostport string) (host string, port int, err error) {
8 host, portStr, err := net.SplitHostPort(hostport)
9 if err != nil {
10 return
11 }
12 port64, err := strconv.ParseInt(portStr, 0, 0)
13 if err != nil {
14 return
15 }
16 port = int(port64)
17 return
18 }
0 package httpfile
1
2 import (
3 "net/http"
4 )
5
6 var DefaultFS = &FS{
7 Client: http.DefaultClient,
8 }
9
10 // Returns the length of the resource in bytes.
11 func GetLength(url string) (ret int64, err error) {
12 return DefaultFS.GetLength(url)
13 }
0 package httpfile
1
2 import (
3 "bytes"
4 "errors"
5 "fmt"
6 "io"
7 "net/http"
8 "os"
9 "strconv"
10
11 "github.com/anacrolix/missinggo"
12 "github.com/anacrolix/missinggo/httptoo"
13 )
14
15 type File struct {
16 off int64
17 r io.ReadCloser
18 rOff int64
19 length int64
20 url string
21 flags int
22 fs *FS
23 }
24
25 func (me *File) headLength() (err error) {
26 l, err := me.fs.GetLength(me.url)
27 if err != nil {
28 return
29 }
30 if l != -1 {
31 me.length = l
32 }
33 return
34 }
35
36 func (me *File) prepareReader() (err error) {
37 if me.r != nil && me.off != me.rOff {
38 me.r.Close()
39 me.r = nil
40 }
41 if me.r != nil {
42 return nil
43 }
44 if me.flags&missinggo.O_ACCMODE == os.O_WRONLY {
45 err = errors.New("read flags missing")
46 return
47 }
48 req, err := http.NewRequest("GET", me.url, nil)
49 if err != nil {
50 return
51 }
52 if me.off != 0 {
53 req.Header.Set("Range", fmt.Sprintf("bytes=%d-", me.off))
54 }
55 resp, err := me.fs.Client.Do(req)
56 if err != nil {
57 return
58 }
59 switch resp.StatusCode {
60 case http.StatusPartialContent:
61 cr, ok := httptoo.ParseBytesContentRange(resp.Header.Get("Content-Range"))
62 if !ok || cr.First != me.off {
63 err = errors.New("bad response")
64 resp.Body.Close()
65 return
66 }
67 me.length = cr.Length
68 case http.StatusOK:
69 if me.off != 0 {
70 err = errors.New("bad response")
71 resp.Body.Close()
72 return
73 }
74 if h := resp.Header.Get("Content-Length"); h != "" {
75 var cl uint64
76 cl, err = strconv.ParseUint(h, 10, 64)
77 if err != nil {
78 resp.Body.Close()
79 return
80 }
81 me.length = int64(cl)
82 }
83 case http.StatusNotFound:
84 err = ErrNotFound
85 resp.Body.Close()
86 return
87 default:
88 err = errors.New(resp.Status)
89 resp.Body.Close()
90 return
91 }
92 me.r = resp.Body
93 me.rOff = me.off
94 return
95 }
96
97 func (me *File) Read(b []byte) (n int, err error) {
98 err = me.prepareReader()
99 if err != nil {
100 return
101 }
102 n, err = me.r.Read(b)
103 me.off += int64(n)
104 me.rOff += int64(n)
105 return
106 }
107
108 func (me *File) Seek(offset int64, whence int) (ret int64, err error) {
109 switch whence {
110 case os.SEEK_SET:
111 ret = offset
112 case os.SEEK_CUR:
113 ret = me.off + offset
114 case os.SEEK_END:
115 // Try to update the resource length.
116 err = me.headLength()
117 if err != nil {
118 if me.length == -1 {
119 // Don't even have an old value.
120 return
121 }
122 err = nil
123 }
124 ret = me.length + offset
125 default:
126 err = fmt.Errorf("unhandled whence: %d", whence)
127 return
128 }
129 me.off = ret
130 return
131 }
132
133 func (me *File) Write(b []byte) (n int, err error) {
134 if me.flags&(os.O_WRONLY|os.O_RDWR) == 0 || me.flags&os.O_CREATE == 0 {
135 err = errors.New("cannot write without write and create flags")
136 return
137 }
138 req, err := http.NewRequest("PATCH", me.url, bytes.NewReader(b))
139 if err != nil {
140 return
141 }
142 req.Header.Set("Content-Range", fmt.Sprintf("bytes=%d-", me.off))
143 req.ContentLength = int64(len(b))
144 resp, err := me.fs.Client.Do(req)
145 if err != nil {
146 return
147 }
148 resp.Body.Close()
149 if resp.StatusCode != http.StatusPartialContent {
150 err = errors.New(resp.Status)
151 return
152 }
153 n = len(b)
154 me.off += int64(n)
155 return
156 }
157
158 func (me *File) Close() error {
159 me.url = ""
160 me.length = -1
161 if me.r != nil {
162 me.r.Close()
163 me.r = nil
164 }
165 return nil
166 }
0 package httpfile
1
2 import (
3 "fmt"
4 "io"
5 "net/http"
6 "os"
7 )
8
9 type FS struct {
10 Client *http.Client
11 }
12
13 func (fs *FS) Delete(urlStr string) (err error) {
14 req, err := http.NewRequest("DELETE", urlStr, nil)
15 if err != nil {
16 return
17 }
18 resp, err := fs.Client.Do(req)
19 if err != nil {
20 return
21 }
22 resp.Body.Close()
23 if resp.StatusCode == http.StatusNotFound {
24 err = ErrNotFound
25 return
26 }
27 if resp.StatusCode != 200 {
28 err = fmt.Errorf("response: %s", resp.Status)
29 }
30 return
31 }
32
33 func (fs *FS) GetLength(url string) (ret int64, err error) {
34 resp, err := fs.Client.Head(url)
35 if err != nil {
36 return
37 }
38 resp.Body.Close()
39 if resp.StatusCode == http.StatusNotFound {
40 err = ErrNotFound
41 return
42 }
43 return instanceLength(resp)
44 }
45
46 func (fs *FS) OpenSectionReader(url string, off, n int64) (ret io.ReadCloser, err error) {
47 req, err := http.NewRequest("GET", url, nil)
48 if err != nil {
49 return
50 }
51 req.Header.Set("Range", fmt.Sprintf("bytes=%d-%d", off, off+n-1))
52 resp, err := fs.Client.Do(req)
53 if err != nil {
54 return
55 }
56 if resp.StatusCode == http.StatusNotFound {
57 err = ErrNotFound
58 resp.Body.Close()
59 return
60 }
61 if resp.StatusCode != http.StatusPartialContent {
62 err = fmt.Errorf("bad response status: %s", resp.Status)
63 resp.Body.Close()
64 return
65 }
66 ret = resp.Body
67 return
68 }
69
70 func (fs *FS) Open(url string, flags int) (ret *File, err error) {
71 ret = &File{
72 url: url,
73 flags: flags,
74 length: -1,
75 fs: fs,
76 }
77 if flags&os.O_CREATE == 0 {
78 err = ret.headLength()
79 }
80 return
81 }
0 package httpfile
1
2 import (
3 "errors"
4 "net/http"
5 "os"
6 "strconv"
7
8 "github.com/anacrolix/missinggo/httptoo"
9 )
10
11 var (
12 ErrNotFound = os.ErrNotExist
13 )
14
15 // ok is false if the response just doesn't specify anything we handle.
16 func instanceLength(r *http.Response) (l int64, err error) {
17 switch r.StatusCode {
18 case http.StatusOK:
19 l, err = strconv.ParseInt(r.Header.Get("Content-Length"), 10, 64)
20 return
21 case http.StatusPartialContent:
22 cr, parseOk := httptoo.ParseBytesContentRange(r.Header.Get("Content-Range"))
23 l = cr.Length
24 if !parseOk {
25 err = errors.New("error parsing Content-Range")
26 }
27 return
28 default:
29 err = errors.New("unhandled status code")
30 return
31 }
32 }
0 package httpmux
1
2 import (
3 "context"
4 "fmt"
5 "net/http"
6 "path"
7 "regexp"
8 "strings"
9
10 "go.opencensus.io/trace"
11 )
12
13 var pathParamContextKey = new(struct{})
14
15 type Mux struct {
16 handlers []Handler
17 }
18
19 func New() *Mux {
20 return new(Mux)
21 }
22
23 type Handler struct {
24 path *regexp.Regexp
25 userHandler http.Handler
26 }
27
28 func (h Handler) Pattern() string {
29 return h.path.String()
30 }
31
32 func (mux *Mux) GetHandler(r *http.Request) *Handler {
33 matches := mux.matchingHandlers(r)
34 if len(matches) == 0 {
35 return nil
36 }
37 return &matches[0].Handler
38 }
39
40 func (me *Mux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
41 matches := me.matchingHandlers(r)
42 if len(matches) == 0 {
43 http.NotFound(w, r)
44 return
45 }
46 m := matches[0]
47 ctx := context.WithValue(r.Context(), pathParamContextKey, &PathParams{m})
48 ctx, span := trace.StartSpan(ctx, m.Handler.path.String(), trace.WithSpanKind(trace.SpanKindServer))
49 defer span.End()
50 r = r.WithContext(ctx)
51 defer func() {
52 r := recover()
53 if r == http.ErrAbortHandler {
54 panic(r)
55 }
56 if r == nil {
57 return
58 }
59 panic(fmt.Sprintf("while handling %q: %s", m.Handler.path.String(), r))
60 }()
61 m.Handler.userHandler.ServeHTTP(w, r)
62 }
63
64 type match struct {
65 Handler Handler
66 submatches []string
67 }
68
69 func (me *Mux) matchingHandlers(r *http.Request) (ret []match) {
70 for _, h := range me.handlers {
71 subs := h.path.FindStringSubmatch(r.URL.Path)
72 if subs == nil {
73 continue
74 }
75 ret = append(ret, match{h, subs})
76 }
77 return
78 }
79
80 func (me *Mux) distinctHandlerRegexp(r *regexp.Regexp) bool {
81 for _, h := range me.handlers {
82 if h.path.String() == r.String() {
83 return false
84 }
85 }
86 return true
87 }
88
89 func (me *Mux) Handle(path string, h http.Handler) {
90 expr := "^" + path
91 if !strings.HasSuffix(expr, "$") {
92 expr += "$"
93 }
94 re, err := regexp.Compile(expr)
95 if err != nil {
96 panic(err)
97 }
98 if !me.distinctHandlerRegexp(re) {
99 panic(fmt.Sprintf("path %q is not distinct", path))
100 }
101 me.handlers = append(me.handlers, Handler{re, h})
102 }
103
104 func (me *Mux) HandleFunc(path string, hf func(http.ResponseWriter, *http.Request)) {
105 me.Handle(path, http.HandlerFunc(hf))
106 }
107
108 func Path(parts ...string) string {
109 return path.Join(parts...)
110 }
111
112 type PathParams struct {
113 match match
114 }
115
116 func (me *PathParams) ByName(name string) string {
117 for i, sn := range me.match.Handler.path.SubexpNames()[1:] {
118 if sn == name {
119 return me.match.submatches[i+1]
120 }
121 }
122 return ""
123 }
124
125 func RequestPathParams(r *http.Request) *PathParams {
126 ctx := r.Context()
127 return ctx.Value(pathParamContextKey).(*PathParams)
128 }
129
130 func PathRegexpParam(name string, re string) string {
131 return fmt.Sprintf("(?P<%s>%s)", name, re)
132 }
133
134 func Param(name string) string {
135 return fmt.Sprintf("(?P<%s>[^/]+)", name)
136 }
137
138 func RestParam(name string) string {
139 return fmt.Sprintf("(?P<%s>.*)$", name)
140 }
141
142 func NonEmptyRestParam(name string) string {
143 return fmt.Sprintf("(?P<%s>.+)$", name)
144 }
0 package missinggo
1
2 // todo move to httptoo as ResponseRecorder
3
4 import (
5 "bufio"
6 "net"
7 "net/http"
8 "time"
9 )
10
11 // A http.ResponseWriter that tracks the status of the response. The status
12 // code, and number of bytes written for example.
13 type StatusResponseWriter struct {
14 http.ResponseWriter
15 Code int
16 BytesWritten int64
17 Started time.Time
18 TimeToFirstByte time.Duration // Time to first byte
19 GotFirstByte bool
20 WroteHeader Event
21 Hijacked bool
22 }
23
24 var _ interface {
25 http.ResponseWriter
26 http.Hijacker
27 } = (*StatusResponseWriter)(nil)
28
29 func (me *StatusResponseWriter) Write(b []byte) (n int, err error) {
30 // Exactly how it's done in the standard library. This ensures Code is
31 // correct.
32 if !me.WroteHeader.IsSet() {
33 me.WriteHeader(http.StatusOK)
34 }
35 if me.Started.IsZero() {
36 panic("Started was not initialized")
37 }
38 timeBeforeWrite := time.Now()
39 n, err = me.ResponseWriter.Write(b)
40 if n > 0 && !me.GotFirstByte {
41 me.TimeToFirstByte = timeBeforeWrite.Sub(me.Started)
42 me.GotFirstByte = true
43 }
44 me.BytesWritten += int64(n)
45 return
46 }
47
48 func (me *StatusResponseWriter) WriteHeader(code int) {
49 me.ResponseWriter.WriteHeader(code)
50 if !me.WroteHeader.IsSet() {
51 me.Code = code
52 me.WroteHeader.Set()
53 }
54 }
55
56 func (me *StatusResponseWriter) Hijack() (c net.Conn, b *bufio.ReadWriter, err error) {
57 me.Hijacked = true
58 c, b, err = me.ResponseWriter.(http.Hijacker).Hijack()
59 if b.Writer.Buffered() != 0 {
60 panic("unexpected buffered writes")
61 }
62 c = responseConn{c, me}
63 return
64 }
65
66 type responseConn struct {
67 net.Conn
68 s *StatusResponseWriter
69 }
70
71 func (me responseConn) Write(b []byte) (n int, err error) {
72 n, err = me.Conn.Write(b)
73 me.s.BytesWritten += int64(n)
74 return
75 }
0 package httptoo
1
2 import (
3 "fmt"
4 "strconv"
5 "strings"
6
7 "github.com/anacrolix/missinggo/mime"
8 )
9
10 func ParseAccept(line string) (parsed AcceptDirectives, err error) {
11 dirs := strings.Split(line, ",")
12 for _, d := range dirs {
13 p := AcceptDirective{
14 Q: 1,
15 }
16 ss := strings.Split(d, ";")
17 switch len(ss) {
18 case 2:
19 p.Q, err = strconv.ParseFloat(ss[1], 32)
20 if err != nil {
21 return
22 }
23 fallthrough
24 case 1:
25 p.MimeType.FromString(ss[0])
26 default:
27 err = fmt.Errorf("error parsing %q", d)
28 return
29 }
30 parsed = append(parsed, p)
31 }
32 return
33 }
34
35 type (
36 AcceptDirectives []AcceptDirective
37 AcceptDirective struct {
38 MimeType mime.Type
39 Q float64
40 }
41 )
0 package httptoo
1
2 import (
3 "fmt"
4 "math"
5 "regexp"
6 "strconv"
7 "strings"
8 )
9
10 type BytesContentRange struct {
11 First, Last, Length int64
12 }
13
14 type BytesRange struct {
15 First, Last int64
16 }
17
18 func (me BytesRange) String() string {
19 if me.Last == math.MaxInt64 {
20 return fmt.Sprintf("bytes=%d-", me.First)
21 }
22 return fmt.Sprintf("bytes=%d-%d", me.First, me.Last)
23 }
24
25 var (
26 httpBytesRangeRegexp = regexp.MustCompile(`bytes[ =](\d+)-(\d*)`)
27 )
28
29 func ParseBytesRange(s string) (ret BytesRange, ok bool) {
30 ss := httpBytesRangeRegexp.FindStringSubmatch(s)
31 if ss == nil {
32 return
33 }
34 var err error
35 ret.First, err = strconv.ParseInt(ss[1], 10, 64)
36 if err != nil {
37 return
38 }
39 if ss[2] == "" {
40 ret.Last = math.MaxInt64
41 } else {
42 ret.Last, err = strconv.ParseInt(ss[2], 10, 64)
43 if err != nil {
44 return
45 }
46 }
47 ok = true
48 return
49 }
50
51 func parseUnitRanges(s string) (unit, ranges string) {
52 s = strings.TrimSpace(s)
53 i := strings.IndexAny(s, " =")
54 if i == -1 {
55 return
56 }
57 unit = s[:i]
58 ranges = s[i+1:]
59 return
60 }
61
62 func parseFirstLast(s string) (first, last int64) {
63 ss := strings.SplitN(s, "-", 2)
64 first, err := strconv.ParseInt(ss[0], 10, 64)
65 if err != nil {
66 panic(err)
67 }
68 last, err = strconv.ParseInt(ss[1], 10, 64)
69 if err != nil {
70 panic(err)
71 }
72 return
73 }
74
75 func parseContentRange(s string) (ret BytesContentRange) {
76 ss := strings.SplitN(s, "/", 2)
77 firstLast := strings.TrimSpace(ss[0])
78 if firstLast == "*" {
79 ret.First = -1
80 ret.Last = -1
81 } else {
82 ret.First, ret.Last = parseFirstLast(firstLast)
83 }
84 il := strings.TrimSpace(ss[1])
85 if il == "*" {
86 ret.Length = -1
87 } else {
88 var err error
89 ret.Length, err = strconv.ParseInt(il, 10, 64)
90 if err != nil {
91 panic(err)
92 }
93 }
94 return
95 }
96
97 func ParseBytesContentRange(s string) (ret BytesContentRange, ok bool) {
98 unit, ranges := parseUnitRanges(s)
99 if unit != "bytes" {
100 return
101 }
102 ret = parseContentRange(ranges)
103 ok = true
104 return
105 }
0 package httptoo
1
2 import (
3 "testing"
4
5 "github.com/stretchr/testify/assert"
6 )
7
8 func TestParseHTTPContentRange(t *testing.T) {
9 for _, _case := range []struct {
10 h string
11 cr *BytesContentRange
12 }{
13 {"", nil},
14 {"1-2/*", nil},
15 {"bytes=1-2/3", &BytesContentRange{1, 2, 3}},
16 {"bytes=12-34/*", &BytesContentRange{12, 34, -1}},
17 {" bytes=12-34/*", &BytesContentRange{12, 34, -1}},
18 {" bytes 12-34/56", &BytesContentRange{12, 34, 56}},
19 {" bytes=*/56", &BytesContentRange{-1, -1, 56}},
20 } {
21 ret, ok := ParseBytesContentRange(_case.h)
22 assert.Equal(t, _case.cr != nil, ok)
23 if _case.cr != nil {
24 assert.Equal(t, *_case.cr, ret)
25 }
26 }
27 }
0 package httptoo
1
2 import (
3 "crypto/tls"
4 "net/http"
5 )
6
7 // Returns the http.Client's TLS Config, traversing and generating any
8 // defaults along the way to get it.
9 func ClientTLSConfig(cl *http.Client) *tls.Config {
10 if cl.Transport == nil {
11 cl.Transport = http.DefaultTransport
12 }
13 tr := cl.Transport.(*http.Transport)
14 if tr.TLSClientConfig == nil {
15 tr.TLSClientConfig = &tls.Config{}
16 }
17 return tr.TLSClientConfig
18 }
0 package httptoo
1
2 import (
3 "net/http"
4 "os"
5 )
6
7 // Wraps a http.FileSystem, disabling directory listings, per the commonly
8 // requested feature at https://groups.google.com/forum/#!topic/golang-
9 // nuts/bStLPdIVM6w .
10 type JustFilesFilesystem struct {
11 Fs http.FileSystem
12 }
13
14 func (fs JustFilesFilesystem) Open(name string) (http.File, error) {
15 f, err := fs.Fs.Open(name)
16 if err != nil {
17 return nil, err
18 }
19 d, err := f.Stat()
20 if err != nil {
21 f.Close()
22 return nil, err
23 }
24 if d.IsDir() {
25 f.Close()
26 // This triggers http.FileServer to show a 404.
27 return nil, os.ErrNotExist
28 }
29 return f, nil
30 }
0 package httptoo
1
2 import (
3 "compress/gzip"
4 "io"
5 "net/http"
6 "strings"
7 )
8
9 type gzipResponseWriter struct {
10 io.Writer
11 http.ResponseWriter
12 haveWritten bool
13 }
14
15 var _ http.ResponseWriter = &gzipResponseWriter{}
16
17 func (w *gzipResponseWriter) Write(b []byte) (int, error) {
18 if w.haveWritten {
19 goto write
20 }
21 w.haveWritten = true
22 if w.Header().Get("Content-Type") != "" {
23 goto write
24 }
25 if type_ := http.DetectContentType(b); type_ != "application/octet-stream" {
26 w.Header().Set("Content-Type", type_)
27 }
28 write:
29 return w.Writer.Write(b)
30 }
31
32 // Gzips response body if the request says it'll allow it.
33 func GzipHandler(h http.Handler) http.Handler {
34 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
35 if !strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") || w.Header().Get("Content-Encoding") != "" || w.Header().Get("Vary") != "" {
36 h.ServeHTTP(w, r)
37 return
38 }
39 w.Header().Set("Content-Encoding", "gzip")
40 w.Header().Set("Vary", "Accept-Encoding")
41 gz := gzip.NewWriter(w)
42 defer gz.Close()
43 h.ServeHTTP(&gzipResponseWriter{
44 Writer: gz,
45 ResponseWriter: w,
46 }, r)
47 })
48 }
0 package httptoo
1
2 import (
3 "compress/gzip"
4 "io/ioutil"
5 "net/http"
6 "net/http/httptest"
7 "testing"
8
9 "github.com/stretchr/testify/assert"
10 "github.com/stretchr/testify/require"
11 )
12
13 const helloWorld = "hello, world\n"
14
15 func helloWorldHandler(w http.ResponseWriter, r *http.Request) {
16 // w.Header().Set("Content-Length", strconv.FormatInt(int64(len(helloWorld)), 10))
17 w.Write([]byte(helloWorld))
18 }
19
20 func requestResponse(h http.Handler, r *http.Request) (*http.Response, error) {
21 s := httptest.NewServer(h)
22 defer s.Close()
23 return http.DefaultClient.Do(r)
24 }
25
26 func TestGzipHandler(t *testing.T) {
27 rr := httptest.NewRecorder()
28 helloWorldHandler(rr, nil)
29 assert.EqualValues(t, helloWorld, rr.Body.String())
30
31 rr = httptest.NewRecorder()
32 GzipHandler(http.HandlerFunc(helloWorldHandler)).ServeHTTP(rr, new(http.Request))
33 assert.EqualValues(t, helloWorld, rr.Body.String())
34
35 rr = httptest.NewRecorder()
36 r, err := http.NewRequest("GET", "/", nil)
37 require.NoError(t, err)
38 r.Header.Set("Accept-Encoding", "gzip")
39 GzipHandler(http.HandlerFunc(helloWorldHandler)).ServeHTTP(rr, r)
40 gr, err := gzip.NewReader(rr.Body)
41 require.NoError(t, err)
42 defer gr.Close()
43 b, err := ioutil.ReadAll(gr)
44 require.NoError(t, err)
45 assert.EqualValues(t, helloWorld, b)
46
47 s := httptest.NewServer(nil)
48 s.Config.Handler = GzipHandler(http.HandlerFunc(helloWorldHandler))
49 req, err := http.NewRequest("GET", s.URL, nil)
50 req.Header.Set("Accept-Encoding", "gzip")
51 resp, err := http.DefaultClient.Do(req)
52 require.NoError(t, err)
53 gr.Close()
54 gr, err = gzip.NewReader(resp.Body)
55 require.NoError(t, err)
56 defer gr.Close()
57 b, err = ioutil.ReadAll(gr)
58 require.NoError(t, err)
59 assert.EqualValues(t, helloWorld, b)
60 assert.EqualValues(t, "text/plain; charset=utf-8", resp.Header.Get("Content-Type"))
61 assert.EqualValues(t, "gzip", resp.Header.Get("Content-Encoding"))
62 }
0 package httptoo
1
2 import (
3 "fmt"
4 "strings"
5 "time"
6 )
7
8 type Visibility int
9
10 const (
11 Default = 0
12 Public = 1
13 Private = 2
14 )
15
16 type CacheControlHeader struct {
17 MaxAge time.Duration
18 Caching Visibility
19 NoStore bool
20 }
21
22 func (me *CacheControlHeader) caching() []string {
23 switch me.Caching {
24 case Public:
25 return []string{"public"}
26 case Private:
27 return []string{"private"}
28 default:
29 return nil
30 }
31 }
32
33 func (me *CacheControlHeader) maxAge() []string {
34 if me.MaxAge == 0 {
35 return nil
36 }
37 d := me.MaxAge
38 if d < 0 {
39 d = 0
40 }
41 return []string{fmt.Sprintf("max-age=%d", d/time.Second)}
42 }
43
44 func (me *CacheControlHeader) noStore() []string {
45 if me.NoStore {
46 return []string{"no-store"}
47 }
48 return nil
49 }
50
51 func (me *CacheControlHeader) concat(sss ...[]string) (ret []string) {
52 for _, ss := range sss {
53 ret = append(ret, ss...)
54 }
55 return
56 }
57
58 func (me CacheControlHeader) String() string {
59 return strings.Join(me.concat(me.caching(), me.maxAge()), ", ")
60 }
0 package httptoo
1
2 import (
3 "testing"
4 "time"
5
6 "github.com/stretchr/testify/assert"
7 )
8
9 func TestCacheControlHeaderString(t *testing.T) {
10 assert.Equal(t, "public, max-age=43200", CacheControlHeader{
11 MaxAge: 12 * time.Hour,
12 Caching: Public,
13 }.String())
14 }
0 package httptoo
1
2 import (
3 "net/http"
4 "strconv"
5 "strings"
6
7 "github.com/bradfitz/iter"
8
9 "github.com/anacrolix/missinggo"
10 )
11
12 func OriginatingProtocol(r *http.Request) string {
13 if fp := r.Header.Get("X-Forwarded-Proto"); fp != "" {
14 return fp
15 } else if r.TLS != nil {
16 return "https"
17 } else {
18 return "http"
19 }
20 }
21
22 // Clears the named cookie for every domain that leads to the current one.
23 func NukeCookie(w http.ResponseWriter, r *http.Request, name, path string) {
24 parts := strings.Split(missinggo.SplitHostMaybePort(r.Host).Host, ".")
25 for i := range iter.N(len(parts) + 1) { // Include the empty domain.
26 http.SetCookie(w, &http.Cookie{
27 Name: name,
28 MaxAge: -1,
29 Path: path,
30 Domain: strings.Join(parts[i:], "."),
31 })
32 }
33 }
34
35 // Performs quoted-string from http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html
36 func EncodeQuotedString(s string) string {
37 return strconv.Quote(s)
38 }
39
40 // https://httpstatuses.com/499
41 const StatusClientCancelledRequest = 499
0 package httptoo
1
2 import (
3 "context"
4 "io"
5 "net/http"
6 "sync"
7
8 "github.com/anacrolix/missinggo"
9 )
10
11 type responseWriter struct {
12 mu sync.Mutex
13 r http.Response
14 headerWritten missinggo.Event
15 bodyWriter io.WriteCloser
16 bodyClosed missinggo.SynchronizedEvent
17 }
18
19 var _ interface {
20 http.ResponseWriter
21 // We're able to emulate this easily enough.
22 http.CloseNotifier
23 } = &responseWriter{}
24
25 // Use Request.Context.Done instead.
26 func (me *responseWriter) CloseNotify() <-chan bool {
27 ret := make(chan bool, 1)
28 go func() {
29 <-me.bodyClosed.C()
30 ret <- true
31 }()
32 return ret
33 }
34
35 func (me *responseWriter) Header() http.Header {
36 if me.r.Header == nil {
37 me.r.Header = make(http.Header)
38 }
39 return me.r.Header
40 }
41
42 func (me *responseWriter) Write(b []byte) (int, error) {
43 me.mu.Lock()
44 if !me.headerWritten.IsSet() {
45 me.writeHeader(200)
46 }
47 me.mu.Unlock()
48 return me.bodyWriter.Write(b)
49 }
50
51 func (me *responseWriter) WriteHeader(status int) {
52 me.mu.Lock()
53 me.writeHeader(status)
54 me.mu.Unlock()
55 }
56
57 func (me *responseWriter) writeHeader(status int) {
58 if me.headerWritten.IsSet() {
59 return
60 }
61 me.r.StatusCode = status
62 me.headerWritten.Set()
63 }
64
65 func (me *responseWriter) runHandler(h http.Handler, req *http.Request) {
66 var pr *io.PipeReader
67 pr, me.bodyWriter = io.Pipe()
68 me.r.Body = struct {
69 io.Reader
70 io.Closer
71 }{pr, eventCloser{pr, &me.bodyClosed}}
72 // Shouldn't be writing to the response after the handler returns.
73 defer me.bodyWriter.Close()
74 // Send a 200 if nothing was written yet.
75 defer me.WriteHeader(200)
76 // Wrap the context in the given Request with one that closes when either
77 // the handler returns, or the response body is closed.
78 ctx, cancel := context.WithCancel(req.Context())
79 defer cancel()
80 go func() {
81 <-me.bodyClosed.C()
82 cancel()
83 }()
84 h.ServeHTTP(me, req.WithContext(ctx))
85 }
86
87 type eventCloser struct {
88 c io.Closer
89 closed *missinggo.SynchronizedEvent
90 }
91
92 func (me eventCloser) Close() (err error) {
93 err = me.c.Close()
94 me.closed.Set()
95 return
96 }
97
98 func RoundTripHandler(req *http.Request, h http.Handler) (*http.Response, error) {
99 rw := responseWriter{}
100 go rw.runHandler(h, req)
101 <-rw.headerWritten.LockedChan(&rw.mu)
102 return &rw.r, nil
103 }
104
105 type InProcRoundTripper struct {
106 Handler http.Handler
107 }
108
109 func (me *InProcRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
110 return RoundTripHandler(req, me.Handler)
111 }
0 package httptoo
1
2 import (
3 "net"
4 "net/http"
5
6 "github.com/anacrolix/missinggo"
7 )
8
9 // Request is intended for localhost, either with a localhost name, or
10 // loopback IP.
11 func RequestIsForLocalhost(r *http.Request) bool {
12 hostHost := missinggo.SplitHostMaybePort(r.Host).Host
13 if ip := net.ParseIP(hostHost); ip != nil {
14 return ip.IsLoopback()
15 }
16 return hostHost == "localhost"
17 }
18
19 // Request originated from a loopback IP.
20 func RequestIsFromLocalhost(r *http.Request) bool {
21 return net.ParseIP(missinggo.SplitHostMaybePort(r.RemoteAddr).Host).IsLoopback()
22 }
0 package httptoo
1
2 import (
3 "bufio"
4 "encoding/gob"
5 "io"
6 "net"
7 "net/http"
8 "net/url"
9 "sync"
10 )
11
12 func deepCopy(dst, src interface{}) error {
13 r, w := io.Pipe()
14 e := gob.NewEncoder(w)
15 d := gob.NewDecoder(r)
16 var decErr, encErr error
17 var wg sync.WaitGroup
18 wg.Add(1)
19 go func() {
20 defer wg.Done()
21 decErr = d.Decode(dst)
22 r.Close()
23 }()
24 encErr = e.Encode(src)
25 // Always returns nil.
26 w.CloseWithError(encErr)
27 wg.Wait()
28 if encErr != nil {
29 return encErr
30 }
31 return decErr
32 }
33
34 // Takes a request, and alters its destination fields, for proxying.
35 func RedirectedRequest(r *http.Request, newUrl string) (ret *http.Request, err error) {
36 u, err := url.Parse(newUrl)
37 if err != nil {
38 return
39 }
40 ret = new(http.Request)
41 *ret = *r
42 ret.Header = nil
43 err = deepCopy(&ret.Header, r.Header)
44 if err != nil {
45 return
46 }
47 ret.URL = u
48 ret.RequestURI = ""
49 return
50 }
51
52 func CopyHeaders(w http.ResponseWriter, r *http.Response) {
53 for h, vs := range r.Header {
54 for _, v := range vs {
55 w.Header().Add(h, v)
56 }
57 }
58 }
59
60 func ForwardResponse(w http.ResponseWriter, r *http.Response) {
61 CopyHeaders(w, r)
62 w.WriteHeader(r.StatusCode)
63 // Errors frequently occur writing the body when the client hangs up.
64 io.Copy(w, r.Body)
65 r.Body.Close()
66 }
67
68 func SetOriginRequestForwardingHeaders(o, f *http.Request) {
69 xff := o.Header.Get("X-Forwarded-For")
70 hop, _, _ := net.SplitHostPort(f.RemoteAddr)
71 if xff == "" {
72 xff = hop
73 } else {
74 xff += "," + hop
75 }
76 o.Header.Set("X-Forwarded-For", xff)
77 o.Header.Set("X-Forwarded-Proto", OriginatingProtocol(f))
78 }
79
80 // w is for the client response. r is the request to send to the origin
81 // (already "forwarded"). originUrl is where to send the request.
82 func ReverseProxyUpgrade(w http.ResponseWriter, r *http.Request, originUrl string) (err error) {
83 u, err := url.Parse(originUrl)
84 if err != nil {
85 return
86 }
87 oc, err := net.Dial("tcp", u.Host)
88 if err != nil {
89 return
90 }
91 defer oc.Close()
92 err = r.Write(oc)
93 if err != nil {
94 return
95 }
96 originConnReadBuffer := bufio.NewReader(oc)
97 originResp, err := http.ReadResponse(originConnReadBuffer, r)
98 if err != nil {
99 return
100 }
101 if originResp.StatusCode != 101 {
102 ForwardResponse(w, originResp)
103 return
104 }
105 cc, _, err := w.(http.Hijacker).Hijack()
106 if err != nil {
107 return
108 }
109 defer cc.Close()
110 originResp.Write(cc)
111 go io.Copy(oc, cc)
112 // Let the origin connection control when this routine returns, as we
113 // should trust it more.
114 io.Copy(cc, originConnReadBuffer)
115 return
116 }
117
118 func ReverseProxy(w http.ResponseWriter, r *http.Request, originUrl string, client *http.Client) (err error) {
119 originRequest, err := RedirectedRequest(r, originUrl)
120 if err != nil {
121 return
122 }
123 SetOriginRequestForwardingHeaders(originRequest, r)
124 if r.Header.Get("Connection") == "Upgrade" {
125 return ReverseProxyUpgrade(w, originRequest, originUrl)
126 }
127 rt := client.Transport
128 if rt == nil {
129 rt = http.DefaultTransport
130 }
131 originResp, err := rt.RoundTrip(originRequest)
132 if err != nil {
133 return
134 }
135 ForwardResponse(w, originResp)
136 return
137 }
0 package httptoo
1
2 import (
3 "net/http"
4 "net/url"
5 )
6
7 // Deep copies a URL. I could call it DeepCopyURL, but what else would you be
8 // copying when you have a *url.URL? Of note is that the Userinfo is deep
9 // copied. The returned URL shares no references with the original.
10 func CopyURL(u *url.URL) (ret *url.URL) {
11 ret = new(url.URL)
12 *ret = *u
13 if u.User != nil {
14 ret.User = new(url.Userinfo)
15 *ret.User = *u.User
16 }
17 return
18 }
19
20 // Reconstructs the URL that would have produced the given Request.
21 // Request.URLs are not fully populated in http.Server handlers.
22 func RequestedURL(r *http.Request) (ret *url.URL) {
23 ret = CopyURL(r.URL)
24 ret.Host = r.Host
25 ret.Scheme = OriginatingProtocol(r)
26 return
27 }
28
29 // The official URL struct parameters, for tracking changes and reference
30 // here.
31 //
32 // Scheme string
33 // Opaque string // encoded opaque data
34 // User *Userinfo // username and password information
35 // Host string // host or host:port
36 // Path string
37 // RawPath string // encoded path hint (Go 1.5 and later only; see EscapedPath method)
38 // ForceQuery bool // append a query ('?') even if RawQuery is empty
39 // RawQuery string // encoded query values, without '?'
40 // Fragment string // fragment for references, without '#'
41
42 // Return the first URL extended with elements of the second, in the manner
43 // that occurs throughout my projects. Noteworthy difference from
44 // url.URL.ResolveReference is that if the reference has a scheme, the base is
45 // not completely ignored.
46 func AppendURL(u, v *url.URL) *url.URL {
47 u = CopyURL(u)
48 clobberString(&u.Scheme, v.Scheme)
49 clobberString(&u.Host, v.Host)
50 u.Path += v.Path
51 q := u.Query()
52 for k, v := range v.Query() {
53 q[k] = append(q[k], v...)
54 }
55 u.RawQuery = q.Encode()
56 return u
57 }
58
59 func clobberString(s *string, value string) {
60 if value != "" {
61 *s = value
62 }
63 }
0 package httptoo
1
2 import (
3 "net/url"
4 "testing"
5
6 "github.com/stretchr/testify/assert"
7 )
8
9 func TestAppendURL(t *testing.T) {
10 assert.EqualValues(t, "http://localhost:8080/trailing/slash/", AppendURL(
11 &url.URL{Scheme: "http", Host: "localhost:8080"},
12 &url.URL{Path: "/trailing/slash/"},
13 ).String())
14 assert.EqualValues(t, "ws://localhost:8080/events?ih=harpdarp", AppendURL(
15 &url.URL{Scheme: "http", Host: "localhost:8080"},
16 &url.URL{Scheme: "ws", Path: "/events", RawQuery: "ih=harpdarp"},
17 ).String())
18 }
0 package inproc
1
2 import (
3 "errors"
4 "io"
5 "math"
6 "net"
7 "strconv"
8 "sync"
9 "time"
10
11 "github.com/anacrolix/missinggo"
12 )
13
14 var (
15 mu sync.Mutex
16 cond = sync.Cond{L: &mu}
17 nextPort int = 1
18 conns = map[int]*packetConn{}
19 )
20
21 type Addr struct {
22 Port int
23 }
24
25 func (Addr) Network() string {
26 return "inproc"
27 }
28
29 func (me Addr) String() string {
30 return ":" + strconv.FormatInt(int64(me.Port), 10)
31 }
32
33 func getPort() (port int) {
34 mu.Lock()
35 defer mu.Unlock()
36 port = nextPort
37 nextPort++
38 return
39 }
40
41 func ResolveAddr(network, str string) (net.Addr, error) {
42 return ResolveInprocAddr(network, str)
43 }
44
45 func ResolveInprocAddr(network, str string) (addr Addr, err error) {
46 if str == "" {
47 addr.Port = getPort()
48 return
49 }
50 _, p, err := net.SplitHostPort(str)
51 if err != nil {
52 return
53 }
54 i64, err := strconv.ParseInt(p, 10, 0)
55 if err != nil {
56 return
57 }
58 addr.Port = int(i64)
59 if addr.Port == 0 {
60 addr.Port = getPort()
61 }
62 return
63 }
64
65 func ListenPacket(network, addrStr string) (nc net.PacketConn, err error) {
66 addr, err := ResolveInprocAddr(network, addrStr)
67 if err != nil {
68 return
69 }
70 mu.Lock()
71 defer mu.Unlock()
72 if _, ok := conns[addr.Port]; ok {
73 err = errors.New("address in use")
74 return
75 }
76 pc := &packetConn{
77 addr: addr,
78 readDeadline: newCondDeadline(&cond),
79 writeDeadline: newCondDeadline(&cond),
80 }
81 conns[addr.Port] = pc
82 nc = pc
83 return
84 }
85
86 type packet struct {
87 data []byte
88 addr Addr
89 }
90
91 type packetConn struct {
92 closed bool
93 addr Addr
94 reads []packet
95 readDeadline *condDeadline
96 writeDeadline *condDeadline
97 }
98
99 func (me *packetConn) Close() error {
100 mu.Lock()
101 defer mu.Unlock()
102 me.closed = true
103 delete(conns, me.addr.Port)
104 cond.Broadcast()
105 return nil
106 }
107
108 func (me *packetConn) LocalAddr() net.Addr {
109 return me.addr
110 }
111
112 type errTimeout struct{}
113
114 func (errTimeout) Error() string {
115 return "i/o timeout"
116 }
117
118 func (errTimeout) Temporary() bool {
119 return false
120 }
121
122 func (errTimeout) Timeout() bool {
123 return true
124 }
125
126 var _ net.Error = errTimeout{}
127
128 func (me *packetConn) WriteTo(b []byte, na net.Addr) (n int, err error) {
129 mu.Lock()
130 defer mu.Unlock()
131 if me.closed {
132 err = errors.New("closed")
133 return
134 }
135 if me.writeDeadline.exceeded() {
136 err = errTimeout{}
137 return
138 }
139 n = len(b)
140 port := missinggo.AddrPort(na)
141 c, ok := conns[port]
142 if !ok {
143 // log.Printf("no conn for port %d", port)
144 return
145 }
146 c.reads = append(c.reads, packet{append([]byte(nil), b...), me.addr})
147 cond.Broadcast()
148 return
149 }
150
151 func (me *packetConn) ReadFrom(b []byte) (n int, addr net.Addr, err error) {
152 mu.Lock()
153 defer mu.Unlock()
154 for {
155 if len(me.reads) != 0 {
156 r := me.reads[0]
157 me.reads = me.reads[1:]
158 n = copy(b, r.data)
159 addr = r.addr
160 // log.Println(addr)
161 return
162 }
163 if me.closed {
164 err = io.EOF
165 return
166 }
167 if me.readDeadline.exceeded() {
168 err = errTimeout{}
169 return
170 }
171 cond.Wait()
172 }
173 }
174
175 func (me *packetConn) SetDeadline(t time.Time) error {
176 me.writeDeadline.setDeadline(t)
177 me.readDeadline.setDeadline(t)
178 return nil
179 }
180
181 func (me *packetConn) SetReadDeadline(t time.Time) error {
182 me.readDeadline.setDeadline(t)
183 return nil
184 }
185
186 func (me *packetConn) SetWriteDeadline(t time.Time) error {
187 me.writeDeadline.setDeadline(t)
188 return nil
189 }
190
191 func newCondDeadline(cond *sync.Cond) (ret *condDeadline) {
192 ret = &condDeadline{
193 timer: time.AfterFunc(math.MaxInt64, func() {
194 mu.Lock()
195 ret._exceeded = true
196 mu.Unlock()
197 cond.Broadcast()
198 }),
199 }
200 ret.setDeadline(time.Time{})
201 return
202 }
203
204 type condDeadline struct {
205 mu sync.Mutex
206 _exceeded bool
207 timer *time.Timer
208 }
209
210 func (me *condDeadline) setDeadline(t time.Time) {
211 me.mu.Lock()
212 defer me.mu.Unlock()
213 me._exceeded = false
214 if t.IsZero() {
215 me.timer.Stop()
216 return
217 }
218 me.timer.Reset(t.Sub(time.Now()))
219 }
220
221 func (me *condDeadline) exceeded() bool {
222 me.mu.Lock()
223 defer me.mu.Unlock()
224 return me._exceeded
225 }
0 package missinggo
1
2 import "io"
3
4 type StatWriter struct {
5 Written int64
6 w io.Writer
7 }
8
9 func (me *StatWriter) Write(b []byte) (n int, err error) {
10 n, err = me.w.Write(b)
11 me.Written += int64(n)
12 return
13 }
14
15 func NewStatWriter(w io.Writer) *StatWriter {
16 return &StatWriter{w: w}
17 }
18
19 var ZeroReader zeroReader
20
21 type zeroReader struct{}
22
23 func (me zeroReader) Read(b []byte) (n int, err error) {
24 for i := range b {
25 b[i] = 0
26 }
27 n = len(b)
28 return
29 }
0 package missinggo
1
2 import (
3 "net"
4 "strconv"
5 )
6
7 type IpPort struct {
8 IP net.IP
9 Port uint16
10 }
11
12 func (me IpPort) String() string {
13 return net.JoinHostPort(me.IP.String(), strconv.FormatUint(uint64(me.Port), 10))
14 }
15
16 func IpPortFromNetAddr(na net.Addr) IpPort {
17 return IpPort{AddrIP(na), uint16(AddrPort(na))}
18 }
0 package iter
1
2 func Chain(fs ...Func) Func {
3 return func(cb Callback) {
4 for _, f := range fs {
5 if !All(cb, f) {
6 break
7 }
8 }
9 }
10 }
0 package iter
1
2 // Callback receives a value and returns true if another value should be
3 // received or false to stop iteration.
4 type Callback func(value interface{}) (more bool)
5
6 // Func iterates by calling Callback for each of its values.
7 type Func func(Callback)
8
9 func All(cb Callback, fs ...Func) bool {
10 for _, f := range fs {
11 all := true
12 f(func(v interface{}) bool {
13 all = all && cb(v)
14 return all
15 })
16 if !all {
17 return false
18 }
19 }
20 return true
21 }
0 package iter
1
2 type groupBy struct {
3 curKey interface{}
4 curKeyOk bool
5 curValue interface{}
6 keyFunc func(interface{}) interface{}
7 input Iterator
8 groupKey interface{}
9 groupKeyOk bool
10 }
11
12 type Group interface {
13 Iterator
14 Key() interface{}
15 }
16
17 type group struct {
18 gb *groupBy
19 key interface{}
20 first bool
21 stopped bool
22 }
23
24 func (me *group) Stop() {
25 me.stopped = true
26 }
27
28 func (me *group) Next() (ok bool) {
29 if me.stopped {
30 return false
31 }
32 if me.first {
33 me.first = false
34 return true
35 }
36 me.gb.advance()
37 if !me.gb.curKeyOk || me.gb.curKey != me.key {
38 me.Stop()
39 return
40 }
41 ok = true
42 return
43 }
44
45 func (me group) Value() (ret interface{}) {
46 if me.stopped {
47 panic("iterator stopped")
48 }
49 ret = me.gb.curValue
50 return
51 }
52
53 func (me group) Key() interface{} {
54 return me.key
55 }
56
57 func (me *groupBy) advance() {
58 me.curKeyOk = me.input.Next()
59 if me.curKeyOk {
60 me.curValue = me.input.Value()
61 me.curKey = me.keyFunc(me.curValue)
62 }
63 }
64
65 func (me *groupBy) Next() (ok bool) {
66 for me.curKey == me.groupKey {
67 ok = me.input.Next()
68 if !ok {
69 return
70 }
71 me.curValue = me.input.Value()
72 me.curKey = me.keyFunc(me.curValue)
73 me.curKeyOk = true
74 }
75 me.groupKey = me.curKey
76 me.groupKeyOk = true
77 return true
78 }
79
80 func (me *groupBy) Value() (ret interface{}) {
81 return &group{me, me.groupKey, true, false}
82 }
83
84 func (me *groupBy) Stop() {
85 }
86
87 // Allows use of nil as a return from the key func.
88 var uniqueKey = new(int)
89
90 // Group by returns an iterator of iterators over the values of the input
91 // iterator that consecutively return the same value when input to the key
92 // function. Note that repeated calls to each value of the GroupBy Iterator
93 // does not return a new iterator over the values for that key.
94 func GroupBy(input Iterator, keyFunc func(interface{}) interface{}) Iterator {
95 if keyFunc == nil {
96 keyFunc = func(a interface{}) interface{} { return a }
97 }
98 return &groupBy{
99 input: input,
100 keyFunc: keyFunc,
101 groupKey: uniqueKey,
102 curKey: uniqueKey,
103 }
104 }
0 package iter
1
2 import (
3 "testing"
4
5 "github.com/stretchr/testify/assert"
6 "github.com/stretchr/testify/require"
7
8 "github.com/anacrolix/missinggo/slices"
9 )
10
11 func TestGroupByKey(t *testing.T) {
12 var ks []byte
13 gb := GroupBy(StringIterator("AAAABBBCCDAABBB"), nil)
14 for gb.Next() {
15 ks = append(ks, gb.Value().(Group).Key().(byte))
16 }
17 t.Log(ks)
18 require.EqualValues(t, "ABCDAB", ks)
19 }
20
21 func TestGroupByList(t *testing.T) {
22 var gs []string
23 gb := GroupBy(StringIterator("AAAABBBCCD"), nil)
24 for gb.Next() {
25 i := gb.Value().(Iterator)
26 var g string
27 for i.Next() {
28 g += string(i.Value().(byte))
29 }
30 gs = append(gs, g)
31 }
32 t.Log(gs)
33 }
34
35 func TestGroupByNiladicKey(t *testing.T) {
36 const s = "AAAABBBCCD"
37 gb := GroupBy(StringIterator(s), func(interface{}) interface{} { return nil })
38 gb.Next()
39 var ss []byte
40 g := ToSlice(ToFunc(gb.Value().(Iterator)))
41 slices.MakeInto(&ss, g)
42 assert.Equal(t, s, string(ss))
43 }
44
45 func TestNilEqualsNil(t *testing.T) {
46 assert.False(t, nil == uniqueKey)
47 }
0 package iter
1
2 func Head(n int, f Func) Func {
3 return func(cb Callback) {
4 if n <= 0 {
5 return
6 }
7 f(func(v interface{}) bool {
8 n--
9 if !cb(v) {
10 return false
11 }
12 return n > 0
13 })
14 }
15 }
0 package iter
1
2 import (
3 "sync"
4
5 "github.com/anacrolix/missinggo"
6 )
7
8 type Iterable interface {
9 Iter(Callback)
10 }
11
12 type iterator struct {
13 it Iterable
14 ch chan interface{}
15 value interface{}
16 ok bool
17 mu sync.Mutex
18 stopped missinggo.Event
19 }
20
21 func NewIterator(it Iterable) (ret *iterator) {
22 ret = &iterator{
23 it: it,
24 ch: make(chan interface{}),
25 }
26 go func() {
27 // Have to do this in a goroutine, because the interface is synchronous.
28 it.Iter(func(value interface{}) bool {
29 select {
30 case ret.ch <- value:
31 return true
32 case <-ret.stopped.LockedChan(&ret.mu):
33 return false
34 }
35 })
36 close(ret.ch)
37 ret.mu.Lock()
38 ret.stopped.Set()
39 ret.mu.Unlock()
40 }()
41 return
42 }
43
44 func (me *iterator) Value() interface{} {
45 if !me.ok {
46 panic("no value")
47 }
48 return me.value
49 }
50
51 func (me *iterator) Next() bool {
52 me.value, me.ok = <-me.ch
53 return me.ok
54 }
55
56 func (me *iterator) Stop() {
57 me.mu.Lock()
58 me.stopped.Set()
59 me.mu.Unlock()
60 }
61
62 func IterableAsSlice(it Iterable) (ret []interface{}) {
63 it.Iter(func(value interface{}) bool {
64 ret = append(ret, value)
65 return true
66 })
67 return
68 }
0 package iter
1
2 import "github.com/anacrolix/missinggo/slices"
3
4 type Iterator interface {
5 // Advances to the next value. Returns false if there are no more values.
6 // Must be called before the first value.
7 Next() bool
8 // Returns the current value. Should panic when the iterator is in an
9 // invalid state.
10 Value() interface{}
11 // Ceases iteration prematurely. This should occur implicitly if Next
12 // returns false.
13 Stop()
14 }
15
16 func ToFunc(it Iterator) Func {
17 return func(cb Callback) {
18 defer it.Stop()
19 for it.Next() {
20 if !cb(it.Value()) {
21 break
22 }
23 }
24 }
25 }
26
27 type sliceIterator struct {
28 slice []interface{}
29 value interface{}
30 ok bool
31 }
32
33 func (me *sliceIterator) Next() bool {
34 if len(me.slice) == 0 {
35 return false
36 }
37 me.value = me.slice[0]
38 me.slice = me.slice[1:]
39 me.ok = true
40 return true
41 }
42
43 func (me *sliceIterator) Value() interface{} {
44 if !me.ok {
45 panic("no value; call Next")
46 }
47 return me.value
48 }
49
50 func (me *sliceIterator) Stop() {}
51
52 func Slice(a []interface{}) Iterator {
53 return &sliceIterator{
54 slice: a,
55 }
56 }
57
58 func StringIterator(a string) Iterator {
59 return Slice(slices.ToEmptyInterface(a))
60 }
61
62 func ToSlice(f Func) (ret []interface{}) {
63 f(func(v interface{}) bool {
64 ret = append(ret, v)
65 return true
66 })
67 return
68 }
0 package iter
1
2 import (
3 "testing"
4
5 "github.com/stretchr/testify/require"
6 )
7
8 func TestIterator(t *testing.T) {
9 const s = "AAAABBBCCDAABBB"
10 si := StringIterator(s)
11 for i := range s {
12 require.True(t, si.Next())
13 require.Equal(t, s[i], si.Value().(byte))
14 }
15 require.False(t, si.Next())
16 }
0 package iter
1
2 import "math/rand"
3
4 type seq struct {
5 i []int
6 }
7
8 // Creates sequence of values from [0, n)
9 func newSeq(n int) seq {
10 return seq{make([]int, n, n)}
11 }
12
13 func (me seq) Index(i int) (ret int) {
14 ret = me.i[i]
15 if ret == 0 {
16 ret = i
17 }
18 return
19 }
20
21 func (me seq) Len() int {
22 return len(me.i)
23 }
24
25 // Remove the nth value from the sequence.
26 func (me *seq) DeleteIndex(index int) {
27 me.i[index] = me.Index(me.Len() - 1)
28 me.i = me.i[:me.Len()-1]
29 }
30
31 func ForPerm(n int, callback func(i int) (more bool)) bool {
32 s := newSeq(n)
33 for s.Len() > 0 {
34 r := rand.Intn(s.Len())
35 if !callback(s.Index(r)) {
36 return false
37 }
38 s.DeleteIndex(r)
39 }
40 return true
41 }
0 package iter
1
2 import "github.com/bradfitz/iter"
3
4 func N(n int) []struct{} {
5 return iter.N(n)
6 }
0 package missinggo
1
2 import (
3 "math/rand"
4 "time"
5 )
6
7 // Returns random duration in the range [average-plusMinus,
8 // average+plusMinus]. Negative plusMinus will likely panic. Be aware that if
9 // plusMinus >= average, you may get a zero or negative Duration. The
10 // distribution function is unspecified, in case I find a more appropriate one
11 // in the future.
12 func JitterDuration(average, plusMinus time.Duration) (ret time.Duration) {
13 ret = average - plusMinus
14 ret += time.Duration(rand.Int63n(2*int64(plusMinus) + 1))
15 return
16 }
0 package missinggo
1
2 import (
3 "testing"
4
5 "github.com/stretchr/testify/assert"
6 )
7
8 func TestJitterDuration(t *testing.T) {
9 assert.Zero(t, JitterDuration(0, 0))
10 assert.Panics(t, func() { JitterDuration(1, -1) })
11 }
0 package leaktest
1
2 import (
3 "runtime"
4 "testing"
5 "time"
6
7 "github.com/bradfitz/iter"
8 )
9
10 // Put defer GoroutineLeakCheck(t)() at the top of your test. Make sure the
11 // goroutine count is steady before your test begins.
12 func GoroutineLeakCheck(t testing.TB) func() {
13 if !testing.Verbose() {
14 return func() {}
15 }
16 numStart := runtime.NumGoroutine()
17 return func() {
18 var numNow int
19 wait := time.Millisecond
20 started := time.Now()
21 for range iter.N(10) { // 1 second
22 numNow = runtime.NumGoroutine()
23 if numNow <= numStart {
24 break
25 }
26 t.Logf("%d excess goroutines after %s", numNow-numStart, time.Since(started))
27 time.Sleep(wait)
28 wait *= 2
29 }
30 // I'd print stacks, or treat this as fatal, but I think
31 // runtime.NumGoroutine is including system routines for which we are
32 // not provided the stacks, and are spawned unpredictably.
33 t.Logf("have %d goroutines, started with %d", numNow, numStart)
34 // select {}
35 }
36 }
0 package missinggo
1
2 // Sets an upper bound on the len of b. max can be any type that will cast to
3 // int64.
4 func LimitLen(b []byte, max ...interface{}) []byte {
5 return b[:MinInt(len(b), max...)]
6 }
0 package mime
1
2 import "strings"
3
4 type Type struct {
5 Class string
6 Specific string
7 }
8
9 func (t Type) String() string {
10 return t.Class + "/" + t.Specific
11 }
12
13 func (t *Type) FromString(s string) {
14 ss := strings.SplitN(s, "/", 1)
15 t.Class = ss[0]
16 t.Specific = ss[1]
17 }
0 package missinggo
1
2 import "reflect"
3
4 func Max(_less interface{}, vals ...interface{}) interface{} {
5 ret := reflect.ValueOf(vals[0])
6 retType := ret.Type()
7 less := reflect.ValueOf(_less)
8 for _, _v := range vals[1:] {
9 v := reflect.ValueOf(_v).Convert(retType)
10 out := less.Call([]reflect.Value{ret, v})
11 if out[0].Bool() {
12 ret = v
13 }
14 }
15 return ret.Interface()
16 }
17
18 func MaxInt(first int64, rest ...interface{}) int64 {
19 return Max(func(l, r interface{}) bool {
20 return l.(int64) < r.(int64)
21 }, append([]interface{}{first}, rest...)...).(int64)
22 }
23
24 func MinInt(first interface{}, rest ...interface{}) int64 {
25 ret := reflect.ValueOf(first).Int()
26 for _, _i := range rest {
27 i := reflect.ValueOf(_i).Int()
28 if i < ret {
29 ret = i
30 }
31 }
32 return ret
33 }
0 package missinggo
1
2 import (
3 "sync"
4 "time"
5 )
6
7 // Monotonic time represents time since an arbitrary point in the past, where
8 // the concept of now is only ever moving in a positive direction.
9 type MonotonicTime struct {
10 skewedStdTime time.Time
11 }
12
13 func (me MonotonicTime) Sub(other MonotonicTime) time.Duration {
14 return me.skewedStdTime.Sub(other.skewedStdTime)
15 }
16
17 var (
18 stdNowFunc = time.Now
19 monotonicMu sync.Mutex
20 lastStdNow time.Time
21 monotonicSkew time.Duration
22 )
23
24 func skewedStdNow() time.Time {
25 monotonicMu.Lock()
26 defer monotonicMu.Unlock()
27 stdNow := stdNowFunc()
28 if !lastStdNow.IsZero() && stdNow.Before(lastStdNow) {
29 monotonicSkew += lastStdNow.Sub(stdNow)
30 }
31 lastStdNow = stdNow
32 return stdNow.Add(monotonicSkew)
33 }
34
35 // Consecutive calls always produce the same or greater time than previous
36 // calls.
37 func MonotonicNow() MonotonicTime {
38 return MonotonicTime{skewedStdNow()}
39 }
40
41 func MonotonicSince(since MonotonicTime) (ret time.Duration) {
42 return skewedStdNow().Sub(since.skewedStdTime)
43 }
0 package missinggo
1
2 import (
3 "testing"
4 "time"
5
6 "github.com/stretchr/testify/assert"
7 )
8
9 // Calls suite with the used time.Now function used by MonotonicNow replaced
10 // with stdNow for the duration of the call.
11 func withCustomStdNow(stdNow func() time.Time, suite func()) {
12 oldStdNow := stdNowFunc
13 oldSkew := monotonicSkew
14 defer func() {
15 stdNowFunc = oldStdNow
16 monotonicSkew = oldSkew
17 }()
18 stdNowFunc = stdNow
19 suite()
20 }
21
22 // Returns a time.Now-like function that walks seq returning time.Unix(0,
23 // seq[i]) in successive calls.
24 func stdNowSeqFunc(seq []int64) func() time.Time {
25 var i int
26 return func() time.Time {
27 defer func() { i++ }()
28 return time.Unix(0, seq[i])
29 }
30 }
31
32 func TestMonotonicTime(t *testing.T) {
33 started := MonotonicNow()
34 withCustomStdNow(stdNowSeqFunc([]int64{2, 1, 3, 3, 2, 3}), func() {
35 i0 := MonotonicNow() // 0
36 i1 := MonotonicNow() // 1
37 assert.EqualValues(t, 0, i0.Sub(i1))
38 assert.EqualValues(t, 2, MonotonicSince(i0)) // 2
39 assert.EqualValues(t, 2, MonotonicSince(i1)) // 3
40 i4 := MonotonicNow()
41 assert.EqualValues(t, 2, i4.Sub(i0))
42 assert.EqualValues(t, 2, i4.Sub(i1))
43 i5 := MonotonicNow()
44 assert.EqualValues(t, 3, i5.Sub(i0))
45 assert.EqualValues(t, 3, i5.Sub(i1))
46 assert.EqualValues(t, 1, i5.Sub(i4))
47 })
48 // Ensure that skew and time function are restored correctly and within
49 // reasonable bounds.
50 assert.True(t, MonotonicSince(started) >= 0 && MonotonicSince(started) < time.Second)
51 }
0 package missinggo
1
2 type (
3 SameLessFunc func() (same, less bool)
4 MultiLess struct {
5 ok bool
6 less bool
7 }
8 )
9
10 func (me *MultiLess) Less() bool {
11 return me.ok && me.less
12 }
13
14 func (me *MultiLess) Final() bool {
15 if !me.ok {
16 panic("undetermined")
17 }
18 return me.less
19 }
20
21 func (me *MultiLess) FinalOk() (left, ok bool) {
22 return me.less, me.ok
23 }
24
25 func (me *MultiLess) Next(f SameLessFunc) {
26 if me.ok {
27 return
28 }
29 same, less := f()
30 if same {
31 return
32 }
33 me.ok = true
34 me.less = less
35 }
36
37 func (me *MultiLess) StrictNext(same, less bool) {
38 if me.ok {
39 return
40 }
41 me.Next(func() (bool, bool) { return same, less })
42 }
43
44 func (me *MultiLess) NextBool(l, r bool) {
45 me.StrictNext(l == r, l)
46 }
0 package missinggo
1
2 import "strings"
3
4 func IsAddrInUse(err error) bool {
5 return strings.Contains(err.Error(), "address already in use")
6 }
0 package oauth
1
2 type Endpoint struct {
3 AuthURL string
4 TokenURL string
5 ProfileURL string
6 }
7
8 var (
9 FacebookEndpoint = Endpoint{
10 AuthURL: "https://www.facebook.com/dialog/oauth",
11 TokenURL: "https://graph.facebook.com/v2.3/oauth/access_token",
12 ProfileURL: "https://graph.facebook.com/me",
13 }
14 GoogleEndpoint = Endpoint{
15 AuthURL: "https://accounts.google.com/o/oauth2/auth",
16 TokenURL: "https://accounts.google.com/o/oauth2/token",
17 ProfileURL: "https://www.googleapis.com/oauth2/v2/userinfo",
18 }
19 PatreonEndpoint = Endpoint{
20 AuthURL: "https://www.patreon.com/oauth2/authorize",
21 TokenURL: "https://api.patreon.com/oauth2/token",
22 ProfileURL: "https://api.patreon.com/oauth2/api/current_user",
23 }
24 )
0 package oauth
1
2 import (
3 "bytes"
4 "encoding/json"
5 "fmt"
6 "io"
7 "net/http"
8 "net/url"
9
10 "github.com/anacrolix/missinggo/patreon"
11 )
12
13 func SimpleParser(r *http.Response) (UserProfile, error) {
14 var sup simpleUserProfile
15 err := json.NewDecoder(r.Body).Decode(&sup)
16 return sup, err
17 }
18
19 type Provider struct {
20 Client *Client
21 Endpoint *Endpoint
22 }
23
24 type Wrapper struct {
25 Scope string
26 Provider Provider
27 ProfileParser func(*http.Response) (UserProfile, error)
28 }
29
30 func (me Wrapper) GetAuthURL(redirectURI, state string) string {
31 return me.Provider.GetAuthURL(redirectURI, state, me.Scope)
32 }
33
34 func (me Wrapper) FetchUser(accessToken string) (up UserProfile, err error) {
35 resp, err := me.Provider.FetchUser(accessToken)
36 if err != nil {
37 return
38 }
39 defer resp.Body.Close()
40 return me.ProfileParser(resp)
41 }
42
43 type Client struct {
44 ID string
45 Secret string
46 }
47
48 func (me *Provider) GetAuthURL(redirectURI, state, scope string) string {
49 params := []string{
50 "client_id", me.Client.ID,
51 "response_type", "code",
52 "redirect_uri", redirectURI,
53 "state", state,
54 // This will ask again for the given scopes if they're not provided.
55 "auth_type", "rerequest",
56 }
57 if scope != "" {
58 params = append(params, "scope", scope)
59 }
60 return renderEndpointURL(me.Endpoint.AuthURL, params...)
61 }
62
63 func (me *Provider) ExchangeCode(code string, redirectURI string) (accessToken string, err error) {
64 v := url.Values{
65 "client_id": {me.Client.ID},
66 "redirect_uri": {redirectURI},
67 "client_secret": {me.Client.Secret},
68 "code": {code},
69 "grant_type": {"authorization_code"},
70 }
71 resp, err := http.Post(me.Endpoint.TokenURL, "application/x-www-form-urlencoded", bytes.NewBufferString(v.Encode()))
72 if err != nil {
73 return
74 }
75 var buf bytes.Buffer
76 io.Copy(&buf, resp.Body)
77 resp.Body.Close()
78 var msg map[string]interface{}
79 err = json.NewDecoder(&buf).Decode(&msg)
80 if err != nil {
81 return
82 }
83 defer func() {
84 r := recover()
85 if r == nil {
86 return
87 }
88 err = fmt.Errorf("bad access_token field in %q: %s", msg, r)
89 }()
90 accessToken = msg["access_token"].(string)
91 return
92 }
93
94 type simpleUserProfile struct {
95 Id string `json:"id"`
96 EmailField string `json:"email"`
97 }
98
99 var _ UserProfile = simpleUserProfile{}
100
101 func (me simpleUserProfile) IsEmailVerified() bool {
102 return true
103 }
104
105 func (me simpleUserProfile) Email() string {
106 return me.EmailField
107 }
108
109 type UserProfile interface {
110 IsEmailVerified() bool
111 Email() string
112 }
113
114 // TODO: Allow fields to be specified.
115 func (me *Provider) FetchUser(accessToken string) (*http.Response, error) {
116 return http.Get(renderEndpointURL(
117 me.Endpoint.ProfileURL,
118 "fields", "email",
119 "access_token", accessToken,
120 ))
121 }
122
123 type PatreonUserProfile struct {
124 Data patreon.ApiUser `json:"data"`
125 }
126
127 var _ UserProfile = PatreonUserProfile{}
128
129 func (me PatreonUserProfile) IsEmailVerified() bool {
130 return me.Data.Attributes.IsEmailVerified
131 }
132
133 func (me PatreonUserProfile) Email() string {
134 return me.Data.Attributes.Email
135 }
136
137 func renderEndpointURL(endpoint string, params ...string) string {
138 u, err := url.Parse(endpoint)
139 if err != nil {
140 panic(err)
141 }
142 v := make(url.Values, len(params)/2)
143 for i := 0; i < len(params); i += 2 {
144 v.Set(params[i], params[i+1])
145 }
146 u.RawQuery = v.Encode()
147 return u.String()
148 }
0 package oauth
1
2 import (
3 "encoding/json"
4 "testing"
5
6 "github.com/stretchr/testify/assert"
7 "github.com/stretchr/testify/require"
8 )
9
10 func TestDecodePatreonUserProfile(t *testing.T) {
11 var pup PatreonUserProfile
12 err := json.Unmarshal([]byte(
13 `{
14 "data": {
15 "attributes": {
16 "about": null,
17 "created": "2017-05-12T12:49:31+00:00",
18 "discord_id": null,
19 "email": "anacrolix@gmail.com",
20 "facebook": null,
21 "facebook_id": "10155425587018447",
22 "first_name": "Matt",
23 "full_name": "Matt Joiner",
24 "gender": 0,
25 "has_password": false,
26 "image_url": "https://c3.patreon.com/2/patreon-user/wS20eHsYaLMqJeDyL5wyK0egvcXDRNdT28JvjeREJ5T80te19Cmn1YZxZyzd2qab.jpeg?t=2145916800&w=400&v=506YL5JlU7aaQH-QyEaRXyWoXFs4ia-vcSjjZuv-dXY%3D",
27 "is_deleted": false,
28 "is_email_verified": true,
29 "is_nuked": false,
30 "is_suspended": false,
31 "last_name": "Joiner",
32 "social_connections": {
33 "deviantart": null,
34 "discord": null,
35 "facebook": null,
36 "spotify": null,
37 "twitch": null,
38 "twitter": null,
39 "youtube": null
40 },
41 "thumb_url": "https://c3.patreon.com/2/patreon-user/wS20eHsYaLMqJeDyL5wyK0egvcXDRNdT28JvjeREJ5T80te19Cmn1YZxZyzd2qab.jpeg?h=100&t=2145916800&w=100&v=SI72bzI4XB5mX0dyfqeZ-Nn4BNTz9FYRSgZ8pLipARg%3D",
42 "twitch": null,
43 "twitter": null,
44 "url": "https://www.patreon.com/anacrolix",
45 "vanity": "anacrolix",
46 "youtube": null
47 },
48 "id": "6126463",
49 "relationships": {
50 "pledges": {
51 "data": []
52 }
53 },
54 "type": "user"
55 },
56 "links": {
57 "self": "https://api.patreon.com/user/6126463"
58 }
59 }`), &pup)
60 require.NoError(t, err)
61 assert.EqualValues(t, "anacrolix@gmail.com", pup.Data.Attributes.Email)
62 assert.True(t, pup.Data.Attributes.IsEmailVerified)
63 }
0 package missinggo
1
2 import (
3 "os"
4 )
5
6 const O_ACCMODE = os.O_RDONLY | os.O_WRONLY | os.O_RDWR
0 package orderedmap
1
2 import (
3 "github.com/anacrolix/missinggo/iter"
4 "github.com/google/btree"
5 )
6
7 type GoogleBTree struct {
8 bt *btree.BTree
9 lesser func(l, r interface{}) bool
10 }
11
12 type googleBTreeItem struct {
13 less func(l, r interface{}) bool
14 key interface{}
15 value interface{}
16 }
17
18 func (me googleBTreeItem) Less(right btree.Item) bool {
19 return me.less(me.key, right.(*googleBTreeItem).key)
20 }
21
22 func NewGoogleBTree(lesser func(l, r interface{}) bool) *GoogleBTree {
23 return &GoogleBTree{
24 bt: btree.New(32),
25 lesser: lesser,
26 }
27 }
28
29 func (me *GoogleBTree) Set(key interface{}, value interface{}) {
30 me.bt.ReplaceOrInsert(&googleBTreeItem{me.lesser, key, value})
31 }
32
33 func (me *GoogleBTree) Get(key interface{}) interface{} {
34 ret, _ := me.GetOk(key)
35 return ret
36 }
37
38 func (me *GoogleBTree) GetOk(key interface{}) (interface{}, bool) {
39 item := me.bt.Get(&googleBTreeItem{me.lesser, key, nil})
40 if item == nil {
41 return nil, false
42 }
43 return item.(*googleBTreeItem).value, true
44 }
45
46 type googleBTreeIter struct {
47 i btree.Item
48 bt *btree.BTree
49 }
50
51 func (me *googleBTreeIter) Next() bool {
52 if me.bt == nil {
53 return false
54 }
55 if me.i == nil {
56 me.bt.Ascend(func(i btree.Item) bool {
57 me.i = i
58 return false
59 })
60 } else {
61 var n int
62 me.bt.AscendGreaterOrEqual(me.i, func(i btree.Item) bool {
63 n++
64 if n == 1 {
65 return true
66 }
67 me.i = i
68 return false
69 })
70 if n != 2 {
71 me.i = nil
72 }
73 }
74 return me.i != nil
75 }
76
77 func (me *googleBTreeIter) Value() interface{} {
78 return me.i.(*googleBTreeItem).value
79 }
80
81 func (me *googleBTreeIter) Stop() {
82 me.bt = nil
83 me.i = nil
84 }
85
86 func (me *GoogleBTree) Iter(f iter.Callback) {
87 me.bt.Ascend(func(i btree.Item) bool {
88 return f(i.(*googleBTreeItem).key)
89 })
90 }
91
92 func (me *GoogleBTree) Unset(key interface{}) {
93 me.bt.Delete(&googleBTreeItem{me.lesser, key, nil})
94 }
95
96 func (me *GoogleBTree) Len() int {
97 return me.bt.Len()
98 }
0 package orderedmap
1
2 import "github.com/anacrolix/missinggo/iter"
3
4 func New(lesser func(l, r interface{}) bool) OrderedMap {
5 return NewGoogleBTree(lesser)
6 }
7
8 type OrderedMap interface {
9 Get(key interface{}) interface{}
10 GetOk(key interface{}) (interface{}, bool)
11 iter.Iterable
12 Set(key, value interface{})
13 Unset(key interface{})
14 Len() int
15 }
0 package orderedmap
1
2 import (
3 "testing"
4
5 "github.com/anacrolix/missinggo/iter"
6 "github.com/stretchr/testify/assert"
7 )
8
9 func slice(om OrderedMap) (ret []interface{}) {
10 om.Iter(func(i interface{}) bool {
11 ret = append(ret, om.Get(i))
12 return true
13 })
14 return
15 }
16
17 func TestSimple(t *testing.T) {
18 om := New(func(l, r interface{}) bool {
19 return l.(int) < r.(int)
20 })
21 om.Set(3, 1)
22 om.Set(2, 2)
23 om.Set(1, 3)
24 assert.EqualValues(t, []interface{}{3, 2, 1}, slice(om))
25 om.Set(3, 2)
26 om.Unset(2)
27 assert.EqualValues(t, []interface{}{3, 2}, slice(om))
28 om.Set(-1, 4)
29 assert.EqualValues(t, []interface{}{4, 3, 2}, slice(om))
30 }
31
32 func TestIterEmpty(t *testing.T) {
33 om := New(nil)
34 it := iter.NewIterator(om)
35 assert.Panics(t, func() { it.Value() })
36 assert.False(t, it.Next())
37 it.Stop()
38 }
0 package orderedmap
1
2 import "github.com/ryszard/goskiplist/skiplist"
3
4 type skiplistOrderedMap struct {
5 sl *skiplist.SkipList
6 }
7
8 func NewSkipList(lesser func(l, r interface{}) bool) *skiplistOrderedMap {
9 return &skiplistOrderedMap{skiplist.NewCustomMap(lesser)}
10 }
11
12 func (me *skiplistOrderedMap) Set(key interface{}, value interface{}) {
13 me.sl.Set(key, value)
14 }
15
16 func (me *skiplistOrderedMap) Get(key interface{}) interface{} {
17 if me == nil {
18 return nil
19 }
20 ret, _ := me.sl.Get(key)
21 return ret
22 }
23
24 func (me *skiplistOrderedMap) GetOk(key interface{}) (interface{}, bool) {
25 if me == nil {
26 return nil, false
27 }
28 return me.sl.Get(key)
29 }
30
31 type Iter struct {
32 it skiplist.Iterator
33 }
34
35 func (me *Iter) Next() bool {
36 if me == nil {
37 return false
38 }
39 return me.it.Next()
40 }
41
42 func (me *Iter) Value() interface{} {
43 return me.it.Value()
44 }
45
46 func (me *skiplistOrderedMap) Iter() *Iter {
47 if me == nil {
48 return nil
49 }
50 return &Iter{me.sl.Iterator()}
51 }
52
53 func (me *skiplistOrderedMap) Unset(key interface{}) {
54 if me == nil {
55 return
56 }
57 me.sl.Delete(key)
58 }
59
60 func (me *skiplistOrderedMap) Len() int {
61 if me.sl == nil {
62 return 0
63 }
64 return me.sl.Len()
65 }
0 package panicif
1
2 func NotNil(x interface{}) {
3 if x != nil {
4 panic(x)
5 }
6 }
0 package missinggo
1
2 import (
3 "os"
4 "path"
5 )
6
7 // Splits the pathname p into Root and Ext, such that Root+Ext==p.
8 func PathSplitExt(p string) (ret struct {
9 Root, Ext string
10 }) {
11 ret.Ext = path.Ext(p)
12 ret.Root = p[:len(p)-len(ret.Ext)]
13 return
14 }
15
16 func FilePathExists(p string) bool {
17 _, err := os.Stat(p)
18 return err == nil
19 }
0 package missinggo
1
2 import (
3 "fmt"
4 )
5
6 func ExamplePathSplitExt() {
7 fmt.Printf("%q\n", PathSplitExt(".cshrc"))
8 fmt.Printf("%q\n", PathSplitExt("dir/a.ext"))
9 fmt.Printf("%q\n", PathSplitExt("dir/.rc"))
10 fmt.Printf("%q\n", PathSplitExt("home/.secret/file"))
11 // Output:
12 // {"" ".cshrc"}
13 // {"dir/a" ".ext"}
14 // {"dir/" ".rc"}
15 // {"home/.secret/file" ""}
16 }
0 package patreon
1
2 import (
3 "encoding/json"
4 "fmt"
5 "io"
6 "net/http"
7 )
8
9 type PledgesApiResponse struct {
10 Pledges []struct {
11 Attributes struct {
12 AmountCents int `json:"amount_cents"`
13 } `json:"attributes"`
14 Relationships struct {
15 Patron struct {
16 Data struct {
17 Id Id `json:"id"`
18 } `json:"data"`
19 } `json:"patron"`
20 } `json:"relationships"`
21 } `json:"data"`
22 Included []ApiUser `json:"included"`
23 }
24
25 type ApiUser struct {
26 Attributes struct {
27 Email string `json:"email"`
28 IsEmailVerified bool `json:"is_email_verified"`
29 } `json:"attributes"`
30 Id Id `json:"id"`
31 }
32
33 type Pledge struct {
34 Email string
35 EmailVerified bool
36 AmountCents int
37 }
38
39 type Id string
40
41 func makeUserMap(par *PledgesApiResponse) (ret map[Id]*ApiUser) {
42 ret = make(map[Id]*ApiUser, len(par.Included))
43 for i := range par.Included {
44 au := &par.Included[i]
45 ret[au.Id] = au
46 }
47 return
48 }
49
50 func ParsePledgesApiResponse(r io.Reader) (ps []Pledge, err error) {
51 var ar PledgesApiResponse
52 err = json.NewDecoder(r).Decode(&ar)
53 if err != nil {
54 return
55 }
56 userMap := makeUserMap(&ar)
57 for _, p := range ar.Pledges {
58 u := userMap[p.Relationships.Patron.Data.Id]
59 ps = append(ps, Pledge{
60 Email: u.Attributes.Email,
61 EmailVerified: u.Attributes.IsEmailVerified,
62 AmountCents: p.Attributes.AmountCents,
63 })
64 }
65 return
66 }
67
68 func GetCampaignPledges(campaign Id, userAccessToken string) (ret []Pledge, err error) {
69 req, err := http.NewRequest("GET", fmt.Sprintf("https://api.patreon.com/oauth2/api/campaigns/%s/pledges", campaign), nil)
70 if err != nil {
71 return
72 }
73 req.Header.Set("Authorization", "Bearer "+userAccessToken)
74 resp, err := http.DefaultClient.Do(req)
75 if err != nil {
76 return
77 }
78 defer resp.Body.Close()
79 if resp.StatusCode != 200 {
80 err = fmt.Errorf("got http response code %d", resp.StatusCode)
81 return
82 }
83 return ParsePledgesApiResponse(resp.Body)
84 }
0 package patreon
1
2 import (
3 "os"
4 "testing"
5
6 "github.com/stretchr/testify/assert"
7 "github.com/stretchr/testify/require"
8 )
9
10 func TestParsePledges(t *testing.T) {
11 f, err := os.Open("testdata/pledges")
12 require.NoError(t, err)
13 defer f.Close()
14 ps, err := ParsePledgesApiResponse(f)
15 require.NoError(t, err)
16 assert.EqualValues(t, []Pledge{{
17 Email: "yonhyaro@gmail.com",
18 EmailVerified: true,
19 AmountCents: 200,
20 }}, ps)
21 }
0 {
1 "data": [
2 {
3 "attributes": {
4 "amount_cents": 200,
5 "created_at": "2017-06-25T06:31:08.327895+00:00",
6 "declined_since": null,
7 "patron_pays_fees": false,
8 "pledge_cap_cents": 200
9 },
10 "id": "6481585",
11 "relationships": {
12 "address": {
13 "data": null
14 },
15 "creator": {
16 "data": {
17 "id": "6126463",
18 "type": "user"
19 },
20 "links": {
21 "related": "https://api.patreon.com/user/6126463"
22 }
23 },
24 "patron": {
25 "data": {
26 "id": "6649264",
27 "type": "user"
28 },
29 "links": {
30 "related": "https://api.patreon.com/user/6649264"
31 }
32 },
33 "reward": {
34 "data": {
35 "id": "1683378",
36 "type": "reward"
37 },
38 "links": {
39 "related": "https://api.patreon.com/rewards/1683378"
40 }
41 }
42 },
43 "type": "pledge"
44 }
45 ],
46 "included": [
47 {
48 "attributes": {
49 "about": null,
50 "created": "2017-06-25T06:27:33+00:00",
51 "email": "yonhyaro@gmail.com",
52 "facebook": null,
53 "first_name": "Ben",
54 "full_name": "Ben Idris",
55 "gender": 0,
56 "image_url": "https://c3.patreon.com/2/patreon-user/oTXlvlk326g1M0aDepVz1WMmE4Tq6eGxlQHmDIeuYA5MAuPUz3oDar8XiAYsJTsF.jpeg?t=2145916800&w=400&v=oERJD4kyEAN7aSeOUX8Ki0p1iyVIbKcJ3pudh7QnZK0%3D",
57 "is_email_verified": true,
58 "last_name": "Idris",
59 "social_connections": {
60 "deviantart": null,
61 "discord": null,
62 "facebook": null,
63 "spotify": null,
64 "twitch": null,
65 "twitter": null,
66 "youtube": null
67 },
68 "thumb_url": "https://c3.patreon.com/2/patreon-user/oTXlvlk326g1M0aDepVz1WMmE4Tq6eGxlQHmDIeuYA5MAuPUz3oDar8XiAYsJTsF.jpeg?h=100&t=2145916800&w=100&v=jXL0mDWCvzTCWk544GNyJ7IgoTIJR2gGAuLnJKcTnAI%3D",
69 "twitch": null,
70 "twitter": null,
71 "url": "https://www.patreon.com/user?u=6649264",
72 "vanity": null,
73 "youtube": null
74 },
75 "id": "6649264",
76 "relationships": {
77 "campaign": {
78 "data": null
79 }
80 },
81 "type": "user"
82 },
83 {
84 "attributes": {
85 "amount": 200,
86 "amount_cents": 200,
87 "created_at": "2017-05-12T13:31:36.623424+00:00",
88 "deleted_at": null,
89 "description": "<ul><li>Elevated quota on test servers</li></ul>",
90 "discord_role_ids": null,
91 "edited_at": "2017-06-13T13:09:39.303442+00:00",
92 "image_url": null,
93 "patron_count": 1,
94 "post_count": null,
95 "published": true,
96 "published_at": "2017-05-12T13:31:36.623424+00:00",
97 "remaining": 19,
98 "requires_shipping": false,
99 "title": "Fan",
100 "unpublished_at": null,
101 "url": "/bePatron?c=925561&rid=1683378",
102 "user_limit": 20
103 },
104 "id": "1683378",
105 "relationships": {
106 "campaign": {
107 "data": {
108 "id": "925561",
109 "type": "campaign"
110 },
111 "links": {
112 "related": "https://api.patreon.com/campaigns/925561"
113 }
114 },
115 "creator": {
116 "data": {
117 "id": "6126463",
118 "type": "user"
119 },
120 "links": {
121 "related": "https://api.patreon.com/user/6126463"
122 }
123 }
124 },
125 "type": "reward"
126 },
127 {
128 "attributes": {
129 "about": "",
130 "created": "2017-05-12T12:49:31+00:00",
131 "discord_id": null,
132 "email": "anacrolix@gmail.com",
133 "facebook": null,
134 "facebook_id": "10155425587018447",
135 "first_name": "Matt",
136 "full_name": "Matt Joiner",
137 "gender": 0,
138 "has_password": false,
139 "image_url": "https://c3.patreon.com/2/patreon-user/wS20eHsYaLMqJeDyL5wyK0egvcXDRNdT28JvjeREJ5T80te19Cmn1YZxZyzd2qab.jpeg?t=2145916800&w=400&v=506YL5JlU7aaQH-QyEaRXyWoXFs4ia-vcSjjZuv-dXY%3D",
140 "is_deleted": false,
141 "is_email_verified": true,
142 "is_nuked": false,
143 "is_suspended": false,
144 "last_name": "Joiner",
145 "social_connections": {
146 "deviantart": null,
147 "discord": null,
148 "facebook": null,
149 "spotify": null,
150 "twitch": null,
151 "twitter": null,
152 "youtube": null
153 },
154 "thumb_url": "https://c3.patreon.com/2/patreon-user/wS20eHsYaLMqJeDyL5wyK0egvcXDRNdT28JvjeREJ5T80te19Cmn1YZxZyzd2qab.jpeg?h=100&t=2145916800&w=100&v=SI72bzI4XB5mX0dyfqeZ-Nn4BNTz9FYRSgZ8pLipARg%3D",
155 "twitch": null,
156 "twitter": null,
157 "url": "https://www.patreon.com/anacrolix",
158 "vanity": "anacrolix",
159 "youtube": null
160 },
161 "id": "6126463",
162 "relationships": {
163 "campaign": {
164 "data": {
165 "id": "925561",
166 "type": "campaign"
167 },
168 "links": {
169 "related": "https://api.patreon.com/campaigns/925561"
170 }
171 }
172 },
173 "type": "user"
174 },
175 {
176 "attributes": {
177 "created_at": "2017-05-12T12:49:39+00:00",
178 "creation_count": 1,
179 "creation_name": "useful websites and technologies",
180 "discord_server_id": null,
181 "display_patron_goals": false,
182 "earnings_visibility": "private",
183 "image_small_url": null,
184 "image_url": null,
185 "is_charged_immediately": false,
186 "is_monthly": true,
187 "is_nsfw": true,
188 "is_plural": false,
189 "main_video_embed": null,
190 "main_video_url": null,
191 "one_liner": null,
192 "outstanding_payment_amount_cents": 0,
193 "patron_count": 1,
194 "pay_per_name": "month",
195 "pledge_sum": 180,
196 "pledge_url": "/bePatron?c=925561",
197 "published_at": "2017-06-13T05:35:54+00:00",
198 "summary": null,
199 "thanks_embed": null,
200 "thanks_msg": "Thank you for helping me pursue my interests. I hope you enjoy the results of my research.",
201 "thanks_video_url": null
202 },
203 "id": "925561",
204 "relationships": {
205 "creator": {
206 "data": {
207 "id": "6126463",
208 "type": "user"
209 },
210 "links": {
211 "related": "https://api.patreon.com/user/6126463"
212 }
213 },
214 "goals": {
215 "data": [
216 {
217 "id": "866143",
218 "type": "goal"
219 },
220 {
221 "id": "866144",
222 "type": "goal"
223 }
224 ]
225 },
226 "rewards": {
227 "data": [
228 {
229 "id": "-1",
230 "type": "reward"
231 },
232 {
233 "id": "0",
234 "type": "reward"
235 },
236 {
237 "id": "1683378",
238 "type": "reward"
239 }
240 ]
241 }
242 },
243 "type": "campaign"
244 },
245 {
246 "attributes": {
247 "amount": 0,
248 "amount_cents": 0,
249 "created_at": null,
250 "description": "Everyone",
251 "remaining": 0,
252 "requires_shipping": false,
253 "type": "reward",
254 "url": null,
255 "user_limit": null
256 },
257 "id": "-1",
258 "relationships": {
259 "creator": {
260 "data": {
261 "id": "6126463",
262 "type": "user"
263 },
264 "links": {
265 "related": "https://api.patreon.com/user/6126463"
266 }
267 }
268 },
269 "type": "reward"
270 },
271 {
272 "attributes": {
273 "amount": 1,
274 "amount_cents": 1,
275 "created_at": null,
276 "description": "Patrons Only",
277 "remaining": 0,
278 "requires_shipping": false,
279 "type": "reward",
280 "url": null,
281 "user_limit": null
282 },
283 "id": "0",
284 "relationships": {
285 "creator": {
286 "data": {
287 "id": "6126463",
288 "type": "user"
289 },
290 "links": {
291 "related": "https://api.patreon.com/user/6126463"
292 }
293 }
294 },
295 "type": "reward"
296 },
297 {
298 "attributes": {
299 "amount": 2000,
300 "amount_cents": 2000,
301 "completed_percentage": 9,
302 "created_at": "2017-06-13T05:41:54+00:00",
303 "description": "Cover minimal server costs",
304 "reached_at": null,
305 "title": ""
306 },
307 "id": "866143",
308 "type": "goal"
309 },
310 {
311 "attributes": {
312 "amount": 5000,
313 "amount_cents": 5000,
314 "completed_percentage": 3,
315 "created_at": "2017-06-13T05:41:54+00:00",
316 "description": "Improve webserver latency",
317 "reached_at": null,
318 "title": ""
319 },
320 "id": "866144",
321 "type": "goal"
322 }
323 ],
324 "links": {
325 "first": "https://api.patreon.com/oauth2/api/campaigns/925561/pledges?page%5Bcount%5D=10&sort=created"
326 },
327 "meta": {
328 "count": 1
329 }
330 }
0 package perf
1
2 import (
3 "math"
4 "sync"
5 "time"
6 )
7
8 type Event struct {
9 Mu sync.RWMutex
10 Count int64
11 Total time.Duration
12 Min time.Duration
13 Max time.Duration
14 }
15
16 func (e *Event) Add(t time.Duration) {
17 e.Mu.Lock()
18 defer e.Mu.Unlock()
19 if t > e.Max {
20 e.Max = t
21 }
22 if t < e.Min {
23 e.Min = t
24 }
25 e.Count++
26 e.Total += t
27 }
28
29 func (e *Event) MeanTime() time.Duration {
30 e.Mu.RLock()
31 defer e.Mu.RUnlock()
32 return e.Total / time.Duration(e.Count)
33 }
34
35 func (e *Event) Init() {
36 e.Min = math.MaxInt64
37 }
0 package perf
1
2 import (
3 "fmt"
4 "io"
5 "net/http"
6 "sort"
7 "sync"
8 "text/tabwriter"
9 )
10
11 var (
12 mu sync.RWMutex
13 events = map[string]*Event{}
14 )
15
16 func init() {
17 http.HandleFunc("/debug/perf", func(w http.ResponseWriter, r *http.Request) {
18 w.Header().Set("Content-Type", "text/plain; charset=UTF-8")
19 WriteEventsTable(w)
20 })
21 }
22
23 func WriteEventsTable(w io.Writer) {
24 tw := tabwriter.NewWriter(w, 0, 0, 2, ' ', 0)
25 fmt.Fprint(tw, "description\ttotal\tcount\tmin\tmean\tmax\n")
26 type t struct {
27 d string
28 e Event
29 }
30 mu.RLock()
31 es := make([]t, 0, len(events))
32 for d, e := range events {
33 e.Mu.RLock()
34 es = append(es, t{d, *e})
35 e.Mu.RUnlock()
36 }
37 mu.RUnlock()
38 sort.Slice(es, func(i, j int) bool {
39 return es[i].e.Total > es[j].e.Total
40 })
41 for _, el := range es {
42 e := el.e
43 fmt.Fprintf(tw, "%s\t%v\t%v\t%v\t%v\t%v\n", el.d, e.Total, e.Count, e.Min, e.MeanTime(), e.Max)
44 }
45 tw.Flush()
46 }
0 package perf
1
2 import (
3 "sync"
4
5 "github.com/anacrolix/missinggo"
6 )
7
8 type TimedLocker struct {
9 L sync.Locker
10 Desc string
11 }
12
13 func (me *TimedLocker) Lock() {
14 tr := NewTimer()
15 me.L.Lock()
16 tr.Mark(me.Desc)
17 }
18
19 func (me *TimedLocker) Unlock() {
20 me.L.Unlock()
21 }
22
23 type TimedRWLocker struct {
24 RWL missinggo.RWLocker
25 WriteDesc string
26 ReadDesc string
27 }
28
29 func (me *TimedRWLocker) Lock() {
30 tr := NewTimer()
31 me.RWL.Lock()
32 tr.Mark(me.WriteDesc)
33 }
34
35 func (me *TimedRWLocker) Unlock() {
36 me.RWL.Unlock()
37 }
38
39 func (me *TimedRWLocker) RLock() {
40 tr := NewTimer()
41 me.RWL.RLock()
42 tr.Mark(me.ReadDesc)
43 }
44
45 func (me *TimedRWLocker) RUnlock() {
46 me.RWL.RUnlock()
47 }
0 package perf
1
2 import (
3 "io/ioutil"
4 "strconv"
5 "testing"
6
7 _ "github.com/anacrolix/envpprof"
8
9 "github.com/bradfitz/iter"
10 )
11
12 func TestTimer(t *testing.T) {
13 tr := NewTimer()
14 tr.Mark("hiyo")
15 tr.Mark("hiyo")
16 WriteEventsTable(ioutil.Discard)
17 }
18
19 func BenchmarkStopWarm(b *testing.B) {
20 tr := NewTimer()
21 for range iter.N(b.N) {
22 tr.Mark("a")
23 }
24 }
25
26 func BenchmarkStopCold(b *testing.B) {
27 tr := NewTimer()
28 for i := range iter.N(b.N) {
29 tr.Mark(strconv.FormatInt(int64(i), 10))
30 }
31 }
0 package perf
1
2 import (
3 "runtime"
4 )
5
6 func ScopeTimer(opts ...timerOpt) func() {
7 t := NewTimer(CallerName(1))
8 return func() { t.Mark("returned") }
9 }
10
11 func ScopeTimerOk(ok *bool) func() {
12 t := NewTimer(CallerName(1))
13 return func() { t.MarkOk(*ok) }
14 }
15
16 func ScopeTimerErr(err *error) func() {
17 t := NewTimer(CallerName(1))
18 return func() {
19 r := recover()
20 if r != nil {
21 t.Mark("panic")
22 panic(r)
23 }
24 t.MarkErr(*err)
25 }
26 }
27
28 func CallerName(skip int) timerOpt {
29 return Name(getCallerName(skip))
30 }
31
32 func getCallerName(skip int) string {
33 var pc [1]uintptr
34 runtime.Callers(3+skip, pc[:])
35 fs := runtime.CallersFrames(pc[:])
36 f, _ := fs.Next()
37 return f.Func.Name()
38 }
0 package perf
1
2 import (
3 "log"
4 "runtime"
5 "time"
6 )
7
8 type Timer struct {
9 started time.Time
10 log bool
11 name string
12 marked bool
13 }
14
15 func NewTimer(opts ...timerOpt) (t *Timer) {
16 t = &Timer{
17 started: time.Now(),
18 }
19 for _, o := range opts {
20 o(t)
21 }
22 if t.log && t.name != "" {
23 log.Printf("starting timer %q", t.name)
24 }
25 runtime.SetFinalizer(t, func(t *Timer) {
26 if t.marked {
27 return
28 }
29 log.Printf("timer %#v was never marked", t)
30 })
31 return
32 }
33
34 type timerOpt func(*Timer)
35
36 func Log(t *Timer) {
37 t.log = true
38 }
39
40 func Name(name string) func(*Timer) {
41 return func(t *Timer) {
42 t.name = name
43 }
44 }
45
46 func (t *Timer) Mark(events ...string) time.Duration {
47 d := time.Since(t.started)
48 if len(events) == 0 {
49 if t.name == "" {
50 panic("no name or events specified")
51 }
52 t.addDuration(t.name, d)
53 } else {
54 for _, e := range events {
55 if t.name != "" {
56 e = t.name + "/" + e
57 }
58 t.addDuration(e, d)
59 }
60 }
61 return d
62 }
63
64 func (t *Timer) MarkOk(ok bool) {
65 if ok {
66 t.Mark("ok")
67 } else {
68 t.Mark("not ok")
69 }
70 }
71
72 func (t *Timer) MarkErr(err error) {
73 if err == nil {
74 t.Mark("success")
75 } else {
76 t.Mark("error")
77 }
78 }
79
80 func (t *Timer) addDuration(desc string, d time.Duration) {
81 t.marked = true
82 mu.RLock()
83 e := events[desc]
84 mu.RUnlock()
85 if e == nil {
86 mu.Lock()
87 e = events[desc]
88 if e == nil {
89 e = new(Event)
90 e.Init()
91 events[desc] = e
92 }
93 mu.Unlock()
94 }
95 e.Add(d)
96 if t.log {
97 if t.name != "" {
98 log.Printf("timer %q got event %q after %s", t.name, desc, d)
99 } else {
100 log.Printf("marking event %q after %s", desc, d)
101 }
102 }
103 }
0 // Package pproffd is for detecting resource leaks due to unclosed handles.
1 package pproffd
2
3 import (
4 "io"
5 "net"
6 "os"
7 "runtime/pprof"
8 )
9
10 const enabled = false
11
12 var p *pprof.Profile
13
14 func init() {
15 if enabled {
16 p = pprof.NewProfile("fds")
17 }
18 }
19
20 type fd int
21
22 func (me *fd) Closed() {
23 p.Remove(me)
24 }
25
26 func add(skip int) (ret *fd) {
27 ret = new(fd)
28 p.Add(ret, skip+2)
29 return
30 }
31
32 type closeWrapper struct {
33 fd *fd
34 c io.Closer
35 }
36
37 func (me closeWrapper) Close() error {
38 me.fd.Closed()
39 return me.c.Close()
40 }
41
42 func newCloseWrapper(c io.Closer) closeWrapper {
43 return closeWrapper{
44 fd: add(2),
45 c: c,
46 }
47 }
48
49 type wrappedNetConn struct {
50 net.Conn
51 closeWrapper
52 }
53
54 func (me wrappedNetConn) Close() error {
55 return me.closeWrapper.Close()
56 }
57
58 // Tracks a net.Conn until Close() is explicitly called.
59 func WrapNetConn(nc net.Conn) net.Conn {
60 if !enabled {
61 return nc
62 }
63 if nc == nil {
64 return nil
65 }
66 return wrappedNetConn{
67 nc,
68 newCloseWrapper(nc),
69 }
70 }
71
72 type OSFile interface {
73 io.Reader
74 io.Seeker
75 io.Closer
76 io.Writer
77 Stat() (os.FileInfo, error)
78 io.ReaderAt
79 io.WriterAt
80 }
81
82 type wrappedOSFile struct {
83 *os.File
84 closeWrapper
85 }
86
87 func (me wrappedOSFile) Close() error {
88 return me.closeWrapper.Close()
89 }
90
91 func WrapOSFile(f *os.File) OSFile {
92 if !enabled {
93 return f
94 }
95 return &wrappedOSFile{f, newCloseWrapper(f)}
96 }
0 // Package prioritybitmap implements a set of integers ordered by attached
1 // priorities.
2 package prioritybitmap
3
4 import (
5 "sync"
6
7 "github.com/anacrolix/missinggo/bitmap"
8 "github.com/anacrolix/missinggo/iter"
9 "github.com/anacrolix/missinggo/orderedmap"
10 )
11
12 var (
13 bitSets = sync.Pool{
14 New: func() interface{} {
15 return make(map[int]struct{}, 1)
16 },
17 }
18 )
19
20 // Maintains set of ints ordered by priority.
21 type PriorityBitmap struct {
22 mu sync.Mutex
23 // From priority to singleton or set of bit indices.
24 om orderedmap.OrderedMap
25 // From bit index to priority
26 priorities map[int]int
27 }
28
29 var _ bitmap.Interface = (*PriorityBitmap)(nil)
30
31 func (me *PriorityBitmap) Contains(bit int) bool {
32 _, ok := me.priorities[bit]
33 return ok
34 }
35
36 func (me *PriorityBitmap) Len() int {
37 return len(me.priorities)
38 }
39
40 func (me *PriorityBitmap) Clear() {
41 me.om = nil
42 me.priorities = nil
43 }
44
45 func (me *PriorityBitmap) deleteBit(bit int) (priority int, ok bool) {
46 priority, ok = me.priorities[bit]
47 if !ok {
48 return
49 }
50 switch v := me.om.Get(priority).(type) {
51 case int:
52 if v != bit {
53 panic("invariant broken")
54 }
55 case map[int]struct{}:
56 if _, ok := v[bit]; !ok {
57 panic("invariant broken")
58 }
59 delete(v, bit)
60 if len(v) != 0 {
61 return
62 }
63 bitSets.Put(v)
64 }
65 me.om.Unset(priority)
66 if me.om.Len() == 0 {
67 me.om = nil
68 }
69 return
70 }
71
72 func bitLess(l, r interface{}) bool {
73 return l.(int) < r.(int)
74 }
75
76 func (me *PriorityBitmap) lazyInit() {
77 me.om = orderedmap.New(func(l, r interface{}) bool {
78 return l.(int) < r.(int)
79 })
80 me.priorities = make(map[int]int)
81 }
82
83 // Returns true if the priority is changed, or the bit wasn't present.
84 func (me *PriorityBitmap) Set(bit int, priority int) bool {
85 if p, ok := me.priorities[bit]; ok && p == priority {
86 return false
87 }
88 if oldPriority, deleted := me.deleteBit(bit); deleted && oldPriority == priority {
89 panic("should have already returned")
90 }
91 if me.priorities == nil {
92 me.priorities = make(map[int]int)
93 }
94 me.priorities[bit] = priority
95 if me.om == nil {
96 me.om = orderedmap.New(bitLess)
97 }
98 _v, ok := me.om.GetOk(priority)
99 if !ok {
100 // No other bits with this priority, set it to a lone int.
101 me.om.Set(priority, bit)
102 return true
103 }
104 switch v := _v.(type) {
105 case int:
106 newV := bitSets.Get().(map[int]struct{})
107 newV[v] = struct{}{}
108 newV[bit] = struct{}{}
109 me.om.Set(priority, newV)
110 case map[int]struct{}:
111 v[bit] = struct{}{}
112 default:
113 panic(v)
114 }
115 return true
116 }
117
118 func (me *PriorityBitmap) Remove(bit int) bool {
119 me.mu.Lock()
120 defer me.mu.Unlock()
121 if _, ok := me.deleteBit(bit); !ok {
122 return false
123 }
124 delete(me.priorities, bit)
125 if len(me.priorities) == 0 {
126 me.priorities = nil
127 }
128 if me.om != nil && me.om.Len() == 0 {
129 me.om = nil
130 }
131 return true
132 }
133
134 func (me *PriorityBitmap) Iter(f iter.Callback) {
135 me.IterTyped(func(i int) bool {
136 return f(i)
137 })
138 }
139
140 func (me *PriorityBitmap) IterTyped(_f func(i bitmap.BitIndex) bool) bool {
141 me.mu.Lock()
142 defer me.mu.Unlock()
143 if me == nil || me.om == nil {
144 return true
145 }
146 f := func(i int) bool {
147 me.mu.Unlock()
148 defer me.mu.Lock()
149 return _f(i)
150 }
151 return iter.All(func(key interface{}) bool {
152 value := me.om.Get(key)
153 switch v := value.(type) {
154 case int:
155 return f(v)
156 case map[int]struct{}:
157 for i := range v {
158 if !f(i) {
159 return false
160 }
161 }
162 }
163 return true
164 }, me.om.Iter)
165 }
166
167 func (me *PriorityBitmap) IsEmpty() bool {
168 if me.om == nil {
169 return true
170 }
171 return me.om.Len() == 0
172 }
173
174 // ok is false if the bit is not set.
175 func (me *PriorityBitmap) GetPriority(bit int) (prio int, ok bool) {
176 prio, ok = me.priorities[bit]
177 return
178 }
0 package prioritybitmap
1
2 import (
3 "math"
4 "testing"
5
6 "github.com/stretchr/testify/assert"
7
8 "github.com/anacrolix/missinggo/iter"
9 )
10
11 func TestEmpty(t *testing.T) {
12 var pb PriorityBitmap
13 it := iter.NewIterator(&pb)
14 assert.Panics(t, func() { it.Value() })
15 assert.False(t, it.Next())
16 }
17
18 func TestIntBounds(t *testing.T) {
19 var pb PriorityBitmap
20 assert.True(t, pb.Set(math.MaxInt32, math.MinInt32))
21 assert.True(t, pb.Set(math.MinInt32, math.MaxInt32))
22 assert.EqualValues(t, []interface{}{math.MaxInt32, math.MinInt32}, iter.IterableAsSlice(&pb))
23 }
24
25 func TestDistinct(t *testing.T) {
26 var pb PriorityBitmap
27 assert.True(t, pb.Set(0, 0))
28 pb.Set(1, 1)
29 assert.EqualValues(t, []interface{}{0, 1}, iter.IterableAsSlice(&pb))
30 pb.Set(0, -1)
31 assert.EqualValues(t, []interface{}{0, 1}, iter.IterableAsSlice(&pb))
32 pb.Set(1, -2)
33 assert.EqualValues(t, []interface{}{1, 0}, iter.IterableAsSlice(&pb))
34 }
35
36 func TestNextAfterIterFinished(t *testing.T) {
37 var pb PriorityBitmap
38 pb.Set(0, 0)
39 it := iter.NewIterator(&pb)
40 assert.True(t, it.Next())
41 assert.False(t, it.Next())
42 assert.False(t, it.Next())
43 }
44
45 func TestMutationResults(t *testing.T) {
46 var pb PriorityBitmap
47 assert.False(t, pb.Remove(1))
48 assert.True(t, pb.Set(1, -1))
49 assert.True(t, pb.Set(1, 2))
50 assert.True(t, pb.Set(2, 2))
51 assert.True(t, pb.Set(2, -1))
52 assert.False(t, pb.Set(1, 2))
53 assert.EqualValues(t, []interface{}{2, 1}, iter.IterableAsSlice(&pb))
54 assert.True(t, pb.Set(1, -1))
55 assert.False(t, pb.Remove(0))
56 assert.True(t, pb.Remove(1))
57 assert.False(t, pb.Remove(0))
58 assert.False(t, pb.Remove(1))
59 assert.True(t, pb.Remove(2))
60 assert.False(t, pb.Remove(2))
61 assert.False(t, pb.Remove(0))
62 assert.True(t, pb.IsEmpty())
63 assert.Len(t, iter.IterableAsSlice(&pb), 0)
64 }
65
66 func TestDoubleRemove(t *testing.T) {
67 var pb PriorityBitmap
68 assert.True(t, pb.Set(0, 0))
69 assert.True(t, pb.Remove(0))
70 assert.False(t, pb.Remove(0))
71 }
0 package xprometheus
1
2 import (
3 "encoding/json"
4 "expvar"
5 "fmt"
6 "strconv"
7
8 "github.com/bradfitz/iter"
9 "github.com/prometheus/client_golang/prometheus"
10 )
11
12 // A Prometheus collector that exposes all vars.
13 type expvarCollector struct {
14 descs map[int]*prometheus.Desc
15 }
16
17 func NewExpvarCollector() expvarCollector {
18 return expvarCollector{
19 descs: make(map[int]*prometheus.Desc),
20 }
21 }
22
23 const (
24 fqName = "go_expvar"
25 help = "All expvars"
26 )
27
28 var desc = prometheus.NewDesc(fqName, help, nil, nil)
29
30 // Describe implements Collector.
31 func (e expvarCollector) Describe(ch chan<- *prometheus.Desc) {
32 ch <- desc
33 }
34
35 // Collect implements Collector.
36 func (e expvarCollector) Collect(ch chan<- prometheus.Metric) {
37 expvar.Do(func(kv expvar.KeyValue) {
38 collector{
39 f: func(m prometheus.Metric) {
40 ch <- m
41 },
42 labelValues: []string{kv.Key},
43 descs: e.descs,
44 }.collectVar(kv.Value)
45 })
46 }
47
48 func labels(n int) (ls []string) {
49 for i := range iter.N(n) {
50 ls = append(ls, "key"+strconv.FormatInt(int64(i), 10))
51 }
52 return
53 }
54
55 type collector struct {
56 f func(prometheus.Metric)
57 labelValues []string
58 descs map[int]*prometheus.Desc
59 }
60
61 func (c *collector) newMetric(f float64) {
62 c.f(prometheus.MustNewConstMetric(
63 c.desc(),
64 prometheus.UntypedValue,
65 float64(f),
66 c.labelValues...))
67 }
68
69 func (c collector) desc() *prometheus.Desc {
70 d, ok := c.descs[len(c.labelValues)]
71 if !ok {
72 d = prometheus.NewDesc(fqName, "", labels(len(c.labelValues)), nil)
73 c.descs[len(c.labelValues)] = d
74 }
75 return d
76 }
77
78 func (c collector) metricError(err error) {
79 c.f(prometheus.NewInvalidMetric(c.desc(), err))
80 }
81
82 func (c collector) withLabelValue(lv string) collector {
83 c.labelValues = append(c.labelValues, lv)
84 return c
85 }
86
87 func (c collector) collectJsonValue(v interface{}) {
88 switch v := v.(type) {
89 case float64:
90 c.newMetric(v)
91 case map[string]interface{}:
92 for k, v := range v {
93 c.withLabelValue(k).collectJsonValue(v)
94 }
95 case bool:
96 if v {
97 c.newMetric(1)
98 } else {
99 c.newMetric(0)
100 }
101 case string:
102 c.f(prometheus.MustNewConstMetric(
103 prometheus.NewDesc("go_expvar", "",
104 append(labels(len(c.labelValues)), "value"),
105 nil),
106 prometheus.UntypedValue,
107 1,
108 append(c.labelValues, v)...,
109 ))
110 case []interface{}:
111 for i, v := range v {
112 c.withLabelValue(strconv.FormatInt(int64(i), 10)).collectJsonValue(v)
113 }
114 default:
115 c.metricError(fmt.Errorf("unhandled json value type %T", v))
116 }
117 }
118
119 func (c collector) collectVar(v expvar.Var) {
120 var jv interface{}
121 if err := json.Unmarshal([]byte(v.String()), &jv); err != nil {
122 c.metricError(err)
123 }
124 c.collectJsonValue(jv)
125 }
0 package xprometheus
1
2 import (
3 "sync"
4 "testing"
5
6 "github.com/bradfitz/iter"
7 "github.com/prometheus/client_golang/prometheus"
8 )
9
10 func BenchmarkExpvarCollector_Collect(b *testing.B) {
11 ec := NewExpvarCollector()
12 ch := make(chan prometheus.Metric)
13 n := 0
14 var wg sync.WaitGroup
15 wg.Add(1)
16 go func() {
17 defer wg.Done()
18 for range ch {
19 n++
20 }
21 }()
22 b.ReportAllocs()
23 for range iter.N(b.N) {
24 ec.Collect(ch)
25 }
26 close(ch)
27 wg.Wait()
28 b.Logf("collected %d metrics (%f per collect)", n, float64(n)/float64(b.N))
29 }
0 package pubsub
1
2 import (
3 "sync"
4 )
5
6 type PubSub struct {
7 mu sync.Mutex
8 next chan item
9 closed bool
10 }
11
12 type item struct {
13 value interface{}
14 next chan item
15 }
16
17 type Subscription struct {
18 next chan item
19 Values chan interface{}
20 mu sync.Mutex
21 closed chan struct{}
22 }
23
24 func NewPubSub() (ret *PubSub) {
25 return new(PubSub)
26 }
27
28 func (me *PubSub) init() {
29 me.next = make(chan item, 1)
30 }
31
32 func (me *PubSub) lazyInit() {
33 me.mu.Lock()
34 defer me.mu.Unlock()
35 if me.closed {
36 return
37 }
38 if me.next == nil {
39 me.init()
40 }
41 }
42
43 func (me *PubSub) Publish(v interface{}) {
44 me.lazyInit()
45 next := make(chan item, 1)
46 i := item{v, next}
47 me.mu.Lock()
48 if !me.closed {
49 me.next <- i
50 me.next = next
51 }
52 me.mu.Unlock()
53 }
54
55 func (me *Subscription) Close() {
56 me.mu.Lock()
57 defer me.mu.Unlock()
58 select {
59 case <-me.closed:
60 default:
61 close(me.closed)
62 }
63 }
64
65 func (me *Subscription) runner() {
66 defer close(me.Values)
67 for {
68 select {
69 case i, ok := <-me.next:
70 if !ok {
71 me.Close()
72 return
73 }
74 // Send the value back into the channel for someone else. This
75 // won't block because the channel has a capacity of 1, and this
76 // is currently the only copy of this value being sent to this
77 // channel.
78 me.next <- i
79 // The next value comes from the channel given to us by the value
80 // we just got.
81 me.next = i.next
82 select {
83 case me.Values <- i.value:
84 case <-me.closed:
85 return
86 }
87 case <-me.closed:
88 return
89 }
90 }
91 }
92
93 func (me *PubSub) Subscribe() (ret *Subscription) {
94 me.lazyInit()
95 ret = &Subscription{
96 closed: make(chan struct{}),
97 Values: make(chan interface{}),
98 }
99 me.mu.Lock()
100 ret.next = me.next
101 me.mu.Unlock()
102 go ret.runner()
103 return
104 }
105
106 func (me *PubSub) Close() {
107 me.mu.Lock()
108 defer me.mu.Unlock()
109 if me.closed {
110 return
111 }
112 if me.next != nil {
113 close(me.next)
114 }
115 me.closed = true
116 }
0 package pubsub
1
2 import (
3 "sync"
4 "testing"
5
6 "github.com/bradfitz/iter"
7 "github.com/stretchr/testify/assert"
8 "github.com/stretchr/testify/require"
9 )
10
11 func TestDoubleClose(t *testing.T) {
12 ps := NewPubSub()
13 ps.Close()
14 ps.Close()
15 }
16
17 func testBroadcast(t testing.TB, subs, vals int) {
18 ps := NewPubSub()
19 var wg sync.WaitGroup
20 for range iter.N(subs) {
21 wg.Add(1)
22 s := ps.Subscribe()
23 go func() {
24 defer wg.Done()
25 var e int
26 for i := range s.Values {
27 assert.Equal(t, e, i.(int))
28 e++
29 }
30 assert.Equal(t, vals, e)
31 }()
32 }
33 for i := range iter.N(vals) {
34 ps.Publish(i)
35 }
36 ps.Close()
37 wg.Wait()
38 }
39
40 func TestBroadcast(t *testing.T) {
41 testBroadcast(t, 100, 10)
42 }
43
44 func BenchmarkBroadcast(b *testing.B) {
45 for range iter.N(b.N) {
46 testBroadcast(b, 10, 1000)
47 }
48 }
49
50 func TestCloseSubscription(t *testing.T) {
51 ps := NewPubSub()
52 ps.Publish(1)
53 s := ps.Subscribe()
54 select {
55 case <-s.Values:
56 t.FailNow()
57 default:
58 }
59 ps.Publish(2)
60 s2 := ps.Subscribe()
61 ps.Publish(3)
62 require.Equal(t, 2, <-s.Values)
63 require.EqualValues(t, 3, <-s.Values)
64 s.Close()
65 _, ok := <-s.Values
66 require.False(t, ok)
67 ps.Publish(4)
68 ps.Close()
69 require.Equal(t, 3, <-s2.Values)
70 require.Equal(t, 4, <-s2.Values)
71 require.Nil(t, <-s2.Values)
72 s2.Close()
73 }
0 package missinggo
1
2 import "context"
3
4 type ContextedReader struct {
5 R ReadContexter
6 Ctx context.Context
7 }
8
9 func (me ContextedReader) Read(b []byte) (int, error) {
10 return me.R.ReadContext(me.Ctx, b)
11 }
12
13 type ReadContexter interface {
14 ReadContext(context.Context, []byte) (int, error)
15 }
0 package refclose
1
2 import (
3 "runtime/pprof"
4 "sync"
5 )
6
7 var profile = pprof.NewProfile("refs")
8
9 type RefPool struct {
10 mu sync.Mutex
11 rs map[interface{}]*resource
12 }
13
14 type Closer func()
15
16 func (me *RefPool) inc(key interface{}) {
17 me.mu.Lock()
18 defer me.mu.Unlock()
19 r := me.rs[key]
20 if r == nil {
21 r = new(resource)
22 if me.rs == nil {
23 me.rs = make(map[interface{}]*resource)
24 }
25 me.rs[key] = r
26 }
27 r.numRefs++
28 }
29
30 func (me *RefPool) dec(key interface{}) {
31 me.mu.Lock()
32 defer me.mu.Unlock()
33 r := me.rs[key]
34 r.numRefs--
35 if r.numRefs > 0 {
36 return
37 }
38 if r.numRefs < 0 {
39 panic(r.numRefs)
40 }
41 r.closer()
42 delete(me.rs, key)
43 }
44
45 type resource struct {
46 closer Closer
47 numRefs int
48 }
49
50 func (me *RefPool) NewRef(key interface{}) (ret *Ref) {
51 me.inc(key)
52 ret = &Ref{
53 pool: me,
54 key: key,
55 }
56 profile.Add(ret, 0)
57 return
58 }
59
60 type Ref struct {
61 mu sync.Mutex
62 pool *RefPool
63 key interface{}
64 closed bool
65 }
66
67 func (me *Ref) SetCloser(closer Closer) {
68 me.pool.mu.Lock()
69 defer me.pool.mu.Unlock()
70 me.pool.rs[me.key].closer = closer
71 }
72
73 func (me *Ref) panicIfClosed() {
74 if me.closed {
75 panic("ref is closed")
76 }
77 }
78
79 func (me *Ref) Release() {
80 me.mu.Lock()
81 defer me.mu.Unlock()
82 me.panicIfClosed()
83 profile.Remove(me)
84 me.pool.dec(me.key)
85 }
86
87 func (me *Ref) Key() interface{} {
88 me.mu.Lock()
89 defer me.mu.Unlock()
90 me.panicIfClosed()
91 return me.key
92 }
0 package refclose
1
2 import (
3 "sync"
4 "testing"
5
6 "github.com/bradfitz/iter"
7 "github.com/stretchr/testify/assert"
8 )
9
10 type refTest struct {
11 pool RefPool
12 key interface{}
13 objs map[*object]struct{}
14 t *testing.T
15 }
16
17 func (me *refTest) run() {
18 me.objs = make(map[*object]struct{})
19 var (
20 mu sync.Mutex
21 curObj *object
22 wg sync.WaitGroup
23 )
24 for range iter.N(1000) {
25 wg.Add(1)
26 go func() {
27 defer wg.Done()
28 ref := me.pool.NewRef(me.key)
29 mu.Lock()
30 if curObj == nil {
31 curObj = new(object)
32 me.objs[curObj] = struct{}{}
33 }
34 // obj := curObj
35 mu.Unlock()
36 ref.SetCloser(func() {
37 mu.Lock()
38 if curObj.closed {
39 panic("object already closed")
40 }
41 curObj.closed = true
42 curObj = nil
43 mu.Unlock()
44 })
45 ref.Release()
46 }()
47 }
48 wg.Wait()
49 me.t.Logf("created %d objects", len(me.objs))
50 assert.True(me.t, len(me.objs) >= 1)
51 for obj := range me.objs {
52 assert.True(me.t, obj.closed)
53 }
54 }
55
56 type object struct {
57 closed bool
58 }
59
60 func Test(t *testing.T) {
61 (&refTest{
62 key: 3,
63 t: t,
64 }).run()
65 }
0 package reqctx
1
2 import (
3 "context"
4 "net/http"
5
6 "github.com/anacrolix/missinggo/futures"
7 )
8
9 var lazyValuesContextKey = new(byte)
10
11 func WithLazyMiddleware() func(http.Handler) http.Handler {
12 return func(h http.Handler) http.Handler {
13 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
14 r = WithLazy(r)
15 h.ServeHTTP(w, r)
16 })
17 }
18 }
19
20 func WithLazy(r *http.Request) *http.Request {
21 if r.Context().Value(lazyValuesContextKey) == nil {
22 r = r.WithContext(context.WithValue(r.Context(), lazyValuesContextKey, &LazyValues{r: r}))
23 }
24 return r
25 }
26
27 func GetLazyValues(ctx context.Context) *LazyValues {
28 return ctx.Value(lazyValuesContextKey).(*LazyValues)
29 }
30
31 type LazyValues struct {
32 values map[interface{}]*futures.F
33 r *http.Request
34 }
35
36 func (me *LazyValues) Get(val *lazyValue) *futures.F {
37 f := me.values[val.key]
38 if f != nil {
39 return f
40 }
41 f = futures.Start(func() (interface{}, error) {
42 return val.get(me.r)
43 })
44 if me.values == nil {
45 me.values = make(map[interface{}]*futures.F)
46 }
47 me.values[val.key] = f
48 return f
49 }
50
51 func NewLazyValue(get func(r *http.Request) (interface{}, error)) *lazyValue {
52 val := &lazyValue{
53 get: get,
54 }
55 val.key = val
56 return val
57 }
58
59 type lazyValue struct {
60 key interface{}
61 get func(r *http.Request) (interface{}, error)
62 }
63
64 func (me *lazyValue) Get(r *http.Request) *futures.F {
65 return me.GetContext(r.Context())
66 }
67
68 func (me *lazyValue) GetContext(ctx context.Context) *futures.F {
69 return GetLazyValues(ctx).Get(me)
70 }
71
72 func (me *lazyValue) Prefetch(r *http.Request) {
73 me.Get(r)
74 }
75
76 func (me *lazyValue) PrefetchMiddleware(h http.Handler) http.Handler {
77 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
78 me.Prefetch(r)
79 h.ServeHTTP(w, r)
80 })
81 }
0 package reqctx
1
2 import (
3 "context"
4 "net/http"
5
6 "github.com/anacrolix/missinggo/expect"
7 )
8
9 func SetNewValue(r *http.Request, key, value interface{}) *http.Request {
10 expect.Nil(r.Context().Value(key))
11 expect.NotNil(value)
12 return r.WithContext(context.WithValue(r.Context(), key, value))
13 }
0 package reqctx
1
2 import (
3 "context"
4 "net/http"
5
6 "github.com/anacrolix/missinggo/expect"
7 )
8
9 func NewValue() *contextValue {
10 return &contextValue{new(byte)}
11 }
12
13 type contextValue struct {
14 key interface{}
15 }
16
17 func (me contextValue) Get(ctx context.Context) interface{} {
18 return ctx.Value(me.key)
19 }
20
21 // Sets the value on the Request. It must not have been already set.
22 func (me contextValue) SetRequestOnce(r *http.Request, val interface{}) *http.Request {
23 expect.Nil(me.Get(r.Context()))
24 return r.WithContext(context.WithValue(r.Context(), me.key, val))
25 }
26
27 // Returns a middleware that sets the value in the Request's Context.
28 func (me contextValue) SetMiddleware(val interface{}) func(http.Handler) http.Handler {
29 return func(h http.Handler) http.Handler {
30 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
31 r = me.SetRequestOnce(r, val)
32 h.ServeHTTP(w, r)
33 })
34 }
35 }
0 package resource
1
2 import (
3 "bytes"
4 "errors"
5 "fmt"
6 "io"
7 "net/http"
8 "net/url"
9 "os"
10 "strconv"
11 "time"
12 )
13
14 // Provides access to resources through a http.Client.
15 type HTTPProvider struct {
16 Client *http.Client
17 }
18
19 var _ Provider = &HTTPProvider{}
20
21 func (me *HTTPProvider) NewInstance(urlStr string) (r Instance, err error) {
22 _r := new(httpInstance)
23 _r.URL, err = url.Parse(urlStr)
24 if err != nil {
25 return
26 }
27 _r.Client = me.Client
28 if _r.Client == nil {
29 _r.Client = http.DefaultClient
30 }
31 r = _r
32 return
33 }
34
35 type httpInstance struct {
36 Client *http.Client
37 URL *url.URL
38 }
39
40 var _ Instance = &httpInstance{}
41
42 func mustNewRequest(method, urlStr string, body io.Reader) *http.Request {
43 req, err := http.NewRequest(method, urlStr, body)
44 if err != nil {
45 panic(err)
46 }
47 return req
48 }
49
50 func responseError(r *http.Response) error {
51 if r.StatusCode == http.StatusNotFound {
52 return os.ErrNotExist
53 }
54 return errors.New(r.Status)
55 }
56
57 func (me *httpInstance) Get() (ret io.ReadCloser, err error) {
58 resp, err := me.Client.Get(me.URL.String())
59 if err != nil {
60 return
61 }
62 if resp.StatusCode == http.StatusOK {
63 ret = resp.Body
64 return
65 }
66 resp.Body.Close()
67 err = responseError(resp)
68 return
69 }
70
71 func (me *httpInstance) Put(r io.Reader) (err error) {
72 resp, err := me.Client.Do(mustNewRequest("PUT", me.URL.String(), r))
73 if err != nil {
74 return
75 }
76 resp.Body.Close()
77 if resp.StatusCode == http.StatusOK {
78 return
79 }
80 err = responseError(resp)
81 return
82 }
83
84 func (me *httpInstance) ReadAt(b []byte, off int64) (n int, err error) {
85 req := mustNewRequest("GET", me.URL.String(), nil)
86 req.Header.Set("Range", fmt.Sprintf("bytes=%d-%d", off, off+int64(len(b))-1))
87 resp, err := me.Client.Do(req)
88 if err != nil {
89 return
90 }
91 defer resp.Body.Close()
92 switch resp.StatusCode {
93 case http.StatusPartialContent:
94 case http.StatusRequestedRangeNotSatisfiable:
95 err = io.EOF
96 return
97 default:
98 err = responseError(resp)
99 return
100 }
101 // TODO: This will crash if ContentLength was not provided (-1). Do
102 // something about that.
103 b = b[:resp.ContentLength]
104 return io.ReadFull(resp.Body, b)
105 }
106
107 func (me *httpInstance) WriteAt(b []byte, off int64) (n int, err error) {
108 req := mustNewRequest("PATCH", me.URL.String(), bytes.NewReader(b))
109 req.ContentLength = int64(len(b))
110 req.Header.Set("Content-Range", fmt.Sprintf("bytes=%d-%d", off, off+int64(len(b))-1))
111 resp, err := me.Client.Do(req)
112 if err != nil {
113 return
114 }
115 resp.Body.Close()
116 if resp.StatusCode != http.StatusOK {
117 err = responseError(resp)
118 }
119 n = len(b)
120 return
121 }
122
123 func (me *httpInstance) Stat() (fi os.FileInfo, err error) {
124 resp, err := me.Client.Head(me.URL.String())
125 if err != nil {
126 return
127 }
128 resp.Body.Close()
129 if resp.StatusCode == http.StatusNotFound {
130 err = os.ErrNotExist
131 return
132 }
133 if resp.StatusCode != http.StatusOK {
134 err = errors.New(resp.Status)
135 return
136 }
137 var _fi httpFileInfo
138 if h := resp.Header.Get("Last-Modified"); h != "" {
139 _fi.lastModified, err = time.Parse(http.TimeFormat, h)
140 if err != nil {
141 err = fmt.Errorf("error parsing Last-Modified header: %s", err)
142 return
143 }
144 }
145 if h := resp.Header.Get("Content-Length"); h != "" {
146 _fi.contentLength, err = strconv.ParseInt(h, 10, 64)
147 if err != nil {
148 err = fmt.Errorf("error parsing Content-Length header: %s", err)
149 return
150 }
151 }
152 fi = _fi
153 return
154 }
155
156 func (me *httpInstance) Delete() (err error) {
157 resp, err := me.Client.Do(mustNewRequest("DELETE", me.URL.String(), nil))
158 if err != nil {
159 return
160 }
161 err = responseError(resp)
162 resp.Body.Close()
163 return
164 }
165
166 type httpFileInfo struct {
167 lastModified time.Time
168 contentLength int64
169 }
170
171 var _ os.FileInfo = httpFileInfo{}
172
173 func (fi httpFileInfo) IsDir() bool {
174 return false
175 }
176
177 func (fi httpFileInfo) Mode() os.FileMode {
178 return 0
179 }
180
181 func (fi httpFileInfo) Name() string {
182 return ""
183 }
184
185 func (fi httpFileInfo) Size() int64 {
186 return fi.contentLength
187 }
188
189 func (fi httpFileInfo) ModTime() time.Time {
190 return fi.lastModified
191 }
192
193 func (fi httpFileInfo) Sys() interface{} {
194 return nil
195 }
0 package resource
1
2 import (
3 "io"
4 "os"
5 )
6
7 // Provides access to resources through the native OS filesystem.
8 type OSFileProvider struct{}
9
10 var _ Provider = OSFileProvider{}
11
12 func (me OSFileProvider) NewInstance(filePath string) (r Instance, err error) {
13 return &osFileInstance{filePath}, nil
14 }
15
16 type osFileInstance struct {
17 path string
18 }
19
20 var _ Instance = &osFileInstance{}
21
22 func (me *osFileInstance) Get() (ret io.ReadCloser, err error) {
23 return os.Open(me.path)
24 }
25
26 func (me *osFileInstance) Put(r io.Reader) (err error) {
27 f, err := os.OpenFile(me.path, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0640)
28 if err != nil {
29 return
30 }
31 defer f.Close()
32 _, err = io.Copy(f, r)
33 return
34 }
35
36 func (me *osFileInstance) ReadAt(b []byte, off int64) (n int, err error) {
37 f, err := os.Open(me.path)
38 if err != nil {
39 return
40 }
41 defer f.Close()
42 return f.ReadAt(b, off)
43 }
44
45 func (me *osFileInstance) WriteAt(b []byte, off int64) (n int, err error) {
46 f, err := os.OpenFile(me.path, os.O_CREATE|os.O_WRONLY, 0640)
47 if err != nil {
48 return
49 }
50 defer f.Close()
51 return f.WriteAt(b, off)
52 }
53
54 func (me *osFileInstance) Stat() (fi os.FileInfo, err error) {
55 return os.Stat(me.path)
56 }
57
58 func (me *osFileInstance) Delete() error {
59 return os.Remove(me.path)
60 }
0 package resource
1
2 type Provider interface {
3 NewInstance(string) (Instance, error)
4 }
5
6 // TranslatedProvider manipulates resource locations, so as to allow
7 // sandboxing, or relative paths for example.
8 type TranslatedProvider struct {
9 // The underlying Provider.
10 BaseProvider Provider
11 // Some location used in calculating final locations.
12 BaseLocation string
13 // Function that takes BaseLocation, and the caller location and returns
14 // the location to be used with the BaseProvider.
15 JoinLocations func(base, rel string) string
16 }
17
18 func (me TranslatedProvider) NewInstance(rel string) (Instance, error) {
19 return me.BaseProvider.NewInstance(me.JoinLocations(me.BaseLocation, rel))
20 }
0 package resource
1
2 import (
3 "io"
4 "os"
5 )
6
7 // An Instance represents the content at some location accessed through some
8 // Provider. It's the data at some URL.
9 type Instance interface {
10 Get() (io.ReadCloser, error)
11 Put(io.Reader) error
12 Stat() (os.FileInfo, error)
13 ReadAt([]byte, int64) (int, error)
14 WriteAt([]byte, int64) (int, error)
15 Delete() error
16 }
17
18 // Creates a io.ReadSeeker to an Instance.
19 func ReadSeeker(r Instance) io.ReadSeeker {
20 fi, err := r.Stat()
21 if err != nil {
22 return nil
23 }
24 return io.NewSectionReader(r, 0, fi.Size())
25 }
26
27 // Move instance content, deleting the source if it succeeds.
28 func Move(from, to Instance) (err error) {
29 rc, err := from.Get()
30 if err != nil {
31 return
32 }
33 defer rc.Close()
34 err = to.Put(rc)
35 if err != nil {
36 return
37 }
38 from.Delete()
39 return
40 }
41
42 func Exists(i Instance) bool {
43 _, err := i.Stat()
44 return err == nil
45 }
0 package missinggo
1
2 // A RunLengthEncoder counts successive duplicate elements and emits the
3 // element and the run length when the element changes or the encoder is
4 // flushed.
5 type RunLengthEncoder interface {
6 // Add a series of identical elements to the stream.
7 Append(element interface{}, count uint64)
8 // Emit the current element and its count if non-zero without waiting for
9 // the element to change.
10 Flush()
11 }
12
13 type runLengthEncoder struct {
14 eachRun func(element interface{}, count uint64)
15 element interface{}
16 count uint64
17 }
18
19 // Creates a new RunLengthEncoder. eachRun is called when an element and its
20 // count is emitted, per the RunLengthEncoder interface.
21 func NewRunLengthEncoder(eachRun func(element interface{}, count uint64)) RunLengthEncoder {
22 return &runLengthEncoder{
23 eachRun: eachRun,
24 }
25 }
26
27 func (me *runLengthEncoder) Append(element interface{}, count uint64) {
28 if element == me.element {
29 me.count += count
30 return
31 }
32 if me.count != 0 {
33 me.eachRun(me.element, me.count)
34 }
35 me.count = count
36 me.element = element
37 }
38
39 func (me *runLengthEncoder) Flush() {
40 if me.count == 0 {
41 return
42 }
43 me.eachRun(me.element, me.count)
44 me.count = 0
45 }
0 package missinggo_test
1
2 import (
3 "fmt"
4
5 "github.com/anacrolix/missinggo"
6 )
7
8 func ExampleNewRunLengthEncoder() {
9 var s string
10 rle := missinggo.NewRunLengthEncoder(func(e interface{}, count uint64) {
11 s += fmt.Sprintf("%d%c", count, e)
12 })
13 for _, e := range "WWWWWWWWWWWWBWWWWWWWWWWWWBBBWWWWWWWWWWWWWWWWWWWWWWWWBWWWWWWWWWWWWWW" {
14 rle.Append(e, 1)
15 }
16 rle.Flush()
17 fmt.Println(s)
18 // Output: 12W1B12W3B24W1B14W
19 }
0 package runid
1
2 import (
3 "context"
4 "database/sql"
5
6 "github.com/anacrolix/missinggo/expect"
7 )
8
9 type T int64
10
11 func New(db *sql.DB) (ret *T) {
12 ctx := context.Background()
13 conn, err := db.Conn(ctx)
14 expect.Nil(err)
15 defer func() {
16 expect.Nil(conn.Close())
17 }()
18 _, err = conn.ExecContext(ctx, `CREATE TABLE if not exists runs (started datetime default (datetime('now')))`)
19 expect.Nil(err)
20 res, err := conn.ExecContext(ctx, "insert into runs default values")
21 expect.Nil(err)
22 expect.OneRowAffected(res)
23 expect.Nil(conn.QueryRowContext(ctx, "select last_insert_rowid()").Scan(&ret))
24 return
25 }
0 package missinggo
1
2 import (
3 "context"
4 "fmt"
5 "io"
6 )
7
8 type sectionReadSeeker struct {
9 base io.ReadSeeker
10 off, size int64
11 }
12
13 type ReadSeekContexter interface {
14 io.ReadSeeker
15 ReadContexter
16 }
17
18 // Returns a ReadSeeker on a section of another ReadSeeker.
19 func NewSectionReadSeeker(base io.ReadSeeker, off, size int64) (ret ReadSeekContexter) {
20 ret = &sectionReadSeeker{
21 base: base,
22 off: off,
23 size: size,
24 }
25 seekOff, err := ret.Seek(0, io.SeekStart)
26 if err != nil {
27 panic(err)
28 }
29 if seekOff != 0 {
30 panic(seekOff)
31 }
32 return
33 }
34
35 func (me *sectionReadSeeker) Seek(off int64, whence int) (ret int64, err error) {
36 switch whence {
37 case io.SeekStart:
38 off += me.off
39 case io.SeekCurrent:
40 case io.SeekEnd:
41 off += me.off + me.size
42 whence = io.SeekStart
43 default:
44 err = fmt.Errorf("unhandled whence: %d", whence)
45 return
46 }
47 ret, err = me.base.Seek(off, whence)
48 ret -= me.off
49 return
50 }
51
52 func (me *sectionReadSeeker) ReadContext(ctx context.Context, b []byte) (int, error) {
53 off, err := me.Seek(0, io.SeekCurrent)
54 if err != nil {
55 return 0, err
56 }
57 left := me.size - off
58 if left <= 0 {
59 return 0, io.EOF
60 }
61 b = LimitLen(b, left)
62 if rc, ok := me.base.(ReadContexter); ok {
63 return rc.ReadContext(ctx, b)
64 }
65 if ctx != context.Background() {
66 // Can't handle cancellation.
67 panic(ctx)
68 }
69 return me.base.Read(b)
70 }
71
72 func (me *sectionReadSeeker) Read(b []byte) (int, error) {
73 return me.ReadContext(context.Background(), b)
74 }
0 package missinggo
1
2 import (
3 "bytes"
4 "io"
5 "os"
6 "testing"
7
8 "github.com/stretchr/testify/assert"
9 )
10
11 func TestSectionReadSeekerReadBeyondEnd(t *testing.T) {
12 base := bytes.NewReader([]byte{1, 2, 3})
13 srs := NewSectionReadSeeker(base, 1, 1)
14 dest := new(bytes.Buffer)
15 n, err := io.Copy(dest, srs)
16 assert.EqualValues(t, 1, n)
17 assert.NoError(t, err)
18 }
19
20 func TestSectionReadSeekerSeekEnd(t *testing.T) {
21 base := bytes.NewReader([]byte{1, 2, 3})
22 srs := NewSectionReadSeeker(base, 1, 1)
23 off, err := srs.Seek(0, os.SEEK_END)
24 assert.NoError(t, err)
25 assert.EqualValues(t, 1, off)
26 }
0 package missinggo
1
2 import "io"
3
4 type SectionWriter struct {
5 w io.WriterAt
6 off, len int64
7 }
8
9 func NewSectionWriter(w io.WriterAt, off, len int64) *SectionWriter {
10 return &SectionWriter{w, off, len}
11 }
12
13 func (me *SectionWriter) WriteAt(b []byte, off int64) (n int, err error) {
14 if off >= me.len {
15 err = io.EOF
16 return
17 }
18 if off+int64(len(b)) > me.len {
19 b = b[:me.len-off]
20 }
21 return me.w.WriteAt(b, me.off+off)
22 }
0 package missinggo
1
2 import (
3 "crypto/ecdsa"
4 "crypto/rand"
5 "crypto/rsa"
6 "crypto/tls"
7 "crypto/x509"
8 "crypto/x509/pkix"
9 "log"
10 "math/big"
11 "time"
12 )
13
14 func publicKey(priv interface{}) interface{} {
15 switch k := priv.(type) {
16 case *rsa.PrivateKey:
17 return &k.PublicKey
18 case *ecdsa.PrivateKey:
19 return &k.PublicKey
20 default:
21 return nil
22 }
23 }
24
25 // Creates a self-signed certificate in memory for use with tls.Config.
26 func NewSelfSignedCertificate() (cert tls.Certificate, err error) {
27 cert.PrivateKey, err = rsa.GenerateKey(rand.Reader, 2048)
28 if err != nil {
29 return
30 }
31 notBefore := time.Now()
32 notAfter := notBefore.Add(365 * 24 * time.Hour)
33
34 serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
35 serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
36 if err != nil {
37 log.Fatalf("failed to generate serial number: %s", err)
38 }
39
40 template := x509.Certificate{
41 SerialNumber: serialNumber,
42 Subject: pkix.Name{
43 Organization: []string{"Acme Co"},
44 },
45 NotBefore: notBefore,
46 NotAfter: notAfter,
47
48 KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
49 ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
50 BasicConstraintsValid: true,
51 }
52
53 derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, publicKey(cert.PrivateKey), cert.PrivateKey)
54 if err != nil {
55 log.Fatalf("Failed to create certificate: %s", err)
56 }
57 cert.Certificate = [][]byte{derBytes}
58 return
59 }
0 package missinggo
1
2 import "sync"
3
4 type ongoing struct {
5 do sync.Mutex
6 users int
7 }
8
9 type SingleFlight struct {
10 mu sync.Mutex
11 ongoing map[string]*ongoing
12 }
13
14 type Operation struct {
15 sf *SingleFlight
16 id string
17 }
18
19 func (op Operation) Unlock() {
20 op.sf.Unlock(op.id)
21 }
22
23 func (me *SingleFlight) Lock(id string) Operation {
24 me.mu.Lock()
25 on, ok := me.ongoing[id]
26 if !ok {
27 on = new(ongoing)
28 if me.ongoing == nil {
29 me.ongoing = make(map[string]*ongoing)
30 }
31 me.ongoing[id] = on
32 }
33 on.users++
34 me.mu.Unlock()
35 on.do.Lock()
36 return Operation{me, id}
37 }
38
39 func (me *SingleFlight) Unlock(id string) {
40 me.mu.Lock()
41 on := me.ongoing[id]
42 on.do.Unlock()
43 on.users--
44 if on.users == 0 {
45 delete(me.ongoing, id)
46 }
47 me.mu.Unlock()
48 }
0 package slices
1
2 import (
3 "reflect"
4
5 "github.com/bradfitz/iter"
6 )
7
8 // Returns a copy of all the elements of slice []T as a slice of interface{}.
9 func ToEmptyInterface(slice interface{}) (ret []interface{}) {
10 v := reflect.ValueOf(slice)
11 l := v.Len()
12 ret = make([]interface{}, 0, l)
13 for i := range iter.N(v.Len()) {
14 ret = append(ret, v.Index(i).Interface())
15 }
16 return
17 }
18
19 // Makes and sets a slice at *ptrTo, and type asserts all the elements from
20 // from to it.
21 func MakeInto(ptrTo interface{}, from interface{}) {
22 fromSliceValue := reflect.ValueOf(from)
23 fromLen := fromSliceValue.Len()
24 if fromLen == 0 {
25 return
26 }
27 // Deref the pointer to slice.
28 slicePtrValue := reflect.ValueOf(ptrTo)
29 if slicePtrValue.Kind() != reflect.Ptr {
30 panic("destination is not a pointer")
31 }
32 destSliceValue := slicePtrValue.Elem()
33 // The type of the elements of the destination slice.
34 destSliceElemType := destSliceValue.Type().Elem()
35 destSliceValue.Set(reflect.MakeSlice(destSliceValue.Type(), fromLen, fromLen))
36 for i := range iter.N(fromSliceValue.Len()) {
37 // The value inside the interface in the slice element.
38 itemValue := fromSliceValue.Index(i)
39 if itemValue.Kind() == reflect.Interface {
40 itemValue = itemValue.Elem()
41 }
42 convertedItem := itemValue.Convert(destSliceElemType)
43 destSliceValue.Index(i).Set(convertedItem)
44 }
45 }
0 package slices
1
2 import (
3 "testing"
4
5 "github.com/stretchr/testify/assert"
6 )
7
8 type herp int
9
10 func TestCastSliceInterface(t *testing.T) {
11 var dest []herp
12 MakeInto(&dest, []interface{}{herp(1), herp(2)})
13 assert.Len(t, dest, 2)
14 assert.EqualValues(t, 1, dest[0])
15 assert.EqualValues(t, 2, dest[1])
16 }
17
18 func TestCastSliceInts(t *testing.T) {
19 var dest []int
20 MakeInto(&dest, []uint32{1, 2})
21 assert.Len(t, dest, 2)
22 assert.EqualValues(t, 1, dest[0])
23 assert.EqualValues(t, 2, dest[1])
24 }
0 // Package slices has several utilities for operating on slices given Go's
1 // lack of generic types. Many functions take an argument of type func(l, r T)
2 // bool, that's expected to compute l < r where T is T in []T, the type of the
3 // given slice.
4 package slices
0 package slices
1
2 import "reflect"
3
4 // sl []T, f is func(*T) bool.
5 func FilterInPlace(sl interface{}, f interface{}) {
6 v := reflect.ValueOf(sl).Elem()
7 j := 0
8 for i := 0; i < v.Len(); i++ {
9 e := v.Index(i)
10 if reflect.ValueOf(f).Call([]reflect.Value{e.Addr()})[0].Bool() {
11 v.Index(j).Set(e)
12 j++
13 }
14 }
15 v.SetLen(j)
16 }
0 package slices
1
2 import "reflect"
3
4 type MapItem struct {
5 Key, Elem interface{}
6 }
7
8 // Creates a []struct{Key K; Value V} for map[K]V.
9 func FromMap(m interface{}) (slice []MapItem) {
10 mapValue := reflect.ValueOf(m)
11 for _, key := range mapValue.MapKeys() {
12 slice = append(slice, MapItem{key.Interface(), mapValue.MapIndex(key).Interface()})
13 }
14 return
15 }
16
17 // Returns all the elements []T, from m where m is map[K]T.
18 func FromMapElems(m interface{}) interface{} {
19 inValue := reflect.ValueOf(m)
20 outValue := reflect.MakeSlice(reflect.SliceOf(inValue.Type().Elem()), inValue.Len(), inValue.Len())
21 for i, key := range inValue.MapKeys() {
22 outValue.Index(i).Set(inValue.MapIndex(key))
23 }
24 return outValue.Interface()
25 }
26
27 // Returns all the elements []K, from m where m is map[K]T.
28 func FromMapKeys(m interface{}) interface{} {
29 inValue := reflect.ValueOf(m)
30 outValue := reflect.MakeSlice(reflect.SliceOf(inValue.Type().Key()), inValue.Len(), inValue.Len())
31 for i, key := range inValue.MapKeys() {
32 outValue.Index(i).Set(key)
33 }
34 return outValue.Interface()
35 }
36
37 // f: (T)T, input: []T, outout: []T
38 func Map(f, input interface{}) interface{} {
39 inputValue := reflect.ValueOf(input)
40 funcValue := reflect.ValueOf(f)
41 _len := inputValue.Len()
42 retValue := reflect.MakeSlice(reflect.TypeOf(input), _len, _len)
43 for i := 0; i < _len; i++ {
44 out := funcValue.Call([]reflect.Value{inputValue.Index(i)})
45 retValue.Index(i).Set(out[0])
46 }
47 return retValue.Interface()
48 }
0 package slices
1
2 import (
3 "testing"
4
5 "github.com/stretchr/testify/assert"
6 )
7
8 func TestFromMap(t *testing.T) {
9 sl := FromMap(map[string]int{"two": 2, "one": 1})
10 assert.Len(t, sl, 2)
11 Sort(sl, func(left, right MapItem) bool {
12 return left.Key.(string) < right.Key.(string)
13 })
14 assert.EqualValues(t, []MapItem{{"one", 1}, {"two", 2}}, sl)
15 }
0 package slices
1
2 import (
3 "container/heap"
4 "reflect"
5 "sort"
6 )
7
8 // Sorts the slice in place. Returns sl for convenience.
9 func Sort(sl interface{}, less interface{}) interface{} {
10 sorter := sorter{
11 sl: reflect.ValueOf(sl),
12 less: reflect.ValueOf(less),
13 }
14 sort.Sort(&sorter)
15 return sorter.sl.Interface()
16 }
17
18 // Creates a modifiable copy of a slice reference. Because you can't modify
19 // non-pointer types inside an interface{}.
20 func addressableSlice(slice interface{}) reflect.Value {
21 v := reflect.ValueOf(slice)
22 p := reflect.New(v.Type())
23 p.Elem().Set(v)
24 return p.Elem()
25 }
26
27 // Returns a "container/heap".Interface for the provided slice.
28 func HeapInterface(sl interface{}, less interface{}) heap.Interface {
29 ret := &sorter{
30 sl: addressableSlice(sl),
31 less: reflect.ValueOf(less),
32 }
33 heap.Init(ret)
34 return ret
35 }
0 package slices
1
2 import (
3 "testing"
4
5 "github.com/stretchr/testify/assert"
6 )
7
8 func TestSort(t *testing.T) {
9 a := []int{3, 2, 1}
10 Sort(a, func(left, right int) bool {
11 return left < right
12 })
13 assert.EqualValues(t, []int{1, 2, 3}, a)
14 }
0 package slices
1
2 import "reflect"
3
4 type sorter struct {
5 sl reflect.Value
6 less reflect.Value
7 }
8
9 func (s *sorter) Len() int {
10 return s.sl.Len()
11 }
12
13 func (s *sorter) Less(i, j int) bool {
14 return s.less.Call([]reflect.Value{
15 s.sl.Index(i),
16 s.sl.Index(j),
17 })[0].Bool()
18 }
19
20 func (s *sorter) Swap(i, j int) {
21 t := reflect.New(s.sl.Type().Elem()).Elem()
22 t.Set(s.sl.Index(i))
23 s.sl.Index(i).Set(s.sl.Index(j))
24 s.sl.Index(j).Set(t)
25 }
26
27 func (s *sorter) Pop() interface{} {
28 ret := s.sl.Index(s.sl.Len() - 1).Interface()
29 s.sl.SetLen(s.sl.Len() - 1)
30 return ret
31 }
32
33 func (s *sorter) Push(val interface{}) {
34 s.sl = reflect.Append(s.sl, reflect.ValueOf(val))
35 }
0 package missinggo
1
2 import (
3 "database/sql"
4 "time"
5 )
6
7 type SqliteTime time.Time
8
9 var _ sql.Scanner = (*SqliteTime)(nil)
10
11 func (me *SqliteTime) Scan(src interface{}) error {
12 var tt time.Time
13 tt, err := time.Parse("2006-01-02 15:04:05", string(src.([]byte)))
14 *me = SqliteTime(tt)
15 return err
16 }
0 package missinggo
1
2 import (
3 "fmt"
4 "io"
5 "runtime"
6 )
7
8 func WriteStack(w io.Writer, stack []uintptr) {
9 for _, pc := range stack {
10 if pc == 0 {
11 break
12 }
13 pc--
14 f := runtime.FuncForPC(pc)
15 if f.Name() == "runtime.goexit" {
16 continue
17 }
18 file, line := f.FileLine(pc)
19 fmt.Fprintf(w, "# %s:\t%s:%d\n", f.Name(), file, line)
20 }
21 fmt.Fprintf(w, "\n")
22 }
0 package missinggo
1
2 import (
3 "strconv"
4 "strings"
5 "unicode"
6 )
7
8 func StringTruth(s string) (ret bool) {
9 s = strings.TrimFunc(s, func(r rune) bool {
10 return r == 0 || unicode.IsSpace(r)
11 })
12 if s == "" {
13 return false
14 }
15 ret, err := strconv.ParseBool(s)
16 if err == nil {
17 return
18 }
19 i, err := strconv.ParseInt(s, 0, 0)
20 if err == nil {
21 return i != 0
22 }
23 return true
24 }
0 package missinggo
1
2 import (
3 "testing"
4
5 "github.com/stretchr/testify/assert"
6 )
7
8 func TestStringTruth(t *testing.T) {
9 for _, s := range []string{
10 "",
11 " ",
12 "\n",
13 "\x00",
14 "0",
15 } {
16 t.Run(s, func(t *testing.T) {
17 assert.False(t, StringTruth(s))
18 })
19 }
20 for _, s := range []string{
21 " 1",
22 "t",
23 } {
24 t.Run(s, func(t *testing.T) {
25 assert.True(t, StringTruth(s))
26 })
27 }
28 }
0 package missinggo
1
2 import (
3 "strings"
4
5 "github.com/huandu/xstrings"
6 )
7
8 func KebabCase(s string) string {
9 return strings.Replace(xstrings.ToSnakeCase(s), "_", "-", -1)
10 }
0 package missinggo
1
2 import (
3 "sync"
4 )
5
6 type RWLocker interface {
7 sync.Locker
8 RLock()
9 RUnlock()
10 }
0 package missinggo
1
2 import (
3 "regexp"
4 "runtime"
5 )
6
7 // It will be the one and only identifier after a package specifier.
8 var testNameRegexp = regexp.MustCompile(`\.(Test[\p{L}_\p{N}]*)`)
9
10 // Returns the name of the test function from the call stack. See
11 // http://stackoverflow.com/q/35535635/149482 for another method.
12 func GetTestName() string {
13 pc := make([]uintptr, 32)
14 n := runtime.Callers(0, pc)
15 for i := 0; i < n; i++ {
16 name := runtime.FuncForPC(pc[i]).Name()
17 ms := testNameRegexp.FindStringSubmatch(name)
18 if ms == nil {
19 continue
20 }
21 return ms[1]
22 }
23 panic("test name could not be recovered")
24 }
0 package missinggo
1
2 import (
3 "testing"
4
5 "github.com/stretchr/testify/assert"
6 )
7
8 // Since GetTestName panics if the test name isn't found, it'll be easy to
9 // expand the tests if we find weird cases.
10 func TestGetTestName(t *testing.T) {
11 assert.EqualValues(t, "TestGetTestName", GetTestName())
12 }
13
14 func TestGetSubtestName(t *testing.T) {
15 t.Run("hello", func(t *testing.T) {
16 assert.Contains(t, "TestGetSubtestName", GetTestName())
17 })
18 t.Run("world", func(t *testing.T) {
19 assert.Contains(t, "TestGetSubtestName", GetTestName())
20 })
21 }
0 package missinggo
1
2 import (
3 "math"
4 "time"
5 )
6
7 // Returns a time.Timer that calls f. The timer is initially stopped.
8 func StoppedFuncTimer(f func()) (t *time.Timer) {
9 t = time.AfterFunc(math.MaxInt64, f)
10 if !t.Stop() {
11 panic("timer already fired")
12 }
13 return
14 }
0 package missinggo
1
2 import (
3 "testing"
4 "time"
5
6 "github.com/bradfitz/iter"
7 "github.com/stretchr/testify/assert"
8 )
9
10 func TestTimerDrain(t *testing.T) {
11 tr := time.NewTimer(0)
12 <-tr.C
13 select {
14 case <-tr.C:
15 assert.Fail(t, "shouldn't have received again on the the expired timer")
16 default:
17 }
18 tr.Reset(1)
19 select {
20 case <-tr.C:
21 assert.Fail(t, "received too soon")
22 default:
23 }
24 time.Sleep(1)
25 <-tr.C
26 // Stop() should return false, as it just fired.
27 assert.False(t, tr.Stop())
28 tr.Reset(0)
29 // Check we receive again after a Reset().
30 <-tr.C
31 }
32
33 func TestTimerDoesNotFireAfterStop(t *testing.T) {
34 t.Skip("the standard library implementation is broken")
35 fail := make(chan struct{})
36 done := make(chan struct{})
37 defer close(done)
38 for range iter.N(1000) {
39 tr := time.NewTimer(0)
40 tr.Stop()
41 // There may or may not be a value in the channel now. But definitely
42 // one should not be added after we receive it.
43 select {
44 case <-tr.C:
45 default:
46 }
47 // Now set the timer to trigger in hour. It definitely shouldn't be
48 // receivable now for an hour.
49 tr.Reset(time.Hour)
50 go func() {
51 select {
52 case <-tr.C:
53 // As soon as the channel receives, notify failure.
54 fail <- struct{}{}
55 case <-done:
56 }
57 }()
58 }
59 select {
60 case <-fail:
61 t.FailNow()
62 case <-time.After(100 * time.Millisecond):
63 }
64 }
0 package missinggo
1
2 import (
3 "crypto/tls"
4 "strings"
5 )
6
7 // Select the best named certificate per the usual behaviour if
8 // c.GetCertificate is nil, and c.NameToCertificate is not.
9 func BestNamedCertificate(c *tls.Config, clientHello *tls.ClientHelloInfo) (*tls.Certificate, bool) {
10 name := strings.ToLower(clientHello.ServerName)
11 for len(name) > 0 && name[len(name)-1] == '.' {
12 name = name[:len(name)-1]
13 }
14
15 if cert, ok := c.NameToCertificate[name]; ok {
16 return cert, true
17 }
18
19 // try replacing labels in the name with wildcards until we get a
20 // match.
21 labels := strings.Split(name, ".")
22 for i := range labels {
23 labels[i] = "*"
24 candidate := strings.Join(labels, ".")
25 if cert, ok := c.NameToCertificate[candidate]; ok {
26 return cert, true
27 }
28 }
29
30 return nil, false
31 }
0 package missinggo
1
2 const MiB = 1 << 20
0 package missinggo
1
2 import (
3 "net/url"
4 "path"
5 )
6
7 // Returns URL opaque as an unrooted path.
8 func URLOpaquePath(u *url.URL) string {
9 if u.Opaque != "" {
10 return u.Opaque
11 }
12 return u.Path
13 }
14
15 // Cleans the (absolute) URL path, removing unnecessary . and .. elements. See
16 // "net/http".cleanPath.
17 func CleanURLPath(p string) string {
18 if p == "" {
19 return "/"
20 }
21 if p[0] != '/' {
22 p = "/" + p
23 }
24 cp := path.Clean(p)
25 // Add the trailing slash back, as it's relevant to a URL.
26 if p[len(p)-1] == '/' && cp != "/" {
27 cp += "/"
28 }
29 return cp
30 }
31
32 func URLJoinSubPath(base, rel string) string {
33 baseURL, err := url.Parse(base)
34 if err != nil {
35 // Honey badger doesn't give a fuck.
36 panic(err)
37 }
38 rel = CleanURLPath(rel)
39 baseURL.Path = path.Join(baseURL.Path, rel)
40 return baseURL.String()
41 }
0 package missinggo
1
2 import (
3 "net/url"
4 "testing"
5
6 "github.com/stretchr/testify/assert"
7 )
8
9 func TestURLOpaquePath(t *testing.T) {
10 assert.Equal(t, "sqlite3://sqlite3.db", (&url.URL{Scheme: "sqlite3", Path: "sqlite3.db"}).String())
11 u, err := url.Parse("sqlite3:sqlite3.db")
12 assert.NoError(t, err)
13 assert.Equal(t, "sqlite3.db", URLOpaquePath(u))
14 assert.Equal(t, "sqlite3:sqlite3.db", (&url.URL{Scheme: "sqlite3", Opaque: "sqlite3.db"}).String())
15 assert.Equal(t, "sqlite3:/sqlite3.db", (&url.URL{Scheme: "sqlite3", Opaque: "/sqlite3.db"}).String())
16 u, err = url.Parse("sqlite3:/sqlite3.db")
17 assert.NoError(t, err)
18 assert.Equal(t, "/sqlite3.db", u.Path)
19 assert.Equal(t, "/sqlite3.db", URLOpaquePath(u))
20 }
0 package missinggo
1
2 import (
3 "reflect"
4 "sync"
5 )
6
7 func WaitEvents(l sync.Locker, evs ...*Event) {
8 cases := make([]reflect.SelectCase, 0, len(evs))
9 for _, ev := range evs {
10 cases = append(cases, reflect.SelectCase{
11 Dir: reflect.SelectRecv,
12 Chan: reflect.ValueOf(ev.C()),
13 })
14 }
15 l.Unlock()
16 reflect.Select(cases)
17 l.Lock()
18 }
0 package x
1
2 // Panic if error. Just fucking add exceptions, please.
3 func Pie(err error) {
4 if err != nil {
5 panic(err)
6 }
7 }