updated godeps
Henry committed
Mar 23, 2015 at 09:51 UTC
c5a69db601bf291fc42b1a92f04377bd9eadb88e
89 files changed
+3406
-5312
Godeps/Godeps.json
+12
-20
@@ -7,7 +7,7 @@
7
"Deps": [
8
{
9
"ImportPath": "bazil.org/fuse",
10
- "Rev": "489e985dcb429718328be2bb055adb685c4ae1e3"
10
+ "Rev": "79d103f5608724e3ccee0a10daccc5e4aff03591"
11
},
12
{
13
"ImportPath": "code.google.com/p/go-uuid/uuid",
@@ -33,8 +33,8 @@
33
},
34
{
35
"ImportPath": "github.com/Sirupsen/logrus",
36
- "Comment": "v0.6.5",
37
- "Rev": "c0f7e35ed2e48f188c37581b4b743cf7383f85c6"
36
+ "Comment": "v0.7.1",
37
+ "Rev": "3fc34d061b9c78a70db853c7cb6b0576b6d4f32d"
38
},
39
{
40
"ImportPath": "github.com/braintree/manners",
@@ -67,7 +67,7 @@
67
},
68
{
69
"ImportPath": "github.com/coreos/go-semver/semver",
70
- "Rev": "6fe83ccda8fb9b7549c9ab4ba47f47858bc950aa"
70
+ "Rev": "568e959cd89871e61434c1143528d9162da89ef2"
71
},
72
{
73
"ImportPath": "github.com/crowdmob/goamz/aws",
@@ -102,14 +102,6 @@
102
"Comment": "v0.5.1",
103
"Rev": "27a863cdffdb0998d13e1e11992b18489aeeaa25"
104
},
105
- {
106
- "ImportPath": "github.com/gorilla/context",
107
- "Rev": "14f550f51af52180c2eefed15e5fd18d63c0a64a"
108
- },
109
- {
110
- "ImportPath": "github.com/gorilla/mux",
111
- "Rev": "4b8fbc56f3b2400a7c7ea3dba9b3539787c486b6"
112
- },
105
{
106
"ImportPath": "github.com/h2so5/utp",
107
"Rev": "654d875bb65e96729678180215cf080fe2810371"
@@ -141,7 +133,7 @@
133
},
134
{
135
"ImportPath": "github.com/jbenet/go-base58",
144
- "Rev": "568a28d73fd97651d3442392036a658b6976eed5"
136
+ "Rev": "6237cf65f3a6f7111cd8a42be3590df99a66bc7d"
137
},
138
{
139
"ImportPath": "github.com/jbenet/go-ctxgroup",
@@ -195,7 +187,7 @@
187
},
188
{
189
"ImportPath": "github.com/jbenet/go-reuseport",
198
- "Rev": "096958438ae3683c9f3c8ae0f7139b5ce600e6a8"
190
+ "Rev": "c71a70ef82a7acb87ad77704de1be7b9cd776a55"
191
},
192
{
193
"ImportPath": "github.com/jbenet/go-sockaddr/net",
@@ -211,7 +203,7 @@
203
},
204
{
205
"ImportPath": "github.com/kardianos/osext",
214
- "Rev": "ccfcd0245381f0c94c68f50626665eed3c6b726a"
206
+ "Rev": "efacde03154693404c65e7aa7d461ac9014acd0c"
207
},
208
{
209
"ImportPath": "github.com/kr/binarydist",
@@ -223,23 +215,23 @@
215
},
216
{
217
"ImportPath": "github.com/syndtr/goleveldb/leveldb",
226
- "Rev": "871eee0a7546bb7d1b2795142e29c4534abc49b3"
218
+ "Rev": "87e4e645d80ae9c537e8f2dee52b28036a5dd75e"
219
},
220
{
221
"ImportPath": "github.com/syndtr/gosnappy/snappy",
230
- "Rev": "ce8acff4829e0c2458a67ead32390ac0a381c862"
222
+ "Rev": "156a073208e131d7d2e212cb749feae7c339e846"
223
},
224
{
225
"ImportPath": "golang.org/x/crypto/blowfish",
234
- "Rev": "1351f936d976c60a0a48d728281922cf63eafb8d"
226
+ "Rev": "b7d6bf2c61544745a02f83dec90393985fc3a065"
227
},
228
{
229
"ImportPath": "golang.org/x/crypto/sha3",
238
- "Rev": "1351f936d976c60a0a48d728281922cf63eafb8d"
230
+ "Rev": "b7d6bf2c61544745a02f83dec90393985fc3a065"
231
},
232
{
233
"ImportPath": "golang.org/x/net/context",
242
- "Rev": "b6fdb7d8a4ccefede406f8fe0f017fb58265054c"
234
+ "Rev": "7dbad50ab5b31073856416cdcfeb2796d682f844"
235
},
236
{
237
"ImportPath": "gopkg.in/fsnotify.v1",
Godeps/_workspace/src/bazil.org/fuse/error_darwin.go
+8
-27
@@ -4,33 +4,14 @@ import (
4
"syscall"
5
)
6
7
-type getxattrError struct {
8
- error
9
-}
10
-
11
-func (getxattrError) Errno() Errno {
12
- return Errno(syscall.ENOATTR)
13
-}
14
-
15
-// getxattr return value for "extended attribute does not exist" is
16
-// ENOATTR on OS X, and ENODATA on Linux and apparently at least
17
-// NetBSD. There may be a #define ENOATTR too, but the value is
18
-// ENODATA in the actual syscalls. ENOATTR is not in any of the
19
-// standards, ENODATA exists but is only used for STREAMs.
20
-//
21
-// https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man2/getxattr.2.html
22
-// http://mail-index.netbsd.org/tech-kern/2012/04/30/msg013090.html
23
-// http://mail-index.netbsd.org/tech-kern/2012/04/30/msg013097.html
24
-// http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/errno.h.html
25
-func translateGetxattrError(err error) error {
26
- ferr, ok := err.(ErrorNumber)
27
- if !ok {
28
- return err
29
- }
7
+const (
8
+ ENOATTR = Errno(syscall.ENOATTR)
9
+)
10
31
- if ferr.Errno() != ENODATA {
32
- return err
33
- }
11
+const (
12
+ errNoXattr = ENOATTR
13
+)
14
35
- return getxattrError{err}
15
+func init() {
16
+ errnoNames[errNoXattr] = "ENOATTR"
17
}
Godeps/_workspace/src/bazil.org/fuse/error_freebsd.go
new
+15
@@ -0,0 +1,15 @@
1
+package fuse
2
+
3
+import "syscall"
4
+
5
+const (
6
+ ENOATTR = Errno(syscall.ENOATTR)
7
+)
8
+
9
+const (
10
+ errNoXattr = ENOATTR
11
+)
12
+
13
+func init() {
14
+ errnoNames[errNoXattr] = "ENOATTR"
15
+}
Godeps/_workspace/src/bazil.org/fuse/error_linux.go
new
+17
@@ -0,0 +1,17 @@
1
+package fuse
2
+
3
+import (
4
+ "syscall"
5
+)
6
+
7
+const (
8
+ ENODATA = Errno(syscall.ENODATA)
9
+)
10
+
11
+const (
12
+ errNoXattr = ENODATA
13
+)
14
+
15
+func init() {
16
+ errnoNames[errNoXattr] = "ENODATA"
17
+}
Godeps/_workspace/src/bazil.org/fuse/error_std.go
+29
-5
@@ -1,7 +1,31 @@
1
-// +build !darwin
2
-
1
package fuse
2
5
-func translateGetxattrError(err error) error {
6
- return err
7
-}
3
+// There is very little commonality in extended attribute errors
4
+// across platforms.
5
+//
6
+// getxattr return value for "extended attribute does not exist" is
7
+// ENOATTR on OS X, and ENODATA on Linux and apparently at least
8
+// NetBSD. There may be a #define ENOATTR on Linux too, but the value
9
+// is ENODATA in the actual syscalls. FreeBSD and OpenBSD have no
10
+// ENODATA, only ENOATTR. ENOATTR is not in any of the standards,
11
+// ENODATA exists but is only used for STREAMs.
12
+//
13
+// Each platform will define it a errNoXattr constant, and this file
14
+// will enforce that it implements the right interfaces and hide the
15
+// implementation.
16
+//
17
+// https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man2/getxattr.2.html
18
+// http://mail-index.netbsd.org/tech-kern/2012/04/30/msg013090.html
19
+// http://mail-index.netbsd.org/tech-kern/2012/04/30/msg013097.html
20
+// http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/errno.h.html
21
+// http://www.freebsd.org/cgi/man.cgi?query=extattr_get_file&sektion=2
22
+// http://nixdoc.net/man-pages/openbsd/man2/extattr_get_file.2.html
23
+
24
+// ErrNoXattr is a platform-independent error value meaning the
25
+// extended attribute was not found. It can be used to respond to
26
+// GetxattrRequest and such.
27
+const ErrNoXattr = errNoXattr
28
+
29
+var _ error = ErrNoXattr
30
+var _ Errno = ErrNoXattr
31
+var _ ErrorNumber = ErrNoXattr
Godeps/_workspace/src/bazil.org/fuse/fs/fstestutil/mountinfo.go
+12
@@ -12,3 +12,15 @@ type MountInfo struct {
12
func GetMountInfo(mnt string) (*MountInfo, error) {
13
return getMountInfo(mnt)
14
}
15
+
16
+// cstr converts a nil-terminated C string into a Go string
17
+func cstr(ca []int8) string {
18
+ s := make([]byte, 0, len(ca))
19
+ for _, c := range ca {
20
+ if c == 0x00 {
21
+ break
22
+ }
23
+ s = append(s, byte(c))
24
+ }
25
+ return string(s)
26
+}
Godeps/_workspace/src/bazil.org/fuse/fs/fstestutil/mountinfo_darwin.go
-12
@@ -5,18 +5,6 @@ import (
5
"syscall"
6
)
7
8
-// cstr converts a nil-terminated C string into a Go string
9
-func cstr(ca []int8) string {
10
- s := make([]byte, 0, len(ca))
11
- for _, c := range ca {
12
- if c == 0x00 {
13
- break
14
- }
15
- s = append(s, byte(c))
16
- }
17
- return string(s)
18
-}
19
-
8
var re = regexp.MustCompile(`\\(.)`)
9
10
// unescape removes backslash-escaping. The escaped characters are not
Godeps/_workspace/src/bazil.org/fuse/fs/fstestutil/mountinfo_freebsd.go
new
+7
@@ -0,0 +1,7 @@
1
+package fstestutil
2
+
3
+import "errors"
4
+
5
+func getMountInfo(mnt string) (*MountInfo, error) {
6
+ return nil, errors.New("FreeBSD has no useful mount information")
7
+}
Godeps/_workspace/src/bazil.org/fuse/fs/fstestutil/record/record.go
+3
-4
@@ -20,8 +20,7 @@ func (w *Writes) Write(ctx context.Context, req *fuse.WriteRequest, resp *fuse.W
20
n, err := w.buf.Write(req.Data)
21
resp.Size = n
22
if err != nil {
23
- // TODO hiding error
24
- return fuse.EIO
23
+ return err
24
}
25
return nil
26
}
@@ -293,7 +292,7 @@ var _ = fs.NodeGetxattrer(&Getxattrs{})
292
func (r *Getxattrs) Getxattr(ctx context.Context, req *fuse.GetxattrRequest, resp *fuse.GetxattrResponse) error {
293
tmp := *req
294
r.rec.RecordRequest(&tmp)
296
- return fuse.ENODATA
295
+ return fuse.ErrNoXattr
296
}
297
298
// RecordedGetxattr returns information about the Getxattr request.
@@ -318,7 +317,7 @@ var _ = fs.NodeListxattrer(&Listxattrs{})
317
func (r *Listxattrs) Listxattr(ctx context.Context, req *fuse.ListxattrRequest, resp *fuse.ListxattrResponse) error {
318
tmp := *req
319
r.rec.RecordRequest(&tmp)
321
- return fuse.ENODATA
320
+ return fuse.ErrNoXattr
321
}
322
323
// RecordedListxattr returns information about the Listxattr request.
Godeps/_workspace/src/bazil.org/fuse/fs/fstestutil/testfs.go
+19
@@ -5,6 +5,7 @@ import (
5
6
"github.com/jbenet/go-ipfs/Godeps/_workspace/src/bazil.org/fuse"
7
"github.com/jbenet/go-ipfs/Godeps/_workspace/src/bazil.org/fuse/fs"
8
+ "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"
9
)
10
11
// SimpleFS is a trivial FS that just implements the Root method.
@@ -27,3 +28,21 @@ func (f File) Attr() fuse.Attr { return fuse.Attr{Mode: 0666} }
28
type Dir struct{}
29
30
func (f Dir) Attr() fuse.Attr { return fuse.Attr{Mode: os.ModeDir | 0777} }
31
+
32
+// ChildMap is a directory with child nodes looked up from a map.
33
+type ChildMap map[string]fs.Node
34
+
35
+var _ = fs.Node(ChildMap{})
36
+var _ = fs.NodeStringLookuper(ChildMap{})
37
+
38
+func (f ChildMap) Attr() fuse.Attr {
39
+ return fuse.Attr{Mode: os.ModeDir | 0777}
40
+}
41
+
42
+func (f ChildMap) Lookup(ctx context.Context, name string) (fs.Node, error) {
43
+ child, ok := f[name]
44
+ if !ok {
45
+ return nil, fuse.ENOENT
46
+ }
47
+ return child, nil
48
+}
Godeps/_workspace/src/bazil.org/fuse/fs/helpers_test.go
new
+67
@@ -0,0 +1,67 @@
1
+package fs_test
2
+
3
+import (
4
+ "errors"
5
+ "flag"
6
+ "os"
7
+ "os/exec"
8
+ "path/filepath"
9
+ "testing"
10
+)
11
+
12
+var childHelpers = map[string]func(){}
13
+
14
+type childProcess struct {
15
+ name string
16
+ fn func()
17
+}
18
+
19
+var _ flag.Value = (*childProcess)(nil)
20
+
21
+func (c *childProcess) String() string {
22
+ return c.name
23
+}
24
+
25
+func (c *childProcess) Set(s string) error {
26
+ fn, ok := childHelpers[s]
27
+ if !ok {
28
+ return errors.New("helper not found")
29
+ }
30
+ c.name = s
31
+ c.fn = fn
32
+ return nil
33
+}
34
+
35
+var childMode childProcess
36
+
37
+func init() {
38
+ flag.Var(&childMode, "fuse.internal.child", "internal use only")
39
+}
40
+
41
+// childCmd prepares a test function to be run in a subprocess, with
42
+// childMode set to true. Caller must still call Run or Start.
43
+//
44
+// Re-using the test executable as the subprocess is useful because
45
+// now test executables can e.g. be cross-compiled, transferred
46
+// between hosts, and run in settings where the whole Go development
47
+// environment is not installed.
48
+func childCmd(childName string) (*exec.Cmd, error) {
49
+ // caller may set cwd, so we can't rely on relative paths
50
+ executable, err := filepath.Abs(os.Args[0])
51
+ if err != nil {
52
+ return nil, err
53
+ }
54
+ cmd := exec.Command(executable, "-fuse.internal.child="+childName)
55
+ cmd.Stdout = os.Stdout
56
+ cmd.Stderr = os.Stderr
57
+ return cmd, nil
58
+}
59
+
60
+func TestMain(m *testing.M) {
61
+ flag.Parse()
62
+ if childMode.fn != nil {
63
+ childMode.fn()
64
+ os.Exit(0)
65
+ }
66
+ os.Exit(m.Run())
67
+}
Godeps/_workspace/src/bazil.org/fuse/fs/serve.go
+30
-6
@@ -101,6 +101,15 @@ type NodeGetattrer interface {
101
102
type NodeSetattrer interface {
103
// Setattr sets the standard metadata for the receiver.
104
+ //
105
+ // Note, this is also used to communicate changes in the size of
106
+ // the file. Not implementing Setattr causes writes to be unable
107
+ // to grow the file (except with OpenDirectIO, which bypasses that
108
+ // mechanism).
109
+ //
110
+ // req.Valid is a bitmask of what fields are actually being set.
111
+ // For example, the method should not change the mode of the file
112
+ // unless req.Valid.Mode() is true.
113
Setattr(ctx context.Context, req *fuse.SetattrRequest, resp *fuse.SetattrResponse) error
114
}
115
@@ -203,9 +212,7 @@ type NodeGetxattrer interface {
212
// Getxattr gets an extended attribute by the given name from the
213
// node.
214
//
206
- // If there is no xattr by that name, returns fuse.ENODATA. This
207
- // will be translated to the platform-specific correct error code
208
- // by the framework.
215
+ // If there is no xattr by that name, returns fuse.ErrNoXattr.
216
Getxattr(ctx context.Context, req *fuse.GetxattrRequest, resp *fuse.GetxattrResponse) error
217
}
218
@@ -223,9 +230,7 @@ type NodeSetxattrer interface {
230
type NodeRemovexattrer interface {
231
// Removexattr removes an extended attribute for the name.
232
//
226
- // If there is no xattr by that name, returns fuse.ENODATA. This
227
- // will be translated to the platform-specific correct error code
228
- // by the framework.
233
+ // If there is no xattr by that name, returns fuse.ErrNoXattr.
234
Removexattr(ctx context.Context, req *fuse.RemovexattrRequest) error
235
}
236
@@ -279,10 +284,29 @@ type HandleReadDirAller interface {
284
}
285
286
type HandleReader interface {
287
+ // Read requests to read data from the handle.
288
+ //
289
+ // There is a page cache in the kernel that normally submits only
290
+ // page-aligned reads spanning one or more pages. However, you
291
+ // should not rely on this. To see individual requests as
292
+ // submitted by the file system clients, set OpenDirectIO.
293
+ //
294
+ // Note that reads beyond the size of the file as reported by Attr
295
+ // are not even attempted (except in OpenDirectIO mode).
296
Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.ReadResponse) error
297
}
298
299
type HandleWriter interface {
300
+ // Write requests to write data into the handle.
301
+ //
302
+ // There is a writeback page cache in the kernel that normally submits
303
+ // only page-aligned writes spanning one or more pages. However,
304
+ // you should not rely on this. To see individual requests as
305
+ // submitted by the file system clients, set OpenDirectIO.
306
+ //
307
+ // Note that file size changes are communicated through Setattr.
308
+ // Writes beyond the size of the file as reported by Attr are not
309
+ // even attempted (except in OpenDirectIO mode).
310
Write(ctx context.Context, req *fuse.WriteRequest, resp *fuse.WriteResponse) error
311
}
312
Godeps/_workspace/src/bazil.org/fuse/fs/serve_test.go
+125
-126
@@ -3,14 +3,11 @@ package fs_test
3
import (
4
"bytes"
5
"errors"
6
- "flag"
6
"io"
7
"io/ioutil"
8
"log"
9
"os"
10
"os/exec"
12
- "path/filepath"
13
- "regexp"
11
"runtime"
12
"strings"
13
"syscall"
@@ -40,55 +37,6 @@ func init() {
37
fstestutil.DebugByDefault()
38
}
39
43
-var childMode bool
44
-
45
-func init() {
46
- flag.BoolVar(&childMode, "fuse.internal.childmode", false, "internal use only")
47
-}
48
-
49
-// childCmd prepares a test function to be run in a subprocess, with
50
-// childMode set to true. Caller must still call Run or Start.
51
-//
52
-// Re-using the test executable as the subprocess is useful because
53
-// now test executables can e.g. be cross-compiled, transferred
54
-// between hosts, and run in settings where the whole Go development
55
-// environment is not installed.
56
-func childCmd(testName string) (*exec.Cmd, error) {
57
- // caller may set cwd, so we can't rely on relative paths
58
- executable, err := filepath.Abs(os.Args[0])
59
- if err != nil {
60
- return nil, err
61
- }
62
- testName = regexp.QuoteMeta(testName)
63
- cmd := exec.Command(executable, "-test.run=^"+testName+"$", "-fuse.internal.childmode")
64
- cmd.Stdout = os.Stdout
65
- cmd.Stderr = os.Stderr
66
- return cmd, nil
67
-}
68
-
69
-// childMapFS is an FS with one fixed child named "child".
70
-type childMapFS map[string]fs.Node
71
-
72
-var _ = fs.FS(childMapFS{})
73
-var _ = fs.Node(childMapFS{})
74
-var _ = fs.NodeStringLookuper(childMapFS{})
75
-
76
-func (f childMapFS) Attr() fuse.Attr {
77
- return fuse.Attr{Inode: 1, Mode: os.ModeDir | 0777}
78
-}
79
-
80
-func (f childMapFS) Root() (fs.Node, error) {
81
- return f, nil
82
-}
83
-
84
-func (f childMapFS) Lookup(ctx context.Context, name string) (fs.Node, error) {
85
- child, ok := f[name]
86
- if !ok {
87
- return nil, fuse.ENOENT
88
- }
89
- return child, nil
90
-}
91
-
40
// symlink can be embedded in a struct to make it look like a symlink.
41
type symlink struct {
42
target string
@@ -271,7 +219,7 @@ func testReadAll(t *testing.T, path string) {
219
220
func TestReadAll(t *testing.T) {
221
t.Parallel()
274
- mnt, err := fstestutil.MountedT(t, childMapFS{"child": readAll{}})
222
+ mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{fstestutil.ChildMap{"child": readAll{}}})
223
if err != nil {
224
t.Fatal(err)
225
}
@@ -300,7 +248,7 @@ func (readWithHandleRead) Read(ctx context.Context, req *fuse.ReadRequest, resp
248
249
func TestReadAllWithHandleRead(t *testing.T) {
250
t.Parallel()
303
- mnt, err := fstestutil.MountedT(t, childMapFS{"child": readWithHandleRead{}})
251
+ mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{fstestutil.ChildMap{"child": readWithHandleRead{}}})
252
if err != nil {
253
t.Fatal(err)
254
}
@@ -319,7 +267,7 @@ type release struct {
267
func TestRelease(t *testing.T) {
268
t.Parallel()
269
r := &release{}
322
- mnt, err := fstestutil.MountedT(t, childMapFS{"child": r})
270
+ mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{fstestutil.ChildMap{"child": r}})
271
if err != nil {
272
t.Fatal(err)
273
}
@@ -346,7 +294,7 @@ type write struct {
294
func TestWrite(t *testing.T) {
295
t.Parallel()
296
w := &write{}
349
- mnt, err := fstestutil.MountedT(t, childMapFS{"child": w})
297
+ mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{fstestutil.ChildMap{"child": w}})
298
if err != nil {
299
t.Fatal(err)
300
}
@@ -393,7 +341,7 @@ type writeLarge struct {
341
func TestWriteLarge(t *testing.T) {
342
t.Parallel()
343
w := &write{}
396
- mnt, err := fstestutil.MountedT(t, childMapFS{"child": w})
344
+ mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{fstestutil.ChildMap{"child": w}})
345
if err != nil {
346
t.Fatal(err)
347
}
@@ -440,7 +388,7 @@ type writeTruncateFlush struct {
388
func TestWriteTruncateFlush(t *testing.T) {
389
t.Parallel()
390
w := &writeTruncateFlush{}
443
- mnt, err := fstestutil.MountedT(t, childMapFS{"child": w})
391
+ mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{fstestutil.ChildMap{"child": w}})
392
if err != nil {
393
t.Fatal(err)
394
}
@@ -838,7 +786,7 @@ func (dataHandleTest) Open(ctx context.Context, req *fuse.OpenRequest, resp *fus
786
func TestDataHandle(t *testing.T) {
787
t.Parallel()
788
f := &dataHandleTest{}
841
- mnt, err := fstestutil.MountedT(t, childMapFS{"child": f})
789
+ mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{fstestutil.ChildMap{"child": f}})
790
if err != nil {
791
t.Fatal(err)
792
}
@@ -883,7 +831,7 @@ func TestInterrupt(t *testing.T) {
831
t.Parallel()
832
f := &interrupt{}
833
f.hanging = make(chan struct{}, 1)
886
- mnt, err := fstestutil.MountedT(t, childMapFS{"child": f})
834
+ mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{fstestutil.ChildMap{"child": f}})
835
if err != nil {
836
t.Fatal(err)
837
}
@@ -947,7 +895,7 @@ type truncate struct {
895
func testTruncate(t *testing.T, toSize int64) {
896
t.Parallel()
897
f := &truncate{}
950
- mnt, err := fstestutil.MountedT(t, childMapFS{"child": f})
898
+ mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{fstestutil.ChildMap{"child": f}})
899
if err != nil {
900
t.Fatal(err)
901
}
@@ -988,7 +936,7 @@ type ftruncate struct {
936
func testFtruncate(t *testing.T, toSize int64) {
937
t.Parallel()
938
f := &ftruncate{}
991
- mnt, err := fstestutil.MountedT(t, childMapFS{"child": f})
939
+ mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{fstestutil.ChildMap{"child": f}})
940
if err != nil {
941
t.Fatal(err)
942
}
@@ -1038,7 +986,7 @@ type truncateWithOpen struct {
986
func TestTruncateWithOpen(t *testing.T) {
987
t.Parallel()
988
f := &truncateWithOpen{}
1041
- mnt, err := fstestutil.MountedT(t, childMapFS{"child": f})
989
+ mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{fstestutil.ChildMap{"child": f}})
990
if err != nil {
991
t.Fatal(err)
992
}
@@ -1165,7 +1113,7 @@ func (f *chmod) Setattr(ctx context.Context, req *fuse.SetattrRequest, resp *fus
1113
func TestChmod(t *testing.T) {
1114
t.Parallel()
1115
f := &chmod{}
1168
- mnt, err := fstestutil.MountedT(t, childMapFS{"child": f})
1116
+ mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{fstestutil.ChildMap{"child": f}})
1117
if err != nil {
1118
t.Fatal(err)
1119
}
@@ -1199,7 +1147,7 @@ func (f *open) Open(ctx context.Context, req *fuse.OpenRequest, resp *fuse.OpenR
1147
func TestOpen(t *testing.T) {
1148
t.Parallel()
1149
f := &open{}
1202
- mnt, err := fstestutil.MountedT(t, childMapFS{"child": f})
1150
+ mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{fstestutil.ChildMap{"child": f}})
1151
if err != nil {
1152
t.Fatal(err)
1153
}
@@ -1307,7 +1255,7 @@ func (f *getxattr) Getxattr(ctx context.Context, req *fuse.GetxattrRequest, resp
1255
func TestGetxattr(t *testing.T) {
1256
t.Parallel()
1257
f := &getxattr{}
1310
- mnt, err := fstestutil.MountedT(t, childMapFS{"child": f})
1258
+ mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{fstestutil.ChildMap{"child": f}})
1259
if err != nil {
1260
t.Fatal(err)
1261
}
@@ -1343,7 +1291,7 @@ func (f *getxattrTooSmall) Getxattr(ctx context.Context, req *fuse.GetxattrReque
1291
func TestGetxattrTooSmall(t *testing.T) {
1292
t.Parallel()
1293
f := &getxattrTooSmall{}
1346
- mnt, err := fstestutil.MountedT(t, childMapFS{"child": f})
1294
+ mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{fstestutil.ChildMap{"child": f}})
1295
if err != nil {
1296
t.Fatal(err)
1297
}
@@ -1374,7 +1322,7 @@ func (f *getxattrSize) Getxattr(ctx context.Context, req *fuse.GetxattrRequest,
1322
func TestGetxattrSize(t *testing.T) {
1323
t.Parallel()
1324
f := &getxattrSize{}
1377
- mnt, err := fstestutil.MountedT(t, childMapFS{"child": f})
1325
+ mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{fstestutil.ChildMap{"child": f}})
1326
if err != nil {
1327
t.Fatal(err)
1328
}
@@ -1406,7 +1354,7 @@ func (f *listxattr) Listxattr(ctx context.Context, req *fuse.ListxattrRequest, r
1354
func TestListxattr(t *testing.T) {
1355
t.Parallel()
1356
f := &listxattr{}
1409
- mnt, err := fstestutil.MountedT(t, childMapFS{"child": f})
1357
+ mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{fstestutil.ChildMap{"child": f}})
1358
if err != nil {
1359
t.Fatal(err)
1360
}
@@ -1445,7 +1393,7 @@ func (f *listxattrTooSmall) Listxattr(ctx context.Context, req *fuse.ListxattrRe
1393
func TestListxattrTooSmall(t *testing.T) {
1394
t.Parallel()
1395
f := &listxattrTooSmall{}
1448
- mnt, err := fstestutil.MountedT(t, childMapFS{"child": f})
1396
+ mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{fstestutil.ChildMap{"child": f}})
1397
if err != nil {
1398
t.Fatal(err)
1399
}
@@ -1476,7 +1424,7 @@ func (f *listxattrSize) Listxattr(ctx context.Context, req *fuse.ListxattrReques
1424
func TestListxattrSize(t *testing.T) {
1425
t.Parallel()
1426
f := &listxattrSize{}
1479
- mnt, err := fstestutil.MountedT(t, childMapFS{"child": f})
1427
+ mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{fstestutil.ChildMap{"child": f}})
1428
if err != nil {
1429
t.Fatal(err)
1430
}
@@ -1507,7 +1455,7 @@ func testSetxattr(t *testing.T, size int) {
1455
1456
t.Parallel()
1457
f := &setxattr{}
1510
- mnt, err := fstestutil.MountedT(t, childMapFS{"child": f})
1458
+ mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{fstestutil.ChildMap{"child": f}})
1459
if err != nil {
1460
t.Fatal(err)
1461
}
@@ -1560,7 +1508,7 @@ type removexattr struct {
1508
func TestRemovexattr(t *testing.T) {
1509
t.Parallel()
1510
f := &removexattr{}
1563
- mnt, err := fstestutil.MountedT(t, childMapFS{"child": f})
1511
+ mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{fstestutil.ChildMap{"child": f}})
1512
if err != nil {
1513
t.Fatal(err)
1514
}
@@ -1681,74 +1629,78 @@ func (f *inMemoryFile) Write(ctx context.Context, req *fuse.WriteRequest, resp *
1629
return nil
1630
}
1631
1684
-type mmap struct {
1685
- inMemoryFile
1686
- // We don't actually care about whether the fsync happened or not;
1687
- // this just lets us force the page cache to send the writes to
1688
- // FUSE, so we can reliably verify they came through.
1689
- record.Fsyncs
1632
+const mmapSize = 16 * 4096
1633
+
1634
+var mmapWrites = map[int]byte{
1635
+ 10: 'a',
1636
+ 4096: 'b',
1637
+ 4097: 'c',
1638
+ mmapSize - 4096: 'd',
1639
+ mmapSize - 1: 'z',
1640
}
1641
1692
-func TestMmap(t *testing.T) {
1693
- const size = 16 * 4096
1694
- writes := map[int]byte{
1695
- 10: 'a',
1696
- 4096: 'b',
1697
- 4097: 'c',
1698
- size - 4096: 'd',
1699
- size - 1: 'z',
1642
+func helperMmap() {
1643
+ f, err := os.Create("child")
1644
+ if err != nil {
1645
+ log.Fatalf("Create: %v", err)
1646
}
1647
+ defer f.Close()
1648
1702
- // Run the mmap-using parts of the test in a subprocess, to avoid
1703
- // an intentional page fault hanging the whole process (because it
1704
- // would need to be served by the same process, and there might
1705
- // not be a thread free to do that). Merely bumping GOMAXPROCS is
1706
- // not enough to prevent the hangs reliably.
1707
- if childMode {
1708
- f, err := os.Create("child")
1709
- if err != nil {
1710
- t.Fatalf("Create: %v", err)
1711
- }
1712
- defer f.Close()
1649
+ data, err := syscall.Mmap(int(f.Fd()), 0, mmapSize, syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_SHARED)
1650
+ if err != nil {
1651
+ log.Fatalf("Mmap: %v", err)
1652
+ }
1653
1714
- data, err := syscall.Mmap(int(f.Fd()), 0, size, syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_SHARED)
1715
- if err != nil {
1716
- t.Fatalf("Mmap: %v", err)
1717
- }
1654
+ for i, b := range mmapWrites {
1655
+ data[i] = b
1656
+ }
1657
1719
- for i, b := range writes {
1720
- data[i] = b
1721
- }
1658
+ if err := syscallx.Msync(data, syscall.MS_SYNC); err != nil {
1659
+ log.Fatalf("Msync: %v", err)
1660
+ }
1661
1723
- if err := syscallx.Msync(data, syscall.MS_SYNC); err != nil {
1724
- t.Fatalf("Msync: %v", err)
1725
- }
1662
+ if err := syscall.Munmap(data); err != nil {
1663
+ log.Fatalf("Munmap: %v", err)
1664
+ }
1665
1727
- if err := syscall.Munmap(data); err != nil {
1728
- t.Fatalf("Munmap: %v", err)
1729
- }
1666
+ if err := f.Sync(); err != nil {
1667
+ log.Fatalf("Fsync = %v", err)
1668
+ }
1669
1731
- if err := f.Sync(); err != nil {
1732
- t.Fatalf("Fsync = %v", err)
1733
- }
1670
+ err = f.Close()
1671
+ if err != nil {
1672
+ log.Fatalf("Close: %v", err)
1673
+ }
1674
+}
1675
1735
- err = f.Close()
1736
- if err != nil {
1737
- t.Fatalf("Close: %v", err)
1738
- }
1676
+func init() {
1677
+ childHelpers["mmap"] = helperMmap
1678
+}
1679
1740
- return
1741
- }
1680
+type mmap struct {
1681
+ inMemoryFile
1682
+ // We don't actually care about whether the fsync happened or not;
1683
+ // this just lets us force the page cache to send the writes to
1684
+ // FUSE, so we can reliably verify they came through.
1685
+ record.Fsyncs
1686
+}
1687
+
1688
+func TestMmap(t *testing.T) {
1689
1690
w := &mmap{}
1744
- w.data = make([]byte, size)
1745
- mnt, err := fstestutil.MountedT(t, childMapFS{"child": w})
1691
+ w.data = make([]byte, mmapSize)
1692
+ mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{fstestutil.ChildMap{"child": w}})
1693
if err != nil {
1694
t.Fatal(err)
1695
}
1696
defer mnt.Close()
1697
1751
- child, err := childCmd("TestMmap")
1698
+ // Run the mmap-using parts of the test in a subprocess, to avoid
1699
+ // an intentional page fault hanging the whole process (because it
1700
+ // would need to be served by the same process, and there might
1701
+ // not be a thread free to do that). Merely bumping GOMAXPROCS is
1702
+ // not enough to prevent the hangs reliably.
1703
+ child, err := childCmd("mmap")
1704
if err != nil {
1705
t.Fatal(err)
1706
}
@@ -1758,12 +1710,12 @@ func TestMmap(t *testing.T) {
1710
}
1711
1712
got := w.data
1761
- if g, e := len(got), size; g != e {
1713
+ if g, e := len(got), mmapSize; g != e {
1714
t.Fatalf("bad write length: %d != %d", g, e)
1715
}
1716
for i, g := range got {
1717
// default '\x00' for writes[i] is good here
1766
- if e := writes[i]; g != e {
1718
+ if e := mmapWrites[i]; g != e {
1719
t.Errorf("wrong byte at offset %d: %q != %q", i, g, e)
1720
}
1721
}
@@ -1790,7 +1742,7 @@ func (directRead) Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.Re
1742
1743
func TestDirectRead(t *testing.T) {
1744
t.Parallel()
1793
- mnt, err := fstestutil.MountedT(t, childMapFS{"child": directRead{}})
1745
+ mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{fstestutil.ChildMap{"child": directRead{}}})
1746
if err != nil {
1747
t.Fatal(err)
1748
}
@@ -1798,3 +1750,50 @@ func TestDirectRead(t *testing.T) {
1750
1751
testReadAll(t, mnt.Dir+"/child")
1752
}
1753
+
1754
+// Test direct Write.
1755
+
1756
+type directWrite struct {
1757
+ fstestutil.File
1758
+ record.Writes
1759
+}
1760
+
1761
+// explicitly not defining Attr / Setattr and managing Size
1762
+
1763
+func (f *directWrite) Open(ctx context.Context, req *fuse.OpenRequest, resp *fuse.OpenResponse) (fs.Handle, error) {
1764
+ // do not allow the kernel to use page cache
1765
+ resp.Flags |= fuse.OpenDirectIO
1766
+ return f, nil
1767
+}
1768
+
1769
+func TestDirectWrite(t *testing.T) {
1770
+ t.Parallel()
1771
+ w := &directWrite{}
1772
+ mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{fstestutil.ChildMap{"child": w}})
1773
+ if err != nil {
1774
+ t.Fatal(err)
1775
+ }
1776
+ defer mnt.Close()
1777
+
1778
+ f, err := os.OpenFile(mnt.Dir+"/child", os.O_RDWR, 0666)
1779
+ if err != nil {
1780
+ t.Fatalf("Create: %v", err)
1781
+ }
1782
+ defer f.Close()
1783
+ n, err := f.Write([]byte(hi))
1784
+ if err != nil {
1785
+ t.Fatalf("Write: %v", err)
1786
+ }
1787
+ if n != len(hi) {
1788
+ t.Fatalf("short write; n=%d; hi=%d", n, len(hi))
1789
+ }
1790
+
1791
+ err = f.Close()
1792
+ if err != nil {
1793
+ t.Fatalf("Close: %v", err)
1794
+ }
1795
+
1796
+ if got := string(w.RecordedWriteData()); got != hi {
1797
+ t.Errorf("write = %q, want %q", got, hi)
1798
+ }
1799
+}
Godeps/_workspace/src/bazil.org/fuse/fuse.go
+24
-37
@@ -47,11 +47,17 @@
47
//
48
// Op(ctx context.Context, req *OpRequest, resp *OpResponse) Error
49
//
50
-// where Op is the name of a FUSE operation. Op reads request parameters
51
-// from req and writes results to resp. An operation whose only result is
52
-// the error result omits the resp parameter. Multiple goroutines may call
53
-// service methods simultaneously; the methods being called are responsible
54
-// for appropriate synchronization.
50
+// where Op is the name of a FUSE operation. Op reads request
51
+// parameters from req and writes results to resp. An operation whose
52
+// only result is the error result omits the resp parameter.
53
+//
54
+// Multiple goroutines may call service methods simultaneously; the
55
+// methods being called are responsible for appropriate
56
+// synchronization.
57
+//
58
+// The operation must not hold on to the request or response,
59
+// including any []byte fields such as WriteRequest.Data or
60
+// SetxattrRequest.Xattr.
61
//
62
// Errors
63
//
@@ -79,9 +85,12 @@
85
//
86
// Authentication
87
//
82
-// All requests types embed a Header, meaning that the method can inspect
83
-// req.Pid, req.Uid, and req.Gid as necessary to implement permission checking.
84
-// Alternately, XXX.
88
+// All requests types embed a Header, meaning that the method can
89
+// inspect req.Pid, req.Uid, and req.Gid as necessary to implement
90
+// permission checking. The kernel FUSE layer normally prevents other
91
+// users from accessing the FUSE file system (to change this, see
92
+// AllowOther, AllowRoot), but does not enforce access modes (to
93
+// change this, see DefaultPermissions).
94
//
95
// Mount Options
96
//
@@ -90,8 +99,6 @@
99
//
100
package fuse
101
93
-// BUG(rsc): The mount code for FreeBSD has not been written yet.
94
-
102
import (
103
"bytes"
104
"encoding/json"
@@ -241,7 +248,6 @@ const (
248
// See also fs.Intr.
249
EINTR = Errno(syscall.EINTR)
250
244
- ENODATA = Errno(syscall.ENODATA)
251
ERANGE = Errno(syscall.ERANGE)
252
ENOTSUP = Errno(syscall.ENOTSUP)
253
EEXIST = Errno(syscall.EEXIST)
@@ -252,14 +258,13 @@ const (
258
const DefaultErrno = EIO
259
260
var errnoNames = map[Errno]string{
255
- ENOSYS: "ENOSYS",
256
- ESTALE: "ESTALE",
257
- ENOENT: "ENOENT",
258
- EIO: "EIO",
259
- EPERM: "EPERM",
260
- EINTR: "EINTR",
261
- ENODATA: "ENODATA",
262
- EEXIST: "EEXIST",
261
+ ENOSYS: "ENOSYS",
262
+ ESTALE: "ESTALE",
263
+ ENOENT: "ENOENT",
264
+ EIO: "EIO",
265
+ EPERM: "EPERM",
266
+ EINTR: "EINTR",
267
+ EEXIST: "EEXIST",
268
}
269
270
// Errno implements Error and ErrorNumber using a syscall.Errno.
@@ -1115,9 +1120,6 @@ func (a *Attr) attr() (out attr) {
1120
out.Mode |= syscall.S_ISGID
1121
}
1122
out.Nlink = a.Nlink
1118
- if out.Nlink < 1 {
1119
- out.Nlink = 1
1120
- }
1123
out.Uid = a.Uid
1124
out.Gid = a.Gid
1125
out.Rdev = a.Rdev
@@ -1195,11 +1197,6 @@ func (r *GetxattrRequest) Respond(resp *GetxattrResponse) {
1197
}
1198
}
1199
1198
-func (r *GetxattrRequest) RespondError(err error) {
1199
- err = translateGetxattrError(err)
1200
- r.Header.RespondError(err)
1201
-}
1202
-
1200
// A GetxattrResponse is the response to a GetxattrRequest.
1201
type GetxattrResponse struct {
1202
Xattr []byte
@@ -1271,11 +1268,6 @@ func (r *RemovexattrRequest) Respond() {
1268
r.respond(out, unsafe.Sizeof(*out))
1269
}
1270
1274
-func (r *RemovexattrRequest) RespondError(err error) {
1275
- err = translateGetxattrError(err)
1276
- r.Header.RespondError(err)
1277
-}
1278
-
1271
// A SetxattrRequest asks to set an extended attribute associated with a file.
1272
type SetxattrRequest struct {
1273
Header `json:"-"`
@@ -1321,11 +1313,6 @@ func (r *SetxattrRequest) Respond() {
1313
r.respond(out, unsafe.Sizeof(*out))
1314
}
1315
1324
-func (r *SetxattrRequest) RespondError(err error) {
1325
- err = translateGetxattrError(err)
1326
- r.Header.RespondError(err)
1327
-}
1328
-
1316
// A LookupRequest asks to look up the given name in the directory named by r.Node.
1317
type LookupRequest struct {
1318
Header `json:"-"`
Godeps/_workspace/src/bazil.org/fuse/fuse_kernel_freebsd.go
new
+60
@@ -0,0 +1,60 @@
1
+package fuse
2
+
3
+import "time"
4
+
5
+type attr struct {
6
+ Ino uint64
7
+ Size uint64
8
+ Blocks uint64
9
+ Atime uint64
10
+ Mtime uint64
11
+ Ctime uint64
12
+ AtimeNsec uint32
13
+ MtimeNsec uint32
14
+ CtimeNsec uint32
15
+ Mode uint32
16
+ Nlink uint32
17
+ Uid uint32
18
+ Gid uint32
19
+ Rdev uint32
20
+}
21
+
22
+func (a *attr) Crtime() time.Time {
23
+ return time.Time{}
24
+}
25
+
26
+func (a *attr) SetCrtime(s uint64, ns uint32) {
27
+ // ignored on freebsd
28
+}
29
+
30
+func (a *attr) SetFlags(f uint32) {
31
+ // ignored on freebsd
32
+}
33
+
34
+type setattrIn struct {
35
+ setattrInCommon
36
+}
37
+
38
+func (in *setattrIn) BkupTime() time.Time {
39
+ return time.Time{}
40
+}
41
+
42
+func (in *setattrIn) Chgtime() time.Time {
43
+ return time.Time{}
44
+}
45
+
46
+func (in *setattrIn) Flags() uint32 {
47
+ return 0
48
+}
49
+
50
+func openFlags(flags uint32) OpenFlags {
51
+ return OpenFlags(flags)
52
+}
53
+
54
+type getxattrIn struct {
55
+ getxattrInCommon
56
+}
57
+
58
+type setxattrIn struct {
59
+ setxattrInCommon
60
+}
Godeps/_workspace/src/bazil.org/fuse/mount_darwin.go
+2
-2
@@ -59,7 +59,7 @@ func callMount(dir string, conf *MountConfig, f *os.File, ready chan<- struct{},
59
if strings.Contains(k, ",") || strings.Contains(v, ",") {
60
// Silly limitation but the mount helper does not
61
// understand any escaping. See TestMountOptionCommaError.
62
- return fmt.Errorf("mount options cannot contain commas on OS X: %q=%q", k, v)
62
+ return fmt.Errorf("mount options cannot contain commas on darwin: %q=%q", k, v)
63
}
64
}
65
cmd := exec.Command(
@@ -89,7 +89,7 @@ func callMount(dir string, conf *MountConfig, f *os.File, ready chan<- struct{},
89
return err
90
}
91
go func() {
92
- err = cmd.Wait()
92
+ err := cmd.Wait()
93
if err != nil {
94
if buf.Len() > 0 {
95
output := buf.Bytes()
Godeps/_workspace/src/bazil.org/fuse/mount_freebsd.go
new
+41
@@ -0,0 +1,41 @@
1
+package fuse
2
+
3
+import (
4
+ "fmt"
5
+ "os"
6
+ "os/exec"
7
+ "strings"
8
+)
9
+
10
+func mount(dir string, conf *MountConfig, ready chan<- struct{}, errp *error) (*os.File, error) {
11
+ for k, v := range conf.options {
12
+ if strings.Contains(k, ",") || strings.Contains(v, ",") {
13
+ // Silly limitation but the mount helper does not
14
+ // understand any escaping. See TestMountOptionCommaError.
15
+ return nil, fmt.Errorf("mount options cannot contain commas on FreeBSD: %q=%q", k, v)
16
+ }
17
+ }
18
+
19
+ f, err := os.OpenFile("/dev/fuse", os.O_RDWR, 0000)
20
+ if err != nil {
21
+ *errp = err
22
+ return nil, err
23
+ }
24
+
25
+ cmd := exec.Command(
26
+ "/sbin/mount_fusefs",
27
+ "--safe",
28
+ "-o", conf.getOptions(),
29
+ "3",
30
+ dir,
31
+ )
32
+ cmd.ExtraFiles = []*os.File{f}
33
+
34
+ out, err := cmd.CombinedOutput()
35
+ if err != nil {
36
+ return nil, fmt.Errorf("mount_fusefs: %q, %v", out, err)
37
+ }
38
+
39
+ close(ready)
40
+ return f, nil
41
+}
Godeps/_workspace/src/bazil.org/fuse/options.go
+32
@@ -5,6 +5,10 @@ import (
5
"strings"
6
)
7
8
+func dummyOption(conf *MountConfig) error {
9
+ return nil
10
+}
11
+
12
// MountConfig holds the configuration for a mount operation.
13
// Use it by passing MountOption values to Mount.
14
type MountConfig struct {
@@ -37,6 +41,8 @@ type MountOption func(*MountConfig) error
41
42
// FSName sets the file system name (also called source) that is
43
// visible in the list of mounted file systems.
44
+//
45
+// FreeBSD ignores this option.
46
func FSName(name string) MountOption {
47
return func(conf *MountConfig) error {
48
conf.options["fsname"] = name
@@ -49,6 +55,7 @@ func FSName(name string) MountOption {
55
// `fuse.foo`.
56
//
57
// OS X ignores this option.
58
+// FreeBSD ignores this option.
59
func Subtype(fstype string) MountOption {
60
return func(conf *MountConfig) error {
61
conf.options["subtype"] = fstype
@@ -89,6 +96,8 @@ func AllowOther() MountOption {
96
// AllowRoot allows other users to access the file system.
97
//
98
// Only one of AllowOther or AllowRoot can be used.
99
+//
100
+// FreeBSD ignores this option.
101
func AllowRoot() MountOption {
102
return func(conf *MountConfig) error {
103
if _, ok := conf.options["allow_other"]; ok {
@@ -98,3 +107,26 @@ func AllowRoot() MountOption {
107
return nil
108
}
109
}
110
+
111
+// DefaultPermissions makes the kernel enforce access control based on
112
+// the file mode (as in chmod).
113
+//
114
+// Without this option, the Node itself decides what is and is not
115
+// allowed. This is normally ok because FUSE file systems cannot be
116
+// accessed by other users without AllowOther/AllowRoot.
117
+//
118
+// FreeBSD ignores this option.
119
+func DefaultPermissions() MountOption {
120
+ return func(conf *MountConfig) error {
121
+ conf.options["default_permissions"] = ""
122
+ return nil
123
+ }
124
+}
125
+
126
+// ReadOnly makes the mount read-only.
127
+func ReadOnly() MountOption {
128
+ return func(conf *MountConfig) error {
129
+ conf.options["ro"] = ""
130
+ return nil
131
+ }
132
+}
Godeps/_workspace/src/bazil.org/fuse/options_darwin_test.go
deleted
-27
@@ -1,27 +0,0 @@
1
-package fuse_test
2
-
3
-import (
4
- "testing"
5
-
6
- "github.com/jbenet/go-ipfs/Godeps/_workspace/src/bazil.org/fuse"
7
- "github.com/jbenet/go-ipfs/Godeps/_workspace/src/bazil.org/fuse/fs/fstestutil"
8
-)
9
-
10
-func TestMountOptionCommaError(t *testing.T) {
11
- t.Parallel()
12
- // this test is not tied to FSName, but needs just some option
13
- // with string content
14
- var name = "FuseTest,Marker"
15
- mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{fstestutil.Dir{}},
16
- fuse.FSName(name),
17
- )
18
- switch {
19
- case err == nil:
20
- mnt.Close()
21
- t.Fatal("expected an error about commas")
22
- case err.Error() == `mount options cannot contain commas on OS X: "fsname"="FuseTest,Marker"`:
23
- // all good
24
- default:
25
- t.Fatalf("expected an error about commas, got: %v", err)
26
- }
27
-}
Godeps/_workspace/src/bazil.org/fuse/options_freebsd.go
new
+9
@@ -0,0 +1,9 @@
1
+package fuse
2
+
3
+func localVolume(conf *MountConfig) error {
4
+ return nil
5
+}
6
+
7
+func volumeName(name string) MountOption {
8
+ return dummyOption
9
+}
Godeps/_workspace/src/bazil.org/fuse/options_helper_test.go
new
+6
@@ -0,0 +1,6 @@
1
+package fuse
2
+
3
+// for TestMountOptionCommaError
4
+func ForTestSetMountOption(conf *MountConfig, k, v string) {
5
+ conf.options[k] = v
6
+}
Godeps/_workspace/src/bazil.org/fuse/options_linux.go
-4
@@ -1,9 +1,5 @@
1
package fuse
2
3
-func dummyOption(conf *MountConfig) error {
4
- return nil
5
-}
6
-
3
func localVolume(conf *MountConfig) error {
4
return nil
5
}
Godeps/_workspace/src/bazil.org/fuse/options_nocomma_test.go
new
+34
@@ -0,0 +1,34 @@
1
+// This file contains tests for platforms that have no escape
2
+// mechanism for including commas in mount options.
3
+//
4
+// +build darwin
5
+
6
+package fuse_test
7
+
8
+import (
9
+ "runtime"
10
+ "testing"
11
+
12
+ "github.com/jbenet/go-ipfs/Godeps/_workspace/src/bazil.org/fuse"
13
+ "github.com/jbenet/go-ipfs/Godeps/_workspace/src/bazil.org/fuse/fs/fstestutil"
14
+)
15
+
16
+func TestMountOptionCommaError(t *testing.T) {
17
+ t.Parallel()
18
+ // this test is not tied to any specific option, it just needs
19
+ // some string content
20
+ var evil = "FuseTest,Marker"
21
+ mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{fstestutil.Dir{}},
22
+ func(conf *fuse.MountConfig) error {
23
+ fuse.ForTestSetMountOption(conf, "fusetest", evil)
24
+ return nil
25
+ },
26
+ )
27
+ if err == nil {
28
+ mnt.Close()
29
+ t.Fatal("expected an error about commas")
30
+ }
31
+ if g, e := err.Error(), `mount options cannot contain commas on `+runtime.GOOS+`: "fusetest"="FuseTest,Marker"`; g != e {
32
+ t.Fatalf("wrong error: %q != %q", g, e)
33
+ }
34
+}
Godeps/_workspace/src/bazil.org/fuse/options_test.go
+85
@@ -1,11 +1,15 @@
1
package fuse_test
2
3
import (
4
+ "os"
5
"runtime"
6
+ "syscall"
7
"testing"
8
9
"github.com/jbenet/go-ipfs/Godeps/_workspace/src/bazil.org/fuse"
10
+ "github.com/jbenet/go-ipfs/Godeps/_workspace/src/bazil.org/fuse/fs"
11
"github.com/jbenet/go-ipfs/Godeps/_workspace/src/bazil.org/fuse/fs/fstestutil"
12
+ "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"
13
)
14
15
func init() {
@@ -13,6 +17,9 @@ func init() {
17
}
18
19
func TestMountOptionFSName(t *testing.T) {
20
+ if runtime.GOOS == "freebsd" {
21
+ t.Skip("FreeBSD does not support FSName")
22
+ }
23
t.Parallel()
24
const name = "FuseTestMarker"
25
mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{fstestutil.Dir{}},
@@ -33,6 +40,9 @@ func TestMountOptionFSName(t *testing.T) {
40
}
41
42
func testMountOptionFSNameEvil(t *testing.T, evil string) {
43
+ if runtime.GOOS == "freebsd" {
44
+ t.Skip("FreeBSD does not support FSName")
45
+ }
46
t.Parallel()
47
var name = "FuseTest" + evil + "Marker"
48
mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{fstestutil.Dir{}},
@@ -87,6 +97,9 @@ func TestMountOptionSubtype(t *testing.T) {
97
if runtime.GOOS == "darwin" {
98
t.Skip("OS X does not support Subtype")
99
}
100
+ if runtime.GOOS == "freebsd" {
101
+ t.Skip("FreeBSD does not support Subtype")
102
+ }
103
t.Parallel()
104
const name = "FuseTestMarker"
105
mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{fstestutil.Dir{}},
@@ -139,3 +152,75 @@ func TestMountOptionAllowRootThenAllowOther(t *testing.T) {
152
t.Fatalf("wrong error: %v != %v", g, e)
153
}
154
}
155
+
156
+type unwritableFile struct{}
157
+
158
+func (f unwritableFile) Attr() fuse.Attr { return fuse.Attr{Mode: 0000} }
159
+
160
+func TestMountOptionDefaultPermissions(t *testing.T) {
161
+ if runtime.GOOS == "freebsd" {
162
+ t.Skip("FreeBSD does not support DefaultPermissions")
163
+ }
164
+ t.Parallel()
165
+ mnt, err := fstestutil.MountedT(t,
166
+ fstestutil.SimpleFS{
167
+ fstestutil.ChildMap{"child": unwritableFile{}},
168
+ },
169
+ fuse.DefaultPermissions(),
170
+ )
171
+
172
+ if err != nil {
173
+ t.Fatal(err)
174
+ }
175
+ defer mnt.Close()
176
+
177
+ // This will be prevented by kernel-level access checking when
178
+ // DefaultPermissions is used.
179
+ f, err := os.OpenFile(mnt.Dir+"/child", os.O_WRONLY, 0000)
180
+ if err == nil {
181
+ f.Close()
182
+ t.Fatal("expected an error")
183
+ }
184
+ if !os.IsPermission(err) {
185
+ t.Fatalf("expected a permission error, got %T: %v", err, err)
186
+ }
187
+}
188
+
189
+type createrDir struct {
190
+ fstestutil.Dir
191
+}
192
+
193
+var _ fs.NodeCreater = createrDir{}
194
+
195
+func (createrDir) Create(ctx context.Context, req *fuse.CreateRequest, resp *fuse.CreateResponse) (fs.Node, fs.Handle, error) {
196
+ // pick a really distinct error, to identify it later
197
+ return nil, nil, fuse.Errno(syscall.ENAMETOOLONG)
198
+}
199
+
200
+func TestMountOptionReadOnly(t *testing.T) {
201
+ t.Parallel()
202
+ mnt, err := fstestutil.MountedT(t,
203
+ fstestutil.SimpleFS{createrDir{}},
204
+ fuse.ReadOnly(),
205
+ )
206
+
207
+ if err != nil {
208
+ t.Fatal(err)
209
+ }
210
+ defer mnt.Close()
211
+
212
+ // This will be prevented by kernel-level access checking when
213
+ // ReadOnly is used.
214
+ f, err := os.Create(mnt.Dir + "/child")
215
+ if err == nil {
216
+ f.Close()
217
+ t.Fatal("expected an error")
218
+ }
219
+ perr, ok := err.(*os.PathError)
220
+ if !ok {
221
+ t.Fatalf("expected PathError, got %T: %v", err, err)
222
+ }
223
+ if perr.Err != syscall.EROFS {
224
+ t.Fatalf("expected EROFS, got %T: %v", err, err)
225
+ }
226
+}
Godeps/_workspace/src/bazil.org/fuse/syscallx/syscallx_std.go
+6
-6
@@ -3,24 +3,24 @@
3
package syscallx
4
5
// This file just contains wrappers for platforms that already have
6
-// the right stuff in stdlib.
6
+// the right stuff in golang.org/x/sys/unix.
7
8
import (
9
- "syscall"
9
+ "golang.org/x/sys/unix"
10
)
11
12
func Getxattr(path string, attr string, dest []byte) (sz int, err error) {
13
- return syscall.Getxattr(path, attr, dest)
13
+ return unix.Getxattr(path, attr, dest)
14
}
15
16
func Listxattr(path string, dest []byte) (sz int, err error) {
17
- return syscall.Listxattr(path, dest)
17
+ return unix.Listxattr(path, dest)
18
}
19
20
func Setxattr(path string, attr string, data []byte, flags int) (err error) {
21
- return syscall.Setxattr(path, attr, data, flags)
21
+ return unix.Setxattr(path, attr, data, flags)
22
}
23
24
func Removexattr(path string, attr string) (err error) {
25
- return syscall.Removexattr(path, attr)
25
+ return unix.Removexattr(path, attr)
26
}
Godeps/_workspace/src/github.com/Sirupsen/logrus/README.md
+12
-39
@@ -82,7 +82,7 @@ func init() {
82
83
// Use the Airbrake hook to report errors that have Error severity or above to
84
// an exception tracker. You can create custom hooks, see the Hooks section.
85
- log.AddHook(&logrus_airbrake.AirbrakeHook{})
85
+ log.AddHook(airbrake.NewHook("https://example.com", "xyz", "development"))
86
87
// Output to stderr instead of stdout, could also be a file.
88
log.SetOutput(os.Stderr)
@@ -164,43 +164,8 @@ You can add hooks for logging levels. For example to send errors to an exception
164
tracking service on `Error`, `Fatal` and `Panic`, info to StatsD or log to
165
multiple places simultaneously, e.g. syslog.
166
167
-```go
168
-// Not the real implementation of the Airbrake hook. Just a simple sample.
169
-import (
170
- log "github.com/Sirupsen/logrus"
171
-)
172
-
173
-func init() {
174
- log.AddHook(new(AirbrakeHook))
175
-}
176
-
177
-type AirbrakeHook struct{}
178
-
179
-// `Fire()` takes the entry that the hook is fired for. `entry.Data[]` contains
180
-// the fields for the entry. See the Fields section of the README.
181
-func (hook *AirbrakeHook) Fire(entry *logrus.Entry) error {
182
- err := airbrake.Notify(entry.Data["error"].(error))
183
- if err != nil {
184
- log.WithFields(log.Fields{
185
- "source": "airbrake",
186
- "endpoint": airbrake.Endpoint,
187
- }).Info("Failed to send error to Airbrake")
188
- }
189
-
190
- return nil
191
-}
192
-
193
-// `Levels()` returns a slice of `Levels` the hook is fired for.
194
-func (hook *AirbrakeHook) Levels() []log.Level {
195
- return []log.Level{
196
- log.ErrorLevel,
197
- log.FatalLevel,
198
- log.PanicLevel,
199
- }
200
-}
201
-```
202
-
203
-Logrus comes with built-in hooks. Add those, or your custom hook, in `init`:
167
+Logrus comes with [built-in hooks](hooks/). Add those, or your custom hook, in
168
+`init`:
169
170
```go
171
import (
@@ -211,7 +176,7 @@ import (
176
)
177
178
func init() {
214
- log.AddHook(new(logrus_airbrake.AirbrakeHook))
179
+ log.AddHook(airbrake.NewHook("https://example.com", "xyz", "development"))
180
181
hook, err := logrus_syslog.NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, "")
182
if err != nil {
@@ -233,6 +198,9 @@ func init() {
198
Send errors to remote syslog server.
199
Uses standard library `log/syslog` behind the scenes.
200
201
+* [`github.com/Sirupsen/logrus/hooks/bugsnag`](https://github.com/Sirupsen/logrus/blob/master/hooks/bugsnag/bugsnag.go)
202
+ Send errors to the Bugsnag exception tracking service.
203
+
204
* [`github.com/nubo/hiprus`](https://github.com/nubo/hiprus)
205
Send errors to a channel in hipchat.
206
@@ -321,6 +289,11 @@ The built-in logging formatters are:
289
field to `true`. To force no colored output even if there is a TTY set the
290
`DisableColors` field to `true`
291
* `logrus.JSONFormatter`. Logs fields as JSON.
292
+* `logrus_logstash.LogstashFormatter`. Logs fields as Logstash Events (http://logstash.net).
293
+
294
+ ```go
295
+ logrus.SetFormatter(&logrus_logstash.LogstashFormatter{Type: “application_name"})
296
+ ```
297
298
Third party logging formatters:
299
Godeps/_workspace/src/github.com/Sirupsen/logrus/examples/basic/basic.go
+10
@@ -9,6 +9,7 @@ var log = logrus.New()
9
func init() {
10
log.Formatter = new(logrus.JSONFormatter)
11
log.Formatter = new(logrus.TextFormatter) // default
12
+ log.Level = logrus.DebugLevel
13
}
14
15
func main() {
@@ -23,6 +24,11 @@ func main() {
24
}
25
}()
26
27
+ log.WithFields(logrus.Fields{
28
+ "animal": "walrus",
29
+ "number": 8,
30
+ }).Debug("Started observing beach")
31
+
32
log.WithFields(logrus.Fields{
33
"animal": "walrus",
34
"size": 10,
@@ -33,6 +39,10 @@ func main() {
39
"number": 122,
40
}).Warn("The group's number increased tremendously!")
41
42
+ log.WithFields(logrus.Fields{
43
+ "temperature": -4,
44
+ }).Debug("Temperature changes")
45
+
46
log.WithFields(logrus.Fields{
47
"animal": "orca",
48
"size": 9009,
Godeps/_workspace/src/github.com/Sirupsen/logrus/examples/hook/hook.go
+3
-7
@@ -3,21 +3,17 @@ package main
3
import (
4
"github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/Sirupsen/logrus"
5
"github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/airbrake"
6
- "github.com/tobi/airbrake-go"
6
)
7
8
var log = logrus.New()
9
10
func init() {
12
- log.Formatter = new(logrus.TextFormatter) // default
13
- log.Hooks.Add(new(logrus_airbrake.AirbrakeHook))
11
+ log.Formatter = // default
12
+ new(logrus.TextFormatter)
13
+ log.Hooks.Add(airbrake.NewHook("https://example.com", "xyz", "development"))
14
}
15
16
func main() {
17
- airbrake.Endpoint = "https://exceptions.whatever.com/notifier_api/v2/notices.xml"
18
- airbrake.ApiKey = "whatever"
19
- airbrake.Environment = "production"
20
-
17
log.WithFields(logrus.Fields{
18
"animal": "walrus",
19
"size": 10,
Godeps/_workspace/src/github.com/Sirupsen/logrus/formatters/logstash/logstash.go
new
+48
@@ -0,0 +1,48 @@
1
+package logstash
2
+
3
+import (
4
+ "encoding/json"
5
+ "fmt"
6
+ "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/Sirupsen/logrus"
7
+ "time"
8
+)
9
+
10
+// Formatter generates json in logstash format.
11
+// Logstash site: http://logstash.net/
12
+type LogstashFormatter struct {
13
+ Type string // if not empty use for logstash type field.
14
+}
15
+
16
+func (f *LogstashFormatter) Format(entry *logrus.Entry) ([]byte, error) {
17
+ entry.Data["@version"] = 1
18
+ entry.Data["@timestamp"] = entry.Time.Format(time.RFC3339)
19
+
20
+ // set message field
21
+ v, ok := entry.Data["message"]
22
+ if ok {
23
+ entry.Data["fields.message"] = v
24
+ }
25
+ entry.Data["message"] = entry.Message
26
+
27
+ // set level field
28
+ v, ok = entry.Data["level"]
29
+ if ok {
30
+ entry.Data["fields.level"] = v
31
+ }
32
+ entry.Data["level"] = entry.Level.String()
33
+
34
+ // set type field
35
+ if f.Type != "" {
36
+ v, ok = entry.Data["type"]
37
+ if ok {
38
+ entry.Data["fields.type"] = v
39
+ }
40
+ entry.Data["type"] = f.Type
41
+ }
42
+
43
+ serialized, err := json.Marshal(entry.Data)
44
+ if err != nil {
45
+ return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err)
46
+ }
47
+ return append(serialized, '\n'), nil
48
+}
Godeps/_workspace/src/github.com/Sirupsen/logrus/formatters/logstash/logstash_test.go
new
+52
@@ -0,0 +1,52 @@
1
+package logstash
2
+
3
+import (
4
+ "bytes"
5
+ "encoding/json"
6
+ "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/Sirupsen/logrus"
7
+ "github.com/stretchr/testify/assert"
8
+ "testing"
9
+)
10
+
11
+func TestLogstashFormatter(t *testing.T) {
12
+ assert := assert.New(t)
13
+
14
+ lf := LogstashFormatter{Type: "abc"}
15
+
16
+ fields := logrus.Fields{
17
+ "message": "def",
18
+ "level": "ijk",
19
+ "type": "lmn",
20
+ "one": 1,
21
+ "pi": 3.14,
22
+ "bool": true,
23
+ }
24
+
25
+ entry := logrus.WithFields(fields)
26
+ entry.Message = "msg"
27
+ entry.Level = logrus.InfoLevel
28
+
29
+ b, _ := lf.Format(entry)
30
+
31
+ var data map[string]interface{}
32
+ dec := json.NewDecoder(bytes.NewReader(b))
33
+ dec.UseNumber()
34
+ dec.Decode(&data)
35
+
36
+ // base fields
37
+ assert.Equal(json.Number("1"), data["@version"])
38
+ assert.NotEmpty(data["@timestamp"])
39
+ assert.Equal("abc", data["type"])
40
+ assert.Equal("msg", data["message"])
41
+ assert.Equal("info", data["level"])
42
+
43
+ // substituted fields
44
+ assert.Equal("def", data["fields.message"])
45
+ assert.Equal("ijk", data["fields.level"])
46
+ assert.Equal("lmn", data["fields.type"])
47
+
48
+ // formats
49
+ assert.Equal(json.Number("1"), data["one"])
50
+ assert.Equal(json.Number("3.14"), data["pi"])
51
+ assert.Equal(true, data["bool"])
52
+}
Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/airbrake/airbrake.go
+30
-30
@@ -1,51 +1,51 @@
1
-package logrus_airbrake
1
+package airbrake
2
3
import (
4
+ "errors"
5
+ "fmt"
6
+
7
"github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/Sirupsen/logrus"
8
"github.com/tobi/airbrake-go"
9
)
10
11
// AirbrakeHook to send exceptions to an exception-tracking service compatible
9
-// with the Airbrake API. You must set:
10
-// * airbrake.Endpoint
11
-// * airbrake.ApiKey
12
-// * airbrake.Environment (only sends exceptions when set to "production")
13
-//
14
-// Before using this hook, to send an error. Entries that trigger an Error,
15
-// Fatal or Panic should now include an "error" field to send to Airbrake.
16
-type AirbrakeHook struct{}
17
-
18
-func (hook *AirbrakeHook) Fire(entry *logrus.Entry) error {
19
- if entry.Data["error"] == nil {
20
- entry.Logger.WithFields(logrus.Fields{
21
- "source": "airbrake",
22
- "endpoint": airbrake.Endpoint,
23
- }).Warn("Exceptions sent to Airbrake must have an 'error' key with the error")
24
- return nil
12
+// with the Airbrake API.
13
+type airbrakeHook struct {
14
+ APIKey string
15
+ Endpoint string
16
+ Environment string
17
+}
18
+
19
+func NewHook(endpoint, apiKey, env string) *airbrakeHook {
20
+ return &airbrakeHook{
21
+ APIKey: apiKey,
22
+ Endpoint: endpoint,
23
+ Environment: env,
24
}
25
+}
26
+
27
+func (hook *airbrakeHook) Fire(entry *logrus.Entry) error {
28
+ airbrake.ApiKey = hook.APIKey
29
+ airbrake.Endpoint = hook.Endpoint
30
+ airbrake.Environment = hook.Environment
31
32
+ var notifyErr error
33
err, ok := entry.Data["error"].(error)
28
- if !ok {
29
- entry.Logger.WithFields(logrus.Fields{
30
- "source": "airbrake",
31
- "endpoint": airbrake.Endpoint,
32
- }).Warn("Exceptions sent to Airbrake must have an `error` key of type `error`")
33
- return nil
34
+ if ok {
35
+ notifyErr = err
36
+ } else {
37
+ notifyErr = errors.New(entry.Message)
38
}
39
36
- airErr := airbrake.Notify(err)
40
+ airErr := airbrake.Notify(notifyErr)
41
if airErr != nil {
38
- entry.Logger.WithFields(logrus.Fields{
39
- "source": "airbrake",
40
- "endpoint": airbrake.Endpoint,
41
- "error": airErr,
42
- }).Warn("Failed to send error to Airbrake")
42
+ return fmt.Errorf("Failed to send error to Airbrake: %s", airErr)
43
}
44
45
return nil
46
}
47
48
-func (hook *AirbrakeHook) Levels() []logrus.Level {
48
+func (hook *airbrakeHook) Levels() []logrus.Level {
49
return []logrus.Level{
50
logrus.ErrorLevel,
51
logrus.FatalLevel,
Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/airbrake/airbrake_test.go
new
+133
@@ -0,0 +1,133 @@
1
+package airbrake
2
+
3
+import (
4
+ "encoding/xml"
5
+ "net/http"
6
+ "net/http/httptest"
7
+ "testing"
8
+ "time"
9
+
10
+ "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/Sirupsen/logrus"
11
+)
12
+
13
+type notice struct {
14
+ Error NoticeError `xml:"error"`
15
+}
16
+type NoticeError struct {
17
+ Class string `xml:"class"`
18
+ Message string `xml:"message"`
19
+}
20
+
21
+type customErr struct {
22
+ msg string
23
+}
24
+
25
+func (e *customErr) Error() string {
26
+ return e.msg
27
+}
28
+
29
+const (
30
+ testAPIKey = "abcxyz"
31
+ testEnv = "development"
32
+ expectedClass = "*airbrake.customErr"
33
+ expectedMsg = "foo"
34
+ unintendedMsg = "Airbrake will not see this string"
35
+)
36
+
37
+var (
38
+ noticeError = make(chan NoticeError, 1)
39
+)
40
+
41
+// TestLogEntryMessageReceived checks if invoking Logrus' log.Error
42
+// method causes an XML payload containing the log entry message is received
43
+// by a HTTP server emulating an Airbrake-compatible endpoint.
44
+func TestLogEntryMessageReceived(t *testing.T) {
45
+ log := logrus.New()
46
+ ts := startAirbrakeServer(t)
47
+ defer ts.Close()
48
+
49
+ hook := NewHook(ts.URL, testAPIKey, "production")
50
+ log.Hooks.Add(hook)
51
+
52
+ log.Error(expectedMsg)
53
+
54
+ select {
55
+ case received := <-noticeError:
56
+ if received.Message != expectedMsg {
57
+ t.Errorf("Unexpected message received: %s", received.Message)
58
+ }
59
+ case <-time.After(time.Second):
60
+ t.Error("Timed out; no notice received by Airbrake API")
61
+ }
62
+}
63
+
64
+// TestLogEntryMessageReceived confirms that, when passing an error type using
65
+// logrus.Fields, a HTTP server emulating an Airbrake endpoint receives the
66
+// error message returned by the Error() method on the error interface
67
+// rather than the logrus.Entry.Message string.
68
+func TestLogEntryWithErrorReceived(t *testing.T) {
69
+ log := logrus.New()
70
+ ts := startAirbrakeServer(t)
71
+ defer ts.Close()
72
+
73
+ hook := NewHook(ts.URL, testAPIKey, "production")
74
+ log.Hooks.Add(hook)
75
+
76
+ log.WithFields(logrus.Fields{
77
+ "error": &customErr{expectedMsg},
78
+ }).Error(unintendedMsg)
79
+
80
+ select {
81
+ case received := <-noticeError:
82
+ if received.Message != expectedMsg {
83
+ t.Errorf("Unexpected message received: %s", received.Message)
84
+ }
85
+ if received.Class != expectedClass {
86
+ t.Errorf("Unexpected error class: %s", received.Class)
87
+ }
88
+ case <-time.After(time.Second):
89
+ t.Error("Timed out; no notice received by Airbrake API")
90
+ }
91
+}
92
+
93
+// TestLogEntryWithNonErrorTypeNotReceived confirms that, when passing a
94
+// non-error type using logrus.Fields, a HTTP server emulating an Airbrake
95
+// endpoint receives the logrus.Entry.Message string.
96
+//
97
+// Only error types are supported when setting the 'error' field using
98
+// logrus.WithFields().
99
+func TestLogEntryWithNonErrorTypeNotReceived(t *testing.T) {
100
+ log := logrus.New()
101
+ ts := startAirbrakeServer(t)
102
+ defer ts.Close()
103
+
104
+ hook := NewHook(ts.URL, testAPIKey, "production")
105
+ log.Hooks.Add(hook)
106
+
107
+ log.WithFields(logrus.Fields{
108
+ "error": expectedMsg,
109
+ }).Error(unintendedMsg)
110
+
111
+ select {
112
+ case received := <-noticeError:
113
+ if received.Message != unintendedMsg {
114
+ t.Errorf("Unexpected message received: %s", received.Message)
115
+ }
116
+ case <-time.After(time.Second):
117
+ t.Error("Timed out; no notice received by Airbrake API")
118
+ }
119
+}
120
+
121
+func startAirbrakeServer(t *testing.T) *httptest.Server {
122
+ ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
123
+ var notice notice
124
+ if err := xml.NewDecoder(r.Body).Decode(¬ice); err != nil {
125
+ t.Error(err)
126
+ }
127
+ r.Body.Close()
128
+
129
+ noticeError <- notice.Error
130
+ }))
131
+
132
+ return ts
133
+}
Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/bugsnag/bugsnag.go
new
+68
@@ -0,0 +1,68 @@
1
+package logrus_bugsnag
2
+
3
+import (
4
+ "errors"
5
+
6
+ "github.com/bugsnag/bugsnag-go"
7
+ "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/Sirupsen/logrus"
8
+)
9
+
10
+type bugsnagHook struct{}
11
+
12
+// ErrBugsnagUnconfigured is returned if NewBugsnagHook is called before
13
+// bugsnag.Configure. Bugsnag must be configured before the hook.
14
+var ErrBugsnagUnconfigured = errors.New("bugsnag must be configured before installing this logrus hook")
15
+
16
+// ErrBugsnagSendFailed indicates that the hook failed to submit an error to
17
+// bugsnag. The error was successfully generated, but `bugsnag.Notify()`
18
+// failed.
19
+type ErrBugsnagSendFailed struct {
20
+ err error
21
+}
22
+
23
+func (e ErrBugsnagSendFailed) Error() string {
24
+ return "failed to send error to Bugsnag: " + e.err.Error()
25
+}
26
+
27
+// NewBugsnagHook initializes a logrus hook which sends exceptions to an
28
+// exception-tracking service compatible with the Bugsnag API. Before using
29
+// this hook, you must call bugsnag.Configure(). The returned object should be
30
+// registered with a log via `AddHook()`
31
+//
32
+// Entries that trigger an Error, Fatal or Panic should now include an "error"
33
+// field to send to Bugsnag.
34
+func NewBugsnagHook() (*bugsnagHook, error) {
35
+ if bugsnag.Config.APIKey == "" {
36
+ return nil, ErrBugsnagUnconfigured
37
+ }
38
+ return &bugsnagHook{}, nil
39
+}
40
+
41
+// Fire forwards an error to Bugsnag. Given a logrus.Entry, it extracts the
42
+// "error" field (or the Message if the error isn't present) and sends it off.
43
+func (hook *bugsnagHook) Fire(entry *logrus.Entry) error {
44
+ var notifyErr error
45
+ err, ok := entry.Data["error"].(error)
46
+ if ok {
47
+ notifyErr = err
48
+ } else {
49
+ notifyErr = errors.New(entry.Message)
50
+ }
51
+
52
+ bugsnagErr := bugsnag.Notify(notifyErr)
53
+ if bugsnagErr != nil {
54
+ return ErrBugsnagSendFailed{bugsnagErr}
55
+ }
56
+
57
+ return nil
58
+}
59
+
60
+// Levels enumerates the log levels on which the error should be forwarded to
61
+// bugsnag: everything at or above the "Error" level.
62
+func (hook *bugsnagHook) Levels() []logrus.Level {
63
+ return []logrus.Level{
64
+ logrus.ErrorLevel,
65
+ logrus.FatalLevel,
66
+ logrus.PanicLevel,
67
+ }
68
+}
Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/bugsnag/bugsnag_test.go
new
+64
@@ -0,0 +1,64 @@
1
+package logrus_bugsnag
2
+
3
+import (
4
+ "encoding/json"
5
+ "errors"
6
+ "io/ioutil"
7
+ "net/http"
8
+ "net/http/httptest"
9
+ "testing"
10
+ "time"
11
+
12
+ "github.com/bugsnag/bugsnag-go"
13
+ "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/Sirupsen/logrus"
14
+)
15
+
16
+type notice struct {
17
+ Events []struct {
18
+ Exceptions []struct {
19
+ Message string `json:"message"`
20
+ } `json:"exceptions"`
21
+ } `json:"events"`
22
+}
23
+
24
+func TestNoticeReceived(t *testing.T) {
25
+ msg := make(chan string, 1)
26
+ expectedMsg := "foo"
27
+
28
+ ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
29
+ var notice notice
30
+ data, _ := ioutil.ReadAll(r.Body)
31
+ if err := json.Unmarshal(data, ¬ice); err != nil {
32
+ t.Error(err)
33
+ }
34
+ _ = r.Body.Close()
35
+
36
+ msg <- notice.Events[0].Exceptions[0].Message
37
+ }))
38
+ defer ts.Close()
39
+
40
+ hook := &bugsnagHook{}
41
+
42
+ bugsnag.Configure(bugsnag.Configuration{
43
+ Endpoint: ts.URL,
44
+ ReleaseStage: "production",
45
+ APIKey: "12345678901234567890123456789012",
46
+ Synchronous: true,
47
+ })
48
+
49
+ log := logrus.New()
50
+ log.Hooks.Add(hook)
51
+
52
+ log.WithFields(logrus.Fields{
53
+ "error": errors.New(expectedMsg),
54
+ }).Error("Bugsnag will not see this string")
55
+
56
+ select {
57
+ case received := <-msg:
58
+ if received != expectedMsg {
59
+ t.Errorf("Unexpected message received: %s", received)
60
+ }
61
+ case <-time.After(time.Second):
62
+ t.Error("Timed out; no notice received by Bugsnag API")
63
+ }
64
+}
Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/sentry/README.md
+1
-1
@@ -57,5 +57,5 @@ with a call to `NewSentryHook`. This can be changed by assigning a value to the
57
58
```go
59
hook, _ := logrus_sentry.NewSentryHook(...)
60
-hook.Timeout = 20*time.Seconds
60
+hook.Timeout = 20*time.Second
61
```
Godeps/_workspace/src/github.com/Sirupsen/logrus/json_formatter.go
+8
-1
@@ -11,7 +11,14 @@ type JSONFormatter struct{}
11
func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
12
data := make(Fields, len(entry.Data)+3)
13
for k, v := range entry.Data {
14
- data[k] = v
14
+ switch v := v.(type) {
15
+ case error:
16
+ // Otherwise errors are ignored by `encoding/json`
17
+ // https://github.com/Sirupsen/logrus/issues/137
18
+ data[k] = v.Error()
19
+ default:
20
+ data[k] = v
21
+ }
22
}
23
prefixFieldClashes(data)
24
data["time"] = entry.Time.Format(time.RFC3339)
Godeps/_workspace/src/github.com/Sirupsen/logrus/json_formatter_test.go
new
+120
@@ -0,0 +1,120 @@
1
+package logrus
2
+
3
+import (
4
+ "encoding/json"
5
+ "errors"
6
+
7
+ "testing"
8
+)
9
+
10
+func TestErrorNotLost(t *testing.T) {
11
+ formatter := &JSONFormatter{}
12
+
13
+ b, err := formatter.Format(WithField("error", errors.New("wild walrus")))
14
+ if err != nil {
15
+ t.Fatal("Unable to format entry: ", err)
16
+ }
17
+
18
+ entry := make(map[string]interface{})
19
+ err = json.Unmarshal(b, &entry)
20
+ if err != nil {
21
+ t.Fatal("Unable to unmarshal formatted entry: ", err)
22
+ }
23
+
24
+ if entry["error"] != "wild walrus" {
25
+ t.Fatal("Error field not set")
26
+ }
27
+}
28
+
29
+func TestErrorNotLostOnFieldNotNamedError(t *testing.T) {
30
+ formatter := &JSONFormatter{}
31
+
32
+ b, err := formatter.Format(WithField("omg", errors.New("wild walrus")))
33
+ if err != nil {
34
+ t.Fatal("Unable to format entry: ", err)
35
+ }
36
+
37
+ entry := make(map[string]interface{})
38
+ err = json.Unmarshal(b, &entry)
39
+ if err != nil {
40
+ t.Fatal("Unable to unmarshal formatted entry: ", err)
41
+ }
42
+
43
+ if entry["omg"] != "wild walrus" {
44
+ t.Fatal("Error field not set")
45
+ }
46
+}
47
+
48
+func TestFieldClashWithTime(t *testing.T) {
49
+ formatter := &JSONFormatter{}
50
+
51
+ b, err := formatter.Format(WithField("time", "right now!"))
52
+ if err != nil {
53
+ t.Fatal("Unable to format entry: ", err)
54
+ }
55
+
56
+ entry := make(map[string]interface{})
57
+ err = json.Unmarshal(b, &entry)
58
+ if err != nil {
59
+ t.Fatal("Unable to unmarshal formatted entry: ", err)
60
+ }
61
+
62
+ if entry["fields.time"] != "right now!" {
63
+ t.Fatal("fields.time not set to original time field")
64
+ }
65
+
66
+ if entry["time"] != "0001-01-01T00:00:00Z" {
67
+ t.Fatal("time field not set to current time, was: ", entry["time"])
68
+ }
69
+}
70
+
71
+func TestFieldClashWithMsg(t *testing.T) {
72
+ formatter := &JSONFormatter{}
73
+
74
+ b, err := formatter.Format(WithField("msg", "something"))
75
+ if err != nil {
76
+ t.Fatal("Unable to format entry: ", err)
77
+ }
78
+
79
+ entry := make(map[string]interface{})
80
+ err = json.Unmarshal(b, &entry)
81
+ if err != nil {
82
+ t.Fatal("Unable to unmarshal formatted entry: ", err)
83
+ }
84
+
85
+ if entry["fields.msg"] != "something" {
86
+ t.Fatal("fields.msg not set to original msg field")
87
+ }
88
+}
89
+
90
+func TestFieldClashWithLevel(t *testing.T) {
91
+ formatter := &JSONFormatter{}
92
+
93
+ b, err := formatter.Format(WithField("level", "something"))
94
+ if err != nil {
95
+ t.Fatal("Unable to format entry: ", err)
96
+ }
97
+
98
+ entry := make(map[string]interface{})
99
+ err = json.Unmarshal(b, &entry)
100
+ if err != nil {
101
+ t.Fatal("Unable to unmarshal formatted entry: ", err)
102
+ }
103
+
104
+ if entry["fields.level"] != "something" {
105
+ t.Fatal("fields.level not set to original level field")
106
+ }
107
+}
108
+
109
+func TestJSONEntryEndsWithNewline(t *testing.T) {
110
+ formatter := &JSONFormatter{}
111
+
112
+ b, err := formatter.Format(WithField("level", "something"))
113
+ if err != nil {
114
+ t.Fatal("Unable to format entry: ", err)
115
+ }
116
+
117
+ if b[len(b)-1] != '\n' {
118
+ t.Fatal("Expected JSON log entry to end with a newline")
119
+ }
120
+}
Godeps/_workspace/src/github.com/Sirupsen/logrus/logger.go
+63
-21
@@ -65,11 +65,15 @@ func (logger *Logger) WithFields(fields Fields) *Entry {
65
}
66
67
func (logger *Logger) Debugf(format string, args ...interface{}) {
68
- NewEntry(logger).Debugf(format, args...)
68
+ if logger.Level >= DebugLevel {
69
+ NewEntry(logger).Debugf(format, args...)
70
+ }
71
}
72
73
func (logger *Logger) Infof(format string, args ...interface{}) {
72
- NewEntry(logger).Infof(format, args...)
74
+ if logger.Level >= InfoLevel {
75
+ NewEntry(logger).Infof(format, args...)
76
+ }
77
}
78
79
func (logger *Logger) Printf(format string, args ...interface{}) {
@@ -77,31 +81,45 @@ func (logger *Logger) Printf(format string, args ...interface{}) {
81
}
82
83
func (logger *Logger) Warnf(format string, args ...interface{}) {
80
- NewEntry(logger).Warnf(format, args...)
84
+ if logger.Level >= WarnLevel {
85
+ NewEntry(logger).Warnf(format, args...)
86
+ }
87
}
88
89
func (logger *Logger) Warningf(format string, args ...interface{}) {
84
- NewEntry(logger).Warnf(format, args...)
90
+ if logger.Level >= WarnLevel {
91
+ NewEntry(logger).Warnf(format, args...)
92
+ }
93
}
94
95
func (logger *Logger) Errorf(format string, args ...interface{}) {
88
- NewEntry(logger).Errorf(format, args...)
96
+ if logger.Level >= ErrorLevel {
97
+ NewEntry(logger).Errorf(format, args...)
98
+ }
99
}
100
101
func (logger *Logger) Fatalf(format string, args ...interface{}) {
92
- NewEntry(logger).Fatalf(format, args...)
102
+ if logger.Level >= FatalLevel {
103
+ NewEntry(logger).Fatalf(format, args...)
104
+ }
105
}
106
107
func (logger *Logger) Panicf(format string, args ...interface{}) {
96
- NewEntry(logger).Panicf(format, args...)
108
+ if logger.Level >= PanicLevel {
109
+ NewEntry(logger).Panicf(format, args...)
110
+ }
111
}
112
113
func (logger *Logger) Debug(args ...interface{}) {
100
- NewEntry(logger).Debug(args...)
114
+ if logger.Level >= DebugLevel {
115
+ NewEntry(logger).Debug(args...)
116
+ }
117
}
118
119
func (logger *Logger) Info(args ...interface{}) {
104
- NewEntry(logger).Info(args...)
120
+ if logger.Level >= InfoLevel {
121
+ NewEntry(logger).Info(args...)
122
+ }
123
}
124
125
func (logger *Logger) Print(args ...interface{}) {
@@ -109,31 +127,45 @@ func (logger *Logger) Print(args ...interface{}) {
127
}
128
129
func (logger *Logger) Warn(args ...interface{}) {
112
- NewEntry(logger).Warn(args...)
130
+ if logger.Level >= WarnLevel {
131
+ NewEntry(logger).Warn(args...)
132
+ }
133
}
134
135
func (logger *Logger) Warning(args ...interface{}) {
116
- NewEntry(logger).Warn(args...)
136
+ if logger.Level >= WarnLevel {
137
+ NewEntry(logger).Warn(args...)
138
+ }
139
}
140
141
func (logger *Logger) Error(args ...interface{}) {
120
- NewEntry(logger).Error(args...)
142
+ if logger.Level >= ErrorLevel {
143
+ NewEntry(logger).Error(args...)
144
+ }
145
}
146
147
func (logger *Logger) Fatal(args ...interface{}) {
124
- NewEntry(logger).Fatal(args...)
148
+ if logger.Level >= FatalLevel {
149
+ NewEntry(logger).Fatal(args...)
150
+ }
151
}
152
153
func (logger *Logger) Panic(args ...interface{}) {
128
- NewEntry(logger).Panic(args...)
154
+ if logger.Level >= PanicLevel {
155
+ NewEntry(logger).Panic(args...)
156
+ }
157
}
158
159
func (logger *Logger) Debugln(args ...interface{}) {
132
- NewEntry(logger).Debugln(args...)
160
+ if logger.Level >= DebugLevel {
161
+ NewEntry(logger).Debugln(args...)
162
+ }
163
}
164
165
func (logger *Logger) Infoln(args ...interface{}) {
136
- NewEntry(logger).Infoln(args...)
166
+ if logger.Level >= InfoLevel {
167
+ NewEntry(logger).Infoln(args...)
168
+ }
169
}
170
171
func (logger *Logger) Println(args ...interface{}) {
@@ -141,21 +173,31 @@ func (logger *Logger) Println(args ...interface{}) {
173
}
174
175
func (logger *Logger) Warnln(args ...interface{}) {
144
- NewEntry(logger).Warnln(args...)
176
+ if logger.Level >= WarnLevel {
177
+ NewEntry(logger).Warnln(args...)
178
+ }
179
}
180
181
func (logger *Logger) Warningln(args ...interface{}) {
148
- NewEntry(logger).Warnln(args...)
182
+ if logger.Level >= WarnLevel {
183
+ NewEntry(logger).Warnln(args...)
184
+ }
185
}
186
187
func (logger *Logger) Errorln(args ...interface{}) {
152
- NewEntry(logger).Errorln(args...)
188
+ if logger.Level >= ErrorLevel {
189
+ NewEntry(logger).Errorln(args...)
190
+ }
191
}
192
193
func (logger *Logger) Fatalln(args ...interface{}) {
156
- NewEntry(logger).Fatalln(args...)
194
+ if logger.Level >= FatalLevel {
195
+ NewEntry(logger).Fatalln(args...)
196
+ }
197
}
198
199
func (logger *Logger) Panicln(args ...interface{}) {
160
- NewEntry(logger).Panicln(args...)
200
+ if logger.Level >= PanicLevel {
201
+ NewEntry(logger).Panicln(args...)
202
+ }
203
}
Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_openbsd.go
-1
@@ -1,4 +1,3 @@
1
-
1
package logrus
2
3
import "syscall"
Godeps/_workspace/src/github.com/Sirupsen/logrus/text_formatter.go
+13
-5
@@ -3,7 +3,6 @@ package logrus
3
import (
4
"bytes"
5
"fmt"
6
- "regexp"
6
"sort"
7
"strings"
8
"time"
@@ -15,12 +14,12 @@ const (
14
green = 32
15
yellow = 33
16
blue = 34
17
+ gray = 37
18
)
19
20
var (
21
baseTimestamp time.Time
22
isTerminal bool
23
- noQuoteNeeded *regexp.Regexp
23
)
24
25
func init() {
@@ -46,15 +45,22 @@ type TextFormatter struct {
45
// Enable logging the full timestamp when a TTY is attached instead of just
46
// the time passed since beginning of execution.
47
FullTimestamp bool
48
+
49
+ // The fields are sorted by default for a consistent output. For applications
50
+ // that log extremely frequently and don't use the JSON formatter this may not
51
+ // be desired.
52
+ DisableSorting bool
53
}
54
55
func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
52
-
56
var keys []string = make([]string, 0, len(entry.Data))
57
for k := range entry.Data {
58
keys = append(keys, k)
59
}
57
- sort.Strings(keys)
60
+
61
+ if !f.DisableSorting {
62
+ sort.Strings(keys)
63
+ }
64
65
b := &bytes.Buffer{}
66
@@ -82,6 +88,8 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
88
func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []string) {
89
var levelColor int
90
switch entry.Level {
91
+ case DebugLevel:
92
+ levelColor = gray
93
case WarnLevel:
94
levelColor = yellow
95
case ErrorLevel, FatalLevel, PanicLevel:
@@ -107,7 +115,7 @@ func needsQuoting(text string) bool {
115
for _, ch := range text {
116
if !((ch >= 'a' && ch <= 'z') ||
117
(ch >= 'A' && ch <= 'Z') ||
110
- (ch >= '0' && ch < '9') ||
118
+ (ch >= '0' && ch <= '9') ||
119
ch == '-' || ch == '.') {
120
return false
121
}
Godeps/_workspace/src/github.com/Sirupsen/logrus/text_formatter_test.go
+4
@@ -25,9 +25,13 @@ func TestQuoting(t *testing.T) {
25
26
checkQuoting(false, "abcd")
27
checkQuoting(false, "v1.0")
28
+ checkQuoting(false, "1234567890")
29
checkQuoting(true, "/foobar")
30
checkQuoting(true, "x y")
31
checkQuoting(true, "x,y")
32
checkQuoting(false, errors.New("invalid"))
33
checkQuoting(true, errors.New("invalid argument"))
34
}
35
+
36
+// TODO add tests for sorting etc., this requires a parser for the text
37
+// formatter output.
Godeps/_workspace/src/github.com/Sirupsen/logrus/writer.go
+1
-1
@@ -6,7 +6,7 @@ import (
6
"runtime"
7
)
8
9
-func (logger *Logger) Writer() (*io.PipeWriter) {
9
+func (logger *Logger) Writer() *io.PipeWriter {
10
reader, writer := io.Pipe()
11
12
go logger.writerScanner(reader)
Godeps/_workspace/src/github.com/coreos/go-semver/semver/semver.go
+7
@@ -58,6 +58,13 @@ func NewVersion(version string) (*Version, error) {
58
return &v, nil
59
}
60
61
+func Must(v *Version, err error) *Version {
62
+ if err != nil {
63
+ panic(err)
64
+ }
65
+ return v
66
+}
67
+
68
func (v *Version) String() string {
69
var buffer bytes.Buffer
70
Godeps/_workspace/src/github.com/coreos/go-semver/semver/semver_test.go
+36
@@ -1,7 +1,9 @@
1
package semver
2
3
import (
4
+ "errors"
5
"math/rand"
6
+ "reflect"
7
"testing"
8
"time"
9
)
@@ -185,3 +187,37 @@ func TestBumpPatch(t *testing.T) {
187
t.Fatalf("bumping major on 1.0.0+build.1-alpha.1 resulted in %v", version)
188
}
189
}
190
+
191
+func TestMust(t *testing.T) {
192
+ tests := []struct {
193
+ versionStr string
194
+
195
+ version *Version
196
+ recov interface{}
197
+ }{
198
+ {
199
+ versionStr: "1.0.0",
200
+ version: &Version{Major: 1},
201
+ },
202
+ {
203
+ versionStr: "version number",
204
+ recov: errors.New("version number is not in dotted-tri format"),
205
+ },
206
+ }
207
+
208
+ for _, tt := range tests {
209
+ func() {
210
+ defer func() {
211
+ recov := recover()
212
+ if !reflect.DeepEqual(tt.recov, recov) {
213
+ t.Fatalf("incorrect panic for %q: want %v, got %v", tt.versionStr, tt.recov, recov)
214
+ }
215
+ }()
216
+
217
+ version := Must(NewVersion(tt.versionStr))
218
+ if !reflect.DeepEqual(tt.version, version) {
219
+ t.Fatalf("incorrect version for %q: want %+v, got %+v", tt.versionStr, tt.version, version)
220
+ }
221
+ }()
222
+ }
223
+}
Godeps/_workspace/src/github.com/gorilla/context/.travis.yml
deleted
-7
@@ -1,7 +0,0 @@
1
-language: go
2
-
3
-go:
4
- - 1.0
5
- - 1.1
6
- - 1.2
7
- - tip
Godeps/_workspace/src/github.com/gorilla/context/LICENSE
deleted
-27
@@ -1,27 +0,0 @@
1
-Copyright (c) 2012 Rodrigo Moraes. All rights reserved.
2
-
3
-Redistribution and use in source and binary forms, with or without
4
-modification, are permitted provided that the following conditions are
5
-met:
6
-
7
- * Redistributions of source code must retain the above copyright
8
-notice, this list of conditions and the following disclaimer.
9
- * Redistributions in binary form must reproduce the above
10
-copyright notice, this list of conditions and the following disclaimer
11
-in the documentation and/or other materials provided with the
12
-distribution.
13
- * Neither the name of Google Inc. nor the names of its
14
-contributors may be used to endorse or promote products derived from
15
-this software without specific prior written permission.
16
-
17
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Godeps/_workspace/src/github.com/gorilla/context/README.md
deleted
-7
@@ -1,7 +0,0 @@
1
-context
2
-=======
3
-[](https://travis-ci.org/gorilla/context)
4
-
5
-gorilla/context is a general purpose registry for global request variables.
6
-
7
-Read the full documentation here: http://www.gorillatoolkit.org/pkg/context
Godeps/_workspace/src/github.com/gorilla/context/context.go
deleted
-143
@@ -1,143 +0,0 @@
1
-// Copyright 2012 The Gorilla Authors. All rights reserved.
2
-// Use of this source code is governed by a BSD-style
3
-// license that can be found in the LICENSE file.
4
-
5
-package context
6
-
7
-import (
8
- "net/http"
9
- "sync"
10
- "time"
11
-)
12
-
13
-var (
14
- mutex sync.RWMutex
15
- data = make(map[*http.Request]map[interface{}]interface{})
16
- datat = make(map[*http.Request]int64)
17
-)
18
-
19
-// Set stores a value for a given key in a given request.
20
-func Set(r *http.Request, key, val interface{}) {
21
- mutex.Lock()
22
- if data[r] == nil {
23
- data[r] = make(map[interface{}]interface{})
24
- datat[r] = time.Now().Unix()
25
- }
26
- data[r][key] = val
27
- mutex.Unlock()
28
-}
29
-
30
-// Get returns a value stored for a given key in a given request.
31
-func Get(r *http.Request, key interface{}) interface{} {
32
- mutex.RLock()
33
- if ctx := data[r]; ctx != nil {
34
- value := ctx[key]
35
- mutex.RUnlock()
36
- return value
37
- }
38
- mutex.RUnlock()
39
- return nil
40
-}
41
-
42
-// GetOk returns stored value and presence state like multi-value return of map access.
43
-func GetOk(r *http.Request, key interface{}) (interface{}, bool) {
44
- mutex.RLock()
45
- if _, ok := data[r]; ok {
46
- value, ok := data[r][key]
47
- mutex.RUnlock()
48
- return value, ok
49
- }
50
- mutex.RUnlock()
51
- return nil, false
52
-}
53
-
54
-// GetAll returns all stored values for the request as a map. Nil is returned for invalid requests.
55
-func GetAll(r *http.Request) map[interface{}]interface{} {
56
- mutex.RLock()
57
- if context, ok := data[r]; ok {
58
- result := make(map[interface{}]interface{}, len(context))
59
- for k, v := range context {
60
- result[k] = v
61
- }
62
- mutex.RUnlock()
63
- return result
64
- }
65
- mutex.RUnlock()
66
- return nil
67
-}
68
-
69
-// GetAllOk returns all stored values for the request as a map and a boolean value that indicates if
70
-// the request was registered.
71
-func GetAllOk(r *http.Request) (map[interface{}]interface{}, bool) {
72
- mutex.RLock()
73
- context, ok := data[r]
74
- result := make(map[interface{}]interface{}, len(context))
75
- for k, v := range context {
76
- result[k] = v
77
- }
78
- mutex.RUnlock()
79
- return result, ok
80
-}
81
-
82
-// Delete removes a value stored for a given key in a given request.
83
-func Delete(r *http.Request, key interface{}) {
84
- mutex.Lock()
85
- if data[r] != nil {
86
- delete(data[r], key)
87
- }
88
- mutex.Unlock()
89
-}
90
-
91
-// Clear removes all values stored for a given request.
92
-//
93
-// This is usually called by a handler wrapper to clean up request
94
-// variables at the end of a request lifetime. See ClearHandler().
95
-func Clear(r *http.Request) {
96
- mutex.Lock()
97
- clear(r)
98
- mutex.Unlock()
99
-}
100
-
101
-// clear is Clear without the lock.
102
-func clear(r *http.Request) {
103
- delete(data, r)
104
- delete(datat, r)
105
-}
106
-
107
-// Purge removes request data stored for longer than maxAge, in seconds.
108
-// It returns the amount of requests removed.
109
-//
110
-// If maxAge <= 0, all request data is removed.
111
-//
112
-// This is only used for sanity check: in case context cleaning was not
113
-// properly set some request data can be kept forever, consuming an increasing
114
-// amount of memory. In case this is detected, Purge() must be called
115
-// periodically until the problem is fixed.
116
-func Purge(maxAge int) int {
117
- mutex.Lock()
118
- count := 0
119
- if maxAge <= 0 {
120
- count = len(data)
121
- data = make(map[*http.Request]map[interface{}]interface{})
122
- datat = make(map[*http.Request]int64)
123
- } else {
124
- min := time.Now().Unix() - int64(maxAge)
125
- for r := range data {
126
- if datat[r] < min {
127
- clear(r)
128
- count++
129
- }
130
- }
131
- }
132
- mutex.Unlock()
133
- return count
134
-}
135
-
136
-// ClearHandler wraps an http.Handler and clears request values at the end
137
-// of a request lifetime.
138
-func ClearHandler(h http.Handler) http.Handler {
139
- return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
140
- defer Clear(r)
141
- h.ServeHTTP(w, r)
142
- })
143
-}
Godeps/_workspace/src/github.com/gorilla/context/context_test.go
deleted
-161
@@ -1,161 +0,0 @@
1
-// Copyright 2012 The Gorilla Authors. All rights reserved.
2
-// Use of this source code is governed by a BSD-style
3
-// license that can be found in the LICENSE file.
4
-
5
-package context
6
-
7
-import (
8
- "net/http"
9
- "testing"
10
-)
11
-
12
-type keyType int
13
-
14
-const (
15
- key1 keyType = iota
16
- key2
17
-)
18
-
19
-func TestContext(t *testing.T) {
20
- assertEqual := func(val interface{}, exp interface{}) {
21
- if val != exp {
22
- t.Errorf("Expected %v, got %v.", exp, val)
23
- }
24
- }
25
-
26
- r, _ := http.NewRequest("GET", "http://localhost:8080/", nil)
27
- emptyR, _ := http.NewRequest("GET", "http://localhost:8080/", nil)
28
-
29
- // Get()
30
- assertEqual(Get(r, key1), nil)
31
-
32
- // Set()
33
- Set(r, key1, "1")
34
- assertEqual(Get(r, key1), "1")
35
- assertEqual(len(data[r]), 1)
36
-
37
- Set(r, key2, "2")
38
- assertEqual(Get(r, key2), "2")
39
- assertEqual(len(data[r]), 2)
40
-
41
- //GetOk
42
- value, ok := GetOk(r, key1)
43
- assertEqual(value, "1")
44
- assertEqual(ok, true)
45
-
46
- value, ok = GetOk(r, "not exists")
47
- assertEqual(value, nil)
48
- assertEqual(ok, false)
49
-
50
- Set(r, "nil value", nil)
51
- value, ok = GetOk(r, "nil value")
52
- assertEqual(value, nil)
53
- assertEqual(ok, true)
54
-
55
- // GetAll()
56
- values := GetAll(r)
57
- assertEqual(len(values), 3)
58
-
59
- // GetAll() for empty request
60
- values = GetAll(emptyR)
61
- if values != nil {
62
- t.Error("GetAll didn't return nil value for invalid request")
63
- }
64
-
65
- // GetAllOk()
66
- values, ok = GetAllOk(r)
67
- assertEqual(len(values), 3)
68
- assertEqual(ok, true)
69
-
70
- // GetAllOk() for empty request
71
- values, ok = GetAllOk(emptyR)
72
- assertEqual(value, nil)
73
- assertEqual(ok, false)
74
-
75
- // Delete()
76
- Delete(r, key1)
77
- assertEqual(Get(r, key1), nil)
78
- assertEqual(len(data[r]), 2)
79
-
80
- Delete(r, key2)
81
- assertEqual(Get(r, key2), nil)
82
- assertEqual(len(data[r]), 1)
83
-
84
- // Clear()
85
- Clear(r)
86
- assertEqual(len(data), 0)
87
-}
88
-
89
-func parallelReader(r *http.Request, key string, iterations int, wait, done chan struct{}) {
90
- <-wait
91
- for i := 0; i < iterations; i++ {
92
- Get(r, key)
93
- }
94
- done <- struct{}{}
95
-
96
-}
97
-
98
-func parallelWriter(r *http.Request, key, value string, iterations int, wait, done chan struct{}) {
99
- <-wait
100
- for i := 0; i < iterations; i++ {
101
- Get(r, key)
102
- }
103
- done <- struct{}{}
104
-
105
-}
106
-
107
-func benchmarkMutex(b *testing.B, numReaders, numWriters, iterations int) {
108
-
109
- b.StopTimer()
110
- r, _ := http.NewRequest("GET", "http://localhost:8080/", nil)
111
- done := make(chan struct{})
112
- b.StartTimer()
113
-
114
- for i := 0; i < b.N; i++ {
115
- wait := make(chan struct{})
116
-
117
- for i := 0; i < numReaders; i++ {
118
- go parallelReader(r, "test", iterations, wait, done)
119
- }
120
-
121
- for i := 0; i < numWriters; i++ {
122
- go parallelWriter(r, "test", "123", iterations, wait, done)
123
- }
124
-
125
- close(wait)
126
-
127
- for i := 0; i < numReaders+numWriters; i++ {
128
- <-done
129
- }
130
-
131
- }
132
-
133
-}
134
-
135
-func BenchmarkMutexSameReadWrite1(b *testing.B) {
136
- benchmarkMutex(b, 1, 1, 32)
137
-}
138
-func BenchmarkMutexSameReadWrite2(b *testing.B) {
139
- benchmarkMutex(b, 2, 2, 32)
140
-}
141
-func BenchmarkMutexSameReadWrite4(b *testing.B) {
142
- benchmarkMutex(b, 4, 4, 32)
143
-}
144
-func BenchmarkMutex1(b *testing.B) {
145
- benchmarkMutex(b, 2, 8, 32)
146
-}
147
-func BenchmarkMutex2(b *testing.B) {
148
- benchmarkMutex(b, 16, 4, 64)
149
-}
150
-func BenchmarkMutex3(b *testing.B) {
151
- benchmarkMutex(b, 1, 2, 128)
152
-}
153
-func BenchmarkMutex4(b *testing.B) {
154
- benchmarkMutex(b, 128, 32, 256)
155
-}
156
-func BenchmarkMutex5(b *testing.B) {
157
- benchmarkMutex(b, 1024, 2048, 64)
158
-}
159
-func BenchmarkMutex6(b *testing.B) {
160
- benchmarkMutex(b, 2048, 1024, 512)
161
-}
Godeps/_workspace/src/github.com/gorilla/context/doc.go
deleted
-82
@@ -1,82 +0,0 @@
1
-// Copyright 2012 The Gorilla Authors. All rights reserved.
2
-// Use of this source code is governed by a BSD-style
3
-// license that can be found in the LICENSE file.
4
-
5
-/*
6
-Package context stores values shared during a request lifetime.
7
-
8
-For example, a router can set variables extracted from the URL and later
9
-application handlers can access those values, or it can be used to store
10
-sessions values to be saved at the end of a request. There are several
11
-others common uses.
12
-
13
-The idea was posted by Brad Fitzpatrick to the go-nuts mailing list:
14
-
15
- http://groups.google.com/group/golang-nuts/msg/e2d679d303aa5d53
16
-
17
-Here's the basic usage: first define the keys that you will need. The key
18
-type is interface{} so a key can be of any type that supports equality.
19
-Here we define a key using a custom int type to avoid name collisions:
20
-
21
- package foo
22
-
23
- import (
24
- "github.com/gorilla/context"
25
- )
26
-
27
- type key int
28
-
29
- const MyKey key = 0
30
-
31
-Then set a variable. Variables are bound to an http.Request object, so you
32
-need a request instance to set a value:
33
-
34
- context.Set(r, MyKey, "bar")
35
-
36
-The application can later access the variable using the same key you provided:
37
-
38
- func MyHandler(w http.ResponseWriter, r *http.Request) {
39
- // val is "bar".
40
- val := context.Get(r, foo.MyKey)
41
-
42
- // returns ("bar", true)
43
- val, ok := context.GetOk(r, foo.MyKey)
44
- // ...
45
- }
46
-
47
-And that's all about the basic usage. We discuss some other ideas below.
48
-
49
-Any type can be stored in the context. To enforce a given type, make the key
50
-private and wrap Get() and Set() to accept and return values of a specific
51
-type:
52
-
53
- type key int
54
-
55
- const mykey key = 0
56
-
57
- // GetMyKey returns a value for this package from the request values.
58
- func GetMyKey(r *http.Request) SomeType {
59
- if rv := context.Get(r, mykey); rv != nil {
60
- return rv.(SomeType)
61
- }
62
- return nil
63
- }
64
-
65
- // SetMyKey sets a value for this package in the request values.
66
- func SetMyKey(r *http.Request, val SomeType) {
67
- context.Set(r, mykey, val)
68
- }
69
-
70
-Variables must be cleared at the end of a request, to remove all values
71
-that were stored. This can be done in an http.Handler, after a request was
72
-served. Just call Clear() passing the request:
73
-
74
- context.Clear(r)
75
-
76
-...or use ClearHandler(), which conveniently wraps an http.Handler to clear
77
-variables at the end of a request lifetime.
78
-
79
-The Routers from the packages gorilla/mux and gorilla/pat call Clear()
80
-so if you are using either of them you don't need to clear the context manually.
81
-*/
82
-package context
Godeps/_workspace/src/github.com/gorilla/mux/.travis.yml
deleted
-7
@@ -1,7 +0,0 @@
1
-language: go
2
-
3
-go:
4
- - 1.0
5
- - 1.1
6
- - 1.2
7
- - tip
Godeps/_workspace/src/github.com/gorilla/mux/LICENSE
deleted
-27
@@ -1,27 +0,0 @@
1
-Copyright (c) 2012 Rodrigo Moraes. All rights reserved.
2
-
3
-Redistribution and use in source and binary forms, with or without
4
-modification, are permitted provided that the following conditions are
5
-met:
6
-
7
- * Redistributions of source code must retain the above copyright
8
-notice, this list of conditions and the following disclaimer.
9
- * Redistributions in binary form must reproduce the above
10
-copyright notice, this list of conditions and the following disclaimer
11
-in the documentation and/or other materials provided with the
12
-distribution.
13
- * Neither the name of Google Inc. nor the names of its
14
-contributors may be used to endorse or promote products derived from
15
-this software without specific prior written permission.
16
-
17
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Godeps/_workspace/src/github.com/gorilla/mux/README.md
deleted
-7
@@ -1,7 +0,0 @@
1
-mux
2
-===
3
-[](https://travis-ci.org/gorilla/mux)
4
-
5
-gorilla/mux is a powerful URL router and dispatcher.
6
-
7
-Read the full documentation here: http://www.gorillatoolkit.org/pkg/mux
Godeps/_workspace/src/github.com/gorilla/mux/bench_test.go
deleted
-21
@@ -1,21 +0,0 @@
1
-// Copyright 2012 The Gorilla Authors. All rights reserved.
2
-// Use of this source code is governed by a BSD-style
3
-// license that can be found in the LICENSE file.
4
-
5
-package mux
6
-
7
-import (
8
- "net/http"
9
- "testing"
10
-)
11
-
12
-func BenchmarkMux(b *testing.B) {
13
- router := new(Router)
14
- handler := func(w http.ResponseWriter, r *http.Request) {}
15
- router.HandleFunc("/v1/{v1}", handler)
16
-
17
- request, _ := http.NewRequest("GET", "/v1/anything", nil)
18
- for i := 0; i < b.N; i++ {
19
- router.ServeHTTP(nil, request)
20
- }
21
-}
Godeps/_workspace/src/github.com/gorilla/mux/doc.go
deleted
-199
@@ -1,199 +0,0 @@
1
-// Copyright 2012 The Gorilla Authors. All rights reserved.
2
-// Use of this source code is governed by a BSD-style
3
-// license that can be found in the LICENSE file.
4
-
5
-/*
6
-Package gorilla/mux implements a request router and dispatcher.
7
-
8
-The name mux stands for "HTTP request multiplexer". Like the standard
9
-http.ServeMux, mux.Router matches incoming requests against a list of
10
-registered routes and calls a handler for the route that matches the URL
11
-or other conditions. The main features are:
12
-
13
- * Requests can be matched based on URL host, path, path prefix, schemes,
14
- header and query values, HTTP methods or using custom matchers.
15
- * URL hosts and paths can have variables with an optional regular
16
- expression.
17
- * Registered URLs can be built, or "reversed", which helps maintaining
18
- references to resources.
19
- * Routes can be used as subrouters: nested routes are only tested if the
20
- parent route matches. This is useful to define groups of routes that
21
- share common conditions like a host, a path prefix or other repeated
22
- attributes. As a bonus, this optimizes request matching.
23
- * It implements the http.Handler interface so it is compatible with the
24
- standard http.ServeMux.
25
-
26
-Let's start registering a couple of URL paths and handlers:
27
-
28
- func main() {
29
- r := mux.NewRouter()
30
- r.HandleFunc("/", HomeHandler)
31
- r.HandleFunc("/products", ProductsHandler)
32
- r.HandleFunc("/articles", ArticlesHandler)
33
- http.Handle("/", r)
34
- }
35
-
36
-Here we register three routes mapping URL paths to handlers. This is
37
-equivalent to how http.HandleFunc() works: if an incoming request URL matches
38
-one of the paths, the corresponding handler is called passing
39
-(http.ResponseWriter, *http.Request) as parameters.
40
-
41
-Paths can have variables. They are defined using the format {name} or
42
-{name:pattern}. If a regular expression pattern is not defined, the matched
43
-variable will be anything until the next slash. For example:
44
-
45
- r := mux.NewRouter()
46
- r.HandleFunc("/products/{key}", ProductHandler)
47
- r.HandleFunc("/articles/{category}/", ArticlesCategoryHandler)
48
- r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler)
49
-
50
-The names are used to create a map of route variables which can be retrieved
51
-calling mux.Vars():
52
-
53
- vars := mux.Vars(request)
54
- category := vars["category"]
55
-
56
-And this is all you need to know about the basic usage. More advanced options
57
-are explained below.
58
-
59
-Routes can also be restricted to a domain or subdomain. Just define a host
60
-pattern to be matched. They can also have variables:
61
-
62
- r := mux.NewRouter()
63
- // Only matches if domain is "www.domain.com".
64
- r.Host("www.domain.com")
65
- // Matches a dynamic subdomain.
66
- r.Host("{subdomain:[a-z]+}.domain.com")
67
-
68
-There are several other matchers that can be added. To match path prefixes:
69
-
70
- r.PathPrefix("/products/")
71
-
72
-...or HTTP methods:
73
-
74
- r.Methods("GET", "POST")
75
-
76
-...or URL schemes:
77
-
78
- r.Schemes("https")
79
-
80
-...or header values:
81
-
82
- r.Headers("X-Requested-With", "XMLHttpRequest")
83
-
84
-...or query values:
85
-
86
- r.Queries("key", "value")
87
-
88
-...or to use a custom matcher function:
89
-
90
- r.MatcherFunc(func(r *http.Request, rm *RouteMatch) bool {
91
- return r.ProtoMajor == 0
92
- })
93
-
94
-...and finally, it is possible to combine several matchers in a single route:
95
-
96
- r.HandleFunc("/products", ProductsHandler).
97
- Host("www.domain.com").
98
- Methods("GET").
99
- Schemes("http")
100
-
101
-Setting the same matching conditions again and again can be boring, so we have
102
-a way to group several routes that share the same requirements.
103
-We call it "subrouting".
104
-
105
-For example, let's say we have several URLs that should only match when the
106
-host is "www.domain.com". Create a route for that host and get a "subrouter"
107
-from it:
108
-
109
- r := mux.NewRouter()
110
- s := r.Host("www.domain.com").Subrouter()
111
-
112
-Then register routes in the subrouter:
113
-
114
- s.HandleFunc("/products/", ProductsHandler)
115
- s.HandleFunc("/products/{key}", ProductHandler)
116
- s.HandleFunc("/articles/{category}/{id:[0-9]+}"), ArticleHandler)
117
-
118
-The three URL paths we registered above will only be tested if the domain is
119
-"www.domain.com", because the subrouter is tested first. This is not
120
-only convenient, but also optimizes request matching. You can create
121
-subrouters combining any attribute matchers accepted by a route.
122
-
123
-Subrouters can be used to create domain or path "namespaces": you define
124
-subrouters in a central place and then parts of the app can register its
125
-paths relatively to a given subrouter.
126
-
127
-There's one more thing about subroutes. When a subrouter has a path prefix,
128
-the inner routes use it as base for their paths:
129
-
130
- r := mux.NewRouter()
131
- s := r.PathPrefix("/products").Subrouter()
132
- // "/products/"
133
- s.HandleFunc("/", ProductsHandler)
134
- // "/products/{key}/"
135
- s.HandleFunc("/{key}/", ProductHandler)
136
- // "/products/{key}/details"
137
- s.HandleFunc("/{key}/details", ProductDetailsHandler)
138
-
139
-Now let's see how to build registered URLs.
140
-
141
-Routes can be named. All routes that define a name can have their URLs built,
142
-or "reversed". We define a name calling Name() on a route. For example:
143
-
144
- r := mux.NewRouter()
145
- r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler).
146
- Name("article")
147
-
148
-To build a URL, get the route and call the URL() method, passing a sequence of
149
-key/value pairs for the route variables. For the previous route, we would do:
150
-
151
- url, err := r.Get("article").URL("category", "technology", "id", "42")
152
-
153
-...and the result will be a url.URL with the following path:
154
-
155
- "/articles/technology/42"
156
-
157
-This also works for host variables:
158
-
159
- r := mux.NewRouter()
160
- r.Host("{subdomain}.domain.com").
161
- Path("/articles/{category}/{id:[0-9]+}").
162
- HandlerFunc(ArticleHandler).
163
- Name("article")
164
-
165
- // url.String() will be "http://news.domain.com/articles/technology/42"
166
- url, err := r.Get("article").URL("subdomain", "news",
167
- "category", "technology",
168
- "id", "42")
169
-
170
-All variables defined in the route are required, and their values must
171
-conform to the corresponding patterns. These requirements guarantee that a
172
-generated URL will always match a registered route -- the only exception is
173
-for explicitly defined "build-only" routes which never match.
174
-
175
-There's also a way to build only the URL host or path for a route:
176
-use the methods URLHost() or URLPath() instead. For the previous route,
177
-we would do:
178
-
179
- // "http://news.domain.com/"
180
- host, err := r.Get("article").URLHost("subdomain", "news")
181
-
182
- // "/articles/technology/42"
183
- path, err := r.Get("article").URLPath("category", "technology", "id", "42")
184
-
185
-And if you use subrouters, host and path defined separately can be built
186
-as well:
187
-
188
- r := mux.NewRouter()
189
- s := r.Host("{subdomain}.domain.com").Subrouter()
190
- s.Path("/articles/{category}/{id:[0-9]+}").
191
- HandlerFunc(ArticleHandler).
192
- Name("article")
193
-
194
- // "http://news.domain.com/articles/technology/42"
195
- url, err := r.Get("article").URL("subdomain", "news",
196
- "category", "technology",
197
- "id", "42")
198
-*/
199
-package mux
Godeps/_workspace/src/github.com/gorilla/mux/mux.go
deleted
-353
@@ -1,353 +0,0 @@
1
-// Copyright 2012 The Gorilla Authors. All rights reserved.
2
-// Use of this source code is governed by a BSD-style
3
-// license that can be found in the LICENSE file.
4
-
5
-package mux
6
-
7
-import (
8
- "fmt"
9
- "net/http"
10
- "path"
11
-
12
- "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/gorilla/context"
13
-)
14
-
15
-// NewRouter returns a new router instance.
16
-func NewRouter() *Router {
17
- return &Router{namedRoutes: make(map[string]*Route), KeepContext: false}
18
-}
19
-
20
-// Router registers routes to be matched and dispatches a handler.
21
-//
22
-// It implements the http.Handler interface, so it can be registered to serve
23
-// requests:
24
-//
25
-// var router = mux.NewRouter()
26
-//
27
-// func main() {
28
-// http.Handle("/", router)
29
-// }
30
-//
31
-// Or, for Google App Engine, register it in a init() function:
32
-//
33
-// func init() {
34
-// http.Handle("/", router)
35
-// }
36
-//
37
-// This will send all incoming requests to the router.
38
-type Router struct {
39
- // Configurable Handler to be used when no route matches.
40
- NotFoundHandler http.Handler
41
- // Parent route, if this is a subrouter.
42
- parent parentRoute
43
- // Routes to be matched, in order.
44
- routes []*Route
45
- // Routes by name for URL building.
46
- namedRoutes map[string]*Route
47
- // See Router.StrictSlash(). This defines the flag for new routes.
48
- strictSlash bool
49
- // If true, do not clear the request context after handling the request
50
- KeepContext bool
51
-}
52
-
53
-// Match matches registered routes against the request.
54
-func (r *Router) Match(req *http.Request, match *RouteMatch) bool {
55
- for _, route := range r.routes {
56
- if route.Match(req, match) {
57
- return true
58
- }
59
- }
60
- return false
61
-}
62
-
63
-// ServeHTTP dispatches the handler registered in the matched route.
64
-//
65
-// When there is a match, the route variables can be retrieved calling
66
-// mux.Vars(request).
67
-func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
68
- // Clean path to canonical form and redirect.
69
- if p := cleanPath(req.URL.Path); p != req.URL.Path {
70
-
71
- // Added 3 lines (Philip Schlump) - It was droping the query string and #whatever from query.
72
- // This matches with fix in go 1.2 r.c. 4 for same problem. Go Issue:
73
- // http://code.google.com/p/go/issues/detail?id=5252
74
- url := *req.URL
75
- url.Path = p
76
- p = url.String()
77
-
78
- w.Header().Set("Location", p)
79
- w.WriteHeader(http.StatusMovedPermanently)
80
- return
81
- }
82
- var match RouteMatch
83
- var handler http.Handler
84
- if r.Match(req, &match) {
85
- handler = match.Handler
86
- setVars(req, match.Vars)
87
- setCurrentRoute(req, match.Route)
88
- }
89
- if handler == nil {
90
- handler = r.NotFoundHandler
91
- if handler == nil {
92
- handler = http.NotFoundHandler()
93
- }
94
- }
95
- if !r.KeepContext {
96
- defer context.Clear(req)
97
- }
98
- handler.ServeHTTP(w, req)
99
-}
100
-
101
-// Get returns a route registered with the given name.
102
-func (r *Router) Get(name string) *Route {
103
- return r.getNamedRoutes()[name]
104
-}
105
-
106
-// GetRoute returns a route registered with the given name. This method
107
-// was renamed to Get() and remains here for backwards compatibility.
108
-func (r *Router) GetRoute(name string) *Route {
109
- return r.getNamedRoutes()[name]
110
-}
111
-
112
-// StrictSlash defines the trailing slash behavior for new routes. The initial
113
-// value is false.
114
-//
115
-// When true, if the route path is "/path/", accessing "/path" will redirect
116
-// to the former and vice versa. In other words, your application will always
117
-// see the path as specified in the route.
118
-//
119
-// When false, if the route path is "/path", accessing "/path/" will not match
120
-// this route and vice versa.
121
-//
122
-// Special case: when a route sets a path prefix using the PathPrefix() method,
123
-// strict slash is ignored for that route because the redirect behavior can't
124
-// be determined from a prefix alone. However, any subrouters created from that
125
-// route inherit the original StrictSlash setting.
126
-func (r *Router) StrictSlash(value bool) *Router {
127
- r.strictSlash = value
128
- return r
129
-}
130
-
131
-// ----------------------------------------------------------------------------
132
-// parentRoute
133
-// ----------------------------------------------------------------------------
134
-
135
-// getNamedRoutes returns the map where named routes are registered.
136
-func (r *Router) getNamedRoutes() map[string]*Route {
137
- if r.namedRoutes == nil {
138
- if r.parent != nil {
139
- r.namedRoutes = r.parent.getNamedRoutes()
140
- } else {
141
- r.namedRoutes = make(map[string]*Route)
142
- }
143
- }
144
- return r.namedRoutes
145
-}
146
-
147
-// getRegexpGroup returns regexp definitions from the parent route, if any.
148
-func (r *Router) getRegexpGroup() *routeRegexpGroup {
149
- if r.parent != nil {
150
- return r.parent.getRegexpGroup()
151
- }
152
- return nil
153
-}
154
-
155
-// ----------------------------------------------------------------------------
156
-// Route factories
157
-// ----------------------------------------------------------------------------
158
-
159
-// NewRoute registers an empty route.
160
-func (r *Router) NewRoute() *Route {
161
- route := &Route{parent: r, strictSlash: r.strictSlash}
162
- r.routes = append(r.routes, route)
163
- return route
164
-}
165
-
166
-// Handle registers a new route with a matcher for the URL path.
167
-// See Route.Path() and Route.Handler().
168
-func (r *Router) Handle(path string, handler http.Handler) *Route {
169
- return r.NewRoute().Path(path).Handler(handler)
170
-}
171
-
172
-// HandleFunc registers a new route with a matcher for the URL path.
173
-// See Route.Path() and Route.HandlerFunc().
174
-func (r *Router) HandleFunc(path string, f func(http.ResponseWriter,
175
- *http.Request)) *Route {
176
- return r.NewRoute().Path(path).HandlerFunc(f)
177
-}
178
-
179
-// Headers registers a new route with a matcher for request header values.
180
-// See Route.Headers().
181
-func (r *Router) Headers(pairs ...string) *Route {
182
- return r.NewRoute().Headers(pairs...)
183
-}
184
-
185
-// Host registers a new route with a matcher for the URL host.
186
-// See Route.Host().
187
-func (r *Router) Host(tpl string) *Route {
188
- return r.NewRoute().Host(tpl)
189
-}
190
-
191
-// MatcherFunc registers a new route with a custom matcher function.
192
-// See Route.MatcherFunc().
193
-func (r *Router) MatcherFunc(f MatcherFunc) *Route {
194
- return r.NewRoute().MatcherFunc(f)
195
-}
196
-
197
-// Methods registers a new route with a matcher for HTTP methods.
198
-// See Route.Methods().
199
-func (r *Router) Methods(methods ...string) *Route {
200
- return r.NewRoute().Methods(methods...)
201
-}
202
-
203
-// Path registers a new route with a matcher for the URL path.
204
-// See Route.Path().
205
-func (r *Router) Path(tpl string) *Route {
206
- return r.NewRoute().Path(tpl)
207
-}
208
-
209
-// PathPrefix registers a new route with a matcher for the URL path prefix.
210
-// See Route.PathPrefix().
211
-func (r *Router) PathPrefix(tpl string) *Route {
212
- return r.NewRoute().PathPrefix(tpl)
213
-}
214
-
215
-// Queries registers a new route with a matcher for URL query values.
216
-// See Route.Queries().
217
-func (r *Router) Queries(pairs ...string) *Route {
218
- return r.NewRoute().Queries(pairs...)
219
-}
220
-
221
-// Schemes registers a new route with a matcher for URL schemes.
222
-// See Route.Schemes().
223
-func (r *Router) Schemes(schemes ...string) *Route {
224
- return r.NewRoute().Schemes(schemes...)
225
-}
226
-
227
-// ----------------------------------------------------------------------------
228
-// Context
229
-// ----------------------------------------------------------------------------
230
-
231
-// RouteMatch stores information about a matched route.
232
-type RouteMatch struct {
233
- Route *Route
234
- Handler http.Handler
235
- Vars map[string]string
236
-}
237
-
238
-type contextKey int
239
-
240
-const (
241
- varsKey contextKey = iota
242
- routeKey
243
-)
244
-
245
-// Vars returns the route variables for the current request, if any.
246
-func Vars(r *http.Request) map[string]string {
247
- if rv := context.Get(r, varsKey); rv != nil {
248
- return rv.(map[string]string)
249
- }
250
- return nil
251
-}
252
-
253
-// CurrentRoute returns the matched route for the current request, if any.
254
-func CurrentRoute(r *http.Request) *Route {
255
- if rv := context.Get(r, routeKey); rv != nil {
256
- return rv.(*Route)
257
- }
258
- return nil
259
-}
260
-
261
-func setVars(r *http.Request, val interface{}) {
262
- context.Set(r, varsKey, val)
263
-}
264
-
265
-func setCurrentRoute(r *http.Request, val interface{}) {
266
- context.Set(r, routeKey, val)
267
-}
268
-
269
-// ----------------------------------------------------------------------------
270
-// Helpers
271
-// ----------------------------------------------------------------------------
272
-
273
-// cleanPath returns the canonical path for p, eliminating . and .. elements.
274
-// Borrowed from the net/http package.
275
-func cleanPath(p string) string {
276
- if p == "" {
277
- return "/"
278
- }
279
- if p[0] != '/' {
280
- p = "/" + p
281
- }
282
- np := path.Clean(p)
283
- // path.Clean removes trailing slash except for root;
284
- // put the trailing slash back if necessary.
285
- if p[len(p)-1] == '/' && np != "/" {
286
- np += "/"
287
- }
288
- return np
289
-}
290
-
291
-// uniqueVars returns an error if two slices contain duplicated strings.
292
-func uniqueVars(s1, s2 []string) error {
293
- for _, v1 := range s1 {
294
- for _, v2 := range s2 {
295
- if v1 == v2 {
296
- return fmt.Errorf("mux: duplicated route variable %q", v2)
297
- }
298
- }
299
- }
300
- return nil
301
-}
302
-
303
-// mapFromPairs converts variadic string parameters to a string map.
304
-func mapFromPairs(pairs ...string) (map[string]string, error) {
305
- length := len(pairs)
306
- if length%2 != 0 {
307
- return nil, fmt.Errorf(
308
- "mux: number of parameters must be multiple of 2, got %v", pairs)
309
- }
310
- m := make(map[string]string, length/2)
311
- for i := 0; i < length; i += 2 {
312
- m[pairs[i]] = pairs[i+1]
313
- }
314
- return m, nil
315
-}
316
-
317
-// matchInArray returns true if the given string value is in the array.
318
-func matchInArray(arr []string, value string) bool {
319
- for _, v := range arr {
320
- if v == value {
321
- return true
322
- }
323
- }
324
- return false
325
-}
326
-
327
-// matchMap returns true if the given key/value pairs exist in a given map.
328
-func matchMap(toCheck map[string]string, toMatch map[string][]string,
329
- canonicalKey bool) bool {
330
- for k, v := range toCheck {
331
- // Check if key exists.
332
- if canonicalKey {
333
- k = http.CanonicalHeaderKey(k)
334
- }
335
- if values := toMatch[k]; values == nil {
336
- return false
337
- } else if v != "" {
338
- // If value was defined as an empty string we only check that the
339
- // key exists. Otherwise we also check for equality.
340
- valueExists := false
341
- for _, value := range values {
342
- if v == value {
343
- valueExists = true
344
- break
345
- }
346
- }
347
- if !valueExists {
348
- return false
349
- }
350
- }
351
- }
352
- return true
353
-}
Godeps/_workspace/src/github.com/gorilla/mux/mux_test.go
deleted
-943
@@ -1,943 +0,0 @@
1
-// Copyright 2012 The Gorilla Authors. All rights reserved.
2
-// Use of this source code is governed by a BSD-style
3
-// license that can be found in the LICENSE file.
4
-
5
-package mux
6
-
7
-import (
8
- "fmt"
9
- "net/http"
10
- "testing"
11
-
12
- "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/gorilla/context"
13
-)
14
-
15
-type routeTest struct {
16
- title string // title of the test
17
- route *Route // the route being tested
18
- request *http.Request // a request to test the route
19
- vars map[string]string // the expected vars of the match
20
- host string // the expected host of the match
21
- path string // the expected path of the match
22
- shouldMatch bool // whether the request is expected to match the route at all
23
- shouldRedirect bool // whether the request should result in a redirect
24
-}
25
-
26
-func TestHost(t *testing.T) {
27
- // newRequestHost a new request with a method, url, and host header
28
- newRequestHost := func(method, url, host string) *http.Request {
29
- req, err := http.NewRequest(method, url, nil)
30
- if err != nil {
31
- panic(err)
32
- }
33
- req.Host = host
34
- return req
35
- }
36
-
37
- tests := []routeTest{
38
- {
39
- title: "Host route match",
40
- route: new(Route).Host("aaa.bbb.ccc"),
41
- request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"),
42
- vars: map[string]string{},
43
- host: "aaa.bbb.ccc",
44
- path: "",
45
- shouldMatch: true,
46
- },
47
- {
48
- title: "Host route, wrong host in request URL",
49
- route: new(Route).Host("aaa.bbb.ccc"),
50
- request: newRequest("GET", "http://aaa.222.ccc/111/222/333"),
51
- vars: map[string]string{},
52
- host: "aaa.bbb.ccc",
53
- path: "",
54
- shouldMatch: false,
55
- },
56
- {
57
- title: "Host route with port, match",
58
- route: new(Route).Host("aaa.bbb.ccc:1234"),
59
- request: newRequest("GET", "http://aaa.bbb.ccc:1234/111/222/333"),
60
- vars: map[string]string{},
61
- host: "aaa.bbb.ccc:1234",
62
- path: "",
63
- shouldMatch: true,
64
- },
65
- {
66
- title: "Host route with port, wrong port in request URL",
67
- route: new(Route).Host("aaa.bbb.ccc:1234"),
68
- request: newRequest("GET", "http://aaa.bbb.ccc:9999/111/222/333"),
69
- vars: map[string]string{},
70
- host: "aaa.bbb.ccc:1234",
71
- path: "",
72
- shouldMatch: false,
73
- },
74
- {
75
- title: "Host route, match with host in request header",
76
- route: new(Route).Host("aaa.bbb.ccc"),
77
- request: newRequestHost("GET", "/111/222/333", "aaa.bbb.ccc"),
78
- vars: map[string]string{},
79
- host: "aaa.bbb.ccc",
80
- path: "",
81
- shouldMatch: true,
82
- },
83
- {
84
- title: "Host route, wrong host in request header",
85
- route: new(Route).Host("aaa.bbb.ccc"),
86
- request: newRequestHost("GET", "/111/222/333", "aaa.222.ccc"),
87
- vars: map[string]string{},
88
- host: "aaa.bbb.ccc",
89
- path: "",
90
- shouldMatch: false,
91
- },
92
- // BUG {new(Route).Host("aaa.bbb.ccc:1234"), newRequestHost("GET", "/111/222/333", "aaa.bbb.ccc:1234"), map[string]string{}, "aaa.bbb.ccc:1234", "", true},
93
- {
94
- title: "Host route with port, wrong host in request header",
95
- route: new(Route).Host("aaa.bbb.ccc:1234"),
96
- request: newRequestHost("GET", "/111/222/333", "aaa.bbb.ccc:9999"),
97
- vars: map[string]string{},
98
- host: "aaa.bbb.ccc:1234",
99
- path: "",
100
- shouldMatch: false,
101
- },
102
- {
103
- title: "Host route with pattern, match",
104
- route: new(Route).Host("aaa.{v1:[a-z]{3}}.ccc"),
105
- request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"),
106
- vars: map[string]string{"v1": "bbb"},
107
- host: "aaa.bbb.ccc",
108
- path: "",
109
- shouldMatch: true,
110
- },
111
- {
112
- title: "Host route with pattern, wrong host in request URL",
113
- route: new(Route).Host("aaa.{v1:[a-z]{3}}.ccc"),
114
- request: newRequest("GET", "http://aaa.222.ccc/111/222/333"),
115
- vars: map[string]string{"v1": "bbb"},
116
- host: "aaa.bbb.ccc",
117
- path: "",
118
- shouldMatch: false,
119
- },
120
- {
121
- title: "Host route with multiple patterns, match",
122
- route: new(Route).Host("{v1:[a-z]{3}}.{v2:[a-z]{3}}.{v3:[a-z]{3}}"),
123
- request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"),
124
- vars: map[string]string{"v1": "aaa", "v2": "bbb", "v3": "ccc"},
125
- host: "aaa.bbb.ccc",
126
- path: "",
127
- shouldMatch: true,
128
- },
129
- {
130
- title: "Host route with multiple patterns, wrong host in request URL",
131
- route: new(Route).Host("{v1:[a-z]{3}}.{v2:[a-z]{3}}.{v3:[a-z]{3}}"),
132
- request: newRequest("GET", "http://aaa.222.ccc/111/222/333"),
133
- vars: map[string]string{"v1": "aaa", "v2": "bbb", "v3": "ccc"},
134
- host: "aaa.bbb.ccc",
135
- path: "",
136
- shouldMatch: false,
137
- },
138
- }
139
- for _, test := range tests {
140
- testRoute(t, test)
141
- }
142
-}
143
-
144
-func TestPath(t *testing.T) {
145
- tests := []routeTest{
146
- {
147
- title: "Path route, match",
148
- route: new(Route).Path("/111/222/333"),
149
- request: newRequest("GET", "http://localhost/111/222/333"),
150
- vars: map[string]string{},
151
- host: "",
152
- path: "/111/222/333",
153
- shouldMatch: true,
154
- },
155
- {
156
- title: "Path route, match with trailing slash in request and path",
157
- route: new(Route).Path("/111/"),
158
- request: newRequest("GET", "http://localhost/111/"),
159
- vars: map[string]string{},
160
- host: "",
161
- path: "/111/",
162
- shouldMatch: true,
163
- },
164
- {
165
- title: "Path route, do not match with trailing slash in path",
166
- route: new(Route).Path("/111/"),
167
- request: newRequest("GET", "http://localhost/111"),
168
- vars: map[string]string{},
169
- host: "",
170
- path: "/111",
171
- shouldMatch: false,
172
- },
173
- {
174
- title: "Path route, do not match with trailing slash in request",
175
- route: new(Route).Path("/111"),
176
- request: newRequest("GET", "http://localhost/111/"),
177
- vars: map[string]string{},
178
- host: "",
179
- path: "/111/",
180
- shouldMatch: false,
181
- },
182
- {
183
- title: "Path route, wrong path in request in request URL",
184
- route: new(Route).Path("/111/222/333"),
185
- request: newRequest("GET", "http://localhost/1/2/3"),
186
- vars: map[string]string{},
187
- host: "",
188
- path: "/111/222/333",
189
- shouldMatch: false,
190
- },
191
- {
192
- title: "Path route with pattern, match",
193
- route: new(Route).Path("/111/{v1:[0-9]{3}}/333"),
194
- request: newRequest("GET", "http://localhost/111/222/333"),
195
- vars: map[string]string{"v1": "222"},
196
- host: "",
197
- path: "/111/222/333",
198
- shouldMatch: true,
199
- },
200
- {
201
- title: "Path route with pattern, URL in request does not match",
202
- route: new(Route).Path("/111/{v1:[0-9]{3}}/333"),
203
- request: newRequest("GET", "http://localhost/111/aaa/333"),
204
- vars: map[string]string{"v1": "222"},
205
- host: "",
206
- path: "/111/222/333",
207
- shouldMatch: false,
208
- },
209
- {
210
- title: "Path route with multiple patterns, match",
211
- route: new(Route).Path("/{v1:[0-9]{3}}/{v2:[0-9]{3}}/{v3:[0-9]{3}}"),
212
- request: newRequest("GET", "http://localhost/111/222/333"),
213
- vars: map[string]string{"v1": "111", "v2": "222", "v3": "333"},
214
- host: "",
215
- path: "/111/222/333",
216
- shouldMatch: true,
217
- },
218
- {
219
- title: "Path route with multiple patterns, URL in request does not match",
220
- route: new(Route).Path("/{v1:[0-9]{3}}/{v2:[0-9]{3}}/{v3:[0-9]{3}}"),
221
- request: newRequest("GET", "http://localhost/111/aaa/333"),
222
- vars: map[string]string{"v1": "111", "v2": "222", "v3": "333"},
223
- host: "",
224
- path: "/111/222/333",
225
- shouldMatch: false,
226
- },
227
- }
228
-
229
- for _, test := range tests {
230
- testRoute(t, test)
231
- }
232
-}
233
-
234
-func TestPathPrefix(t *testing.T) {
235
- tests := []routeTest{
236
- {
237
- title: "PathPrefix route, match",
238
- route: new(Route).PathPrefix("/111"),
239
- request: newRequest("GET", "http://localhost/111/222/333"),
240
- vars: map[string]string{},
241
- host: "",
242
- path: "/111",
243
- shouldMatch: true,
244
- },
245
- {
246
- title: "PathPrefix route, match substring",
247
- route: new(Route).PathPrefix("/1"),
248
- request: newRequest("GET", "http://localhost/111/222/333"),
249
- vars: map[string]string{},
250
- host: "",
251
- path: "/1",
252
- shouldMatch: true,
253
- },
254
- {
255
- title: "PathPrefix route, URL prefix in request does not match",
256
- route: new(Route).PathPrefix("/111"),
257
- request: newRequest("GET", "http://localhost/1/2/3"),
258
- vars: map[string]string{},
259
- host: "",
260
- path: "/111",
261
- shouldMatch: false,
262
- },
263
- {
264
- title: "PathPrefix route with pattern, match",
265
- route: new(Route).PathPrefix("/111/{v1:[0-9]{3}}"),
266
- request: newRequest("GET", "http://localhost/111/222/333"),
267
- vars: map[string]string{"v1": "222"},
268
- host: "",
269
- path: "/111/222",
270
- shouldMatch: true,
271
- },
272
- {
273
- title: "PathPrefix route with pattern, URL prefix in request does not match",
274
- route: new(Route).PathPrefix("/111/{v1:[0-9]{3}}"),
275
- request: newRequest("GET", "http://localhost/111/aaa/333"),
276
- vars: map[string]string{"v1": "222"},
277
- host: "",
278
- path: "/111/222",
279
- shouldMatch: false,
280
- },
281
- {
282
- title: "PathPrefix route with multiple patterns, match",
283
- route: new(Route).PathPrefix("/{v1:[0-9]{3}}/{v2:[0-9]{3}}"),
284
- request: newRequest("GET", "http://localhost/111/222/333"),
285
- vars: map[string]string{"v1": "111", "v2": "222"},
286
- host: "",
287
- path: "/111/222",
288
- shouldMatch: true,
289
- },
290
- {
291
- title: "PathPrefix route with multiple patterns, URL prefix in request does not match",
292
- route: new(Route).PathPrefix("/{v1:[0-9]{3}}/{v2:[0-9]{3}}"),
293
- request: newRequest("GET", "http://localhost/111/aaa/333"),
294
- vars: map[string]string{"v1": "111", "v2": "222"},
295
- host: "",
296
- path: "/111/222",
297
- shouldMatch: false,
298
- },
299
- }
300
-
301
- for _, test := range tests {
302
- testRoute(t, test)
303
- }
304
-}
305
-
306
-func TestHostPath(t *testing.T) {
307
- tests := []routeTest{
308
- {
309
- title: "Host and Path route, match",
310
- route: new(Route).Host("aaa.bbb.ccc").Path("/111/222/333"),
311
- request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"),
312
- vars: map[string]string{},
313
- host: "",
314
- path: "",
315
- shouldMatch: true,
316
- },
317
- {
318
- title: "Host and Path route, wrong host in request URL",
319
- route: new(Route).Host("aaa.bbb.ccc").Path("/111/222/333"),
320
- request: newRequest("GET", "http://aaa.222.ccc/111/222/333"),
321
- vars: map[string]string{},
322
- host: "",
323
- path: "",
324
- shouldMatch: false,
325
- },
326
- {
327
- title: "Host and Path route with pattern, match",
328
- route: new(Route).Host("aaa.{v1:[a-z]{3}}.ccc").Path("/111/{v2:[0-9]{3}}/333"),
329
- request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"),
330
- vars: map[string]string{"v1": "bbb", "v2": "222"},
331
- host: "aaa.bbb.ccc",
332
- path: "/111/222/333",
333
- shouldMatch: true,
334
- },
335
- {
336
- title: "Host and Path route with pattern, URL in request does not match",
337
- route: new(Route).Host("aaa.{v1:[a-z]{3}}.ccc").Path("/111/{v2:[0-9]{3}}/333"),
338
- request: newRequest("GET", "http://aaa.222.ccc/111/222/333"),
339
- vars: map[string]string{"v1": "bbb", "v2": "222"},
340
- host: "aaa.bbb.ccc",
341
- path: "/111/222/333",
342
- shouldMatch: false,
343
- },
344
- {
345
- title: "Host and Path route with multiple patterns, match",
346
- route: new(Route).Host("{v1:[a-z]{3}}.{v2:[a-z]{3}}.{v3:[a-z]{3}}").Path("/{v4:[0-9]{3}}/{v5:[0-9]{3}}/{v6:[0-9]{3}}"),
347
- request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"),
348
- vars: map[string]string{"v1": "aaa", "v2": "bbb", "v3": "ccc", "v4": "111", "v5": "222", "v6": "333"},
349
- host: "aaa.bbb.ccc",
350
- path: "/111/222/333",
351
- shouldMatch: true,
352
- },
353
- {
354
- title: "Host and Path route with multiple patterns, URL in request does not match",
355
- route: new(Route).Host("{v1:[a-z]{3}}.{v2:[a-z]{3}}.{v3:[a-z]{3}}").Path("/{v4:[0-9]{3}}/{v5:[0-9]{3}}/{v6:[0-9]{3}}"),
356
- request: newRequest("GET", "http://aaa.222.ccc/111/222/333"),
357
- vars: map[string]string{"v1": "aaa", "v2": "bbb", "v3": "ccc", "v4": "111", "v5": "222", "v6": "333"},
358
- host: "aaa.bbb.ccc",
359
- path: "/111/222/333",
360
- shouldMatch: false,
361
- },
362
- }
363
-
364
- for _, test := range tests {
365
- testRoute(t, test)
366
- }
367
-}
368
-
369
-func TestHeaders(t *testing.T) {
370
- // newRequestHeaders creates a new request with a method, url, and headers
371
- newRequestHeaders := func(method, url string, headers map[string]string) *http.Request {
372
- req, err := http.NewRequest(method, url, nil)
373
- if err != nil {
374
- panic(err)
375
- }
376
- for k, v := range headers {
377
- req.Header.Add(k, v)
378
- }
379
- return req
380
- }
381
-
382
- tests := []routeTest{
383
- {
384
- title: "Headers route, match",
385
- route: new(Route).Headers("foo", "bar", "baz", "ding"),
386
- request: newRequestHeaders("GET", "http://localhost", map[string]string{"foo": "bar", "baz": "ding"}),
387
- vars: map[string]string{},
388
- host: "",
389
- path: "",
390
- shouldMatch: true,
391
- },
392
- {
393
- title: "Headers route, bad header values",
394
- route: new(Route).Headers("foo", "bar", "baz", "ding"),
395
- request: newRequestHeaders("GET", "http://localhost", map[string]string{"foo": "bar", "baz": "dong"}),
396
- vars: map[string]string{},
397
- host: "",
398
- path: "",
399
- shouldMatch: false,
400
- },
401
- }
402
-
403
- for _, test := range tests {
404
- testRoute(t, test)
405
- }
406
-
407
-}
408
-
409
-func TestMethods(t *testing.T) {
410
- tests := []routeTest{
411
- {
412
- title: "Methods route, match GET",
413
- route: new(Route).Methods("GET", "POST"),
414
- request: newRequest("GET", "http://localhost"),
415
- vars: map[string]string{},
416
- host: "",
417
- path: "",
418
- shouldMatch: true,
419
- },
420
- {
421
- title: "Methods route, match POST",
422
- route: new(Route).Methods("GET", "POST"),
423
- request: newRequest("POST", "http://localhost"),
424
- vars: map[string]string{},
425
- host: "",
426
- path: "",
427
- shouldMatch: true,
428
- },
429
- {
430
- title: "Methods route, bad method",
431
- route: new(Route).Methods("GET", "POST"),
432
- request: newRequest("PUT", "http://localhost"),
433
- vars: map[string]string{},
434
- host: "",
435
- path: "",
436
- shouldMatch: false,
437
- },
438
- }
439
-
440
- for _, test := range tests {
441
- testRoute(t, test)
442
- }
443
-}
444
-
445
-func TestQueries(t *testing.T) {
446
- tests := []routeTest{
447
- {
448
- title: "Queries route, match",
449
- route: new(Route).Queries("foo", "bar", "baz", "ding"),
450
- request: newRequest("GET", "http://localhost?foo=bar&baz=ding"),
451
- vars: map[string]string{},
452
- host: "",
453
- path: "",
454
- shouldMatch: true,
455
- },
456
- {
457
- title: "Queries route, match with a query string",
458
- route: new(Route).Host("www.example.com").Path("/api").Queries("foo", "bar", "baz", "ding"),
459
- request: newRequest("GET", "http://www.example.com/api?foo=bar&baz=ding"),
460
- vars: map[string]string{},
461
- host: "",
462
- path: "",
463
- shouldMatch: true,
464
- },
465
- {
466
- title: "Queries route, match with a query string out of order",
467
- route: new(Route).Host("www.example.com").Path("/api").Queries("foo", "bar", "baz", "ding"),
468
- request: newRequest("GET", "http://www.example.com/api?baz=ding&foo=bar"),
469
- vars: map[string]string{},
470
- host: "",
471
- path: "",
472
- shouldMatch: true,
473
- },
474
- {
475
- title: "Queries route, bad query",
476
- route: new(Route).Queries("foo", "bar", "baz", "ding"),
477
- request: newRequest("GET", "http://localhost?foo=bar&baz=dong"),
478
- vars: map[string]string{},
479
- host: "",
480
- path: "",
481
- shouldMatch: false,
482
- },
483
- {
484
- title: "Queries route with pattern, match",
485
- route: new(Route).Queries("foo", "{v1}"),
486
- request: newRequest("GET", "http://localhost?foo=bar"),
487
- vars: map[string]string{"v1": "bar"},
488
- host: "",
489
- path: "",
490
- shouldMatch: true,
491
- },
492
- {
493
- title: "Queries route with multiple patterns, match",
494
- route: new(Route).Queries("foo", "{v1}", "baz", "{v2}"),
495
- request: newRequest("GET", "http://localhost?foo=bar&baz=ding"),
496
- vars: map[string]string{"v1": "bar", "v2": "ding"},
497
- host: "",
498
- path: "",
499
- shouldMatch: true,
500
- },
501
- {
502
- title: "Queries route with regexp pattern, match",
503
- route: new(Route).Queries("foo", "{v1:[0-9]+}"),
504
- request: newRequest("GET", "http://localhost?foo=10"),
505
- vars: map[string]string{"v1": "10"},
506
- host: "",
507
- path: "",
508
- shouldMatch: true,
509
- },
510
- {
511
- title: "Queries route with regexp pattern, regexp does not match",
512
- route: new(Route).Queries("foo", "{v1:[0-9]+}"),
513
- request: newRequest("GET", "http://localhost?foo=a"),
514
- vars: map[string]string{},
515
- host: "",
516
- path: "",
517
- shouldMatch: false,
518
- },
519
- }
520
-
521
- for _, test := range tests {
522
- testRoute(t, test)
523
- }
524
-}
525
-
526
-func TestSchemes(t *testing.T) {
527
- tests := []routeTest{
528
- // Schemes
529
- {
530
- title: "Schemes route, match https",
531
- route: new(Route).Schemes("https", "ftp"),
532
- request: newRequest("GET", "https://localhost"),
533
- vars: map[string]string{},
534
- host: "",
535
- path: "",
536
- shouldMatch: true,
537
- },
538
- {
539
- title: "Schemes route, match ftp",
540
- route: new(Route).Schemes("https", "ftp"),
541
- request: newRequest("GET", "ftp://localhost"),
542
- vars: map[string]string{},
543
- host: "",
544
- path: "",
545
- shouldMatch: true,
546
- },
547
- {
548
- title: "Schemes route, bad scheme",
549
- route: new(Route).Schemes("https", "ftp"),
550
- request: newRequest("GET", "http://localhost"),
551
- vars: map[string]string{},
552
- host: "",
553
- path: "",
554
- shouldMatch: false,
555
- },
556
- }
557
- for _, test := range tests {
558
- testRoute(t, test)
559
- }
560
-}
561
-
562
-func TestMatcherFunc(t *testing.T) {
563
- m := func(r *http.Request, m *RouteMatch) bool {
564
- if r.URL.Host == "aaa.bbb.ccc" {
565
- return true
566
- }
567
- return false
568
- }
569
-
570
- tests := []routeTest{
571
- {
572
- title: "MatchFunc route, match",
573
- route: new(Route).MatcherFunc(m),
574
- request: newRequest("GET", "http://aaa.bbb.ccc"),
575
- vars: map[string]string{},
576
- host: "",
577
- path: "",
578
- shouldMatch: true,
579
- },
580
- {
581
- title: "MatchFunc route, non-match",
582
- route: new(Route).MatcherFunc(m),
583
- request: newRequest("GET", "http://aaa.222.ccc"),
584
- vars: map[string]string{},
585
- host: "",
586
- path: "",
587
- shouldMatch: false,
588
- },
589
- }
590
-
591
- for _, test := range tests {
592
- testRoute(t, test)
593
- }
594
-}
595
-
596
-func TestSubRouter(t *testing.T) {
597
- subrouter1 := new(Route).Host("{v1:[a-z]+}.google.com").Subrouter()
598
- subrouter2 := new(Route).PathPrefix("/foo/{v1}").Subrouter()
599
-
600
- tests := []routeTest{
601
- {
602
- route: subrouter1.Path("/{v2:[a-z]+}"),
603
- request: newRequest("GET", "http://aaa.google.com/bbb"),
604
- vars: map[string]string{"v1": "aaa", "v2": "bbb"},
605
- host: "aaa.google.com",
606
- path: "/bbb",
607
- shouldMatch: true,
608
- },
609
- {
610
- route: subrouter1.Path("/{v2:[a-z]+}"),
611
- request: newRequest("GET", "http://111.google.com/111"),
612
- vars: map[string]string{"v1": "aaa", "v2": "bbb"},
613
- host: "aaa.google.com",
614
- path: "/bbb",
615
- shouldMatch: false,
616
- },
617
- {
618
- route: subrouter2.Path("/baz/{v2}"),
619
- request: newRequest("GET", "http://localhost/foo/bar/baz/ding"),
620
- vars: map[string]string{"v1": "bar", "v2": "ding"},
621
- host: "",
622
- path: "/foo/bar/baz/ding",
623
- shouldMatch: true,
624
- },
625
- {
626
- route: subrouter2.Path("/baz/{v2}"),
627
- request: newRequest("GET", "http://localhost/foo/bar"),
628
- vars: map[string]string{"v1": "bar", "v2": "ding"},
629
- host: "",
630
- path: "/foo/bar/baz/ding",
631
- shouldMatch: false,
632
- },
633
- }
634
-
635
- for _, test := range tests {
636
- testRoute(t, test)
637
- }
638
-}
639
-
640
-func TestNamedRoutes(t *testing.T) {
641
- r1 := NewRouter()
642
- r1.NewRoute().Name("a")
643
- r1.NewRoute().Name("b")
644
- r1.NewRoute().Name("c")
645
-
646
- r2 := r1.NewRoute().Subrouter()
647
- r2.NewRoute().Name("d")
648
- r2.NewRoute().Name("e")
649
- r2.NewRoute().Name("f")
650
-
651
- r3 := r2.NewRoute().Subrouter()
652
- r3.NewRoute().Name("g")
653
- r3.NewRoute().Name("h")
654
- r3.NewRoute().Name("i")
655
-
656
- if r1.namedRoutes == nil || len(r1.namedRoutes) != 9 {
657
- t.Errorf("Expected 9 named routes, got %v", r1.namedRoutes)
658
- } else if r1.Get("i") == nil {
659
- t.Errorf("Subroute name not registered")
660
- }
661
-}
662
-
663
-func TestStrictSlash(t *testing.T) {
664
- r := NewRouter()
665
- r.StrictSlash(true)
666
-
667
- tests := []routeTest{
668
- {
669
- title: "Redirect path without slash",
670
- route: r.NewRoute().Path("/111/"),
671
- request: newRequest("GET", "http://localhost/111"),
672
- vars: map[string]string{},
673
- host: "",
674
- path: "/111/",
675
- shouldMatch: true,
676
- shouldRedirect: true,
677
- },
678
- {
679
- title: "Do not redirect path with slash",
680
- route: r.NewRoute().Path("/111/"),
681
- request: newRequest("GET", "http://localhost/111/"),
682
- vars: map[string]string{},
683
- host: "",
684
- path: "/111/",
685
- shouldMatch: true,
686
- shouldRedirect: false,
687
- },
688
- {
689
- title: "Redirect path with slash",
690
- route: r.NewRoute().Path("/111"),
691
- request: newRequest("GET", "http://localhost/111/"),
692
- vars: map[string]string{},
693
- host: "",
694
- path: "/111",
695
- shouldMatch: true,
696
- shouldRedirect: true,
697
- },
698
- {
699
- title: "Do not redirect path without slash",
700
- route: r.NewRoute().Path("/111"),
701
- request: newRequest("GET", "http://localhost/111"),
702
- vars: map[string]string{},
703
- host: "",
704
- path: "/111",
705
- shouldMatch: true,
706
- shouldRedirect: false,
707
- },
708
- {
709
- title: "Propagate StrictSlash to subrouters",
710
- route: r.NewRoute().PathPrefix("/static/").Subrouter().Path("/images/"),
711
- request: newRequest("GET", "http://localhost/static/images"),
712
- vars: map[string]string{},
713
- host: "",
714
- path: "/static/images/",
715
- shouldMatch: true,
716
- shouldRedirect: true,
717
- },
718
- {
719
- title: "Ignore StrictSlash for path prefix",
720
- route: r.NewRoute().PathPrefix("/static/"),
721
- request: newRequest("GET", "http://localhost/static/logo.png"),
722
- vars: map[string]string{},
723
- host: "",
724
- path: "/static/",
725
- shouldMatch: true,
726
- shouldRedirect: false,
727
- },
728
- }
729
-
730
- for _, test := range tests {
731
- testRoute(t, test)
732
- }
733
-}
734
-
735
-// ----------------------------------------------------------------------------
736
-// Helpers
737
-// ----------------------------------------------------------------------------
738
-
739
-func getRouteTemplate(route *Route) string {
740
- host, path := "none", "none"
741
- if route.regexp != nil {
742
- if route.regexp.host != nil {
743
- host = route.regexp.host.template
744
- }
745
- if route.regexp.path != nil {
746
- path = route.regexp.path.template
747
- }
748
- }
749
- return fmt.Sprintf("Host: %v, Path: %v", host, path)
750
-}
751
-
752
-func testRoute(t *testing.T, test routeTest) {
753
- request := test.request
754
- route := test.route
755
- vars := test.vars
756
- shouldMatch := test.shouldMatch
757
- host := test.host
758
- path := test.path
759
- url := test.host + test.path
760
- shouldRedirect := test.shouldRedirect
761
-
762
- var match RouteMatch
763
- ok := route.Match(request, &match)
764
- if ok != shouldMatch {
765
- msg := "Should match"
766
- if !shouldMatch {
767
- msg = "Should not match"
768
- }
769
- t.Errorf("(%v) %v:\nRoute: %#v\nRequest: %#v\nVars: %v\n", test.title, msg, route, request, vars)
770
- return
771
- }
772
- if shouldMatch {
773
- if test.vars != nil && !stringMapEqual(test.vars, match.Vars) {
774
- t.Errorf("(%v) Vars not equal: expected %v, got %v", test.title, vars, match.Vars)
775
- return
776
- }
777
- if host != "" {
778
- u, _ := test.route.URLHost(mapToPairs(match.Vars)...)
779
- if host != u.Host {
780
- t.Errorf("(%v) URLHost not equal: expected %v, got %v -- %v", test.title, host, u.Host, getRouteTemplate(route))
781
- return
782
- }
783
- }
784
- if path != "" {
785
- u, _ := route.URLPath(mapToPairs(match.Vars)...)
786
- if path != u.Path {
787
- t.Errorf("(%v) URLPath not equal: expected %v, got %v -- %v", test.title, path, u.Path, getRouteTemplate(route))
788
- return
789
- }
790
- }
791
- if url != "" {
792
- u, _ := route.URL(mapToPairs(match.Vars)...)
793
- if url != u.Host+u.Path {
794
- t.Errorf("(%v) URL not equal: expected %v, got %v -- %v", test.title, url, u.Host+u.Path, getRouteTemplate(route))
795
- return
796
- }
797
- }
798
- if shouldRedirect && match.Handler == nil {
799
- t.Errorf("(%v) Did not redirect", test.title)
800
- return
801
- }
802
- if !shouldRedirect && match.Handler != nil {
803
- t.Errorf("(%v) Unexpected redirect", test.title)
804
- return
805
- }
806
- }
807
-}
808
-
809
-// Tests that the context is cleared or not cleared properly depending on
810
-// the configuration of the router
811
-func TestKeepContext(t *testing.T) {
812
- func1 := func(w http.ResponseWriter, r *http.Request) {}
813
-
814
- r := NewRouter()
815
- r.HandleFunc("/", func1).Name("func1")
816
-
817
- req, _ := http.NewRequest("GET", "http://localhost/", nil)
818
- context.Set(req, "t", 1)
819
-
820
- res := new(http.ResponseWriter)
821
- r.ServeHTTP(*res, req)
822
-
823
- if _, ok := context.GetOk(req, "t"); ok {
824
- t.Error("Context should have been cleared at end of request")
825
- }
826
-
827
- r.KeepContext = true
828
-
829
- req, _ = http.NewRequest("GET", "http://localhost/", nil)
830
- context.Set(req, "t", 1)
831
-
832
- r.ServeHTTP(*res, req)
833
- if _, ok := context.GetOk(req, "t"); !ok {
834
- t.Error("Context should NOT have been cleared at end of request")
835
- }
836
-
837
-}
838
-
839
-type TestA301ResponseWriter struct {
840
- hh http.Header
841
- status int
842
-}
843
-
844
-func (ho TestA301ResponseWriter) Header() http.Header {
845
- return http.Header(ho.hh)
846
-}
847
-
848
-func (ho TestA301ResponseWriter) Write(b []byte) (int, error) {
849
- return 0, nil
850
-}
851
-
852
-func (ho TestA301ResponseWriter) WriteHeader(code int) {
853
- ho.status = code
854
-}
855
-
856
-func Test301Redirect(t *testing.T) {
857
- m := make(http.Header)
858
-
859
- func1 := func(w http.ResponseWriter, r *http.Request) {}
860
- func2 := func(w http.ResponseWriter, r *http.Request) {}
861
-
862
- r := NewRouter()
863
- r.HandleFunc("/api/", func2).Name("func2")
864
- r.HandleFunc("/", func1).Name("func1")
865
-
866
- req, _ := http.NewRequest("GET", "http://localhost//api/?abc=def", nil)
867
-
868
- res := TestA301ResponseWriter{
869
- hh: m,
870
- status: 0,
871
- }
872
- r.ServeHTTP(&res, req)
873
-
874
- if "http://localhost/api/?abc=def" != res.hh["Location"][0] {
875
- t.Errorf("Should have complete URL with query string")
876
- }
877
-}
878
-
879
-// https://plus.google.com/101022900381697718949/posts/eWy6DjFJ6uW
880
-func TestSubrouterHeader(t *testing.T) {
881
- expected := "func1 response"
882
- func1 := func(w http.ResponseWriter, r *http.Request) {
883
- fmt.Fprint(w, expected)
884
- }
885
- func2 := func(http.ResponseWriter, *http.Request) {}
886
-
887
- r := NewRouter()
888
- s := r.Headers("SomeSpecialHeader", "").Subrouter()
889
- s.HandleFunc("/", func1).Name("func1")
890
- r.HandleFunc("/", func2).Name("func2")
891
-
892
- req, _ := http.NewRequest("GET", "http://localhost/", nil)
893
- req.Header.Add("SomeSpecialHeader", "foo")
894
- match := new(RouteMatch)
895
- matched := r.Match(req, match)
896
- if !matched {
897
- t.Errorf("Should match request")
898
- }
899
- if match.Route.GetName() != "func1" {
900
- t.Errorf("Expecting func1 handler, got %s", match.Route.GetName())
901
- }
902
- resp := NewRecorder()
903
- match.Handler.ServeHTTP(resp, req)
904
- if resp.Body.String() != expected {
905
- t.Errorf("Expecting %q", expected)
906
- }
907
-}
908
-
909
-// mapToPairs converts a string map to a slice of string pairs
910
-func mapToPairs(m map[string]string) []string {
911
- var i int
912
- p := make([]string, len(m)*2)
913
- for k, v := range m {
914
- p[i] = k
915
- p[i+1] = v
916
- i += 2
917
- }
918
- return p
919
-}
920
-
921
-// stringMapEqual checks the equality of two string maps
922
-func stringMapEqual(m1, m2 map[string]string) bool {
923
- nil1 := m1 == nil
924
- nil2 := m2 == nil
925
- if nil1 != nil2 || len(m1) != len(m2) {
926
- return false
927
- }
928
- for k, v := range m1 {
929
- if v != m2[k] {
930
- return false
931
- }
932
- }
933
- return true
934
-}
935
-
936
-// newRequest is a helper function to create a new request with a method and url
937
-func newRequest(method, url string) *http.Request {
938
- req, err := http.NewRequest(method, url, nil)
939
- if err != nil {
940
- panic(err)
941
- }
942
- return req
943
-}
Godeps/_workspace/src/github.com/gorilla/mux/old_test.go
deleted
-714
@@ -1,714 +0,0 @@
1
-// Old tests ported to Go1. This is a mess. Want to drop it one day.
2
-
3
-// Copyright 2011 Gorilla Authors. All rights reserved.
4
-// Use of this source code is governed by a BSD-style
5
-// license that can be found in the LICENSE file.
6
-
7
-package mux
8
-
9
-import (
10
- "bytes"
11
- "net/http"
12
- "testing"
13
-)
14
-
15
-// ----------------------------------------------------------------------------
16
-// ResponseRecorder
17
-// ----------------------------------------------------------------------------
18
-// Copyright 2009 The Go Authors. All rights reserved.
19
-// Use of this source code is governed by a BSD-style
20
-// license that can be found in the LICENSE file.
21
-
22
-// ResponseRecorder is an implementation of http.ResponseWriter that
23
-// records its mutations for later inspection in tests.
24
-type ResponseRecorder struct {
25
- Code int // the HTTP response code from WriteHeader
26
- HeaderMap http.Header // the HTTP response headers
27
- Body *bytes.Buffer // if non-nil, the bytes.Buffer to append written data to
28
- Flushed bool
29
-}
30
-
31
-// NewRecorder returns an initialized ResponseRecorder.
32
-func NewRecorder() *ResponseRecorder {
33
- return &ResponseRecorder{
34
- HeaderMap: make(http.Header),
35
- Body: new(bytes.Buffer),
36
- }
37
-}
38
-
39
-// DefaultRemoteAddr is the default remote address to return in RemoteAddr if
40
-// an explicit DefaultRemoteAddr isn't set on ResponseRecorder.
41
-const DefaultRemoteAddr = "1.2.3.4"
42
-
43
-// Header returns the response headers.
44
-func (rw *ResponseRecorder) Header() http.Header {
45
- return rw.HeaderMap
46
-}
47
-
48
-// Write always succeeds and writes to rw.Body, if not nil.
49
-func (rw *ResponseRecorder) Write(buf []byte) (int, error) {
50
- if rw.Body != nil {
51
- rw.Body.Write(buf)
52
- }
53
- if rw.Code == 0 {
54
- rw.Code = http.StatusOK
55
- }
56
- return len(buf), nil
57
-}
58
-
59
-// WriteHeader sets rw.Code.
60
-func (rw *ResponseRecorder) WriteHeader(code int) {
61
- rw.Code = code
62
-}
63
-
64
-// Flush sets rw.Flushed to true.
65
-func (rw *ResponseRecorder) Flush() {
66
- rw.Flushed = true
67
-}
68
-
69
-// ----------------------------------------------------------------------------
70
-
71
-func TestRouteMatchers(t *testing.T) {
72
- var scheme, host, path, query, method string
73
- var headers map[string]string
74
- var resultVars map[bool]map[string]string
75
-
76
- router := NewRouter()
77
- router.NewRoute().Host("{var1}.google.com").
78
- Path("/{var2:[a-z]+}/{var3:[0-9]+}").
79
- Queries("foo", "bar").
80
- Methods("GET").
81
- Schemes("https").
82
- Headers("x-requested-with", "XMLHttpRequest")
83
- router.NewRoute().Host("www.{var4}.com").
84
- PathPrefix("/foo/{var5:[a-z]+}/{var6:[0-9]+}").
85
- Queries("baz", "ding").
86
- Methods("POST").
87
- Schemes("http").
88
- Headers("Content-Type", "application/json")
89
-
90
- reset := func() {
91
- // Everything match.
92
- scheme = "https"
93
- host = "www.google.com"
94
- path = "/product/42"
95
- query = "?foo=bar"
96
- method = "GET"
97
- headers = map[string]string{"X-Requested-With": "XMLHttpRequest"}
98
- resultVars = map[bool]map[string]string{
99
- true: {"var1": "www", "var2": "product", "var3": "42"},
100
- false: {},
101
- }
102
- }
103
-
104
- reset2 := func() {
105
- // Everything match.
106
- scheme = "http"
107
- host = "www.google.com"
108
- path = "/foo/product/42/path/that/is/ignored"
109
- query = "?baz=ding"
110
- method = "POST"
111
- headers = map[string]string{"Content-Type": "application/json"}
112
- resultVars = map[bool]map[string]string{
113
- true: {"var4": "google", "var5": "product", "var6": "42"},
114
- false: {},
115
- }
116
- }
117
-
118
- match := func(shouldMatch bool) {
119
- url := scheme + "://" + host + path + query
120
- request, _ := http.NewRequest(method, url, nil)
121
- for key, value := range headers {
122
- request.Header.Add(key, value)
123
- }
124
-
125
- var routeMatch RouteMatch
126
- matched := router.Match(request, &routeMatch)
127
- if matched != shouldMatch {
128
- // Need better messages. :)
129
- if matched {
130
- t.Errorf("Should match.")
131
- } else {
132
- t.Errorf("Should not match.")
133
- }
134
- }
135
-
136
- if matched {
137
- currentRoute := routeMatch.Route
138
- if currentRoute == nil {
139
- t.Errorf("Expected a current route.")
140
- }
141
- vars := routeMatch.Vars
142
- expectedVars := resultVars[shouldMatch]
143
- if len(vars) != len(expectedVars) {
144
- t.Errorf("Expected vars: %v Got: %v.", expectedVars, vars)
145
- }
146
- for name, value := range vars {
147
- if expectedVars[name] != value {
148
- t.Errorf("Expected vars: %v Got: %v.", expectedVars, vars)
149
- }
150
- }
151
- }
152
- }
153
-
154
- // 1st route --------------------------------------------------------------
155
-
156
- // Everything match.
157
- reset()
158
- match(true)
159
-
160
- // Scheme doesn't match.
161
- reset()
162
- scheme = "http"
163
- match(false)
164
-
165
- // Host doesn't match.
166
- reset()
167
- host = "www.mygoogle.com"
168
- match(false)
169
-
170
- // Path doesn't match.
171
- reset()
172
- path = "/product/notdigits"
173
- match(false)
174
-
175
- // Query doesn't match.
176
- reset()
177
- query = "?foo=baz"
178
- match(false)
179
-
180
- // Method doesn't match.
181
- reset()
182
- method = "POST"
183
- match(false)
184
-
185
- // Header doesn't match.
186
- reset()
187
- headers = map[string]string{}
188
- match(false)
189
-
190
- // Everything match, again.
191
- reset()
192
- match(true)
193
-
194
- // 2nd route --------------------------------------------------------------
195
-
196
- // Everything match.
197
- reset2()
198
- match(true)
199
-
200
- // Scheme doesn't match.
201
- reset2()
202
- scheme = "https"
203
- match(false)
204
-
205
- // Host doesn't match.
206
- reset2()
207
- host = "sub.google.com"
208
- match(false)
209
-
210
- // Path doesn't match.
211
- reset2()
212
- path = "/bar/product/42"
213
- match(false)
214
-
215
- // Query doesn't match.
216
- reset2()
217
- query = "?foo=baz"
218
- match(false)
219
-
220
- // Method doesn't match.
221
- reset2()
222
- method = "GET"
223
- match(false)
224
-
225
- // Header doesn't match.
226
- reset2()
227
- headers = map[string]string{}
228
- match(false)
229
-
230
- // Everything match, again.
231
- reset2()
232
- match(true)
233
-}
234
-
235
-type headerMatcherTest struct {
236
- matcher headerMatcher
237
- headers map[string]string
238
- result bool
239
-}
240
-
241
-var headerMatcherTests = []headerMatcherTest{
242
- {
243
- matcher: headerMatcher(map[string]string{"x-requested-with": "XMLHttpRequest"}),
244
- headers: map[string]string{"X-Requested-With": "XMLHttpRequest"},
245
- result: true,
246
- },
247
- {
248
- matcher: headerMatcher(map[string]string{"x-requested-with": ""}),
249
- headers: map[string]string{"X-Requested-With": "anything"},
250
- result: true,
251
- },
252
- {
253
- matcher: headerMatcher(map[string]string{"x-requested-with": "XMLHttpRequest"}),
254
- headers: map[string]string{},
255
- result: false,
256
- },
257
-}
258
-
259
-type hostMatcherTest struct {
260
- matcher *Route
261
- url string
262
- vars map[string]string
263
- result bool
264
-}
265
-
266
-var hostMatcherTests = []hostMatcherTest{
267
- {
268
- matcher: NewRouter().NewRoute().Host("{foo:[a-z][a-z][a-z]}.{bar:[a-z][a-z][a-z]}.{baz:[a-z][a-z][a-z]}"),
269
- url: "http://abc.def.ghi/",
270
- vars: map[string]string{"foo": "abc", "bar": "def", "baz": "ghi"},
271
- result: true,
272
- },
273
- {
274
- matcher: NewRouter().NewRoute().Host("{foo:[a-z][a-z][a-z]}.{bar:[a-z][a-z][a-z]}.{baz:[a-z][a-z][a-z]}"),
275
- url: "http://a.b.c/",
276
- vars: map[string]string{"foo": "abc", "bar": "def", "baz": "ghi"},
277
- result: false,
278
- },
279
-}
280
-
281
-type methodMatcherTest struct {
282
- matcher methodMatcher
283
- method string
284
- result bool
285
-}
286
-
287
-var methodMatcherTests = []methodMatcherTest{
288
- {
289
- matcher: methodMatcher([]string{"GET", "POST", "PUT"}),
290
- method: "GET",
291
- result: true,
292
- },
293
- {
294
- matcher: methodMatcher([]string{"GET", "POST", "PUT"}),
295
- method: "POST",
296
- result: true,
297
- },
298
- {
299
- matcher: methodMatcher([]string{"GET", "POST", "PUT"}),
300
- method: "PUT",
301
- result: true,
302
- },
303
- {
304
- matcher: methodMatcher([]string{"GET", "POST", "PUT"}),
305
- method: "DELETE",
306
- result: false,
307
- },
308
-}
309
-
310
-type pathMatcherTest struct {
311
- matcher *Route
312
- url string
313
- vars map[string]string
314
- result bool
315
-}
316
-
317
-var pathMatcherTests = []pathMatcherTest{
318
- {
319
- matcher: NewRouter().NewRoute().Path("/{foo:[0-9][0-9][0-9]}/{bar:[0-9][0-9][0-9]}/{baz:[0-9][0-9][0-9]}"),
320
- url: "http://localhost:8080/123/456/789",
321
- vars: map[string]string{"foo": "123", "bar": "456", "baz": "789"},
322
- result: true,
323
- },
324
- {
325
- matcher: NewRouter().NewRoute().Path("/{foo:[0-9][0-9][0-9]}/{bar:[0-9][0-9][0-9]}/{baz:[0-9][0-9][0-9]}"),
326
- url: "http://localhost:8080/1/2/3",
327
- vars: map[string]string{"foo": "123", "bar": "456", "baz": "789"},
328
- result: false,
329
- },
330
-}
331
-
332
-type schemeMatcherTest struct {
333
- matcher schemeMatcher
334
- url string
335
- result bool
336
-}
337
-
338
-var schemeMatcherTests = []schemeMatcherTest{
339
- {
340
- matcher: schemeMatcher([]string{"http", "https"}),
341
- url: "http://localhost:8080/",
342
- result: true,
343
- },
344
- {
345
- matcher: schemeMatcher([]string{"http", "https"}),
346
- url: "https://localhost:8080/",
347
- result: true,
348
- },
349
- {
350
- matcher: schemeMatcher([]string{"https"}),
351
- url: "http://localhost:8080/",
352
- result: false,
353
- },
354
- {
355
- matcher: schemeMatcher([]string{"http"}),
356
- url: "https://localhost:8080/",
357
- result: false,
358
- },
359
-}
360
-
361
-type urlBuildingTest struct {
362
- route *Route
363
- vars []string
364
- url string
365
-}
366
-
367
-var urlBuildingTests = []urlBuildingTest{
368
- {
369
- route: new(Route).Host("foo.domain.com"),
370
- vars: []string{},
371
- url: "http://foo.domain.com",
372
- },
373
- {
374
- route: new(Route).Host("{subdomain}.domain.com"),
375
- vars: []string{"subdomain", "bar"},
376
- url: "http://bar.domain.com",
377
- },
378
- {
379
- route: new(Route).Host("foo.domain.com").Path("/articles"),
380
- vars: []string{},
381
- url: "http://foo.domain.com/articles",
382
- },
383
- {
384
- route: new(Route).Path("/articles"),
385
- vars: []string{},
386
- url: "/articles",
387
- },
388
- {
389
- route: new(Route).Path("/articles/{category}/{id:[0-9]+}"),
390
- vars: []string{"category", "technology", "id", "42"},
391
- url: "/articles/technology/42",
392
- },
393
- {
394
- route: new(Route).Host("{subdomain}.domain.com").Path("/articles/{category}/{id:[0-9]+}"),
395
- vars: []string{"subdomain", "foo", "category", "technology", "id", "42"},
396
- url: "http://foo.domain.com/articles/technology/42",
397
- },
398
-}
399
-
400
-func TestHeaderMatcher(t *testing.T) {
401
- for _, v := range headerMatcherTests {
402
- request, _ := http.NewRequest("GET", "http://localhost:8080/", nil)
403
- for key, value := range v.headers {
404
- request.Header.Add(key, value)
405
- }
406
- var routeMatch RouteMatch
407
- result := v.matcher.Match(request, &routeMatch)
408
- if result != v.result {
409
- if v.result {
410
- t.Errorf("%#v: should match %v.", v.matcher, request.Header)
411
- } else {
412
- t.Errorf("%#v: should not match %v.", v.matcher, request.Header)
413
- }
414
- }
415
- }
416
-}
417
-
418
-func TestHostMatcher(t *testing.T) {
419
- for _, v := range hostMatcherTests {
420
- request, _ := http.NewRequest("GET", v.url, nil)
421
- var routeMatch RouteMatch
422
- result := v.matcher.Match(request, &routeMatch)
423
- vars := routeMatch.Vars
424
- if result != v.result {
425
- if v.result {
426
- t.Errorf("%#v: should match %v.", v.matcher, v.url)
427
- } else {
428
- t.Errorf("%#v: should not match %v.", v.matcher, v.url)
429
- }
430
- }
431
- if result {
432
- if len(vars) != len(v.vars) {
433
- t.Errorf("%#v: vars length should be %v, got %v.", v.matcher, len(v.vars), len(vars))
434
- }
435
- for name, value := range vars {
436
- if v.vars[name] != value {
437
- t.Errorf("%#v: expected value %v for key %v, got %v.", v.matcher, v.vars[name], name, value)
438
- }
439
- }
440
- } else {
441
- if len(vars) != 0 {
442
- t.Errorf("%#v: vars length should be 0, got %v.", v.matcher, len(vars))
443
- }
444
- }
445
- }
446
-}
447
-
448
-func TestMethodMatcher(t *testing.T) {
449
- for _, v := range methodMatcherTests {
450
- request, _ := http.NewRequest(v.method, "http://localhost:8080/", nil)
451
- var routeMatch RouteMatch
452
- result := v.matcher.Match(request, &routeMatch)
453
- if result != v.result {
454
- if v.result {
455
- t.Errorf("%#v: should match %v.", v.matcher, v.method)
456
- } else {
457
- t.Errorf("%#v: should not match %v.", v.matcher, v.method)
458
- }
459
- }
460
- }
461
-}
462
-
463
-func TestPathMatcher(t *testing.T) {
464
- for _, v := range pathMatcherTests {
465
- request, _ := http.NewRequest("GET", v.url, nil)
466
- var routeMatch RouteMatch
467
- result := v.matcher.Match(request, &routeMatch)
468
- vars := routeMatch.Vars
469
- if result != v.result {
470
- if v.result {
471
- t.Errorf("%#v: should match %v.", v.matcher, v.url)
472
- } else {
473
- t.Errorf("%#v: should not match %v.", v.matcher, v.url)
474
- }
475
- }
476
- if result {
477
- if len(vars) != len(v.vars) {
478
- t.Errorf("%#v: vars length should be %v, got %v.", v.matcher, len(v.vars), len(vars))
479
- }
480
- for name, value := range vars {
481
- if v.vars[name] != value {
482
- t.Errorf("%#v: expected value %v for key %v, got %v.", v.matcher, v.vars[name], name, value)
483
- }
484
- }
485
- } else {
486
- if len(vars) != 0 {
487
- t.Errorf("%#v: vars length should be 0, got %v.", v.matcher, len(vars))
488
- }
489
- }
490
- }
491
-}
492
-
493
-func TestSchemeMatcher(t *testing.T) {
494
- for _, v := range schemeMatcherTests {
495
- request, _ := http.NewRequest("GET", v.url, nil)
496
- var routeMatch RouteMatch
497
- result := v.matcher.Match(request, &routeMatch)
498
- if result != v.result {
499
- if v.result {
500
- t.Errorf("%#v: should match %v.", v.matcher, v.url)
501
- } else {
502
- t.Errorf("%#v: should not match %v.", v.matcher, v.url)
503
- }
504
- }
505
- }
506
-}
507
-
508
-func TestUrlBuilding(t *testing.T) {
509
-
510
- for _, v := range urlBuildingTests {
511
- u, _ := v.route.URL(v.vars...)
512
- url := u.String()
513
- if url != v.url {
514
- t.Errorf("expected %v, got %v", v.url, url)
515
- /*
516
- reversePath := ""
517
- reverseHost := ""
518
- if v.route.pathTemplate != nil {
519
- reversePath = v.route.pathTemplate.Reverse
520
- }
521
- if v.route.hostTemplate != nil {
522
- reverseHost = v.route.hostTemplate.Reverse
523
- }
524
-
525
- t.Errorf("%#v:\nexpected: %q\ngot: %q\nreverse path: %q\nreverse host: %q", v.route, v.url, url, reversePath, reverseHost)
526
- */
527
- }
528
- }
529
-
530
- ArticleHandler := func(w http.ResponseWriter, r *http.Request) {
531
- }
532
-
533
- router := NewRouter()
534
- router.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler).Name("article")
535
-
536
- url, _ := router.Get("article").URL("category", "technology", "id", "42")
537
- expected := "/articles/technology/42"
538
- if url.String() != expected {
539
- t.Errorf("Expected %v, got %v", expected, url.String())
540
- }
541
-}
542
-
543
-func TestMatchedRouteName(t *testing.T) {
544
- routeName := "stock"
545
- router := NewRouter()
546
- route := router.NewRoute().Path("/products/").Name(routeName)
547
-
548
- url := "http://www.domain.com/products/"
549
- request, _ := http.NewRequest("GET", url, nil)
550
- var rv RouteMatch
551
- ok := router.Match(request, &rv)
552
-
553
- if !ok || rv.Route != route {
554
- t.Errorf("Expected same route, got %+v.", rv.Route)
555
- }
556
-
557
- retName := rv.Route.GetName()
558
- if retName != routeName {
559
- t.Errorf("Expected %q, got %q.", routeName, retName)
560
- }
561
-}
562
-
563
-func TestSubRouting(t *testing.T) {
564
- // Example from docs.
565
- router := NewRouter()
566
- subrouter := router.NewRoute().Host("www.domain.com").Subrouter()
567
- route := subrouter.NewRoute().Path("/products/").Name("products")
568
-
569
- url := "http://www.domain.com/products/"
570
- request, _ := http.NewRequest("GET", url, nil)
571
- var rv RouteMatch
572
- ok := router.Match(request, &rv)
573
-
574
- if !ok || rv.Route != route {
575
- t.Errorf("Expected same route, got %+v.", rv.Route)
576
- }
577
-
578
- u, _ := router.Get("products").URL()
579
- builtUrl := u.String()
580
- // Yay, subroute aware of the domain when building!
581
- if builtUrl != url {
582
- t.Errorf("Expected %q, got %q.", url, builtUrl)
583
- }
584
-}
585
-
586
-func TestVariableNames(t *testing.T) {
587
- route := new(Route).Host("{arg1}.domain.com").Path("/{arg1}/{arg2:[0-9]+}")
588
- if route.err == nil {
589
- t.Errorf("Expected error for duplicated variable names")
590
- }
591
-}
592
-
593
-func TestRedirectSlash(t *testing.T) {
594
- var route *Route
595
- var routeMatch RouteMatch
596
- r := NewRouter()
597
-
598
- r.StrictSlash(false)
599
- route = r.NewRoute()
600
- if route.strictSlash != false {
601
- t.Errorf("Expected false redirectSlash.")
602
- }
603
-
604
- r.StrictSlash(true)
605
- route = r.NewRoute()
606
- if route.strictSlash != true {
607
- t.Errorf("Expected true redirectSlash.")
608
- }
609
-
610
- route = new(Route)
611
- route.strictSlash = true
612
- route.Path("/{arg1}/{arg2:[0-9]+}/")
613
- request, _ := http.NewRequest("GET", "http://localhost/foo/123", nil)
614
- routeMatch = RouteMatch{}
615
- _ = route.Match(request, &routeMatch)
616
- vars := routeMatch.Vars
617
- if vars["arg1"] != "foo" {
618
- t.Errorf("Expected foo.")
619
- }
620
- if vars["arg2"] != "123" {
621
- t.Errorf("Expected 123.")
622
- }
623
- rsp := NewRecorder()
624
- routeMatch.Handler.ServeHTTP(rsp, request)
625
- if rsp.HeaderMap.Get("Location") != "http://localhost/foo/123/" {
626
- t.Errorf("Expected redirect header.")
627
- }
628
-
629
- route = new(Route)
630
- route.strictSlash = true
631
- route.Path("/{arg1}/{arg2:[0-9]+}")
632
- request, _ = http.NewRequest("GET", "http://localhost/foo/123/", nil)
633
- routeMatch = RouteMatch{}
634
- _ = route.Match(request, &routeMatch)
635
- vars = routeMatch.Vars
636
- if vars["arg1"] != "foo" {
637
- t.Errorf("Expected foo.")
638
- }
639
- if vars["arg2"] != "123" {
640
- t.Errorf("Expected 123.")
641
- }
642
- rsp = NewRecorder()
643
- routeMatch.Handler.ServeHTTP(rsp, request)
644
- if rsp.HeaderMap.Get("Location") != "http://localhost/foo/123" {
645
- t.Errorf("Expected redirect header.")
646
- }
647
-}
648
-
649
-// Test for the new regexp library, still not available in stable Go.
650
-func TestNewRegexp(t *testing.T) {
651
- var p *routeRegexp
652
- var matches []string
653
-
654
- tests := map[string]map[string][]string{
655
- "/{foo:a{2}}": {
656
- "/a": nil,
657
- "/aa": {"aa"},
658
- "/aaa": nil,
659
- "/aaaa": nil,
660
- },
661
- "/{foo:a{2,}}": {
662
- "/a": nil,
663
- "/aa": {"aa"},
664
- "/aaa": {"aaa"},
665
- "/aaaa": {"aaaa"},
666
- },
667
- "/{foo:a{2,3}}": {
668
- "/a": nil,
669
- "/aa": {"aa"},
670
- "/aaa": {"aaa"},
671
- "/aaaa": nil,
672
- },
673
- "/{foo:[a-z]{3}}/{bar:[a-z]{2}}": {
674
- "/a": nil,
675
- "/ab": nil,
676
- "/abc": nil,
677
- "/abcd": nil,
678
- "/abc/ab": {"abc", "ab"},
679
- "/abc/abc": nil,
680
- "/abcd/ab": nil,
681
- },
682
- `/{foo:\w{3,}}/{bar:\d{2,}}`: {
683
- "/a": nil,
684
- "/ab": nil,
685
- "/abc": nil,
686
- "/abc/1": nil,
687
- "/abc/12": {"abc", "12"},
688
- "/abcd/12": {"abcd", "12"},
689
- "/abcd/123": {"abcd", "123"},
690
- },
691
- }
692
-
693
- for pattern, paths := range tests {
694
- p, _ = newRouteRegexp(pattern, false, false, false, false)
695
- for path, result := range paths {
696
- matches = p.regexp.FindStringSubmatch(path)
697
- if result == nil {
698
- if matches != nil {
699
- t.Errorf("%v should not match %v.", pattern, path)
700
- }
701
- } else {
702
- if len(matches) != len(result)+1 {
703
- t.Errorf("Expected %v matches, got %v.", len(result)+1, len(matches))
704
- } else {
705
- for k, v := range result {
706
- if matches[k+1] != v {
707
- t.Errorf("Expected %v, got %v.", v, matches[k+1])
708
- }
709
- }
710
- }
711
- }
712
- }
713
- }
714
-}
Godeps/_workspace/src/github.com/gorilla/mux/regexp.go
deleted
-274
@@ -1,274 +0,0 @@
1
-// Copyright 2012 The Gorilla Authors. All rights reserved.
2
-// Use of this source code is governed by a BSD-style
3
-// license that can be found in the LICENSE file.
4
-
5
-package mux
6
-
7
-import (
8
- "bytes"
9
- "fmt"
10
- "net/http"
11
- "net/url"
12
- "regexp"
13
- "strings"
14
-)
15
-
16
-// newRouteRegexp parses a route template and returns a routeRegexp,
17
-// used to match a host, a path or a query string.
18
-//
19
-// It will extract named variables, assemble a regexp to be matched, create
20
-// a "reverse" template to build URLs and compile regexps to validate variable
21
-// values used in URL building.
22
-//
23
-// Previously we accepted only Python-like identifiers for variable
24
-// names ([a-zA-Z_][a-zA-Z0-9_]*), but currently the only restriction is that
25
-// name and pattern can't be empty, and names can't contain a colon.
26
-func newRouteRegexp(tpl string, matchHost, matchPrefix, matchQuery, strictSlash bool) (*routeRegexp, error) {
27
- // Check if it is well-formed.
28
- idxs, errBraces := braceIndices(tpl)
29
- if errBraces != nil {
30
- return nil, errBraces
31
- }
32
- // Backup the original.
33
- template := tpl
34
- // Now let's parse it.
35
- defaultPattern := "[^/]+"
36
- if matchQuery {
37
- defaultPattern = "[^?&]+"
38
- matchPrefix, strictSlash = true, false
39
- } else if matchHost {
40
- defaultPattern = "[^.]+"
41
- matchPrefix, strictSlash = false, false
42
- }
43
- if matchPrefix {
44
- strictSlash = false
45
- }
46
- // Set a flag for strictSlash.
47
- endSlash := false
48
- if strictSlash && strings.HasSuffix(tpl, "/") {
49
- tpl = tpl[:len(tpl)-1]
50
- endSlash = true
51
- }
52
- varsN := make([]string, len(idxs)/2)
53
- varsR := make([]*regexp.Regexp, len(idxs)/2)
54
- pattern := bytes.NewBufferString("")
55
- if !matchQuery {
56
- pattern.WriteByte('^')
57
- }
58
- reverse := bytes.NewBufferString("")
59
- var end int
60
- var err error
61
- for i := 0; i < len(idxs); i += 2 {
62
- // Set all values we are interested in.
63
- raw := tpl[end:idxs[i]]
64
- end = idxs[i+1]
65
- parts := strings.SplitN(tpl[idxs[i]+1:end-1], ":", 2)
66
- name := parts[0]
67
- patt := defaultPattern
68
- if len(parts) == 2 {
69
- patt = parts[1]
70
- }
71
- // Name or pattern can't be empty.
72
- if name == "" || patt == "" {
73
- return nil, fmt.Errorf("mux: missing name or pattern in %q",
74
- tpl[idxs[i]:end])
75
- }
76
- // Build the regexp pattern.
77
- fmt.Fprintf(pattern, "%s(%s)", regexp.QuoteMeta(raw), patt)
78
- // Build the reverse template.
79
- fmt.Fprintf(reverse, "%s%%s", raw)
80
- // Append variable name and compiled pattern.
81
- varsN[i/2] = name
82
- varsR[i/2], err = regexp.Compile(fmt.Sprintf("^%s$", patt))
83
- if err != nil {
84
- return nil, err
85
- }
86
- }
87
- // Add the remaining.
88
- raw := tpl[end:]
89
- pattern.WriteString(regexp.QuoteMeta(raw))
90
- if strictSlash {
91
- pattern.WriteString("[/]?")
92
- }
93
- if !matchPrefix {
94
- pattern.WriteByte('$')
95
- }
96
- reverse.WriteString(raw)
97
- if endSlash {
98
- reverse.WriteByte('/')
99
- }
100
- // Compile full regexp.
101
- reg, errCompile := regexp.Compile(pattern.String())
102
- if errCompile != nil {
103
- return nil, errCompile
104
- }
105
- // Done!
106
- return &routeRegexp{
107
- template: template,
108
- matchHost: matchHost,
109
- matchQuery: matchQuery,
110
- strictSlash: strictSlash,
111
- regexp: reg,
112
- reverse: reverse.String(),
113
- varsN: varsN,
114
- varsR: varsR,
115
- }, nil
116
-}
117
-
118
-// routeRegexp stores a regexp to match a host or path and information to
119
-// collect and validate route variables.
120
-type routeRegexp struct {
121
- // The unmodified template.
122
- template string
123
- // True for host match, false for path or query string match.
124
- matchHost bool
125
- // True for query string match, false for path and host match.
126
- matchQuery bool
127
- // The strictSlash value defined on the route, but disabled if PathPrefix was used.
128
- strictSlash bool
129
- // Expanded regexp.
130
- regexp *regexp.Regexp
131
- // Reverse template.
132
- reverse string
133
- // Variable names.
134
- varsN []string
135
- // Variable regexps (validators).
136
- varsR []*regexp.Regexp
137
-}
138
-
139
-// Match matches the regexp against the URL host or path.
140
-func (r *routeRegexp) Match(req *http.Request, match *RouteMatch) bool {
141
- if !r.matchHost {
142
- if r.matchQuery {
143
- return r.regexp.MatchString(req.URL.RawQuery)
144
- } else {
145
- return r.regexp.MatchString(req.URL.Path)
146
- }
147
- }
148
- return r.regexp.MatchString(getHost(req))
149
-}
150
-
151
-// url builds a URL part using the given values.
152
-func (r *routeRegexp) url(pairs ...string) (string, error) {
153
- values, err := mapFromPairs(pairs...)
154
- if err != nil {
155
- return "", err
156
- }
157
- urlValues := make([]interface{}, len(r.varsN))
158
- for k, v := range r.varsN {
159
- value, ok := values[v]
160
- if !ok {
161
- return "", fmt.Errorf("mux: missing route variable %q", v)
162
- }
163
- urlValues[k] = value
164
- }
165
- rv := fmt.Sprintf(r.reverse, urlValues...)
166
- if !r.regexp.MatchString(rv) {
167
- // The URL is checked against the full regexp, instead of checking
168
- // individual variables. This is faster but to provide a good error
169
- // message, we check individual regexps if the URL doesn't match.
170
- for k, v := range r.varsN {
171
- if !r.varsR[k].MatchString(values[v]) {
172
- return "", fmt.Errorf(
173
- "mux: variable %q doesn't match, expected %q", values[v],
174
- r.varsR[k].String())
175
- }
176
- }
177
- }
178
- return rv, nil
179
-}
180
-
181
-// braceIndices returns the first level curly brace indices from a string.
182
-// It returns an error in case of unbalanced braces.
183
-func braceIndices(s string) ([]int, error) {
184
- var level, idx int
185
- idxs := make([]int, 0)
186
- for i := 0; i < len(s); i++ {
187
- switch s[i] {
188
- case '{':
189
- if level++; level == 1 {
190
- idx = i
191
- }
192
- case '}':
193
- if level--; level == 0 {
194
- idxs = append(idxs, idx, i+1)
195
- } else if level < 0 {
196
- return nil, fmt.Errorf("mux: unbalanced braces in %q", s)
197
- }
198
- }
199
- }
200
- if level != 0 {
201
- return nil, fmt.Errorf("mux: unbalanced braces in %q", s)
202
- }
203
- return idxs, nil
204
-}
205
-
206
-// ----------------------------------------------------------------------------
207
-// routeRegexpGroup
208
-// ----------------------------------------------------------------------------
209
-
210
-// routeRegexpGroup groups the route matchers that carry variables.
211
-type routeRegexpGroup struct {
212
- host *routeRegexp
213
- path *routeRegexp
214
- queries []*routeRegexp
215
-}
216
-
217
-// setMatch extracts the variables from the URL once a route matches.
218
-func (v *routeRegexpGroup) setMatch(req *http.Request, m *RouteMatch, r *Route) {
219
- // Store host variables.
220
- if v.host != nil {
221
- hostVars := v.host.regexp.FindStringSubmatch(getHost(req))
222
- if hostVars != nil {
223
- for k, v := range v.host.varsN {
224
- m.Vars[v] = hostVars[k+1]
225
- }
226
- }
227
- }
228
- // Store path variables.
229
- if v.path != nil {
230
- pathVars := v.path.regexp.FindStringSubmatch(req.URL.Path)
231
- if pathVars != nil {
232
- for k, v := range v.path.varsN {
233
- m.Vars[v] = pathVars[k+1]
234
- }
235
- // Check if we should redirect.
236
- if v.path.strictSlash {
237
- p1 := strings.HasSuffix(req.URL.Path, "/")
238
- p2 := strings.HasSuffix(v.path.template, "/")
239
- if p1 != p2 {
240
- u, _ := url.Parse(req.URL.String())
241
- if p1 {
242
- u.Path = u.Path[:len(u.Path)-1]
243
- } else {
244
- u.Path += "/"
245
- }
246
- m.Handler = http.RedirectHandler(u.String(), 301)
247
- }
248
- }
249
- }
250
- }
251
- // Store query string variables.
252
- rawQuery := req.URL.RawQuery
253
- for _, q := range v.queries {
254
- queryVars := q.regexp.FindStringSubmatch(rawQuery)
255
- if queryVars != nil {
256
- for k, v := range q.varsN {
257
- m.Vars[v] = queryVars[k+1]
258
- }
259
- }
260
- }
261
-}
262
-
263
-// getHost tries its best to return the request host.
264
-func getHost(r *http.Request) string {
265
- if !r.URL.IsAbs() {
266
- host := r.Host
267
- // Slice off any port information.
268
- if i := strings.Index(host, ":"); i != -1 {
269
- host = host[:i]
270
- }
271
- return host
272
- }
273
- return r.URL.Host
274
-}
Godeps/_workspace/src/github.com/gorilla/mux/route.go
deleted
-524
@@ -1,524 +0,0 @@
1
-// Copyright 2012 The Gorilla Authors. All rights reserved.
2
-// Use of this source code is governed by a BSD-style
3
-// license that can be found in the LICENSE file.
4
-
5
-package mux
6
-
7
-import (
8
- "errors"
9
- "fmt"
10
- "net/http"
11
- "net/url"
12
- "strings"
13
-)
14
-
15
-// Route stores information to match a request and build URLs.
16
-type Route struct {
17
- // Parent where the route was registered (a Router).
18
- parent parentRoute
19
- // Request handler for the route.
20
- handler http.Handler
21
- // List of matchers.
22
- matchers []matcher
23
- // Manager for the variables from host and path.
24
- regexp *routeRegexpGroup
25
- // If true, when the path pattern is "/path/", accessing "/path" will
26
- // redirect to the former and vice versa.
27
- strictSlash bool
28
- // If true, this route never matches: it is only used to build URLs.
29
- buildOnly bool
30
- // The name used to build URLs.
31
- name string
32
- // Error resulted from building a route.
33
- err error
34
-}
35
-
36
-// Match matches the route against the request.
37
-func (r *Route) Match(req *http.Request, match *RouteMatch) bool {
38
- if r.buildOnly || r.err != nil {
39
- return false
40
- }
41
- // Match everything.
42
- for _, m := range r.matchers {
43
- if matched := m.Match(req, match); !matched {
44
- return false
45
- }
46
- }
47
- // Yay, we have a match. Let's collect some info about it.
48
- if match.Route == nil {
49
- match.Route = r
50
- }
51
- if match.Handler == nil {
52
- match.Handler = r.handler
53
- }
54
- if match.Vars == nil {
55
- match.Vars = make(map[string]string)
56
- }
57
- // Set variables.
58
- if r.regexp != nil {
59
- r.regexp.setMatch(req, match, r)
60
- }
61
- return true
62
-}
63
-
64
-// ----------------------------------------------------------------------------
65
-// Route attributes
66
-// ----------------------------------------------------------------------------
67
-
68
-// GetError returns an error resulted from building the route, if any.
69
-func (r *Route) GetError() error {
70
- return r.err
71
-}
72
-
73
-// BuildOnly sets the route to never match: it is only used to build URLs.
74
-func (r *Route) BuildOnly() *Route {
75
- r.buildOnly = true
76
- return r
77
-}
78
-
79
-// Handler --------------------------------------------------------------------
80
-
81
-// Handler sets a handler for the route.
82
-func (r *Route) Handler(handler http.Handler) *Route {
83
- if r.err == nil {
84
- r.handler = handler
85
- }
86
- return r
87
-}
88
-
89
-// HandlerFunc sets a handler function for the route.
90
-func (r *Route) HandlerFunc(f func(http.ResponseWriter, *http.Request)) *Route {
91
- return r.Handler(http.HandlerFunc(f))
92
-}
93
-
94
-// GetHandler returns the handler for the route, if any.
95
-func (r *Route) GetHandler() http.Handler {
96
- return r.handler
97
-}
98
-
99
-// Name -----------------------------------------------------------------------
100
-
101
-// Name sets the name for the route, used to build URLs.
102
-// If the name was registered already it will be overwritten.
103
-func (r *Route) Name(name string) *Route {
104
- if r.name != "" {
105
- r.err = fmt.Errorf("mux: route already has name %q, can't set %q",
106
- r.name, name)
107
- }
108
- if r.err == nil {
109
- r.name = name
110
- r.getNamedRoutes()[name] = r
111
- }
112
- return r
113
-}
114
-
115
-// GetName returns the name for the route, if any.
116
-func (r *Route) GetName() string {
117
- return r.name
118
-}
119
-
120
-// ----------------------------------------------------------------------------
121
-// Matchers
122
-// ----------------------------------------------------------------------------
123
-
124
-// matcher types try to match a request.
125
-type matcher interface {
126
- Match(*http.Request, *RouteMatch) bool
127
-}
128
-
129
-// addMatcher adds a matcher to the route.
130
-func (r *Route) addMatcher(m matcher) *Route {
131
- if r.err == nil {
132
- r.matchers = append(r.matchers, m)
133
- }
134
- return r
135
-}
136
-
137
-// addRegexpMatcher adds a host or path matcher and builder to a route.
138
-func (r *Route) addRegexpMatcher(tpl string, matchHost, matchPrefix, matchQuery bool) error {
139
- if r.err != nil {
140
- return r.err
141
- }
142
- r.regexp = r.getRegexpGroup()
143
- if !matchHost && !matchQuery {
144
- if len(tpl) == 0 || tpl[0] != '/' {
145
- return fmt.Errorf("mux: path must start with a slash, got %q", tpl)
146
- }
147
- if r.regexp.path != nil {
148
- tpl = strings.TrimRight(r.regexp.path.template, "/") + tpl
149
- }
150
- }
151
- rr, err := newRouteRegexp(tpl, matchHost, matchPrefix, matchQuery, r.strictSlash)
152
- if err != nil {
153
- return err
154
- }
155
- for _, q := range r.regexp.queries {
156
- if err = uniqueVars(rr.varsN, q.varsN); err != nil {
157
- return err
158
- }
159
- }
160
- if matchHost {
161
- if r.regexp.path != nil {
162
- if err = uniqueVars(rr.varsN, r.regexp.path.varsN); err != nil {
163
- return err
164
- }
165
- }
166
- r.regexp.host = rr
167
- } else {
168
- if r.regexp.host != nil {
169
- if err = uniqueVars(rr.varsN, r.regexp.host.varsN); err != nil {
170
- return err
171
- }
172
- }
173
- if matchQuery {
174
- r.regexp.queries = append(r.regexp.queries, rr)
175
- } else {
176
- r.regexp.path = rr
177
- }
178
- }
179
- r.addMatcher(rr)
180
- return nil
181
-}
182
-
183
-// Headers --------------------------------------------------------------------
184
-
185
-// headerMatcher matches the request against header values.
186
-type headerMatcher map[string]string
187
-
188
-func (m headerMatcher) Match(r *http.Request, match *RouteMatch) bool {
189
- return matchMap(m, r.Header, true)
190
-}
191
-
192
-// Headers adds a matcher for request header values.
193
-// It accepts a sequence of key/value pairs to be matched. For example:
194
-//
195
-// r := mux.NewRouter()
196
-// r.Headers("Content-Type", "application/json",
197
-// "X-Requested-With", "XMLHttpRequest")
198
-//
199
-// The above route will only match if both request header values match.
200
-//
201
-// It the value is an empty string, it will match any value if the key is set.
202
-func (r *Route) Headers(pairs ...string) *Route {
203
- if r.err == nil {
204
- var headers map[string]string
205
- headers, r.err = mapFromPairs(pairs...)
206
- return r.addMatcher(headerMatcher(headers))
207
- }
208
- return r
209
-}
210
-
211
-// Host -----------------------------------------------------------------------
212
-
213
-// Host adds a matcher for the URL host.
214
-// It accepts a template with zero or more URL variables enclosed by {}.
215
-// Variables can define an optional regexp pattern to me matched:
216
-//
217
-// - {name} matches anything until the next dot.
218
-//
219
-// - {name:pattern} matches the given regexp pattern.
220
-//
221
-// For example:
222
-//
223
-// r := mux.NewRouter()
224
-// r.Host("www.domain.com")
225
-// r.Host("{subdomain}.domain.com")
226
-// r.Host("{subdomain:[a-z]+}.domain.com")
227
-//
228
-// Variable names must be unique in a given route. They can be retrieved
229
-// calling mux.Vars(request).
230
-func (r *Route) Host(tpl string) *Route {
231
- r.err = r.addRegexpMatcher(tpl, true, false, false)
232
- return r
233
-}
234
-
235
-// MatcherFunc ----------------------------------------------------------------
236
-
237
-// MatcherFunc is the function signature used by custom matchers.
238
-type MatcherFunc func(*http.Request, *RouteMatch) bool
239
-
240
-func (m MatcherFunc) Match(r *http.Request, match *RouteMatch) bool {
241
- return m(r, match)
242
-}
243
-
244
-// MatcherFunc adds a custom function to be used as request matcher.
245
-func (r *Route) MatcherFunc(f MatcherFunc) *Route {
246
- return r.addMatcher(f)
247
-}
248
-
249
-// Methods --------------------------------------------------------------------
250
-
251
-// methodMatcher matches the request against HTTP methods.
252
-type methodMatcher []string
253
-
254
-func (m methodMatcher) Match(r *http.Request, match *RouteMatch) bool {
255
- return matchInArray(m, r.Method)
256
-}
257
-
258
-// Methods adds a matcher for HTTP methods.
259
-// It accepts a sequence of one or more methods to be matched, e.g.:
260
-// "GET", "POST", "PUT".
261
-func (r *Route) Methods(methods ...string) *Route {
262
- for k, v := range methods {
263
- methods[k] = strings.ToUpper(v)
264
- }
265
- return r.addMatcher(methodMatcher(methods))
266
-}
267
-
268
-// Path -----------------------------------------------------------------------
269
-
270
-// Path adds a matcher for the URL path.
271
-// It accepts a template with zero or more URL variables enclosed by {}. The
272
-// template must start with a "/".
273
-// Variables can define an optional regexp pattern to me matched:
274
-//
275
-// - {name} matches anything until the next slash.
276
-//
277
-// - {name:pattern} matches the given regexp pattern.
278
-//
279
-// For example:
280
-//
281
-// r := mux.NewRouter()
282
-// r.Path("/products/").Handler(ProductsHandler)
283
-// r.Path("/products/{key}").Handler(ProductsHandler)
284
-// r.Path("/articles/{category}/{id:[0-9]+}").
285
-// Handler(ArticleHandler)
286
-//
287
-// Variable names must be unique in a given route. They can be retrieved
288
-// calling mux.Vars(request).
289
-func (r *Route) Path(tpl string) *Route {
290
- r.err = r.addRegexpMatcher(tpl, false, false, false)
291
- return r
292
-}
293
-
294
-// PathPrefix -----------------------------------------------------------------
295
-
296
-// PathPrefix adds a matcher for the URL path prefix. This matches if the given
297
-// template is a prefix of the full URL path. See Route.Path() for details on
298
-// the tpl argument.
299
-//
300
-// Note that it does not treat slashes specially ("/foobar/" will be matched by
301
-// the prefix "/foo") so you may want to use a trailing slash here.
302
-//
303
-// Also note that the setting of Router.StrictSlash() has no effect on routes
304
-// with a PathPrefix matcher.
305
-func (r *Route) PathPrefix(tpl string) *Route {
306
- r.err = r.addRegexpMatcher(tpl, false, true, false)
307
- return r
308
-}
309
-
310
-// Query ----------------------------------------------------------------------
311
-
312
-// Queries adds a matcher for URL query values.
313
-// It accepts a sequence of key/value pairs. Values may define variables.
314
-// For example:
315
-//
316
-// r := mux.NewRouter()
317
-// r.Queries("foo", "bar", "id", "{id:[0-9]+}")
318
-//
319
-// The above route will only match if the URL contains the defined queries
320
-// values, e.g.: ?foo=bar&id=42.
321
-//
322
-// It the value is an empty string, it will match any value if the key is set.
323
-//
324
-// Variables can define an optional regexp pattern to me matched:
325
-//
326
-// - {name} matches anything until the next slash.
327
-//
328
-// - {name:pattern} matches the given regexp pattern.
329
-func (r *Route) Queries(pairs ...string) *Route {
330
- length := len(pairs)
331
- if length%2 != 0 {
332
- r.err = fmt.Errorf(
333
- "mux: number of parameters must be multiple of 2, got %v", pairs)
334
- return nil
335
- }
336
- for i := 0; i < length; i += 2 {
337
- if r.err = r.addRegexpMatcher(pairs[i]+"="+pairs[i+1], false, true, true); r.err != nil {
338
- return r
339
- }
340
- }
341
-
342
- return r
343
-}
344
-
345
-// Schemes --------------------------------------------------------------------
346
-
347
-// schemeMatcher matches the request against URL schemes.
348
-type schemeMatcher []string
349
-
350
-func (m schemeMatcher) Match(r *http.Request, match *RouteMatch) bool {
351
- return matchInArray(m, r.URL.Scheme)
352
-}
353
-
354
-// Schemes adds a matcher for URL schemes.
355
-// It accepts a sequence of schemes to be matched, e.g.: "http", "https".
356
-func (r *Route) Schemes(schemes ...string) *Route {
357
- for k, v := range schemes {
358
- schemes[k] = strings.ToLower(v)
359
- }
360
- return r.addMatcher(schemeMatcher(schemes))
361
-}
362
-
363
-// Subrouter ------------------------------------------------------------------
364
-
365
-// Subrouter creates a subrouter for the route.
366
-//
367
-// It will test the inner routes only if the parent route matched. For example:
368
-//
369
-// r := mux.NewRouter()
370
-// s := r.Host("www.domain.com").Subrouter()
371
-// s.HandleFunc("/products/", ProductsHandler)
372
-// s.HandleFunc("/products/{key}", ProductHandler)
373
-// s.HandleFunc("/articles/{category}/{id:[0-9]+}"), ArticleHandler)
374
-//
375
-// Here, the routes registered in the subrouter won't be tested if the host
376
-// doesn't match.
377
-func (r *Route) Subrouter() *Router {
378
- router := &Router{parent: r, strictSlash: r.strictSlash}
379
- r.addMatcher(router)
380
- return router
381
-}
382
-
383
-// ----------------------------------------------------------------------------
384
-// URL building
385
-// ----------------------------------------------------------------------------
386
-
387
-// URL builds a URL for the route.
388
-//
389
-// It accepts a sequence of key/value pairs for the route variables. For
390
-// example, given this route:
391
-//
392
-// r := mux.NewRouter()
393
-// r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler).
394
-// Name("article")
395
-//
396
-// ...a URL for it can be built using:
397
-//
398
-// url, err := r.Get("article").URL("category", "technology", "id", "42")
399
-//
400
-// ...which will return an url.URL with the following path:
401
-//
402
-// "/articles/technology/42"
403
-//
404
-// This also works for host variables:
405
-//
406
-// r := mux.NewRouter()
407
-// r.Host("{subdomain}.domain.com").
408
-// HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler).
409
-// Name("article")
410
-//
411
-// // url.String() will be "http://news.domain.com/articles/technology/42"
412
-// url, err := r.Get("article").URL("subdomain", "news",
413
-// "category", "technology",
414
-// "id", "42")
415
-//
416
-// All variables defined in the route are required, and their values must
417
-// conform to the corresponding patterns.
418
-func (r *Route) URL(pairs ...string) (*url.URL, error) {
419
- if r.err != nil {
420
- return nil, r.err
421
- }
422
- if r.regexp == nil {
423
- return nil, errors.New("mux: route doesn't have a host or path")
424
- }
425
- var scheme, host, path string
426
- var err error
427
- if r.regexp.host != nil {
428
- // Set a default scheme.
429
- scheme = "http"
430
- if host, err = r.regexp.host.url(pairs...); err != nil {
431
- return nil, err
432
- }
433
- }
434
- if r.regexp.path != nil {
435
- if path, err = r.regexp.path.url(pairs...); err != nil {
436
- return nil, err
437
- }
438
- }
439
- return &url.URL{
440
- Scheme: scheme,
441
- Host: host,
442
- Path: path,
443
- }, nil
444
-}
445
-
446
-// URLHost builds the host part of the URL for a route. See Route.URL().
447
-//
448
-// The route must have a host defined.
449
-func (r *Route) URLHost(pairs ...string) (*url.URL, error) {
450
- if r.err != nil {
451
- return nil, r.err
452
- }
453
- if r.regexp == nil || r.regexp.host == nil {
454
- return nil, errors.New("mux: route doesn't have a host")
455
- }
456
- host, err := r.regexp.host.url(pairs...)
457
- if err != nil {
458
- return nil, err
459
- }
460
- return &url.URL{
461
- Scheme: "http",
462
- Host: host,
463
- }, nil
464
-}
465
-
466
-// URLPath builds the path part of the URL for a route. See Route.URL().
467
-//
468
-// The route must have a path defined.
469
-func (r *Route) URLPath(pairs ...string) (*url.URL, error) {
470
- if r.err != nil {
471
- return nil, r.err
472
- }
473
- if r.regexp == nil || r.regexp.path == nil {
474
- return nil, errors.New("mux: route doesn't have a path")
475
- }
476
- path, err := r.regexp.path.url(pairs...)
477
- if err != nil {
478
- return nil, err
479
- }
480
- return &url.URL{
481
- Path: path,
482
- }, nil
483
-}
484
-
485
-// ----------------------------------------------------------------------------
486
-// parentRoute
487
-// ----------------------------------------------------------------------------
488
-
489
-// parentRoute allows routes to know about parent host and path definitions.
490
-type parentRoute interface {
491
- getNamedRoutes() map[string]*Route
492
- getRegexpGroup() *routeRegexpGroup
493
-}
494
-
495
-// getNamedRoutes returns the map where named routes are registered.
496
-func (r *Route) getNamedRoutes() map[string]*Route {
497
- if r.parent == nil {
498
- // During tests router is not always set.
499
- r.parent = NewRouter()
500
- }
501
- return r.parent.getNamedRoutes()
502
-}
503
-
504
-// getRegexpGroup returns regexp definitions from this route.
505
-func (r *Route) getRegexpGroup() *routeRegexpGroup {
506
- if r.regexp == nil {
507
- if r.parent == nil {
508
- // During tests router is not always set.
509
- r.parent = NewRouter()
510
- }
511
- regexp := r.parent.getRegexpGroup()
512
- if regexp == nil {
513
- r.regexp = new(routeRegexpGroup)
514
- } else {
515
- // Copy.
516
- r.regexp = &routeRegexpGroup{
517
- host: regexp.host,
518
- path: regexp.path,
519
- queries: regexp.queries,
520
- }
521
- }
522
- }
523
- return r.regexp
524
-}
Godeps/_workspace/src/github.com/jbenet/go-base58/base58_test.go
+34
@@ -94,3 +94,37 @@ func TestBase58(t *testing.T) {
94
}
95
}
96
}
97
+
98
+func BenchmarkDecodeShort(b *testing.B) {
99
+ const in = "1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L"
100
+ b.ReportAllocs()
101
+ for i := 0; i < b.N; i++ {
102
+ _ = Decode(in)
103
+ }
104
+}
105
+
106
+func BenchmarkEncodeShort(b *testing.B) {
107
+ var in = []byte("00eb15231dfceb60925886b67d065299925915aeb172c06647")
108
+ b.ReportAllocs()
109
+ for i := 0; i < b.N; i++ {
110
+ _ = Encode(in)
111
+ }
112
+}
113
+
114
+func BenchmarkDecodeOneKilo(b *testing.B) {
115
+ const in = "3GimCffBLAHhXMCeNxX2nST6dBem9pbUi3KVKykW73LmewcFtMk9oh9eNPdNR2eSzNqp7Z3E21vrWUkGHzJ7w2yqDUDJ4LKo1w5D6aafZ4SUoNQyrSVxyVG3pwgoZkKXMZVixRyiPZVUpekrsTvZuUoW7mB6BQgDTXbDuMMSRoNR7yiUTKpgwTD61DLmhNZopNxfFjn4avpYPgzsTB94iWueq1yU3EoruWCUMvp6fc1CEbDrZY3pkx9oUbUaSMC37rruBKSSGHh1ZE3XK3kQXBCFraMmUQf8dagofMEg5aTnDiLAZjLyWJMdnQwW1FqKKztP8KAQS2JX8GCCfc68KB4VGf2CfEGXtaapnsNWFrHuWi7Wo5vqyuHd21zGm1u5rsiR6tKNCsFC4nzf3WUNxJNoZrDSdF9KERqhTWWmmcM4qdKRCtBWKTrs1DJD2oiK6BK9BgwoW2dfQdKuxojFyFvmxqPKDDAEZPPpJ51wHoFzBFMM1tUBBkN15cT2GpNwKzDcjHPKJAQ6FNRgppfQytzqpq76sSeZaWAB8hhULMJCQGU57ZUjvP7xYAQwtACBnYrjdxA91XwXFbq5AsQJwAmLw6euKVWNyv11BuHrejVmnNViWg5kuZBrtgL6NtzRWHtdxngHDMtuyky3brqGXaGQhUyXrkSpeknkkHL6NLThHH5NPnfFMVPwn2xf5UM5R51X2nTBzADSVcpi4cT7i44dT7o3yRKWtKfUzZiuNyTcSSrfH8KVdLap5ZKLmdPuXM65M2Z5wJVh3Uc4iv6iZKk44RKikM7zs1hqC4sBxRwLZjxhKvvMXDjDcYFkzyUkues4y7fjdCnVTxc4vTYUqcbY2k2WMssyj9SDseVc7dVrEvWCLQtYy79mJFoz1Hmsfk5ynE28ipznzQ3yTBugLHA6j6qW3S74eY4pJ6iynFEuXT4RqqkLGFcMh3goqS7CkphUMzP4wuJyGnzqa5tCno4U3dJ2jUL7Povg8voRqYAfiHyXC8zuhn225EdmRcSnu2pAuutQVV9hN3bkjfzAFUhUWKki8SwXtFSjy6NJyrYUiaze4p7ApsjHQBCgg2zAoBaGCwVN8991Jny31B5vPyYHy1oRSE4xTVZ7tTw9FyQ7w9p1NSEF4sziCxZHh5rFWZKAajc5c7KaMNDvHPNV6S62MTFGTyuKPQNbv9yHRGN4eH6SnZGW6snvEVdYCspWZ1U3Nbxo6vCmBK95UyYpcxHgg1CCGdU4s3edju2NDQkMifyPkJdkabzzHVDhJJbChAJc1ACQfNW74VXXwrBZmeZyA2R28MBctDyXuSuffiwueys2LVowLu9wiTHUox7KQjtHK2c9howk9czzx2mpnYzkVYH42CYsWa5514EM4CJEXPJSSbXSgJJ"
116
+ b.SetBytes(int64(len(in))) // 1024
117
+ b.ReportAllocs()
118
+ for i := 0; i < b.N; i++ {
119
+ _ = Decode(in)
120
+ }
121
+}
122
+
123
+func BenchmarkEncodeOneKilo(b *testing.B) {
124
+ var in = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\x00\x00\x04\xff\xfb\x63\xc9\x7e\x5f\x97\x68\xe5\x10\x08\xe5\xa5\x9a\x7c\x24\x75\x35\xbe\xaf\x37\x0b\xc3\xf4\x18\x62\x4a\xb7\x18\xd0\x10\x0b\x6b\x62\xe1\x36\x0f\x62\xa6\xeb\x78\xa5\xf3\x33\x52\xbc\xdf\x04\xcb\x37\xcf\x3d\x97\x5c\xb8\x75\x09\x1f\x18\x9f\xfc\xa9\xda\x1e\x59\x77\x09\x9c\x5d\xb6\xf2\x9e\x45\xb7\x5e\x5d\x11\xf1\x20\x14\x85\xf8\x54\x87\x8c\x1e\x2c\x2e\x15\x57\x89\xe7\x5d\x49\xb6\xae\x24\x3a\x20\x50\x0e\xa7\x5b\x10\xbf\x0a\xb4\x01\x42\xed\xce\x2d\x45\x21\xb6\xe8\x64\x73\x4e\x7e\x0a\x36\x1d\x57\x0a\x5e\x1c\x21\xc2\xb8\xe7\x89\x82\xe4\x04\x7e\x50\xff\xda\x4f\xfe\x11\x95\xfb\x35\xf9\x6d\x32\xce\xef\x8f\x3d\x1b\xdb\x38\xfa\xcd\x26\x36\x12\x93\xa0\x96\xea\x42\xbe\xd6\x85\x86\xc1\xc1\xe2\x55\x41\xd1\x7f\x8d\x0e\x00\x81\x58\xb4\x10\xbb\x64\x92\x05\x07\xa9\xd5\xd9\x40\x28\x8b\x9b\x4c\x8d\x8e\x4e\x69\xf9\xc9\x35\xea\xda\x2f\x61\x87\x35\x2d\x6b\x25\x32\xf0\x7e\x89\x1a\xcb\xc0\xea\x66\x88\x99\x39\xe0\x3b\x24\x3b\x05\x74\xd3\x72\xf6\x48\x15\xdc\x02\x0a\xbf\xc8\x49\x42\x10\x22\xeb\xe9\x44\x71\x55\xaf\x67\x67\xe6\x2a\x40\x31\x81\xb9\x6f\x65\x86\x0f\x0f\x9d\x58\x4c\x51\xc1\x2e\x4e\x60\x7e\xe8\x93\x39\x90\xda\xe5\xbe\xec\xe4\xdd\xbc\x1d\xba\x40\xa6\x85\xd9\xb2\xec\xb4\x26\x74\xee\xc1\xec\xe3\x40\xb9\x49\xa3\xe1\x26\x76\x8a\xeb\x95\xc8\x72\xb0\x85\x36\x19\x3f\x55\x06\x7b\xcd\x3e\xd0\xdf\x7e\x8d\x2a\xea\xa6\x24\xc6\xf6\xfb\xda\xe0\x45\xcf\x32\x0e\xbc\xf4\x41\x7d\x71\x3d\x86\xf9\xb4\xaf\x07\xa0\xd1\x34\x8a\x02\x28\x56\xd4\xcc\x36\x44\x98\x44\xcb\x9d\xc5\xfc\x45\x2d\xc4\x5c\xfe\xce\xaa\x44\xda\x66\x52\x2d\x32\x6e\x13\x32\xac\xaf\x13\x72\x87\x79\xd2\x92\x54\x9f\xc7\xb9\xf3\x21\xae\xdd\x69\x44\xe9\x46\x94\x1c\x62\x84\x03\xe0\xbf\x66\xfb\xe0\x79\xf9\x57\x9e\x22\x9e\x23\x2d\x2a\x73\xeb\x74\x38\xf0\xea\x5d\xb3\x8f\x87\x26\x3e\x3c\x54\x11\xb7\x98\xbd\x7f\x78\x64\xa3\xf1\x8f\xa9\x5e\x4f\x18\x3f\xa7\x1f\x3a\x29\x27\x27\xb7\x49\x40\x16\x18\x1f\xd3\xed\x86\x61\xbd\xc3\x4e\x4a\x53\x37\x78\x5c\x00\xd3\x50\x45\x1c\x55\xc0\x9b\xd7\x62\x29\x88\x2e\xa4\x0d\x6a\x15\x6c\x33\x3c\xe7\x31\xfa\xc1\xaf\xdf\x7a\x3e\x37\x3e\xe5\xbc\xfd\xfb\x9b\x72\x10\x35\x90\x25\x6e\x87\x0d\x74\x1c\xfd\xe3\x0b\xee\xf5\x92\x28\x8d\x22\x8a\x49\x7b\xcd\xbb\xd8\x24\x6b\x5e\x58\x40\xec\x1b\x6c\xed\x8e\xcb\x56\x62\xa6\xb4\x42\x3d\x7d\xa2\xef\x27\x27\x46\x50\xbc\x5e\x37\x9b\x27\x72\xf0\xea\xa7\xe7\x4d\xf4\xae\x7e\x95\x8f\x91\x2e\x58\xc4\x6a\x06\xda\x7a\x06\x5c\x8d\xfe\xef\xf5\xb3\x0f\xb4\x0a\x20\x53\xd8\x35\x80\x02\xca\x97\x81\xb6\x1c\x4b\x8f\xb7\xee\xd0\xc3\x88\x6c\x76\x3e\xb0\x28\xce\xa1\x9f\x76\x5f\xaa\xc3\x53\x44\x09\x70\xa3\x95\xd9\x8c\x54\xba\x8a\x9a\x6b\xce\xc3\x07\xdf\x13\x6d\xea\x0f\x51\x9c\xe2\x81\x87\xf6\x82\x7a\x70\xd8\xfa\xe2\xa8\x32\xc1\x5e\x53\xc2\x85\xe9\x61\x8a\x17\x82\x12\xab\x92\x79\x2b\xed\x07\xca\x1e\x93\x23\x9c\x4b\xd2\x89\x86\xac\x55\xf9\x50\x23\x8f\x9e\xd3\xab\x22\x57\x91\x5a\x0b\x48\xd7\xa2\xb8\x06\xbb\x74\xae\xe9\xca\x06\x41\x8d\x6a\x00\x42\xc4\x40\xa9\xfe\xae\x88\x42\xc2\x83\xe0\x8a\xd8\x5c\xbb\x5a\xb5\x9c\x1d\xa5\xbe\x67\x50\xb1\x4e\xec\x96\x65\xaa\x87\x5b\xb0\x76\x88\xe3\x1b\xcb\x38\x21\x02\x8e\xc9\xe7\xf5\xc7\xe1\x1d\xe8\xeb\x54\x0e\x0b\xea\xd1\x2e\xad\xbb\xec\x22\x21\xb3\x64\x36\x29\x34\x5e\x3a\x22\xe8\x03\x4b\x86\xb1\x67\x7d\x4f\x48\x6d\xfb\x4b\xde\xe6\x4c\xb0\xaf\x40\x66\xab\xe9\x1a\x4e\xae\x1a\x7e\x05\xc5\x67\x2a\x95\x6d\xc2\x61\x35\x20\xfe\x33\xc3\x2c\x7f\x9b\xbe\x9f\x9a\xd5\xf0\x63\x28\xa1\x94\xb1\x5c\xc1\x18\x6b\x5b\x33\xb4\x4d\xcf\xbe\xf7\xb2\x94\x58\xaa\xcf\xad\xc8\x75\x93\x1a\x08\xf4\xd2\xd9\xf6\x95\x03\x3b\xf3\x4e\xfb\x15\xe4\x28\xed\xd5\x79\xd9\xbf\xb7\x8f\xb2\x70\x16\x4c\x2d\x65\xf6\xec\x33\x1e\xaf\xea\x46\x69\xc6\x9a\x6b\xdd\xf3\x57\xe0\x1d\x28\xcd\xf8\x83\x3d\x94\x4c\x2f\x6e\xfd\x51\x3d\xa8\xff\xcb\x33\xad\x32\x42\x0e\xd3\x00\x0a\xe5\x71\x76\x3b\x83\xc9\x2a\x67\x50\xc3\xa5\xeb\x4d\x8d\x67\xd6\xd9\x1b\x9a\x5a\xbe\xdd\xc5\x15\x00\xcf\x97\x0f\x47\x44\x34\x1d\x4e\xb6\x6f\x91\x31\xf3\x45\x0f\x59\x48\x10\x23\x53\x40\x49\x83\xe6\xc8\xdf\x51\x6c\xa8\x9f\x3a\x43\x3d\xb9\xd4\xea\x30\x4d\xe0\xd2\xb8\x44\xf3\x91\x20\x79\xdb\x7b\xe6\x50\xf9\x0f\xfb\x4c\xac\x79\x93\xf6\xf8\x96\x0d\x55\x7c\x41\x9b\x1a\x86\xad\x4b\xd1\xf9\x5d\xed\x3a\x4f\xc9\x64\x72\xd4\x22\x53\x59\x2f\x01\x00\x00\xff\xff\xc6\xfd\xa0\x37\x00\x04\x00\x00")
125
+ b.SetBytes(int64(len(in))) // 1024
126
+ b.ReportAllocs()
127
+ for i := 0; i < b.N; i++ {
128
+ _ = Encode(in)
129
+ }
130
+}
Godeps/_workspace/src/github.com/jbenet/go-reuseport/available_unix.go
+1
@@ -1,4 +1,5 @@
1
// +build darwin freebsd dragonfly netbsd openbsd linux
2
+
3
package reuseport
4
5
import (
Godeps/_workspace/src/github.com/jbenet/go-reuseport/impl_windows.go
+9
-3
@@ -1,8 +1,6 @@
1
package reuseport
2
3
-import (
4
- "net"
5
-)
3
+import "net"
4
5
// TODO. for now, just pass it over to net.Listen/net.Dial
6
@@ -10,6 +8,14 @@ func listen(network, address string) (net.Listener, error) {
8
return net.Listen(network, address)
9
}
10
11
+func listenPacket(netw, laddr string) (net.PacketConn, error) {
12
+ return net.ListenPacket(netw, laddr)
13
+}
14
+
15
+func listenStream(netw, addr string) (net.Listener, error) {
16
+ return listen(netw, addr)
17
+}
18
+
19
func dial(dialer net.Dialer, network, address string) (net.Conn, error) {
20
return dialer.Dial(network, address)
21
}
Godeps/_workspace/src/github.com/jbenet/go-reuseport/opts_posix.go
+1
-1
@@ -2,7 +2,7 @@
2
// Use of this source code is governed by a BSD-style
3
// license that can be found in the LICENSE file.
4
5
-// +build darwin dragonfly freebsd linux netbsd openbsd solaris windows
5
+// +build darwin dragonfly freebsd linux netbsd openbsd solaris
6
7
package reuseport
8
Godeps/_workspace/src/github.com/jbenet/go-reuseport/poll/poll_bsd.go
+1
-1
@@ -1,4 +1,4 @@
1
-// +build darwin freebsd dragonfly netbsd openbsd
1
+// +build darwin,amd64 freebsd dragonfly netbsd openbsd
2
3
package poll
4
Godeps/_workspace/src/github.com/jbenet/go-reuseport/poll/poll_darwin_386.go
new
+57
@@ -0,0 +1,57 @@
1
+package poll
2
+
3
+import (
4
+ "syscall"
5
+ "time"
6
+)
7
+
8
+type Poller struct {
9
+ kqfd int
10
+ event syscall.Kevent_t
11
+}
12
+
13
+func New(fd int) (p *Poller, err error) {
14
+ p = &Poller{}
15
+
16
+ p.kqfd, err = syscall.Kqueue()
17
+ if p.kqfd == -1 || err != nil {
18
+ return nil, err
19
+ }
20
+
21
+ p.event = syscall.Kevent_t{
22
+ Ident: uint32(fd),
23
+ Filter: syscall.EVFILT_WRITE,
24
+ Flags: syscall.EV_ADD | syscall.EV_ENABLE | syscall.EV_ONESHOT,
25
+ Fflags: 0,
26
+ Data: 0,
27
+ Udata: nil,
28
+ }
29
+ return p, nil
30
+}
31
+
32
+func (p *Poller) Close() error {
33
+ return syscall.Close(p.kqfd)
34
+}
35
+
36
+func (p *Poller) WaitWrite(deadline time.Time) error {
37
+
38
+ // setup timeout
39
+ var timeout *syscall.Timespec
40
+ if !deadline.IsZero() {
41
+ d := deadline.Sub(time.Now())
42
+ t := syscall.NsecToTimespec(d.Nanoseconds())
43
+ timeout = &t
44
+ }
45
+
46
+ // wait on kevent
47
+ events := make([]syscall.Kevent_t, 1)
48
+ n, err := syscall.Kevent(p.kqfd, []syscall.Kevent_t{p.event}, events, timeout)
49
+ if err != nil {
50
+ return err
51
+ }
52
+
53
+ if n < 1 {
54
+ return errTimeout
55
+ }
56
+ return nil
57
+}
Godeps/_workspace/src/github.com/kardianos/osext/osext_procfs.go
+7
-1
@@ -11,12 +11,18 @@ import (
11
"fmt"
12
"os"
13
"runtime"
14
+ "strings"
15
)
16
17
func executable() (string, error) {
18
switch runtime.GOOS {
19
case "linux":
19
- return os.Readlink("/proc/self/exe")
20
+ const deletedSuffix = " (deleted)"
21
+ execpath, err := os.Readlink("/proc/self/exe")
22
+ if err != nil {
23
+ return execpath, err
24
+ }
25
+ return strings.TrimSuffix(execpath, deletedSuffix), nil
26
case "netbsd":
27
return os.Readlink("/proc/curproc/exe")
28
case "openbsd", "dragonfly":
Godeps/_workspace/src/github.com/kardianos/osext/osext_test.go
+119
-18
@@ -7,35 +7,42 @@
7
package osext
8
9
import (
10
+ "bytes"
11
"fmt"
12
+ "io"
13
"os"
12
- oexec "os/exec"
14
+ "os/exec"
15
"path/filepath"
16
"runtime"
17
"testing"
18
)
19
18
-const execPath_EnvVar = "OSTEST_OUTPUT_EXECPATH"
20
+const (
21
+ executableEnvVar = "OSTEST_OUTPUT_EXECUTABLE"
22
20
-func TestExecPath(t *testing.T) {
23
+ executableEnvValueMatch = "match"
24
+ executableEnvValueDelete = "delete"
25
+)
26
+
27
+func TestExecutableMatch(t *testing.T) {
28
ep, err := Executable()
29
if err != nil {
23
- t.Fatalf("ExecPath failed: %v", err)
30
+ t.Fatalf("Executable failed: %v", err)
31
}
25
- // we want fn to be of the form "dir/prog"
32
+
33
+ // fullpath to be of the form "dir/prog".
34
dir := filepath.Dir(filepath.Dir(ep))
27
- fn, err := filepath.Rel(dir, ep)
35
+ fullpath, err := filepath.Rel(dir, ep)
36
if err != nil {
37
t.Fatalf("filepath.Rel: %v", err)
38
}
31
- cmd := &oexec.Cmd{}
32
- // make child start with a relative program path
33
- cmd.Dir = dir
34
- cmd.Path = fn
35
- // forge argv[0] for child, so that we can verify we could correctly
36
- // get real path of the executable without influenced by argv[0].
37
- cmd.Args = []string{"-", "-test.run=XXXX"}
38
- cmd.Env = []string{fmt.Sprintf("%s=1", execPath_EnvVar)}
39
+ // Make child start with a relative program path.
40
+ // Alter argv[0] for child to verify getting real path without argv[0].
41
+ cmd := &exec.Cmd{
42
+ Dir: dir,
43
+ Path: fullpath,
44
+ Env: []string{fmt.Sprintf("%s=%s", executableEnvVar, executableEnvValueMatch)},
45
+ }
46
out, err := cmd.CombinedOutput()
47
if err != nil {
48
t.Fatalf("exec(self) failed: %v", err)
@@ -49,6 +56,63 @@ func TestExecPath(t *testing.T) {
56
}
57
}
58
59
+func TestExecutableDelete(t *testing.T) {
60
+ if runtime.GOOS != "linux" {
61
+ t.Skip()
62
+ }
63
+ fpath, err := Executable()
64
+ if err != nil {
65
+ t.Fatalf("Executable failed: %v", err)
66
+ }
67
+
68
+ r, w := io.Pipe()
69
+ stderrBuff := &bytes.Buffer{}
70
+ stdoutBuff := &bytes.Buffer{}
71
+ cmd := &exec.Cmd{
72
+ Path: fpath,
73
+ Env: []string{fmt.Sprintf("%s=%s", executableEnvVar, executableEnvValueDelete)},
74
+ Stdin: r,
75
+ Stderr: stderrBuff,
76
+ Stdout: stdoutBuff,
77
+ }
78
+ err = cmd.Start()
79
+ if err != nil {
80
+ t.Fatalf("exec(self) start failed: %v", err)
81
+ }
82
+
83
+ tempPath := fpath + "_copy"
84
+ _ = os.Remove(tempPath)
85
+
86
+ err = copyFile(tempPath, fpath)
87
+ if err != nil {
88
+ t.Fatalf("copy file failed: %v", err)
89
+ }
90
+ err = os.Remove(fpath)
91
+ if err != nil {
92
+ t.Fatalf("remove running test file failed: %v", err)
93
+ }
94
+ err = os.Rename(tempPath, fpath)
95
+ if err != nil {
96
+ t.Fatalf("rename copy to previous name failed: %v", err)
97
+ }
98
+
99
+ w.Write([]byte{0})
100
+ w.Close()
101
+
102
+ err = cmd.Wait()
103
+ if err != nil {
104
+ t.Fatalf("exec wait failed: %v", err)
105
+ }
106
+
107
+ childPath := stderrBuff.String()
108
+ if !filepath.IsAbs(childPath) {
109
+ t.Fatalf("Child returned %q, want an absolute path", childPath)
110
+ }
111
+ if !sameFile(childPath, fpath) {
112
+ t.Fatalf("Child returned %q, not the same file as %q", childPath, fpath)
113
+ }
114
+}
115
+
116
func sameFile(fn1, fn2 string) bool {
117
fi1, err := os.Stat(fn1)
118
if err != nil {
@@ -60,10 +124,30 @@ func sameFile(fn1, fn2 string) bool {
124
}
125
return os.SameFile(fi1, fi2)
126
}
127
+func copyFile(dest, src string) error {
128
+ df, err := os.Create(dest)
129
+ if err != nil {
130
+ return err
131
+ }
132
+ defer df.Close()
133
+
134
+ sf, err := os.Open(src)
135
+ if err != nil {
136
+ return err
137
+ }
138
+ defer sf.Close()
139
64
-func init() {
65
- if e := os.Getenv(execPath_EnvVar); e != "" {
66
- // first chdir to another path
140
+ _, err = io.Copy(df, sf)
141
+ return err
142
+}
143
+
144
+func TestMain(m *testing.M) {
145
+ env := os.Getenv(executableEnvVar)
146
+ switch env {
147
+ case "":
148
+ os.Exit(m.Run())
149
+ case executableEnvValueMatch:
150
+ // First chdir to another path.
151
dir := "/"
152
if runtime.GOOS == "windows" {
153
dir = filepath.VolumeName(".")
@@ -74,6 +158,23 @@ func init() {
158
} else {
159
fmt.Fprint(os.Stderr, ep)
160
}
77
- os.Exit(0)
161
+ case executableEnvValueDelete:
162
+ bb := make([]byte, 1)
163
+ var err error
164
+ n, err := os.Stdin.Read(bb)
165
+ if err != nil {
166
+ fmt.Fprint(os.Stderr, "ERROR: ", err)
167
+ os.Exit(2)
168
+ }
169
+ if n != 1 {
170
+ fmt.Fprint(os.Stderr, "ERROR: n != 1, n == ", n)
171
+ os.Exit(2)
172
+ }
173
+ if ep, err := Executable(); err != nil {
174
+ fmt.Fprint(os.Stderr, "ERROR: ", err)
175
+ } else {
176
+ fmt.Fprint(os.Stderr, ep)
177
+ }
178
}
179
+ os.Exit(0)
180
}
Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/bench2_test.go
renamed
+1
-1
@@ -4,7 +4,7 @@
4
// Use of this source code is governed by a BSD-style license that can be
5
// found in the LICENSE file.
6
7
-// +build go1.3
7
+// +build !go1.2
8
9
package leveldb
10
Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/cache/bench2_test.go
new
+30
@@ -0,0 +1,30 @@
1
+// Copyright (c) 2012, Suryandaru Triandana <syndtr@gmail.com>
2
+// All rights reserved.
3
+//
4
+// Use of this source code is governed by a BSD-style license that can be
5
+// found in the LICENSE file.
6
+
7
+// +build !go1.2
8
+
9
+package cache
10
+
11
+import (
12
+ "math/rand"
13
+ "testing"
14
+)
15
+
16
+func BenchmarkLRUCache(b *testing.B) {
17
+ c := NewCache(NewLRU(10000))
18
+
19
+ b.SetParallelism(10)
20
+ b.RunParallel(func(pb *testing.PB) {
21
+ r := rand.New(rand.NewSource(time.Now().UnixNano()))
22
+
23
+ for pb.Next() {
24
+ key := uint64(r.Intn(1000000))
25
+ c.Get(0, key, func() (int, Value) {
26
+ return 1, key
27
+ }).Release()
28
+ }
29
+ })
30
+}
Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/cache/cache.go
+639
-122
@@ -8,152 +8,669 @@
8
package cache
9
10
import (
11
+ "sync"
12
"sync/atomic"
13
+ "unsafe"
14
+
15
+ "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/util"
16
)
17
14
-// SetFunc is the function that will be called by Namespace.Get to create
15
-// a cache object, if charge is less than one than the cache object will
16
-// not be registered to cache tree, if value is nil then the cache object
17
-// will not be created.
18
-type SetFunc func() (charge int, value interface{})
18
+// Cacher provides interface to implements a caching functionality.
19
+// An implementation must be goroutine-safe.
20
+type Cacher interface {
21
+ // Capacity returns cache capacity.
22
+ Capacity() int
23
+
24
+ // SetCapacity sets cache capacity.
25
+ SetCapacity(capacity int)
26
20
-// DelFin is the function that will be called as the result of a delete operation.
21
-// Exist == true is indication that the object is exist, and pending == true is
22
-// indication of deletion already happen but haven't done yet (wait for all handles
23
-// to be released). And exist == false means the object doesn't exist.
24
-type DelFin func(exist, pending bool)
27
+ // Promote promotes the 'cache node'.
28
+ Promote(n *Node)
29
26
-// PurgeFin is the function that will be called as the result of a purge operation.
27
-type PurgeFin func(ns, key uint64)
30
+ // Ban evicts the 'cache node' and prevent subsequent 'promote'.
31
+ Ban(n *Node)
32
29
-// Cache is a cache tree. A cache instance must be goroutine-safe.
30
-type Cache interface {
31
- // SetCapacity sets cache tree capacity.
32
- SetCapacity(capacity int)
33
+ // Evict evicts the 'cache node'.
34
+ Evict(n *Node)
35
34
- // Capacity returns cache tree capacity.
35
- Capacity() int
36
+ // EvictNS evicts 'cache node' with the given namespace.
37
+ EvictNS(ns uint64)
38
37
- // Used returns used cache tree capacity.
38
- Used() int
39
-
40
- // Size returns entire alive cache objects size.
41
- Size() int
42
-
43
- // NumObjects returns number of alive objects.
44
- NumObjects() int
45
-
46
- // GetNamespace gets cache namespace with the given id.
47
- // GetNamespace is never return nil.
48
- GetNamespace(id uint64) Namespace
49
-
50
- // PurgeNamespace purges cache namespace with the given id from this cache tree.
51
- // Also read Namespace.Purge.
52
- PurgeNamespace(id uint64, fin PurgeFin)
53
-
54
- // ZapNamespace detaches cache namespace with the given id from this cache tree.
55
- // Also read Namespace.Zap.
56
- ZapNamespace(id uint64)
57
-
58
- // Purge purges all cache namespace from this cache tree.
59
- // This is behave the same as calling Namespace.Purge method on all cache namespace.
60
- Purge(fin PurgeFin)
61
-
62
- // Zap detaches all cache namespace from this cache tree.
63
- // This is behave the same as calling Namespace.Zap method on all cache namespace.
64
- Zap()
65
-}
66
-
67
-// Namespace is a cache namespace. A namespace instance must be goroutine-safe.
68
-type Namespace interface {
69
- // Get gets cache object with the given key.
70
- // If cache object is not found and setf is not nil, Get will atomically creates
71
- // the cache object by calling setf. Otherwise Get will returns nil.
72
- //
73
- // The returned cache handle should be released after use by calling Release
74
- // method.
75
- Get(key uint64, setf SetFunc) Handle
76
-
77
- // Delete removes cache object with the given key from cache tree.
78
- // A deleted cache object will be released as soon as all of its handles have
79
- // been released.
80
- // Delete only happen once, subsequent delete will consider cache object doesn't
81
- // exist, even if the cache object ins't released yet.
82
- //
83
- // If not nil, fin will be called if the cache object doesn't exist or when
84
- // finally be released.
85
- //
86
- // Delete returns true if such cache object exist and never been deleted.
87
- Delete(key uint64, fin DelFin) bool
88
-
89
- // Purge removes all cache objects within this namespace from cache tree.
90
- // This is the same as doing delete on all cache objects.
91
- //
92
- // If not nil, fin will be called on all cache objects when its finally be
93
- // released.
94
- Purge(fin PurgeFin)
95
-
96
- // Zap detaches namespace from cache tree and release all its cache objects.
97
- // A zapped namespace can never be filled again.
98
- // Calling Get on zapped namespace will always return nil.
99
- Zap()
100
-}
101
-
102
-// Handle is a cache handle.
103
-type Handle interface {
104
- // Release releases this cache handle. This method can be safely called mutiple
105
- // times.
106
- Release()
107
-
108
- // Value returns value of this cache handle.
109
- // Value will returns nil after this cache handle have be released.
110
- Value() interface{}
39
+ // EvictAll evicts all 'cache node'.
40
+ EvictAll()
41
+
42
+ // Close closes the 'cache tree'
43
+ Close() error
44
}
45
113
-const (
114
- DelNotExist = iota
115
- DelExist
116
- DelPendig
117
-)
46
+// Value is a 'cacheable object'. It may implements util.Releaser, if
47
+// so the the Release method will be called once object is released.
48
+type Value interface{}
49
119
-// Namespace state.
120
-type nsState int
50
+type CacheGetter struct {
51
+ Cache *Cache
52
+ NS uint64
53
+}
54
122
-const (
123
- nsEffective nsState = iota
124
- nsZapped
125
-)
55
+func (g *CacheGetter) Get(key uint64, setFunc func() (size int, value Value)) *Handle {
56
+ return g.Cache.Get(g.NS, key, setFunc)
57
+}
58
127
-// Node state.
128
-type nodeState int
59
+// The hash tables implementation is based on:
60
+// "Dynamic-Sized Nonblocking Hash Tables", by Yujie Liu, Kunlong Zhang, and Michael Spear. ACM Symposium on Principles of Distributed Computing, Jul 2014.
61
62
const (
131
- nodeZero nodeState = iota
132
- nodeEffective
133
- nodeEvicted
134
- nodeDeleted
63
+ mInitialSize = 1 << 4
64
+ mOverflowThreshold = 1 << 5
65
+ mOverflowGrowThreshold = 1 << 7
66
)
67
137
-// Fake handle.
138
-type fakeHandle struct {
139
- value interface{}
140
- fin func()
141
- once uint32
68
+type mBucket struct {
69
+ mu sync.Mutex
70
+ node []*Node
71
+ frozen bool
72
+}
73
+
74
+func (b *mBucket) freeze() []*Node {
75
+ b.mu.Lock()
76
+ defer b.mu.Unlock()
77
+ if !b.frozen {
78
+ b.frozen = true
79
+ }
80
+ return b.node
81
+}
82
+
83
+func (b *mBucket) get(r *Cache, h *mNode, hash uint32, ns, key uint64, noset bool) (done, added bool, n *Node) {
84
+ b.mu.Lock()
85
+
86
+ if b.frozen {
87
+ b.mu.Unlock()
88
+ return
89
+ }
90
+
91
+ // Scan the node.
92
+ for _, n := range b.node {
93
+ if n.hash == hash && n.ns == ns && n.key == key {
94
+ atomic.AddInt32(&n.ref, 1)
95
+ b.mu.Unlock()
96
+ return true, false, n
97
+ }
98
+ }
99
+
100
+ // Get only.
101
+ if noset {
102
+ b.mu.Unlock()
103
+ return true, false, nil
104
+ }
105
+
106
+ // Create node.
107
+ n = &Node{
108
+ r: r,
109
+ hash: hash,
110
+ ns: ns,
111
+ key: key,
112
+ ref: 1,
113
+ }
114
+ // Add node to bucket.
115
+ b.node = append(b.node, n)
116
+ bLen := len(b.node)
117
+ b.mu.Unlock()
118
+
119
+ // Update counter.
120
+ grow := atomic.AddInt32(&r.nodes, 1) >= h.growThreshold
121
+ if bLen > mOverflowThreshold {
122
+ grow = grow || atomic.AddInt32(&h.overflow, 1) >= mOverflowGrowThreshold
123
+ }
124
+
125
+ // Grow.
126
+ if grow && atomic.CompareAndSwapInt32(&h.resizeInProgess, 0, 1) {
127
+ nhLen := len(h.buckets) << 1
128
+ nh := &mNode{
129
+ buckets: make([]unsafe.Pointer, nhLen),
130
+ mask: uint32(nhLen) - 1,
131
+ pred: unsafe.Pointer(h),
132
+ growThreshold: int32(nhLen * mOverflowThreshold),
133
+ shrinkThreshold: int32(nhLen >> 1),
134
+ }
135
+ ok := atomic.CompareAndSwapPointer(&r.mHead, unsafe.Pointer(h), unsafe.Pointer(nh))
136
+ if !ok {
137
+ panic("BUG: failed swapping head")
138
+ }
139
+ go nh.initBuckets()
140
+ }
141
+
142
+ return true, true, n
143
}
144
144
-func (h *fakeHandle) Value() interface{} {
145
- if atomic.LoadUint32(&h.once) == 0 {
146
- return h.value
145
+func (b *mBucket) delete(r *Cache, h *mNode, hash uint32, ns, key uint64) (done, deleted bool) {
146
+ b.mu.Lock()
147
+
148
+ if b.frozen {
149
+ b.mu.Unlock()
150
+ return
151
+ }
152
+
153
+ // Scan the node.
154
+ var (
155
+ n *Node
156
+ bLen int
157
+ )
158
+ for i := range b.node {
159
+ n = b.node[i]
160
+ if n.ns == ns && n.key == key {
161
+ if atomic.LoadInt32(&n.ref) == 0 {
162
+ deleted = true
163
+
164
+ // Call releaser.
165
+ if n.value != nil {
166
+ if r, ok := n.value.(util.Releaser); ok {
167
+ r.Release()
168
+ }
169
+ n.value = nil
170
+ }
171
+
172
+ // Remove node from bucket.
173
+ b.node = append(b.node[:i], b.node[i+1:]...)
174
+ bLen = len(b.node)
175
+ }
176
+ break
177
+ }
178
+ }
179
+ b.mu.Unlock()
180
+
181
+ if deleted {
182
+ // Call OnDel.
183
+ for _, f := range n.onDel {
184
+ f()
185
+ }
186
+
187
+ // Update counter.
188
+ atomic.AddInt32(&r.size, int32(n.size)*-1)
189
+ shrink := atomic.AddInt32(&r.nodes, -1) < h.shrinkThreshold
190
+ if bLen >= mOverflowThreshold {
191
+ atomic.AddInt32(&h.overflow, -1)
192
+ }
193
+
194
+ // Shrink.
195
+ if shrink && len(h.buckets) > mInitialSize && atomic.CompareAndSwapInt32(&h.resizeInProgess, 0, 1) {
196
+ nhLen := len(h.buckets) >> 1
197
+ nh := &mNode{
198
+ buckets: make([]unsafe.Pointer, nhLen),
199
+ mask: uint32(nhLen) - 1,
200
+ pred: unsafe.Pointer(h),
201
+ growThreshold: int32(nhLen * mOverflowThreshold),
202
+ shrinkThreshold: int32(nhLen >> 1),
203
+ }
204
+ ok := atomic.CompareAndSwapPointer(&r.mHead, unsafe.Pointer(h), unsafe.Pointer(nh))
205
+ if !ok {
206
+ panic("BUG: failed swapping head")
207
+ }
208
+ go nh.initBuckets()
209
+ }
210
+ }
211
+
212
+ return true, deleted
213
+}
214
+
215
+type mNode struct {
216
+ buckets []unsafe.Pointer // []*mBucket
217
+ mask uint32
218
+ pred unsafe.Pointer // *mNode
219
+ resizeInProgess int32
220
+
221
+ overflow int32
222
+ growThreshold int32
223
+ shrinkThreshold int32
224
+}
225
+
226
+func (n *mNode) initBucket(i uint32) *mBucket {
227
+ if b := (*mBucket)(atomic.LoadPointer(&n.buckets[i])); b != nil {
228
+ return b
229
+ }
230
+
231
+ p := (*mNode)(atomic.LoadPointer(&n.pred))
232
+ if p != nil {
233
+ var node []*Node
234
+ if n.mask > p.mask {
235
+ // Grow.
236
+ pb := (*mBucket)(atomic.LoadPointer(&p.buckets[i&p.mask]))
237
+ if pb == nil {
238
+ pb = p.initBucket(i & p.mask)
239
+ }
240
+ m := pb.freeze()
241
+ // Split nodes.
242
+ for _, x := range m {
243
+ if x.hash&n.mask == i {
244
+ node = append(node, x)
245
+ }
246
+ }
247
+ } else {
248
+ // Shrink.
249
+ pb0 := (*mBucket)(atomic.LoadPointer(&p.buckets[i]))
250
+ if pb0 == nil {
251
+ pb0 = p.initBucket(i)
252
+ }
253
+ pb1 := (*mBucket)(atomic.LoadPointer(&p.buckets[i+uint32(len(n.buckets))]))
254
+ if pb1 == nil {
255
+ pb1 = p.initBucket(i + uint32(len(n.buckets)))
256
+ }
257
+ m0 := pb0.freeze()
258
+ m1 := pb1.freeze()
259
+ // Merge nodes.
260
+ node = make([]*Node, 0, len(m0)+len(m1))
261
+ node = append(node, m0...)
262
+ node = append(node, m1...)
263
+ }
264
+ b := &mBucket{node: node}
265
+ if atomic.CompareAndSwapPointer(&n.buckets[i], nil, unsafe.Pointer(b)) {
266
+ if len(node) > mOverflowThreshold {
267
+ atomic.AddInt32(&n.overflow, int32(len(node)-mOverflowThreshold))
268
+ }
269
+ return b
270
+ }
271
+ }
272
+
273
+ return (*mBucket)(atomic.LoadPointer(&n.buckets[i]))
274
+}
275
+
276
+func (n *mNode) initBuckets() {
277
+ for i := range n.buckets {
278
+ n.initBucket(uint32(i))
279
+ }
280
+ atomic.StorePointer(&n.pred, nil)
281
+}
282
+
283
+// Cache is a 'cache map'.
284
+type Cache struct {
285
+ mu sync.RWMutex
286
+ mHead unsafe.Pointer // *mNode
287
+ nodes int32
288
+ size int32
289
+ cacher Cacher
290
+ closed bool
291
+}
292
+
293
+// NewCache creates a new 'cache map'. The cacher is optional and
294
+// may be nil.
295
+func NewCache(cacher Cacher) *Cache {
296
+ h := &mNode{
297
+ buckets: make([]unsafe.Pointer, mInitialSize),
298
+ mask: mInitialSize - 1,
299
+ growThreshold: int32(mInitialSize * mOverflowThreshold),
300
+ shrinkThreshold: 0,
301
+ }
302
+ for i := range h.buckets {
303
+ h.buckets[i] = unsafe.Pointer(&mBucket{})
304
+ }
305
+ r := &Cache{
306
+ mHead: unsafe.Pointer(h),
307
+ cacher: cacher,
308
+ }
309
+ return r
310
+}
311
+
312
+func (r *Cache) getBucket(hash uint32) (*mNode, *mBucket) {
313
+ h := (*mNode)(atomic.LoadPointer(&r.mHead))
314
+ i := hash & h.mask
315
+ b := (*mBucket)(atomic.LoadPointer(&h.buckets[i]))
316
+ if b == nil {
317
+ b = h.initBucket(i)
318
+ }
319
+ return h, b
320
+}
321
+
322
+func (r *Cache) delete(n *Node) bool {
323
+ for {
324
+ h, b := r.getBucket(n.hash)
325
+ done, deleted := b.delete(r, h, n.hash, n.ns, n.key)
326
+ if done {
327
+ return deleted
328
+ }
329
+ }
330
+ return false
331
+}
332
+
333
+// Nodes returns number of 'cache node' in the map.
334
+func (r *Cache) Nodes() int {
335
+ return int(atomic.LoadInt32(&r.nodes))
336
+}
337
+
338
+// Size returns sums of 'cache node' size in the map.
339
+func (r *Cache) Size() int {
340
+ return int(atomic.LoadInt32(&r.size))
341
+}
342
+
343
+// Capacity returns cache capacity.
344
+func (r *Cache) Capacity() int {
345
+ if r.cacher == nil {
346
+ return 0
347
+ }
348
+ return r.cacher.Capacity()
349
+}
350
+
351
+// SetCapacity sets cache capacity.
352
+func (r *Cache) SetCapacity(capacity int) {
353
+ if r.cacher != nil {
354
+ r.cacher.SetCapacity(capacity)
355
+ }
356
+}
357
+
358
+// Get gets 'cache node' with the given namespace and key.
359
+// If cache node is not found and setFunc is not nil, Get will atomically creates
360
+// the 'cache node' by calling setFunc. Otherwise Get will returns nil.
361
+//
362
+// The returned 'cache handle' should be released after use by calling Release
363
+// method.
364
+func (r *Cache) Get(ns, key uint64, setFunc func() (size int, value Value)) *Handle {
365
+ r.mu.RLock()
366
+ defer r.mu.RUnlock()
367
+ if r.closed {
368
+ return nil
369
+ }
370
+
371
+ hash := murmur32(ns, key, 0xf00)
372
+ for {
373
+ h, b := r.getBucket(hash)
374
+ done, _, n := b.get(r, h, hash, ns, key, setFunc == nil)
375
+ if done {
376
+ if n != nil {
377
+ n.mu.Lock()
378
+ if n.value == nil {
379
+ if setFunc == nil {
380
+ n.mu.Unlock()
381
+ n.unref()
382
+ return nil
383
+ }
384
+
385
+ n.size, n.value = setFunc()
386
+ if n.value == nil {
387
+ n.size = 0
388
+ n.mu.Unlock()
389
+ n.unref()
390
+ return nil
391
+ }
392
+ atomic.AddInt32(&r.size, int32(n.size))
393
+ }
394
+ n.mu.Unlock()
395
+ if r.cacher != nil {
396
+ r.cacher.Promote(n)
397
+ }
398
+ return &Handle{unsafe.Pointer(n)}
399
+ }
400
+
401
+ break
402
+ }
403
}
404
return nil
405
}
406
151
-func (h *fakeHandle) Release() {
152
- if !atomic.CompareAndSwapUint32(&h.once, 0, 1) {
407
+// Delete removes and ban 'cache node' with the given namespace and key.
408
+// A banned 'cache node' will never inserted into the 'cache tree'. Ban
409
+// only attributed to the particular 'cache node', so when a 'cache node'
410
+// is recreated it will not be banned.
411
+//
412
+// If onDel is not nil, then it will be executed if such 'cache node'
413
+// doesn't exist or once the 'cache node' is released.
414
+//
415
+// Delete return true is such 'cache node' exist.
416
+func (r *Cache) Delete(ns, key uint64, onDel func()) bool {
417
+ r.mu.RLock()
418
+ defer r.mu.RUnlock()
419
+ if r.closed {
420
+ return false
421
+ }
422
+
423
+ hash := murmur32(ns, key, 0xf00)
424
+ for {
425
+ h, b := r.getBucket(hash)
426
+ done, _, n := b.get(r, h, hash, ns, key, true)
427
+ if done {
428
+ if n != nil {
429
+ if onDel != nil {
430
+ n.mu.Lock()
431
+ n.onDel = append(n.onDel, onDel)
432
+ n.mu.Unlock()
433
+ }
434
+ if r.cacher != nil {
435
+ r.cacher.Ban(n)
436
+ }
437
+ n.unref()
438
+ return true
439
+ }
440
+
441
+ break
442
+ }
443
+ }
444
+
445
+ if onDel != nil {
446
+ onDel()
447
+ }
448
+
449
+ return false
450
+}
451
+
452
+// Evict evicts 'cache node' with the given namespace and key. This will
453
+// simply call Cacher.Evict.
454
+//
455
+// Evict return true is such 'cache node' exist.
456
+func (r *Cache) Evict(ns, key uint64) bool {
457
+ r.mu.RLock()
458
+ defer r.mu.RUnlock()
459
+ if r.closed {
460
+ return false
461
+ }
462
+
463
+ hash := murmur32(ns, key, 0xf00)
464
+ for {
465
+ h, b := r.getBucket(hash)
466
+ done, _, n := b.get(r, h, hash, ns, key, true)
467
+ if done {
468
+ if n != nil {
469
+ if r.cacher != nil {
470
+ r.cacher.Evict(n)
471
+ }
472
+ n.unref()
473
+ return true
474
+ }
475
+
476
+ break
477
+ }
478
+ }
479
+
480
+ return false
481
+}
482
+
483
+// EvictNS evicts 'cache node' with the given namespace. This will
484
+// simply call Cacher.EvictNS.
485
+func (r *Cache) EvictNS(ns uint64) {
486
+ r.mu.RLock()
487
+ defer r.mu.RUnlock()
488
+ if r.closed {
489
+ return
490
+ }
491
+
492
+ if r.cacher != nil {
493
+ r.cacher.EvictNS(ns)
494
+ }
495
+}
496
+
497
+// EvictAll evicts all 'cache node'. This will simply call Cacher.EvictAll.
498
+func (r *Cache) EvictAll() {
499
+ r.mu.RLock()
500
+ defer r.mu.RUnlock()
501
+ if r.closed {
502
return
503
}
155
- if h.fin != nil {
156
- h.fin()
157
- h.fin = nil
504
+
505
+ if r.cacher != nil {
506
+ r.cacher.EvictAll()
507
+ }
508
+}
509
+
510
+// Close closes the 'cache map' and releases all 'cache node'.
511
+func (r *Cache) Close() error {
512
+ r.mu.Lock()
513
+ if !r.closed {
514
+ r.closed = true
515
+
516
+ if r.cacher != nil {
517
+ if err := r.cacher.Close(); err != nil {
518
+ return err
519
+ }
520
+ }
521
+
522
+ h := (*mNode)(r.mHead)
523
+ h.initBuckets()
524
+
525
+ for i := range h.buckets {
526
+ b := (*mBucket)(h.buckets[i])
527
+ for _, n := range b.node {
528
+ // Call releaser.
529
+ if n.value != nil {
530
+ if r, ok := n.value.(util.Releaser); ok {
531
+ r.Release()
532
+ }
533
+ n.value = nil
534
+ }
535
+
536
+ // Call OnDel.
537
+ for _, f := range n.onDel {
538
+ f()
539
+ }
540
+ }
541
+ }
542
+ }
543
+ r.mu.Unlock()
544
+ return nil
545
+}
546
+
547
+// Node is a 'cache node'.
548
+type Node struct {
549
+ r *Cache
550
+
551
+ hash uint32
552
+ ns, key uint64
553
+
554
+ mu sync.Mutex
555
+ size int
556
+ value Value
557
+
558
+ ref int32
559
+ onDel []func()
560
+
561
+ CacheData unsafe.Pointer
562
+}
563
+
564
+// NS returns this 'cache node' namespace.
565
+func (n *Node) NS() uint64 {
566
+ return n.ns
567
+}
568
+
569
+// Key returns this 'cache node' key.
570
+func (n *Node) Key() uint64 {
571
+ return n.key
572
+}
573
+
574
+// Size returns this 'cache node' size.
575
+func (n *Node) Size() int {
576
+ return n.size
577
+}
578
+
579
+// Value returns this 'cache node' value.
580
+func (n *Node) Value() Value {
581
+ return n.value
582
+}
583
+
584
+// Ref returns this 'cache node' ref counter.
585
+func (n *Node) Ref() int32 {
586
+ return atomic.LoadInt32(&n.ref)
587
+}
588
+
589
+// GetHandle returns an handle for this 'cache node'.
590
+func (n *Node) GetHandle() *Handle {
591
+ if atomic.AddInt32(&n.ref, 1) <= 1 {
592
+ panic("BUG: Node.GetHandle on zero ref")
593
+ }
594
+ return &Handle{unsafe.Pointer(n)}
595
+}
596
+
597
+func (n *Node) unref() {
598
+ if atomic.AddInt32(&n.ref, -1) == 0 {
599
+ n.r.delete(n)
600
+ }
601
+}
602
+
603
+func (n *Node) unrefLocked() {
604
+ if atomic.AddInt32(&n.ref, -1) == 0 {
605
+ n.r.mu.RLock()
606
+ if !n.r.closed {
607
+ n.r.delete(n)
608
+ }
609
+ n.r.mu.RUnlock()
610
}
611
}
612
+
613
+type Handle struct {
614
+ n unsafe.Pointer // *Node
615
+}
616
+
617
+func (h *Handle) Value() Value {
618
+ n := (*Node)(atomic.LoadPointer(&h.n))
619
+ if n != nil {
620
+ return n.value
621
+ }
622
+ return nil
623
+}
624
+
625
+func (h *Handle) Release() {
626
+ nPtr := atomic.LoadPointer(&h.n)
627
+ if nPtr != nil && atomic.CompareAndSwapPointer(&h.n, nPtr, nil) {
628
+ n := (*Node)(nPtr)
629
+ n.unrefLocked()
630
+ }
631
+}
632
+
633
+func murmur32(ns, key uint64, seed uint32) uint32 {
634
+ const (
635
+ m = uint32(0x5bd1e995)
636
+ r = 24
637
+ )
638
+
639
+ k1 := uint32(ns >> 32)
640
+ k2 := uint32(ns)
641
+ k3 := uint32(key >> 32)
642
+ k4 := uint32(key)
643
+
644
+ k1 *= m
645
+ k1 ^= k1 >> r
646
+ k1 *= m
647
+
648
+ k2 *= m
649
+ k2 ^= k2 >> r
650
+ k2 *= m
651
+
652
+ k3 *= m
653
+ k3 ^= k3 >> r
654
+ k3 *= m
655
+
656
+ k4 *= m
657
+ k4 ^= k4 >> r
658
+ k4 *= m
659
+
660
+ h := seed
661
+
662
+ h *= m
663
+ h ^= k1
664
+ h *= m
665
+ h ^= k2
666
+ h *= m
667
+ h ^= k3
668
+ h *= m
669
+ h ^= k4
670
+
671
+ h ^= h >> 13
672
+ h *= m
673
+ h ^= h >> 15
674
+
675
+ return h
676
+}
Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/cache/cache_test.go
+359
-460
@@ -13,11 +13,26 @@ import (
13
"sync/atomic"
14
"testing"
15
"time"
16
+ "unsafe"
17
)
18
19
+type int32o int32
20
+
21
+func (o *int32o) acquire() {
22
+ if atomic.AddInt32((*int32)(o), 1) != 1 {
23
+ panic("BUG: invalid ref")
24
+ }
25
+}
26
+
27
+func (o *int32o) Release() {
28
+ if atomic.AddInt32((*int32)(o), -1) != 0 {
29
+ panic("BUG: invalid ref")
30
+ }
31
+}
32
+
33
type releaserFunc struct {
34
fn func()
20
- value interface{}
35
+ value Value
36
}
37
38
func (r releaserFunc) Release() {
@@ -26,8 +41,8 @@ func (r releaserFunc) Release() {
41
}
42
}
43
29
-func set(ns Namespace, key uint64, value interface{}, charge int, relf func()) Handle {
30
- return ns.Get(key, func() (int, interface{}) {
44
+func set(c *Cache, ns, key uint64, value Value, charge int, relf func()) *Handle {
45
+ return c.Get(ns, key, func() (int, Value) {
46
if relf != nil {
47
return charge, releaserFunc{relf, value}
48
} else {
@@ -36,7 +51,246 @@ func set(ns Namespace, key uint64, value interface{}, charge int, relf func()) H
51
})
52
}
53
39
-func TestCache_HitMiss(t *testing.T) {
54
+func TestCacheMap(t *testing.T) {
55
+ runtime.GOMAXPROCS(runtime.NumCPU())
56
+
57
+ nsx := []struct {
58
+ nobjects, nhandles, concurrent, repeat int
59
+ }{
60
+ {10000, 400, 50, 3},
61
+ {100000, 1000, 100, 10},
62
+ }
63
+
64
+ var (
65
+ objects [][]int32o
66
+ handles [][]unsafe.Pointer
67
+ )
68
+
69
+ for _, x := range nsx {
70
+ objects = append(objects, make([]int32o, x.nobjects))
71
+ handles = append(handles, make([]unsafe.Pointer, x.nhandles))
72
+ }
73
+
74
+ c := NewCache(nil)
75
+
76
+ wg := new(sync.WaitGroup)
77
+ var done int32
78
+
79
+ for ns, x := range nsx {
80
+ for i := 0; i < x.concurrent; i++ {
81
+ wg.Add(1)
82
+ go func(ns, i, repeat int, objects []int32o, handles []unsafe.Pointer) {
83
+ defer wg.Done()
84
+ r := rand.New(rand.NewSource(time.Now().UnixNano()))
85
+
86
+ for j := len(objects) * repeat; j >= 0; j-- {
87
+ key := uint64(r.Intn(len(objects)))
88
+ h := c.Get(uint64(ns), key, func() (int, Value) {
89
+ o := &objects[key]
90
+ o.acquire()
91
+ return 1, o
92
+ })
93
+ if v := h.Value().(*int32o); v != &objects[key] {
94
+ t.Fatalf("#%d invalid value: want=%p got=%p", ns, &objects[key], v)
95
+ }
96
+ if objects[key] != 1 {
97
+ t.Fatalf("#%d invalid object %d: %d", ns, key, objects[key])
98
+ }
99
+ if !atomic.CompareAndSwapPointer(&handles[r.Intn(len(handles))], nil, unsafe.Pointer(h)) {
100
+ h.Release()
101
+ }
102
+ }
103
+ }(ns, i, x.repeat, objects[ns], handles[ns])
104
+ }
105
+
106
+ go func(handles []unsafe.Pointer) {
107
+ r := rand.New(rand.NewSource(time.Now().UnixNano()))
108
+
109
+ for atomic.LoadInt32(&done) == 0 {
110
+ i := r.Intn(len(handles))
111
+ h := (*Handle)(atomic.LoadPointer(&handles[i]))
112
+ if h != nil && atomic.CompareAndSwapPointer(&handles[i], unsafe.Pointer(h), nil) {
113
+ h.Release()
114
+ }
115
+ time.Sleep(time.Millisecond)
116
+ }
117
+ }(handles[ns])
118
+ }
119
+
120
+ go func() {
121
+ handles := make([]*Handle, 100000)
122
+ for atomic.LoadInt32(&done) == 0 {
123
+ for i := range handles {
124
+ handles[i] = c.Get(999999999, uint64(i), func() (int, Value) {
125
+ return 1, 1
126
+ })
127
+ }
128
+ for _, h := range handles {
129
+ h.Release()
130
+ }
131
+ }
132
+ }()
133
+
134
+ wg.Wait()
135
+
136
+ atomic.StoreInt32(&done, 1)
137
+
138
+ for _, handles0 := range handles {
139
+ for i := range handles0 {
140
+ h := (*Handle)(atomic.LoadPointer(&handles0[i]))
141
+ if h != nil && atomic.CompareAndSwapPointer(&handles0[i], unsafe.Pointer(h), nil) {
142
+ h.Release()
143
+ }
144
+ }
145
+ }
146
+
147
+ for ns, objects0 := range objects {
148
+ for i, o := range objects0 {
149
+ if o != 0 {
150
+ t.Fatalf("invalid object #%d.%d: ref=%d", ns, i, o)
151
+ }
152
+ }
153
+ }
154
+}
155
+
156
+func TestCacheMap_NodesAndSize(t *testing.T) {
157
+ c := NewCache(nil)
158
+ if c.Nodes() != 0 {
159
+ t.Errorf("invalid nodes counter: want=%d got=%d", 0, c.Nodes())
160
+ }
161
+ if c.Size() != 0 {
162
+ t.Errorf("invalid size counter: want=%d got=%d", 0, c.Size())
163
+ }
164
+ set(c, 0, 1, 1, 1, nil)
165
+ set(c, 0, 2, 2, 2, nil)
166
+ set(c, 1, 1, 3, 3, nil)
167
+ set(c, 2, 1, 4, 1, nil)
168
+ if c.Nodes() != 4 {
169
+ t.Errorf("invalid nodes counter: want=%d got=%d", 4, c.Nodes())
170
+ }
171
+ if c.Size() != 7 {
172
+ t.Errorf("invalid size counter: want=%d got=%d", 4, c.Size())
173
+ }
174
+}
175
+
176
+func TestLRUCache_Capacity(t *testing.T) {
177
+ c := NewCache(NewLRU(10))
178
+ if c.Capacity() != 10 {
179
+ t.Errorf("invalid capacity: want=%d got=%d", 10, c.Capacity())
180
+ }
181
+ set(c, 0, 1, 1, 1, nil).Release()
182
+ set(c, 0, 2, 2, 2, nil).Release()
183
+ set(c, 1, 1, 3, 3, nil).Release()
184
+ set(c, 2, 1, 4, 1, nil).Release()
185
+ set(c, 2, 2, 5, 1, nil).Release()
186
+ set(c, 2, 3, 6, 1, nil).Release()
187
+ set(c, 2, 4, 7, 1, nil).Release()
188
+ set(c, 2, 5, 8, 1, nil).Release()
189
+ if c.Nodes() != 7 {
190
+ t.Errorf("invalid nodes counter: want=%d got=%d", 7, c.Nodes())
191
+ }
192
+ if c.Size() != 10 {
193
+ t.Errorf("invalid size counter: want=%d got=%d", 10, c.Size())
194
+ }
195
+ c.SetCapacity(9)
196
+ if c.Capacity() != 9 {
197
+ t.Errorf("invalid capacity: want=%d got=%d", 9, c.Capacity())
198
+ }
199
+ if c.Nodes() != 6 {
200
+ t.Errorf("invalid nodes counter: want=%d got=%d", 6, c.Nodes())
201
+ }
202
+ if c.Size() != 8 {
203
+ t.Errorf("invalid size counter: want=%d got=%d", 8, c.Size())
204
+ }
205
+}
206
+
207
+func TestCacheMap_NilValue(t *testing.T) {
208
+ c := NewCache(NewLRU(10))
209
+ h := c.Get(0, 0, func() (size int, value Value) {
210
+ return 1, nil
211
+ })
212
+ if h != nil {
213
+ t.Error("cache handle is non-nil")
214
+ }
215
+ if c.Nodes() != 0 {
216
+ t.Errorf("invalid nodes counter: want=%d got=%d", 0, c.Nodes())
217
+ }
218
+ if c.Size() != 0 {
219
+ t.Errorf("invalid size counter: want=%d got=%d", 0, c.Size())
220
+ }
221
+}
222
+
223
+func TestLRUCache_GetLatency(t *testing.T) {
224
+ runtime.GOMAXPROCS(runtime.NumCPU())
225
+
226
+ const (
227
+ concurrentSet = 30
228
+ concurrentGet = 3
229
+ duration = 3 * time.Second
230
+ delay = 3 * time.Millisecond
231
+ maxkey = 100000
232
+ )
233
+
234
+ var (
235
+ set, getHit, getAll int32
236
+ getMaxLatency, getDuration int64
237
+ )
238
+
239
+ c := NewCache(NewLRU(5000))
240
+ wg := &sync.WaitGroup{}
241
+ until := time.Now().Add(duration)
242
+ for i := 0; i < concurrentSet; i++ {
243
+ wg.Add(1)
244
+ go func(i int) {
245
+ defer wg.Done()
246
+ r := rand.New(rand.NewSource(time.Now().UnixNano()))
247
+ for time.Now().Before(until) {
248
+ c.Get(0, uint64(r.Intn(maxkey)), func() (int, Value) {
249
+ time.Sleep(delay)
250
+ atomic.AddInt32(&set, 1)
251
+ return 1, 1
252
+ }).Release()
253
+ }
254
+ }(i)
255
+ }
256
+ for i := 0; i < concurrentGet; i++ {
257
+ wg.Add(1)
258
+ go func(i int) {
259
+ defer wg.Done()
260
+ r := rand.New(rand.NewSource(time.Now().UnixNano()))
261
+ for {
262
+ mark := time.Now()
263
+ if mark.Before(until) {
264
+ h := c.Get(0, uint64(r.Intn(maxkey)), nil)
265
+ latency := int64(time.Now().Sub(mark))
266
+ m := atomic.LoadInt64(&getMaxLatency)
267
+ if latency > m {
268
+ atomic.CompareAndSwapInt64(&getMaxLatency, m, latency)
269
+ }
270
+ atomic.AddInt64(&getDuration, latency)
271
+ if h != nil {
272
+ atomic.AddInt32(&getHit, 1)
273
+ h.Release()
274
+ }
275
+ atomic.AddInt32(&getAll, 1)
276
+ } else {
277
+ break
278
+ }
279
+ }
280
+ }(i)
281
+ }
282
+
283
+ wg.Wait()
284
+ getAvglatency := time.Duration(getDuration) / time.Duration(getAll)
285
+ t.Logf("set=%d getHit=%d getAll=%d getMaxLatency=%v getAvgLatency=%v",
286
+ set, getHit, getAll, time.Duration(getMaxLatency), getAvglatency)
287
+
288
+ if getAvglatency > delay/3 {
289
+ t.Errorf("get avg latency > %v: got=%v", delay/3, getAvglatency)
290
+ }
291
+}
292
+
293
+func TestLRUCache_HitMiss(t *testing.T) {
294
cases := []struct {
295
key uint64
296
value string
@@ -54,14 +308,13 @@ func TestCache_HitMiss(t *testing.T) {
308
}
309
310
setfin := 0
57
- c := NewLRUCache(1000)
58
- ns := c.GetNamespace(0)
311
+ c := NewCache(NewLRU(1000))
312
for i, x := range cases {
60
- set(ns, x.key, x.value, len(x.value), func() {
313
+ set(c, 0, x.key, x.value, len(x.value), func() {
314
setfin++
315
}).Release()
316
for j, y := range cases {
64
- h := ns.Get(y.key, nil)
317
+ h := c.Get(0, y.key, nil)
318
if j <= i {
319
// should hit
320
if h == nil {
@@ -85,7 +338,7 @@ func TestCache_HitMiss(t *testing.T) {
338
339
for i, x := range cases {
340
finalizerOk := false
88
- ns.Delete(x.key, func(exist, pending bool) {
341
+ c.Delete(0, x.key, func() {
342
finalizerOk = true
343
})
344
@@ -94,7 +347,7 @@ func TestCache_HitMiss(t *testing.T) {
347
}
348
349
for j, y := range cases {
97
- h := ns.Get(y.key, nil)
350
+ h := c.Get(0, y.key, nil)
351
if j > i {
352
// should hit
353
if h == nil {
@@ -122,20 +375,19 @@ func TestCache_HitMiss(t *testing.T) {
375
}
376
377
func TestLRUCache_Eviction(t *testing.T) {
125
- c := NewLRUCache(12)
126
- ns := c.GetNamespace(0)
127
- o1 := set(ns, 1, 1, 1, nil)
128
- set(ns, 2, 2, 1, nil).Release()
129
- set(ns, 3, 3, 1, nil).Release()
130
- set(ns, 4, 4, 1, nil).Release()
131
- set(ns, 5, 5, 1, nil).Release()
132
- if h := ns.Get(2, nil); h != nil { // 1,3,4,5,2
378
+ c := NewCache(NewLRU(12))
379
+ o1 := set(c, 0, 1, 1, 1, nil)
380
+ set(c, 0, 2, 2, 1, nil).Release()
381
+ set(c, 0, 3, 3, 1, nil).Release()
382
+ set(c, 0, 4, 4, 1, nil).Release()
383
+ set(c, 0, 5, 5, 1, nil).Release()
384
+ if h := c.Get(0, 2, nil); h != nil { // 1,3,4,5,2
385
h.Release()
386
}
135
- set(ns, 9, 9, 10, nil).Release() // 5,2,9
387
+ set(c, 0, 9, 9, 10, nil).Release() // 5,2,9
388
389
for _, key := range []uint64{9, 2, 5, 1} {
138
- h := ns.Get(key, nil)
390
+ h := c.Get(0, key, nil)
391
if h == nil {
392
t.Errorf("miss for key '%d'", key)
393
} else {
@@ -147,7 +399,7 @@ func TestLRUCache_Eviction(t *testing.T) {
399
}
400
o1.Release()
401
for _, key := range []uint64{1, 2, 5} {
150
- h := ns.Get(key, nil)
402
+ h := c.Get(0, key, nil)
403
if h == nil {
404
t.Errorf("miss for key '%d'", key)
405
} else {
@@ -158,7 +410,7 @@ func TestLRUCache_Eviction(t *testing.T) {
410
}
411
}
412
for _, key := range []uint64{3, 4, 9} {
161
- h := ns.Get(key, nil)
413
+ h := c.Get(0, key, nil)
414
if h != nil {
415
t.Errorf("hit for key '%d'", key)
416
if x := h.Value().(int); x != int(key) {
@@ -169,487 +421,134 @@ func TestLRUCache_Eviction(t *testing.T) {
421
}
422
}
423
172
-func TestLRUCache_SetGet(t *testing.T) {
173
- c := NewLRUCache(13)
174
- ns := c.GetNamespace(0)
175
- for i := 0; i < 200; i++ {
176
- n := uint64(rand.Intn(99999) % 20)
177
- set(ns, n, n, 1, nil).Release()
178
- if h := ns.Get(n, nil); h != nil {
179
- if h.Value() == nil {
180
- t.Errorf("key '%d' contains nil value", n)
424
+func TestLRUCache_Evict(t *testing.T) {
425
+ c := NewCache(NewLRU(6))
426
+ set(c, 0, 1, 1, 1, nil).Release()
427
+ set(c, 0, 2, 2, 1, nil).Release()
428
+ set(c, 1, 1, 4, 1, nil).Release()
429
+ set(c, 1, 2, 5, 1, nil).Release()
430
+ set(c, 2, 1, 6, 1, nil).Release()
431
+ set(c, 2, 2, 7, 1, nil).Release()
432
+
433
+ for ns := 0; ns < 3; ns++ {
434
+ for key := 1; key < 3; key++ {
435
+ if h := c.Get(uint64(ns), uint64(key), nil); h != nil {
436
+ h.Release()
437
} else {
182
- if x := h.Value().(uint64); x != n {
183
- t.Errorf("invalid value for key '%d' want '%d', got '%d'", n, n, x)
184
- }
438
+ t.Errorf("Cache.Get on #%d.%d return nil", ns, key)
439
}
186
- h.Release()
187
- } else {
188
- t.Errorf("key '%d' doesn't exist", n)
440
}
441
}
191
-}
442
193
-func TestLRUCache_Purge(t *testing.T) {
194
- c := NewLRUCache(3)
195
- ns1 := c.GetNamespace(0)
196
- o1 := set(ns1, 1, 1, 1, nil)
197
- o2 := set(ns1, 2, 2, 1, nil)
198
- ns1.Purge(nil)
199
- set(ns1, 3, 3, 1, nil).Release()
200
- for _, key := range []uint64{1, 2, 3} {
201
- h := ns1.Get(key, nil)
202
- if h == nil {
203
- t.Errorf("miss for key '%d'", key)
204
- } else {
205
- if x := h.Value().(int); x != int(key) {
206
- t.Errorf("invalid value for key '%d' want '%d', got '%d'", key, key, x)
207
- }
208
- h.Release()
209
- }
443
+ if ok := c.Evict(0, 1); !ok {
444
+ t.Error("first Cache.Evict on #0.1 return false")
445
}
211
- o1.Release()
212
- o2.Release()
213
- for _, key := range []uint64{1, 2} {
214
- h := ns1.Get(key, nil)
215
- if h != nil {
216
- t.Errorf("hit for key '%d'", key)
217
- if x := h.Value().(int); x != int(key) {
218
- t.Errorf("invalid value for key '%d' want '%d', got '%d'", key, key, x)
219
- }
220
- h.Release()
221
- }
446
+ if ok := c.Evict(0, 1); ok {
447
+ t.Error("second Cache.Evict on #0.1 return true")
448
}
223
-}
224
-
225
-type testingCacheObjectCounter struct {
226
- created uint
227
- released uint
228
-}
229
-
230
-func (c *testingCacheObjectCounter) createOne() {
231
- c.created++
232
-}
233
-
234
-func (c *testingCacheObjectCounter) releaseOne() {
235
- c.released++
236
-}
237
-
238
-type testingCacheObject struct {
239
- t *testing.T
240
- cnt *testingCacheObjectCounter
241
-
242
- ns, key uint64
243
-
244
- releaseCalled bool
245
-}
246
-
247
-func (x *testingCacheObject) Release() {
248
- if !x.releaseCalled {
249
- x.releaseCalled = true
250
- x.cnt.releaseOne()
251
- } else {
252
- x.t.Errorf("duplicate setfin NS#%d KEY#%d", x.ns, x.key)
449
+ if h := c.Get(0, 1, nil); h != nil {
450
+ t.Errorf("Cache.Get on #0.1 return non-nil: %v", h.Value())
451
}
254
-}
255
-
256
-func TestLRUCache_ConcurrentSetGet(t *testing.T) {
257
- runtime.GOMAXPROCS(runtime.NumCPU())
258
-
259
- seed := time.Now().UnixNano()
260
- t.Logf("seed=%d", seed)
452
262
- const (
263
- N = 2000000
264
- M = 4000
265
- C = 3
266
- )
267
-
268
- var set, get uint32
269
-
270
- wg := &sync.WaitGroup{}
271
- c := NewLRUCache(M / 4)
272
- for ni := uint64(0); ni < C; ni++ {
273
- r0 := rand.New(rand.NewSource(seed + int64(ni)))
274
- r1 := rand.New(rand.NewSource(seed + int64(ni) + 1))
275
- ns := c.GetNamespace(ni)
276
-
277
- wg.Add(2)
278
- go func(ns Namespace, r *rand.Rand) {
279
- for i := 0; i < N; i++ {
280
- x := uint64(r.Int63n(M))
281
- o := ns.Get(x, func() (int, interface{}) {
282
- atomic.AddUint32(&set, 1)
283
- return 1, x
284
- })
285
- if v := o.Value().(uint64); v != x {
286
- t.Errorf("#%d invalid value, got=%d", x, v)
287
- }
288
- o.Release()
289
- }
290
- wg.Done()
291
- }(ns, r0)
292
- go func(ns Namespace, r *rand.Rand) {
293
- for i := 0; i < N; i++ {
294
- x := uint64(r.Int63n(M))
295
- o := ns.Get(x, nil)
296
- if o != nil {
297
- atomic.AddUint32(&get, 1)
298
- if v := o.Value().(uint64); v != x {
299
- t.Errorf("#%d invalid value, got=%d", x, v)
300
- }
301
- o.Release()
302
- }
303
- }
304
- wg.Done()
305
- }(ns, r1)
453
+ c.EvictNS(1)
454
+ if h := c.Get(1, 1, nil); h != nil {
455
+ t.Errorf("Cache.Get on #1.1 return non-nil: %v", h.Value())
456
}
307
-
308
- wg.Wait()
309
-
310
- t.Logf("set=%d get=%d", set, get)
311
-}
312
-
313
-func TestLRUCache_Finalizer(t *testing.T) {
314
- const (
315
- capacity = 100
316
- goroutines = 100
317
- iterations = 10000
318
- keymax = 8000
319
- )
320
-
321
- cnt := &testingCacheObjectCounter{}
322
-
323
- c := NewLRUCache(capacity)
324
-
325
- type instance struct {
326
- seed int64
327
- rnd *rand.Rand
328
- nsid uint64
329
- ns Namespace
330
- effective int
331
- handles []Handle
332
- handlesMap map[uint64]int
333
-
334
- delete bool
335
- purge bool
336
- zap bool
337
- wantDel int
338
- delfinCalled int
339
- delfinCalledAll int
340
- delfinCalledEff int
341
- purgefinCalled int
342
- }
343
-
344
- instanceGet := func(p *instance, key uint64) {
345
- h := p.ns.Get(key, func() (charge int, value interface{}) {
346
- to := &testingCacheObject{
347
- t: t, cnt: cnt,
348
- ns: p.nsid,
349
- key: key,
350
- }
351
- p.effective++
352
- cnt.createOne()
353
- return 1, releaserFunc{func() {
354
- to.Release()
355
- p.effective--
356
- }, to}
357
- })
358
- p.handles = append(p.handles, h)
359
- p.handlesMap[key] = p.handlesMap[key] + 1
360
- }
361
- instanceRelease := func(p *instance, i int) {
362
- h := p.handles[i]
363
- key := h.Value().(releaserFunc).value.(*testingCacheObject).key
364
- if n := p.handlesMap[key]; n == 0 {
365
- t.Fatal("key ref == 0")
366
- } else if n > 1 {
367
- p.handlesMap[key] = n - 1
368
- } else {
369
- delete(p.handlesMap, key)
370
- }
371
- h.Release()
372
- p.handles = append(p.handles[:i], p.handles[i+1:]...)
373
- p.handles[len(p.handles) : len(p.handles)+1][0] = nil
374
- }
375
-
376
- seed := time.Now().UnixNano()
377
- t.Logf("seed=%d", seed)
378
-
379
- instances := make([]*instance, goroutines)
380
- for i := range instances {
381
- p := &instance{}
382
- p.handlesMap = make(map[uint64]int)
383
- p.seed = seed + int64(i)
384
- p.rnd = rand.New(rand.NewSource(p.seed))
385
- p.nsid = uint64(i)
386
- p.ns = c.GetNamespace(p.nsid)
387
- p.delete = i%6 == 0
388
- p.purge = i%8 == 0
389
- p.zap = i%12 == 0 || i%3 == 0
390
- instances[i] = p
391
- }
392
-
393
- runr := rand.New(rand.NewSource(seed - 1))
394
- run := func(rnd *rand.Rand, x []*instance, init func(p *instance) bool, fn func(p *instance, i int) bool) {
395
- var (
396
- rx []*instance
397
- rn []int
398
- )
399
- if init == nil {
400
- rx = append([]*instance{}, x...)
401
- rn = make([]int, len(x))
402
- } else {
403
- for _, p := range x {
404
- if init(p) {
405
- rx = append(rx, p)
406
- rn = append(rn, 0)
407
- }
408
- }
409
- }
410
- for len(rx) > 0 {
411
- i := rand.Intn(len(rx))
412
- if fn(rx[i], rn[i]) {
413
- rn[i]++
414
- } else {
415
- rx = append(rx[:i], rx[i+1:]...)
416
- rn = append(rn[:i], rn[i+1:]...)
417
- }
418
- }
457
+ if h := c.Get(1, 2, nil); h != nil {
458
+ t.Errorf("Cache.Get on #1.2 return non-nil: %v", h.Value())
459
}
460
421
- // Get and release.
422
- run(runr, instances, nil, func(p *instance, i int) bool {
423
- if i < iterations {
424
- if len(p.handles) == 0 || p.rnd.Int()%2 == 0 {
425
- instanceGet(p, uint64(p.rnd.Intn(keymax)))
426
- } else {
427
- instanceRelease(p, p.rnd.Intn(len(p.handles)))
461
+ c.EvictAll()
462
+ for ns := 0; ns < 3; ns++ {
463
+ for key := 1; key < 3; key++ {
464
+ if h := c.Get(uint64(ns), uint64(key), nil); h != nil {
465
+ t.Errorf("Cache.Get on #%d.%d return non-nil: %v", ns, key, h.Value())
466
}
429
- return true
430
- } else {
431
- return false
432
- }
433
- })
434
-
435
- if used, cap := c.Used(), c.Capacity(); used > cap {
436
- t.Errorf("Used > capacity, used=%d cap=%d", used, cap)
437
- }
438
-
439
- // Check effective objects.
440
- for i, p := range instances {
441
- if int(p.effective) < len(p.handlesMap) {
442
- t.Errorf("#%d effective objects < acquired handle, eo=%d ah=%d", i, p.effective, len(p.handlesMap))
467
}
468
}
469
+}
470
446
- if want := int(cnt.created - cnt.released); c.Size() != want {
447
- t.Errorf("Invalid cache size, want=%d got=%d", want, c.Size())
471
+func TestLRUCache_Delete(t *testing.T) {
472
+ delFuncCalled := 0
473
+ delFunc := func() {
474
+ delFuncCalled++
475
}
476
450
- // First delete.
451
- run(runr, instances, func(p *instance) bool {
452
- p.wantDel = p.effective
453
- return p.delete
454
- }, func(p *instance, i int) bool {
455
- key := uint64(i)
456
- if key < keymax {
457
- _, wantExist := p.handlesMap[key]
458
- gotExist := p.ns.Delete(key, func(exist, pending bool) {
459
- p.delfinCalledAll++
460
- if exist {
461
- p.delfinCalledEff++
462
- }
463
- })
464
- if !gotExist && wantExist {
465
- t.Errorf("delete on NS#%d KEY#%d not found", p.nsid, key)
466
- }
467
- return true
468
- } else {
469
- return false
470
- }
471
- })
472
-
473
- // Second delete.
474
- run(runr, instances, func(p *instance) bool {
475
- p.delfinCalled = 0
476
- return p.delete
477
- }, func(p *instance, i int) bool {
478
- key := uint64(i)
479
- if key < keymax {
480
- gotExist := p.ns.Delete(key, func(exist, pending bool) {
481
- if exist && !pending {
482
- t.Errorf("delete fin on NS#%d KEY#%d exist and not pending for deletion", p.nsid, key)
483
- }
484
- p.delfinCalled++
485
- })
486
- if gotExist {
487
- t.Errorf("delete on NS#%d KEY#%d found", p.nsid, key)
488
- }
489
- return true
490
- } else {
491
- if p.delfinCalled != keymax {
492
- t.Errorf("(2) NS#%d not all delete fin called, diff=%d", p.nsid, keymax-p.delfinCalled)
493
- }
494
- return false
495
- }
496
- })
477
+ c := NewCache(NewLRU(2))
478
+ set(c, 0, 1, 1, 1, nil).Release()
479
+ set(c, 0, 2, 2, 1, nil).Release()
480
498
- // Purge.
499
- run(runr, instances, func(p *instance) bool {
500
- return p.purge
501
- }, func(p *instance, i int) bool {
502
- p.ns.Purge(func(ns, key uint64) {
503
- p.purgefinCalled++
504
- })
505
- return false
506
- })
507
-
508
- if want := int(cnt.created - cnt.released); c.Size() != want {
509
- t.Errorf("Invalid cache size, want=%d got=%d", want, c.Size())
481
+ if ok := c.Delete(0, 1, delFunc); !ok {
482
+ t.Error("Cache.Delete on #1 return false")
483
}
511
-
512
- // Release.
513
- run(runr, instances, func(p *instance) bool {
514
- return !p.zap
515
- }, func(p *instance, i int) bool {
516
- if len(p.handles) > 0 {
517
- instanceRelease(p, len(p.handles)-1)
518
- return true
519
- } else {
520
- return false
521
- }
522
- })
523
-
524
- if want := int(cnt.created - cnt.released); c.Size() != want {
525
- t.Errorf("Invalid cache size, want=%d got=%d", want, c.Size())
484
+ if h := c.Get(0, 1, nil); h != nil {
485
+ t.Errorf("Cache.Get on #1 return non-nil: %v", h.Value())
486
}
527
-
528
- // Zap.
529
- run(runr, instances, func(p *instance) bool {
530
- return p.zap
531
- }, func(p *instance, i int) bool {
532
- p.ns.Zap()
533
- p.handles = nil
534
- p.handlesMap = nil
535
- return false
536
- })
537
-
538
- if want := int(cnt.created - cnt.released); c.Size() != want {
539
- t.Errorf("Invalid cache size, want=%d got=%d", want, c.Size())
487
+ if ok := c.Delete(0, 1, delFunc); ok {
488
+ t.Error("Cache.Delete on #1 return true")
489
}
490
542
- if notrel, used := int(cnt.created-cnt.released), c.Used(); notrel != used {
543
- t.Errorf("Invalid used value, want=%d got=%d", notrel, used)
491
+ h2 := c.Get(0, 2, nil)
492
+ if h2 == nil {
493
+ t.Error("Cache.Get on #2 return nil")
494
+ }
495
+ if ok := c.Delete(0, 2, delFunc); !ok {
496
+ t.Error("(1) Cache.Delete on #2 return false")
497
+ }
498
+ if ok := c.Delete(0, 2, delFunc); !ok {
499
+ t.Error("(2) Cache.Delete on #2 return false")
500
}
501
546
- c.Purge(nil)
502
+ set(c, 0, 3, 3, 1, nil).Release()
503
+ set(c, 0, 4, 4, 1, nil).Release()
504
+ c.Get(0, 2, nil).Release()
505
548
- for _, p := range instances {
549
- if p.delete {
550
- if p.delfinCalledAll != keymax {
551
- t.Errorf("#%d not all delete fin called, purge=%v zap=%v diff=%d", p.nsid, p.purge, p.zap, keymax-p.delfinCalledAll)
552
- }
553
- if p.delfinCalledEff != p.wantDel {
554
- t.Errorf("#%d not all effective delete fin called, diff=%d", p.nsid, p.wantDel-p.delfinCalledEff)
555
- }
556
- if p.purge && p.purgefinCalled > 0 {
557
- t.Errorf("#%d some purge fin called, delete=%v zap=%v n=%d", p.nsid, p.delete, p.zap, p.purgefinCalled)
558
- }
506
+ for key := 2; key <= 4; key++ {
507
+ if h := c.Get(0, uint64(key), nil); h != nil {
508
+ h.Release()
509
} else {
560
- if p.purge {
561
- if p.purgefinCalled != p.wantDel {
562
- t.Errorf("#%d not all purge fin called, delete=%v zap=%v diff=%d", p.nsid, p.delete, p.zap, p.wantDel-p.purgefinCalled)
563
- }
564
- }
510
+ t.Errorf("Cache.Get on #%d return nil", key)
511
}
512
}
513
568
- if cnt.created != cnt.released {
569
- t.Errorf("Some cache object weren't released, created=%d released=%d", cnt.created, cnt.released)
514
+ h2.Release()
515
+ if h := c.Get(0, 2, nil); h != nil {
516
+ t.Errorf("Cache.Get on #2 return non-nil: %v", h.Value())
517
}
571
-}
518
573
-func BenchmarkLRUCache_Set(b *testing.B) {
574
- c := NewLRUCache(0)
575
- ns := c.GetNamespace(0)
576
- b.ResetTimer()
577
- for i := uint64(0); i < uint64(b.N); i++ {
578
- set(ns, i, "", 1, nil)
519
+ if delFuncCalled != 4 {
520
+ t.Errorf("delFunc isn't called 4 times: got=%d", delFuncCalled)
521
}
522
}
523
582
-func BenchmarkLRUCache_Get(b *testing.B) {
583
- c := NewLRUCache(0)
584
- ns := c.GetNamespace(0)
585
- b.ResetTimer()
586
- for i := uint64(0); i < uint64(b.N); i++ {
587
- set(ns, i, "", 1, nil)
524
+func TestLRUCache_Close(t *testing.T) {
525
+ relFuncCalled := 0
526
+ relFunc := func() {
527
+ relFuncCalled++
528
}
589
- b.ResetTimer()
590
- for i := uint64(0); i < uint64(b.N); i++ {
591
- ns.Get(i, nil)
529
+ delFuncCalled := 0
530
+ delFunc := func() {
531
+ delFuncCalled++
532
}
593
-}
533
595
-func BenchmarkLRUCache_Get2(b *testing.B) {
596
- c := NewLRUCache(0)
597
- ns := c.GetNamespace(0)
598
- b.ResetTimer()
599
- for i := uint64(0); i < uint64(b.N); i++ {
600
- set(ns, i, "", 1, nil)
601
- }
602
- b.ResetTimer()
603
- for i := uint64(0); i < uint64(b.N); i++ {
604
- ns.Get(i, func() (charge int, value interface{}) {
605
- return 0, nil
606
- })
607
- }
608
-}
534
+ c := NewCache(NewLRU(2))
535
+ set(c, 0, 1, 1, 1, relFunc).Release()
536
+ set(c, 0, 2, 2, 1, relFunc).Release()
537
610
-func BenchmarkLRUCache_Release(b *testing.B) {
611
- c := NewLRUCache(0)
612
- ns := c.GetNamespace(0)
613
- handles := make([]Handle, b.N)
614
- for i := uint64(0); i < uint64(b.N); i++ {
615
- handles[i] = set(ns, i, "", 1, nil)
538
+ h3 := set(c, 0, 3, 3, 1, relFunc)
539
+ if h3 == nil {
540
+ t.Error("Cache.Get on #3 return nil")
541
}
617
- b.ResetTimer()
618
- for _, h := range handles {
619
- h.Release()
542
+ if ok := c.Delete(0, 3, delFunc); !ok {
543
+ t.Error("Cache.Delete on #3 return false")
544
}
621
-}
545
623
-func BenchmarkLRUCache_SetRelease(b *testing.B) {
624
- capacity := b.N / 100
625
- if capacity <= 0 {
626
- capacity = 10
627
- }
628
- c := NewLRUCache(capacity)
629
- ns := c.GetNamespace(0)
630
- b.ResetTimer()
631
- for i := uint64(0); i < uint64(b.N); i++ {
632
- set(ns, i, "", 1, nil).Release()
633
- }
634
-}
635
-
636
-func BenchmarkLRUCache_SetReleaseTwice(b *testing.B) {
637
- capacity := b.N / 100
638
- if capacity <= 0 {
639
- capacity = 10
640
- }
641
- c := NewLRUCache(capacity)
642
- ns := c.GetNamespace(0)
643
- b.ResetTimer()
644
-
645
- na := b.N / 2
646
- nb := b.N - na
546
+ c.Close()
547
648
- for i := uint64(0); i < uint64(na); i++ {
649
- set(ns, i, "", 1, nil).Release()
548
+ if relFuncCalled != 3 {
549
+ t.Errorf("relFunc isn't called 3 times: got=%d", relFuncCalled)
550
}
651
-
652
- for i := uint64(0); i < uint64(nb); i++ {
653
- set(ns, i, "", 1, nil).Release()
551
+ if delFuncCalled != 1 {
552
+ t.Errorf("delFunc isn't called 1 times: got=%d", delFuncCalled)
553
}
554
}
Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/cache/lru.go
new
+195
@@ -0,0 +1,195 @@
1
+// Copyright (c) 2012, Suryandaru Triandana <syndtr@gmail.com>
2
+// All rights reserved.
3
+//
4
+// Use of this source code is governed by a BSD-style license that can be
5
+// found in the LICENSE file.
6
+
7
+package cache
8
+
9
+import (
10
+ "sync"
11
+ "unsafe"
12
+)
13
+
14
+type lruNode struct {
15
+ n *Node
16
+ h *Handle
17
+ ban bool
18
+
19
+ next, prev *lruNode
20
+}
21
+
22
+func (n *lruNode) insert(at *lruNode) {
23
+ x := at.next
24
+ at.next = n
25
+ n.prev = at
26
+ n.next = x
27
+ x.prev = n
28
+}
29
+
30
+func (n *lruNode) remove() {
31
+ if n.prev != nil {
32
+ n.prev.next = n.next
33
+ n.next.prev = n.prev
34
+ n.prev = nil
35
+ n.next = nil
36
+ } else {
37
+ panic("BUG: removing removed node")
38
+ }
39
+}
40
+
41
+type lru struct {
42
+ mu sync.Mutex
43
+ capacity int
44
+ used int
45
+ recent lruNode
46
+}
47
+
48
+func (r *lru) reset() {
49
+ r.recent.next = &r.recent
50
+ r.recent.prev = &r.recent
51
+ r.used = 0
52
+}
53
+
54
+func (r *lru) Capacity() int {
55
+ r.mu.Lock()
56
+ defer r.mu.Unlock()
57
+ return r.capacity
58
+}
59
+
60
+func (r *lru) SetCapacity(capacity int) {
61
+ var evicted []*lruNode
62
+
63
+ r.mu.Lock()
64
+ r.capacity = capacity
65
+ for r.used > r.capacity {
66
+ rn := r.recent.prev
67
+ if rn == nil {
68
+ panic("BUG: invalid LRU used or capacity counter")
69
+ }
70
+ rn.remove()
71
+ rn.n.CacheData = nil
72
+ r.used -= rn.n.Size()
73
+ evicted = append(evicted, rn)
74
+ }
75
+ r.mu.Unlock()
76
+
77
+ for _, rn := range evicted {
78
+ rn.h.Release()
79
+ }
80
+}
81
+
82
+func (r *lru) Promote(n *Node) {
83
+ var evicted []*lruNode
84
+
85
+ r.mu.Lock()
86
+ if n.CacheData == nil {
87
+ if n.Size() <= r.capacity {
88
+ rn := &lruNode{n: n, h: n.GetHandle()}
89
+ rn.insert(&r.recent)
90
+ n.CacheData = unsafe.Pointer(rn)
91
+ r.used += n.Size()
92
+
93
+ for r.used > r.capacity {
94
+ rn := r.recent.prev
95
+ if rn == nil {
96
+ panic("BUG: invalid LRU used or capacity counter")
97
+ }
98
+ rn.remove()
99
+ rn.n.CacheData = nil
100
+ r.used -= rn.n.Size()
101
+ evicted = append(evicted, rn)
102
+ }
103
+ }
104
+ } else {
105
+ rn := (*lruNode)(n.CacheData)
106
+ if !rn.ban {
107
+ rn.remove()
108
+ rn.insert(&r.recent)
109
+ }
110
+ }
111
+ r.mu.Unlock()
112
+
113
+ for _, rn := range evicted {
114
+ rn.h.Release()
115
+ }
116
+}
117
+
118
+func (r *lru) Ban(n *Node) {
119
+ r.mu.Lock()
120
+ if n.CacheData == nil {
121
+ n.CacheData = unsafe.Pointer(&lruNode{n: n, ban: true})
122
+ } else {
123
+ rn := (*lruNode)(n.CacheData)
124
+ if !rn.ban {
125
+ rn.remove()
126
+ rn.ban = true
127
+ r.used -= rn.n.Size()
128
+ r.mu.Unlock()
129
+
130
+ rn.h.Release()
131
+ rn.h = nil
132
+ return
133
+ }
134
+ }
135
+ r.mu.Unlock()
136
+}
137
+
138
+func (r *lru) Evict(n *Node) {
139
+ r.mu.Lock()
140
+ rn := (*lruNode)(n.CacheData)
141
+ if rn == nil || rn.ban {
142
+ r.mu.Unlock()
143
+ return
144
+ }
145
+ n.CacheData = nil
146
+ r.mu.Unlock()
147
+
148
+ rn.h.Release()
149
+}
150
+
151
+func (r *lru) EvictNS(ns uint64) {
152
+ var evicted []*lruNode
153
+
154
+ r.mu.Lock()
155
+ for e := r.recent.prev; e != &r.recent; {
156
+ rn := e
157
+ e = e.prev
158
+ if rn.n.NS() == ns {
159
+ rn.remove()
160
+ rn.n.CacheData = nil
161
+ r.used -= rn.n.Size()
162
+ evicted = append(evicted, rn)
163
+ }
164
+ }
165
+ r.mu.Unlock()
166
+
167
+ for _, rn := range evicted {
168
+ rn.h.Release()
169
+ }
170
+}
171
+
172
+func (r *lru) EvictAll() {
173
+ r.mu.Lock()
174
+ back := r.recent.prev
175
+ for rn := back; rn != &r.recent; rn = rn.prev {
176
+ rn.n.CacheData = nil
177
+ }
178
+ r.reset()
179
+ r.mu.Unlock()
180
+
181
+ for rn := back; rn != &r.recent; rn = rn.prev {
182
+ rn.h.Release()
183
+ }
184
+}
185
+
186
+func (r *lru) Close() error {
187
+ return nil
188
+}
189
+
190
+// NewLRU create a new LRU-cache.
191
+func NewLRU(capacity int) Cacher {
192
+ r := &lru{capacity: capacity}
193
+ r.reset()
194
+ return r
195
+}
Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/cache/lru_cache.go
deleted
-622
@@ -1,622 +0,0 @@
1
-// Copyright (c) 2012, Suryandaru Triandana <syndtr@gmail.com>
2
-// All rights reserved.
3
-//
4
-// Use of this source code is governed by a BSD-style license that can be
5
-// found in the LICENSE file.
6
-
7
-package cache
8
-
9
-import (
10
- "sync"
11
- "sync/atomic"
12
-
13
- "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/util"
14
-)
15
-
16
-// The LLRB implementation were taken from https://github.com/petar/GoLLRB.
17
-// Which contains the following header:
18
-//
19
-// Copyright 2010 Petar Maymounkov. All rights reserved.
20
-// Use of this source code is governed by a BSD-style
21
-// license that can be found in the LICENSE file.
22
-
23
-// lruCache represent a LRU cache state.
24
-type lruCache struct {
25
- mu sync.Mutex
26
- recent lruNode
27
- table map[uint64]*lruNs
28
- capacity int
29
- used, size, alive int
30
-}
31
-
32
-// NewLRUCache creates a new initialized LRU cache with the given capacity.
33
-func NewLRUCache(capacity int) Cache {
34
- c := &lruCache{
35
- table: make(map[uint64]*lruNs),
36
- capacity: capacity,
37
- }
38
- c.recent.rNext = &c.recent
39
- c.recent.rPrev = &c.recent
40
- return c
41
-}
42
-
43
-func (c *lruCache) Capacity() int {
44
- c.mu.Lock()
45
- defer c.mu.Unlock()
46
- return c.capacity
47
-}
48
-
49
-func (c *lruCache) Used() int {
50
- c.mu.Lock()
51
- defer c.mu.Unlock()
52
- return c.used
53
-}
54
-
55
-func (c *lruCache) Size() int {
56
- c.mu.Lock()
57
- defer c.mu.Unlock()
58
- return c.size
59
-}
60
-
61
-func (c *lruCache) NumObjects() int {
62
- c.mu.Lock()
63
- defer c.mu.Unlock()
64
- return c.alive
65
-}
66
-
67
-// SetCapacity set cache capacity.
68
-func (c *lruCache) SetCapacity(capacity int) {
69
- c.mu.Lock()
70
- c.capacity = capacity
71
- c.evict()
72
- c.mu.Unlock()
73
-}
74
-
75
-// GetNamespace return namespace object for given id.
76
-func (c *lruCache) GetNamespace(id uint64) Namespace {
77
- c.mu.Lock()
78
- defer c.mu.Unlock()
79
-
80
- if ns, ok := c.table[id]; ok {
81
- return ns
82
- }
83
-
84
- ns := &lruNs{lru: c, id: id}
85
- c.table[id] = ns
86
- return ns
87
-}
88
-
89
-func (c *lruCache) ZapNamespace(id uint64) {
90
- c.mu.Lock()
91
- if ns, exist := c.table[id]; exist {
92
- ns.zapNB()
93
- delete(c.table, id)
94
- }
95
- c.mu.Unlock()
96
-}
97
-
98
-func (c *lruCache) PurgeNamespace(id uint64, fin PurgeFin) {
99
- c.mu.Lock()
100
- if ns, exist := c.table[id]; exist {
101
- ns.purgeNB(fin)
102
- }
103
- c.mu.Unlock()
104
-}
105
-
106
-// Purge purge entire cache.
107
-func (c *lruCache) Purge(fin PurgeFin) {
108
- c.mu.Lock()
109
- for _, ns := range c.table {
110
- ns.purgeNB(fin)
111
- }
112
- c.mu.Unlock()
113
-}
114
-
115
-func (c *lruCache) Zap() {
116
- c.mu.Lock()
117
- for _, ns := range c.table {
118
- ns.zapNB()
119
- }
120
- c.table = make(map[uint64]*lruNs)
121
- c.mu.Unlock()
122
-}
123
-
124
-func (c *lruCache) evict() {
125
- top := &c.recent
126
- for n := c.recent.rPrev; c.used > c.capacity && n != top; {
127
- if n.state != nodeEffective {
128
- panic("evicting non effective node")
129
- }
130
- n.state = nodeEvicted
131
- n.rRemove()
132
- n.derefNB()
133
- c.used -= n.charge
134
- n = c.recent.rPrev
135
- }
136
-}
137
-
138
-type lruNs struct {
139
- lru *lruCache
140
- id uint64
141
- rbRoot *lruNode
142
- state nsState
143
-}
144
-
145
-func (ns *lruNs) rbGetOrCreateNode(h *lruNode, key uint64) (hn, n *lruNode) {
146
- if h == nil {
147
- n = &lruNode{ns: ns, key: key}
148
- return n, n
149
- }
150
-
151
- if key < h.key {
152
- hn, n = ns.rbGetOrCreateNode(h.rbLeft, key)
153
- if hn != nil {
154
- h.rbLeft = hn
155
- } else {
156
- return nil, n
157
- }
158
- } else if key > h.key {
159
- hn, n = ns.rbGetOrCreateNode(h.rbRight, key)
160
- if hn != nil {
161
- h.rbRight = hn
162
- } else {
163
- return nil, n
164
- }
165
- } else {
166
- return nil, h
167
- }
168
-
169
- if rbIsRed(h.rbRight) && !rbIsRed(h.rbLeft) {
170
- h = rbRotLeft(h)
171
- }
172
- if rbIsRed(h.rbLeft) && rbIsRed(h.rbLeft.rbLeft) {
173
- h = rbRotRight(h)
174
- }
175
- if rbIsRed(h.rbLeft) && rbIsRed(h.rbRight) {
176
- rbFlip(h)
177
- }
178
- return h, n
179
-}
180
-
181
-func (ns *lruNs) getOrCreateNode(key uint64) *lruNode {
182
- hn, n := ns.rbGetOrCreateNode(ns.rbRoot, key)
183
- if hn != nil {
184
- ns.rbRoot = hn
185
- ns.rbRoot.rbBlack = true
186
- }
187
- return n
188
-}
189
-
190
-func (ns *lruNs) rbGetNode(key uint64) *lruNode {
191
- h := ns.rbRoot
192
- for h != nil {
193
- switch {
194
- case key < h.key:
195
- h = h.rbLeft
196
- case key > h.key:
197
- h = h.rbRight
198
- default:
199
- return h
200
- }
201
- }
202
- return nil
203
-}
204
-
205
-func (ns *lruNs) getNode(key uint64) *lruNode {
206
- return ns.rbGetNode(key)
207
-}
208
-
209
-func (ns *lruNs) rbDeleteNode(h *lruNode, key uint64) *lruNode {
210
- if h == nil {
211
- return nil
212
- }
213
-
214
- if key < h.key {
215
- if h.rbLeft == nil { // key not present. Nothing to delete
216
- return h
217
- }
218
- if !rbIsRed(h.rbLeft) && !rbIsRed(h.rbLeft.rbLeft) {
219
- h = rbMoveLeft(h)
220
- }
221
- h.rbLeft = ns.rbDeleteNode(h.rbLeft, key)
222
- } else {
223
- if rbIsRed(h.rbLeft) {
224
- h = rbRotRight(h)
225
- }
226
- // If @key equals @h.key and no right children at @h
227
- if h.key == key && h.rbRight == nil {
228
- return nil
229
- }
230
- if h.rbRight != nil && !rbIsRed(h.rbRight) && !rbIsRed(h.rbRight.rbLeft) {
231
- h = rbMoveRight(h)
232
- }
233
- // If @key equals @h.key, and (from above) 'h.Right != nil'
234
- if h.key == key {
235
- var x *lruNode
236
- h.rbRight, x = rbDeleteMin(h.rbRight)
237
- if x == nil {
238
- panic("logic")
239
- }
240
- x.rbLeft, h.rbLeft = h.rbLeft, nil
241
- x.rbRight, h.rbRight = h.rbRight, nil
242
- x.rbBlack = h.rbBlack
243
- h = x
244
- } else { // Else, @key is bigger than @h.key
245
- h.rbRight = ns.rbDeleteNode(h.rbRight, key)
246
- }
247
- }
248
-
249
- return rbFixup(h)
250
-}
251
-
252
-func (ns *lruNs) deleteNode(key uint64) {
253
- ns.rbRoot = ns.rbDeleteNode(ns.rbRoot, key)
254
- if ns.rbRoot != nil {
255
- ns.rbRoot.rbBlack = true
256
- }
257
-}
258
-
259
-func (ns *lruNs) rbIterateNodes(h *lruNode, pivot uint64, iter func(n *lruNode) bool) bool {
260
- if h == nil {
261
- return true
262
- }
263
- if h.key >= pivot {
264
- if !ns.rbIterateNodes(h.rbLeft, pivot, iter) {
265
- return false
266
- }
267
- if !iter(h) {
268
- return false
269
- }
270
- }
271
- return ns.rbIterateNodes(h.rbRight, pivot, iter)
272
-}
273
-
274
-func (ns *lruNs) iterateNodes(iter func(n *lruNode) bool) {
275
- ns.rbIterateNodes(ns.rbRoot, 0, iter)
276
-}
277
-
278
-func (ns *lruNs) Get(key uint64, setf SetFunc) Handle {
279
- ns.lru.mu.Lock()
280
- defer ns.lru.mu.Unlock()
281
-
282
- if ns.state != nsEffective {
283
- return nil
284
- }
285
-
286
- var n *lruNode
287
- if setf == nil {
288
- n = ns.getNode(key)
289
- if n == nil {
290
- return nil
291
- }
292
- } else {
293
- n = ns.getOrCreateNode(key)
294
- }
295
- switch n.state {
296
- case nodeZero:
297
- charge, value := setf()
298
- if value == nil {
299
- ns.deleteNode(key)
300
- return nil
301
- }
302
- if charge < 0 {
303
- charge = 0
304
- }
305
-
306
- n.value = value
307
- n.charge = charge
308
- n.state = nodeEvicted
309
-
310
- ns.lru.size += charge
311
- ns.lru.alive++
312
-
313
- fallthrough
314
- case nodeEvicted:
315
- if n.charge == 0 {
316
- break
317
- }
318
-
319
- // Insert to recent list.
320
- n.state = nodeEffective
321
- n.ref++
322
- ns.lru.used += n.charge
323
- ns.lru.evict()
324
-
325
- fallthrough
326
- case nodeEffective:
327
- // Bump to front.
328
- n.rRemove()
329
- n.rInsert(&ns.lru.recent)
330
- case nodeDeleted:
331
- // Do nothing.
332
- default:
333
- panic("invalid state")
334
- }
335
- n.ref++
336
-
337
- return &lruHandle{node: n}
338
-}
339
-
340
-func (ns *lruNs) Delete(key uint64, fin DelFin) bool {
341
- ns.lru.mu.Lock()
342
- defer ns.lru.mu.Unlock()
343
-
344
- if ns.state != nsEffective {
345
- if fin != nil {
346
- fin(false, false)
347
- }
348
- return false
349
- }
350
-
351
- n := ns.getNode(key)
352
- if n == nil {
353
- if fin != nil {
354
- fin(false, false)
355
- }
356
- return false
357
-
358
- }
359
-
360
- switch n.state {
361
- case nodeEffective:
362
- ns.lru.used -= n.charge
363
- n.state = nodeDeleted
364
- n.delfin = fin
365
- n.rRemove()
366
- n.derefNB()
367
- case nodeEvicted:
368
- n.state = nodeDeleted
369
- n.delfin = fin
370
- case nodeDeleted:
371
- if fin != nil {
372
- fin(true, true)
373
- }
374
- return false
375
- default:
376
- panic("invalid state")
377
- }
378
-
379
- return true
380
-}
381
-
382
-func (ns *lruNs) purgeNB(fin PurgeFin) {
383
- if ns.state == nsEffective {
384
- var nodes []*lruNode
385
- ns.iterateNodes(func(n *lruNode) bool {
386
- nodes = append(nodes, n)
387
- return true
388
- })
389
- for _, n := range nodes {
390
- switch n.state {
391
- case nodeEffective:
392
- ns.lru.used -= n.charge
393
- n.state = nodeDeleted
394
- n.purgefin = fin
395
- n.rRemove()
396
- n.derefNB()
397
- case nodeEvicted:
398
- n.state = nodeDeleted
399
- n.purgefin = fin
400
- case nodeDeleted:
401
- default:
402
- panic("invalid state")
403
- }
404
- }
405
- }
406
-}
407
-
408
-func (ns *lruNs) Purge(fin PurgeFin) {
409
- ns.lru.mu.Lock()
410
- ns.purgeNB(fin)
411
- ns.lru.mu.Unlock()
412
-}
413
-
414
-func (ns *lruNs) zapNB() {
415
- if ns.state == nsEffective {
416
- ns.state = nsZapped
417
-
418
- ns.iterateNodes(func(n *lruNode) bool {
419
- if n.state == nodeEffective {
420
- ns.lru.used -= n.charge
421
- n.rRemove()
422
- }
423
- ns.lru.size -= n.charge
424
- n.state = nodeDeleted
425
- n.fin()
426
-
427
- return true
428
- })
429
- ns.rbRoot = nil
430
- }
431
-}
432
-
433
-func (ns *lruNs) Zap() {
434
- ns.lru.mu.Lock()
435
- ns.zapNB()
436
- delete(ns.lru.table, ns.id)
437
- ns.lru.mu.Unlock()
438
-}
439
-
440
-type lruNode struct {
441
- ns *lruNs
442
-
443
- rNext, rPrev *lruNode
444
- rbLeft, rbRight *lruNode
445
- rbBlack bool
446
-
447
- key uint64
448
- value interface{}
449
- charge int
450
- ref int
451
- state nodeState
452
- delfin DelFin
453
- purgefin PurgeFin
454
-}
455
-
456
-func (n *lruNode) rInsert(at *lruNode) {
457
- x := at.rNext
458
- at.rNext = n
459
- n.rPrev = at
460
- n.rNext = x
461
- x.rPrev = n
462
-}
463
-
464
-func (n *lruNode) rRemove() bool {
465
- if n.rPrev == nil {
466
- return false
467
- }
468
-
469
- n.rPrev.rNext = n.rNext
470
- n.rNext.rPrev = n.rPrev
471
- n.rPrev = nil
472
- n.rNext = nil
473
-
474
- return true
475
-}
476
-
477
-func (n *lruNode) fin() {
478
- if r, ok := n.value.(util.Releaser); ok {
479
- r.Release()
480
- }
481
- if n.purgefin != nil {
482
- if n.delfin != nil {
483
- panic("conflicting delete and purge fin")
484
- }
485
- n.purgefin(n.ns.id, n.key)
486
- n.purgefin = nil
487
- } else if n.delfin != nil {
488
- n.delfin(true, false)
489
- n.delfin = nil
490
- }
491
-}
492
-
493
-func (n *lruNode) derefNB() {
494
- n.ref--
495
- if n.ref == 0 {
496
- if n.ns.state == nsEffective {
497
- // Remove elemement.
498
- n.ns.deleteNode(n.key)
499
- n.ns.lru.size -= n.charge
500
- n.ns.lru.alive--
501
- n.fin()
502
- }
503
- n.value = nil
504
- } else if n.ref < 0 {
505
- panic("leveldb/cache: lruCache: negative node reference")
506
- }
507
-}
508
-
509
-func (n *lruNode) deref() {
510
- n.ns.lru.mu.Lock()
511
- n.derefNB()
512
- n.ns.lru.mu.Unlock()
513
-}
514
-
515
-type lruHandle struct {
516
- node *lruNode
517
- once uint32
518
-}
519
-
520
-func (h *lruHandle) Value() interface{} {
521
- if atomic.LoadUint32(&h.once) == 0 {
522
- return h.node.value
523
- }
524
- return nil
525
-}
526
-
527
-func (h *lruHandle) Release() {
528
- if !atomic.CompareAndSwapUint32(&h.once, 0, 1) {
529
- return
530
- }
531
- h.node.deref()
532
- h.node = nil
533
-}
534
-
535
-func rbIsRed(h *lruNode) bool {
536
- if h == nil {
537
- return false
538
- }
539
- return !h.rbBlack
540
-}
541
-
542
-func rbRotLeft(h *lruNode) *lruNode {
543
- x := h.rbRight
544
- if x.rbBlack {
545
- panic("rotating a black link")
546
- }
547
- h.rbRight = x.rbLeft
548
- x.rbLeft = h
549
- x.rbBlack = h.rbBlack
550
- h.rbBlack = false
551
- return x
552
-}
553
-
554
-func rbRotRight(h *lruNode) *lruNode {
555
- x := h.rbLeft
556
- if x.rbBlack {
557
- panic("rotating a black link")
558
- }
559
- h.rbLeft = x.rbRight
560
- x.rbRight = h
561
- x.rbBlack = h.rbBlack
562
- h.rbBlack = false
563
- return x
564
-}
565
-
566
-func rbFlip(h *lruNode) {
567
- h.rbBlack = !h.rbBlack
568
- h.rbLeft.rbBlack = !h.rbLeft.rbBlack
569
- h.rbRight.rbBlack = !h.rbRight.rbBlack
570
-}
571
-
572
-func rbMoveLeft(h *lruNode) *lruNode {
573
- rbFlip(h)
574
- if rbIsRed(h.rbRight.rbLeft) {
575
- h.rbRight = rbRotRight(h.rbRight)
576
- h = rbRotLeft(h)
577
- rbFlip(h)
578
- }
579
- return h
580
-}
581
-
582
-func rbMoveRight(h *lruNode) *lruNode {
583
- rbFlip(h)
584
- if rbIsRed(h.rbLeft.rbLeft) {
585
- h = rbRotRight(h)
586
- rbFlip(h)
587
- }
588
- return h
589
-}
590
-
591
-func rbFixup(h *lruNode) *lruNode {
592
- if rbIsRed(h.rbRight) {
593
- h = rbRotLeft(h)
594
- }
595
-
596
- if rbIsRed(h.rbLeft) && rbIsRed(h.rbLeft.rbLeft) {
597
- h = rbRotRight(h)
598
- }
599
-
600
- if rbIsRed(h.rbLeft) && rbIsRed(h.rbRight) {
601
- rbFlip(h)
602
- }
603
-
604
- return h
605
-}
606
-
607
-func rbDeleteMin(h *lruNode) (hn, n *lruNode) {
608
- if h == nil {
609
- return nil, nil
610
- }
611
- if h.rbLeft == nil {
612
- return nil, h
613
- }
614
-
615
- if !rbIsRed(h.rbLeft) && !rbIsRed(h.rbLeft.rbLeft) {
616
- h = rbMoveLeft(h)
617
- }
618
-
619
- h.rbLeft, n = rbDeleteMin(h.rbLeft)
620
-
621
- return rbFixup(h), n
622
-}
Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/corrupt_test.go
+8
-10
@@ -9,14 +9,12 @@ package leveldb
9
import (
10
"bytes"
11
"fmt"
12
- "io"
13
- "math/rand"
14
- "testing"
15
-
16
- "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/cache"
12
"github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/filter"
13
"github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/opt"
14
"github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/storage"
15
+ "io"
16
+ "math/rand"
17
+ "testing"
18
)
19
20
const ctValSize = 1000
@@ -33,8 +31,8 @@ func newDbCorruptHarnessWopt(t *testing.T, o *opt.Options) *dbCorruptHarness {
31
32
func newDbCorruptHarness(t *testing.T) *dbCorruptHarness {
33
return newDbCorruptHarnessWopt(t, &opt.Options{
36
- BlockCache: cache.NewLRUCache(100),
37
- Strict: opt.StrictJournalChecksum,
34
+ BlockCacheCapacity: 100,
35
+ Strict: opt.StrictJournalChecksum,
36
})
37
}
38
@@ -269,9 +267,9 @@ func TestCorruptDB_TableIndex(t *testing.T) {
267
func TestCorruptDB_MissingManifest(t *testing.T) {
268
rnd := rand.New(rand.NewSource(0x0badda7a))
269
h := newDbCorruptHarnessWopt(t, &opt.Options{
272
- BlockCache: cache.NewLRUCache(100),
273
- Strict: opt.StrictJournalChecksum,
274
- WriteBuffer: 1000 * 60,
270
+ BlockCacheCapacity: 100,
271
+ Strict: opt.StrictJournalChecksum,
272
+ WriteBuffer: 1000 * 60,
273
})
274
275
h.build(1000)
Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/db.go
+11
-10
@@ -27,8 +27,7 @@ import (
27
"github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/util"
28
)
29
30
-// DB is a LevelDB database.
31
-type DB struct {
30
+type DB struct { // DB is a LevelDB database.
31
// Need 64-bit alignment.
32
seq uint64
33
@@ -347,12 +346,14 @@ func recoverTable(s *session, o *opt.Options) error {
346
return err
347
}
348
iter := tr.NewIterator(nil, nil)
350
- iter.(iterator.ErrorCallbackSetter).SetErrorCallback(func(err error) {
351
- if errors.IsCorrupted(err) {
352
- s.logf("table@recovery block corruption @%d %q", file.Num(), err)
353
- tcorruptedBlock++
354
- }
355
- })
349
+ if itererr, ok := iter.(iterator.ErrorCallbackSetter); ok {
350
+ itererr.SetErrorCallback(func(err error) {
351
+ if errors.IsCorrupted(err) {
352
+ s.logf("table@recovery block corruption @%d %q", file.Num(), err)
353
+ tcorruptedBlock++
354
+ }
355
+ })
356
+ }
357
358
// Scan the table.
359
for iter.Next() {
@@ -823,8 +824,8 @@ func (db *DB) GetProperty(name string) (value string, err error) {
824
case p == "blockpool":
825
value = fmt.Sprintf("%v", db.s.tops.bpool)
826
case p == "cachedblock":
826
- if bc := db.s.o.GetBlockCache(); bc != nil {
827
- value = fmt.Sprintf("%d", bc.Size())
827
+ if db.s.tops.bcache != nil {
828
+ value = fmt.Sprintf("%d", db.s.tops.bcache.Size())
829
} else {
830
value = "<nil>"
831
}
Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/db_test.go
+7
-7
@@ -1271,7 +1271,7 @@ func TestDB_DeletionMarkers2(t *testing.T) {
1271
}
1272
1273
func TestDB_CompactionTableOpenError(t *testing.T) {
1274
- h := newDbHarnessWopt(t, &opt.Options{CachedOpenFiles: -1})
1274
+ h := newDbHarnessWopt(t, &opt.Options{OpenFilesCacheCapacity: -1})
1275
defer h.close()
1276
1277
im := 10
@@ -1629,8 +1629,8 @@ func TestDB_ManualCompaction(t *testing.T) {
1629
1630
func TestDB_BloomFilter(t *testing.T) {
1631
h := newDbHarnessWopt(t, &opt.Options{
1632
- BlockCache: opt.NoCache,
1633
- Filter: filter.NewBloomFilter(10),
1632
+ DisableBlockCache: true,
1633
+ Filter: filter.NewBloomFilter(10),
1634
})
1635
defer h.close()
1636
@@ -2066,8 +2066,8 @@ func TestDB_GetProperties(t *testing.T) {
2066
2067
func TestDB_GoleveldbIssue72and83(t *testing.T) {
2068
h := newDbHarnessWopt(t, &opt.Options{
2069
- WriteBuffer: 1 * opt.MiB,
2070
- CachedOpenFiles: 3,
2069
+ WriteBuffer: 1 * opt.MiB,
2070
+ OpenFilesCacheCapacity: 3,
2071
})
2072
defer h.close()
2073
@@ -2200,7 +2200,7 @@ func TestDB_GoleveldbIssue72and83(t *testing.T) {
2200
func TestDB_TransientError(t *testing.T) {
2201
h := newDbHarnessWopt(t, &opt.Options{
2202
WriteBuffer: 128 * opt.KiB,
2203
- CachedOpenFiles: 3,
2203
+ OpenFilesCacheCapacity: 3,
2204
DisableCompactionBackoff: true,
2205
})
2206
defer h.close()
@@ -2410,7 +2410,7 @@ func TestDB_TableCompactionBuilder(t *testing.T) {
2410
CompactionTableSize: 43 * opt.KiB,
2411
CompactionExpandLimitFactor: 1,
2412
CompactionGPOverlapsFactor: 1,
2413
- BlockCache: opt.NoCache,
2413
+ DisableBlockCache: true,
2414
}
2415
s, err := newSession(stor, o)
2416
if err != nil {
Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/external_test.go
+8
-8
@@ -17,14 +17,14 @@ import (
17
var _ = testutil.Defer(func() {
18
Describe("Leveldb external", func() {
19
o := &opt.Options{
20
- BlockCache: opt.NoCache,
21
- BlockRestartInterval: 5,
22
- BlockSize: 80,
23
- Compression: opt.NoCompression,
24
- CachedOpenFiles: -1,
25
- Strict: opt.StrictAll,
26
- WriteBuffer: 1000,
27
- CompactionTableSize: 2000,
20
+ DisableBlockCache: true,
21
+ BlockRestartInterval: 5,
22
+ BlockSize: 80,
23
+ Compression: opt.NoCompression,
24
+ OpenFilesCacheCapacity: -1,
25
+ Strict: opt.StrictAll,
26
+ WriteBuffer: 1000,
27
+ CompactionTableSize: 2000,
28
}
29
30
Describe("write test", func() {
Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/opt/options.go
+86
-47
@@ -20,8 +20,9 @@ const (
20
GiB = MiB * 1024
21
)
22
23
-const (
24
- DefaultBlockCacheSize = 8 * MiB
23
+var (
24
+ DefaultBlockCacher = LRUCacher
25
+ DefaultBlockCacheCapacity = 8 * MiB
26
DefaultBlockRestartInterval = 16
27
DefaultBlockSize = 4 * KiB
28
DefaultCompactionExpandLimitFactor = 25
@@ -33,7 +34,8 @@ const (
34
DefaultCompactionTotalSize = 10 * MiB
35
DefaultCompactionTotalSizeMultiplier = 10.0
36
DefaultCompressionType = SnappyCompression
36
- DefaultCachedOpenFiles = 500
37
+ DefaultOpenFilesCacher = LRUCacher
38
+ DefaultOpenFilesCacheCapacity = 500
39
DefaultMaxMemCompationLevel = 2
40
DefaultNumLevel = 7
41
DefaultWriteBuffer = 4 * MiB
@@ -41,22 +43,33 @@ const (
43
DefaultWriteL0SlowdownTrigger = 8
44
)
45
44
-type noCache struct{}
46
+// Cacher is a caching algorithm.
47
+type Cacher interface {
48
+ New(capacity int) cache.Cacher
49
+}
50
46
-func (noCache) SetCapacity(capacity int) {}
47
-func (noCache) Capacity() int { return 0 }
48
-func (noCache) Used() int { return 0 }
49
-func (noCache) Size() int { return 0 }
50
-func (noCache) NumObjects() int { return 0 }
51
-func (noCache) GetNamespace(id uint64) cache.Namespace { return nil }
52
-func (noCache) PurgeNamespace(id uint64, fin cache.PurgeFin) {}
53
-func (noCache) ZapNamespace(id uint64) {}
54
-func (noCache) Purge(fin cache.PurgeFin) {}
55
-func (noCache) Zap() {}
51
+type CacherFunc struct {
52
+ NewFunc func(capacity int) cache.Cacher
53
+}
54
57
-var NoCache cache.Cache = noCache{}
55
+func (f *CacherFunc) New(capacity int) cache.Cacher {
56
+ if f.NewFunc != nil {
57
+ return f.NewFunc(capacity)
58
+ }
59
+ return nil
60
+}
61
+
62
+func noCacher(int) cache.Cacher { return nil }
63
+
64
+var (
65
+ // LRUCacher is the LRU-cache algorithm.
66
+ LRUCacher = &CacherFunc{cache.NewLRU}
67
+
68
+ // NoCacher is the value to disable caching algorithm.
69
+ NoCacher = &CacherFunc{}
70
+)
71
59
-// Compression is the per-block compression algorithm to use.
72
+// Compression is the 'sorted table' block compression algorithm to use.
73
type Compression uint
74
75
func (c Compression) String() string {
@@ -133,16 +146,17 @@ type Options struct {
146
// The default value is nil
147
AltFilters []filter.Filter
148
136
- // BlockCache provides per-block caching for LevelDB. Specify NoCache to
137
- // disable block caching.
149
+ // BlockCacher provides cache algorithm for LevelDB 'sorted table' block caching.
150
+ // Specify NoCacher to disable caching algorithm.
151
//
139
- // By default LevelDB will create LRU-cache with capacity of BlockCacheSize.
140
- BlockCache cache.Cache
152
+ // The default value is LRUCacher.
153
+ BlockCacher Cacher
154
142
- // BlockCacheSize defines the capacity of the default 'block cache'.
155
+ // BlockCacheCapacity defines the capacity of the 'sorted table' block caching.
156
+ // Use -1 for zero, this has same effect as specifying NoCacher to BlockCacher.
157
//
158
// The default value is 8MiB.
145
- BlockCacheSize int
159
+ BlockCacheCapacity int
160
161
// BlockRestartInterval is the number of keys between restart points for
162
// delta encoding of keys.
@@ -156,13 +170,6 @@ type Options struct {
170
// The default value is 4KiB.
171
BlockSize int
172
159
- // CachedOpenFiles defines number of open files to kept around when not
160
- // in-use, the counting includes still in-use files.
161
- // Set this to negative value to disable caching.
162
- //
163
- // The default value is 500.
164
- CachedOpenFiles int
165
-
173
// CompactionExpandLimitFactor limits compaction size after expanded.
174
// This will be multiplied by table size limit at compaction target level.
175
//
@@ -237,11 +244,17 @@ type Options struct {
244
// The default value uses the same ordering as bytes.Compare.
245
Comparer comparer.Comparer
246
240
- // Compression defines the per-block compression to use.
247
+ // Compression defines the 'sorted table' block compression to use.
248
//
249
// The default value (DefaultCompression) uses snappy compression.
250
Compression Compression
251
252
+ // DisableBlockCache allows disable use of cache.Cache functionality on
253
+ // 'sorted table' block.
254
+ //
255
+ // The default value is false.
256
+ DisableBlockCache bool
257
+
258
// DisableCompactionBackoff allows disable compaction retry backoff.
259
//
260
// The default value is false.
@@ -288,6 +301,18 @@ type Options struct {
301
// The default is 7.
302
NumLevel int
303
304
+ // OpenFilesCacher provides cache algorithm for open files caching.
305
+ // Specify NoCacher to disable caching algorithm.
306
+ //
307
+ // The default value is LRUCacher.
308
+ OpenFilesCacher Cacher
309
+
310
+ // OpenFilesCacheCapacity defines the capacity of the open files caching.
311
+ // Use -1 for zero, this has same effect as specifying NoCacher to OpenFilesCacher.
312
+ //
313
+ // The default value is 500.
314
+ OpenFilesCacheCapacity int
315
+
316
// Strict defines the DB strict level.
317
Strict Strict
318
@@ -320,18 +345,22 @@ func (o *Options) GetAltFilters() []filter.Filter {
345
return o.AltFilters
346
}
347
323
-func (o *Options) GetBlockCache() cache.Cache {
324
- if o == nil {
348
+func (o *Options) GetBlockCacher() Cacher {
349
+ if o == nil || o.BlockCacher == nil {
350
+ return DefaultBlockCacher
351
+ } else if o.BlockCacher == NoCacher {
352
return nil
353
}
327
- return o.BlockCache
354
+ return o.BlockCacher
355
}
356
330
-func (o *Options) GetBlockCacheSize() int {
331
- if o == nil || o.BlockCacheSize <= 0 {
332
- return DefaultBlockCacheSize
357
+func (o *Options) GetBlockCacheCapacity() int {
358
+ if o == nil || o.BlockCacheCapacity == 0 {
359
+ return DefaultBlockCacheCapacity
360
+ } else if o.BlockCacheCapacity < 0 {
361
+ return 0
362
}
334
- return o.BlockCacheSize
363
+ return o.BlockCacheCapacity
364
}
365
366
func (o *Options) GetBlockRestartInterval() int {
@@ -348,15 +377,6 @@ func (o *Options) GetBlockSize() int {
377
return o.BlockSize
378
}
379
351
-func (o *Options) GetCachedOpenFiles() int {
352
- if o == nil || o.CachedOpenFiles == 0 {
353
- return DefaultCachedOpenFiles
354
- } else if o.CachedOpenFiles < 0 {
355
- return 0
356
- }
357
- return o.CachedOpenFiles
358
-}
359
-
380
func (o *Options) GetCompactionExpandLimit(level int) int {
381
factor := DefaultCompactionExpandLimitFactor
382
if o != nil && o.CompactionExpandLimitFactor > 0 {
@@ -477,7 +497,7 @@ func (o *Options) GetMaxMemCompationLevel() int {
497
if o != nil {
498
if o.MaxMemCompationLevel > 0 {
499
level = o.MaxMemCompationLevel
480
- } else if o.MaxMemCompationLevel == -1 {
500
+ } else if o.MaxMemCompationLevel < 0 {
501
level = 0
502
}
503
}
@@ -494,6 +514,25 @@ func (o *Options) GetNumLevel() int {
514
return o.NumLevel
515
}
516
517
+func (o *Options) GetOpenFilesCacher() Cacher {
518
+ if o == nil || o.OpenFilesCacher == nil {
519
+ return DefaultOpenFilesCacher
520
+ }
521
+ if o.OpenFilesCacher == NoCacher {
522
+ return nil
523
+ }
524
+ return o.OpenFilesCacher
525
+}
526
+
527
+func (o *Options) GetOpenFilesCacheCapacity() int {
528
+ if o == nil || o.OpenFilesCacheCapacity == 0 {
529
+ return DefaultOpenFilesCacheCapacity
530
+ } else if o.OpenFilesCacheCapacity < 0 {
531
+ return 0
532
+ }
533
+ return o.OpenFilesCacheCapacity
534
+}
535
+
536
func (o *Options) GetStrict(strict Strict) bool {
537
if o == nil || o.Strict == 0 {
538
return DefaultStrict&strict != 0
Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/options.go
-8
@@ -7,7 +7,6 @@
7
package leveldb
8
9
import (
10
- "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/cache"
10
"github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/filter"
11
"github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/opt"
12
)
@@ -32,13 +31,6 @@ func (s *session) setOptions(o *opt.Options) {
31
no.AltFilters[i] = &iFilter{filter}
32
}
33
}
35
- // Block cache.
36
- switch o.GetBlockCache() {
37
- case nil:
38
- no.BlockCache = cache.NewLRUCache(o.GetBlockCacheSize())
39
- case opt.NoCache:
40
- no.BlockCache = nil
41
- }
34
// Comparer.
35
s.icmp = &iComparer{o.GetComparer()}
36
no.Comparer = s.icmp
Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/session.go
+1
-4
@@ -73,7 +73,7 @@ func newSession(stor storage.Storage, o *opt.Options) (s *session, err error) {
73
stCompPtrs: make([]iKey, o.GetNumLevel()),
74
}
75
s.setOptions(o)
76
- s.tops = newTableOps(s, s.o.GetCachedOpenFiles())
76
+ s.tops = newTableOps(s)
77
s.setVersion(newVersion(s))
78
s.log("log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed")
79
return
@@ -82,9 +82,6 @@ func newSession(stor storage.Storage, o *opt.Options) (s *session, err error) {
82
// Close session.
83
func (s *session) close() {
84
s.tops.close()
85
- if bc := s.o.GetBlockCache(); bc != nil {
86
- bc.Purge(nil)
87
- }
85
if s.manifest != nil {
86
s.manifest.Close()
87
}
Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/session_util.go
+1
-3
@@ -14,10 +14,8 @@ import (
14
"github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/storage"
15
)
16
17
-// Logging.
18
-
17
type dropper struct {
20
- s *session
18
+ s *session // Logging.
19
file storage.File
20
}
21
Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/table.go
+43
-27
@@ -286,10 +286,10 @@ func (x *tFilesSortByNum) Less(i, j int) bool {
286
287
// Table operations.
288
type tOps struct {
289
- s *session
290
- cache cache.Cache
291
- cacheNS cache.Namespace
292
- bpool *util.BufferPool
289
+ s *session
290
+ cache *cache.Cache
291
+ bcache *cache.Cache
292
+ bpool *util.BufferPool
293
}
294
295
// Creates an empty table and returns table writer.
@@ -338,26 +338,28 @@ func (t *tOps) createFrom(src iterator.Iterator) (f *tFile, n int, err error) {
338
339
// Opens table. It returns a cache handle, which should
340
// be released after use.
341
-func (t *tOps) open(f *tFile) (ch cache.Handle, err error) {
341
+func (t *tOps) open(f *tFile) (ch *cache.Handle, err error) {
342
num := f.file.Num()
343
- ch = t.cacheNS.Get(num, func() (charge int, value interface{}) {
343
+ ch = t.cache.Get(0, num, func() (size int, value cache.Value) {
344
var r storage.Reader
345
r, err = f.file.Open()
346
if err != nil {
347
return 0, nil
348
}
349
350
- var bcacheNS cache.Namespace
351
- if bc := t.s.o.GetBlockCache(); bc != nil {
352
- bcacheNS = bc.GetNamespace(num)
350
+ var bcache *cache.CacheGetter
351
+ if t.bcache != nil {
352
+ bcache = &cache.CacheGetter{Cache: t.bcache, NS: num}
353
}
354
+
355
var tr *table.Reader
355
- tr, err = table.NewReader(r, int64(f.size), storage.NewFileInfo(f.file), bcacheNS, t.bpool, t.s.o.Options)
356
+ tr, err = table.NewReader(r, int64(f.size), storage.NewFileInfo(f.file), bcache, t.bpool, t.s.o.Options)
357
if err != nil {
358
r.Close()
359
return 0, nil
360
}
361
return 1, tr
362
+
363
})
364
if ch == nil && err == nil {
365
err = ErrClosed
@@ -412,16 +414,14 @@ func (t *tOps) newIterator(f *tFile, slice *util.Range, ro *opt.ReadOptions) ite
414
// no one use the the table.
415
func (t *tOps) remove(f *tFile) {
416
num := f.file.Num()
415
- t.cacheNS.Delete(num, func(exist, pending bool) {
416
- if !pending {
417
- if err := f.file.Remove(); err != nil {
418
- t.s.logf("table@remove removing @%d %q", num, err)
419
- } else {
420
- t.s.logf("table@remove removed @%d", num)
421
- }
422
- if bc := t.s.o.GetBlockCache(); bc != nil {
423
- bc.ZapNamespace(num)
424
- }
417
+ t.cache.Delete(0, num, func() {
418
+ if err := f.file.Remove(); err != nil {
419
+ t.s.logf("table@remove removing @%d %q", num, err)
420
+ } else {
421
+ t.s.logf("table@remove removed @%d", num)
422
+ }
423
+ if t.bcache != nil {
424
+ t.bcache.EvictNS(num)
425
}
426
})
427
}
@@ -429,18 +429,34 @@ func (t *tOps) remove(f *tFile) {
429
// Closes the table ops instance. It will close all tables,
430
// regadless still used or not.
431
func (t *tOps) close() {
432
- t.cache.Zap()
432
t.bpool.Close()
433
+ t.cache.Close()
434
+ if t.bcache != nil {
435
+ t.bcache.Close()
436
+ }
437
}
438
439
// Creates new initialized table ops instance.
437
-func newTableOps(s *session, cacheCap int) *tOps {
438
- c := cache.NewLRUCache(cacheCap)
440
+func newTableOps(s *session) *tOps {
441
+ var (
442
+ cacher cache.Cacher
443
+ bcache *cache.Cache
444
+ )
445
+ if s.o.GetOpenFilesCacheCapacity() > 0 {
446
+ cacher = cache.NewLRU(s.o.GetOpenFilesCacheCapacity())
447
+ }
448
+ if !s.o.DisableBlockCache {
449
+ var bcacher cache.Cacher
450
+ if s.o.GetBlockCacheCapacity() > 0 {
451
+ bcacher = cache.NewLRU(s.o.GetBlockCacheCapacity())
452
+ }
453
+ bcache = cache.NewCache(bcacher)
454
+ }
455
return &tOps{
440
- s: s,
441
- cache: c,
442
- cacheNS: c.GetNamespace(0),
443
- bpool: util.NewBufferPool(s.o.GetBlockSize() + 5),
456
+ s: s,
457
+ cache: cache.NewCache(cacher),
458
+ bcache: bcache,
459
+ bpool: util.NewBufferPool(s.o.GetBlockSize() + 5),
460
}
461
}
462
Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/table/reader.go
+35
-28
@@ -14,8 +14,6 @@ import (
14
"strings"
15
"sync"
16
17
- "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/syndtr/gosnappy/snappy"
18
-
17
"github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/cache"
18
"github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/comparer"
19
"github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/errors"
@@ -24,6 +22,7 @@ import (
22
"github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/opt"
23
"github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/storage"
24
"github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/util"
25
+ "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/syndtr/gosnappy/snappy"
26
)
27
28
var (
@@ -509,7 +508,7 @@ type Reader struct {
508
mu sync.RWMutex
509
fi *storage.FileInfo
510
reader io.ReaderAt
512
- cache cache.Namespace
511
+ cache *cache.CacheGetter
512
err error
513
bpool *util.BufferPool
514
// Options
@@ -613,18 +612,22 @@ func (r *Reader) readBlock(bh blockHandle, verifyChecksum bool) (*block, error)
612
613
func (r *Reader) readBlockCached(bh blockHandle, verifyChecksum, fillCache bool) (*block, util.Releaser, error) {
614
if r.cache != nil {
616
- var err error
617
- ch := r.cache.Get(bh.offset, func() (charge int, value interface{}) {
618
- if !fillCache {
619
- return 0, nil
620
- }
621
- var b *block
622
- b, err = r.readBlock(bh, verifyChecksum)
623
- if err != nil {
624
- return 0, nil
625
- }
626
- return cap(b.data), b
627
- })
615
+ var (
616
+ err error
617
+ ch *cache.Handle
618
+ )
619
+ if fillCache {
620
+ ch = r.cache.Get(bh.offset, func() (size int, value cache.Value) {
621
+ var b *block
622
+ b, err = r.readBlock(bh, verifyChecksum)
623
+ if err != nil {
624
+ return 0, nil
625
+ }
626
+ return cap(b.data), b
627
+ })
628
+ } else {
629
+ ch = r.cache.Get(bh.offset, nil)
630
+ }
631
if ch != nil {
632
b, ok := ch.Value().(*block)
633
if !ok {
@@ -667,18 +670,22 @@ func (r *Reader) readFilterBlock(bh blockHandle) (*filterBlock, error) {
670
671
func (r *Reader) readFilterBlockCached(bh blockHandle, fillCache bool) (*filterBlock, util.Releaser, error) {
672
if r.cache != nil {
670
- var err error
671
- ch := r.cache.Get(bh.offset, func() (charge int, value interface{}) {
672
- if !fillCache {
673
- return 0, nil
674
- }
675
- var b *filterBlock
676
- b, err = r.readFilterBlock(bh)
677
- if err != nil {
678
- return 0, nil
679
- }
680
- return cap(b.data), b
681
- })
673
+ var (
674
+ err error
675
+ ch *cache.Handle
676
+ )
677
+ if fillCache {
678
+ ch = r.cache.Get(bh.offset, func() (size int, value cache.Value) {
679
+ var b *filterBlock
680
+ b, err = r.readFilterBlock(bh)
681
+ if err != nil {
682
+ return 0, nil
683
+ }
684
+ return cap(b.data), b
685
+ })
686
+ } else {
687
+ ch = r.cache.Get(bh.offset, nil)
688
+ }
689
if ch != nil {
690
b, ok := ch.Value().(*filterBlock)
691
if !ok {
@@ -980,7 +987,7 @@ func (r *Reader) Release() {
987
// The fi, cache and bpool is optional and can be nil.
988
//
989
// The returned table reader instance is goroutine-safe.
983
-func NewReader(f io.ReaderAt, size int64, fi *storage.FileInfo, cache cache.Namespace, bpool *util.BufferPool, o *opt.Options) (*Reader, error) {
990
+func NewReader(f io.ReaderAt, size int64, fi *storage.FileInfo, cache *cache.CacheGetter, bpool *util.BufferPool, o *opt.Options) (*Reader, error) {
991
if f == nil {
992
return nil, errors.New("leveldb/table: nil file")
993
}
Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/table/writer.go
+1
-2
@@ -12,12 +12,11 @@ import (
12
"fmt"
13
"io"
14
15
- "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/syndtr/gosnappy/snappy"
16
-
15
"github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/comparer"
16
"github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/filter"
17
"github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/opt"
18
"github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/util"
19
+ "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/syndtr/gosnappy/snappy"
20
)
21
22
func sharedPrefixLen(a, b []byte) int {
Godeps/_workspace/src/github.com/syndtr/gosnappy/snappy/decode.go
+170
-2
@@ -7,10 +7,15 @@ package snappy
7
import (
8
"encoding/binary"
9
"errors"
10
+ "io"
11
)
12
12
-// ErrCorrupt reports that the input is invalid.
13
-var ErrCorrupt = errors.New("snappy: corrupt input")
13
+var (
14
+ // ErrCorrupt reports that the input is invalid.
15
+ ErrCorrupt = errors.New("snappy: corrupt input")
16
+ // ErrUnsupported reports that the input isn't supported.
17
+ ErrUnsupported = errors.New("snappy: unsupported input")
18
+)
19
20
// DecodedLen returns the length of the decoded block.
21
func DecodedLen(src []byte) (int, error) {
@@ -122,3 +127,166 @@ func Decode(dst, src []byte) ([]byte, error) {
127
}
128
return dst[:d], nil
129
}
130
+
131
+// NewReader returns a new Reader that decompresses from r, using the framing
132
+// format described at
133
+// https://code.google.com/p/snappy/source/browse/trunk/framing_format.txt
134
+func NewReader(r io.Reader) *Reader {
135
+ return &Reader{
136
+ r: r,
137
+ decoded: make([]byte, maxUncompressedChunkLen),
138
+ buf: make([]byte, MaxEncodedLen(maxUncompressedChunkLen)+checksumSize),
139
+ }
140
+}
141
+
142
+// Reader is an io.Reader than can read Snappy-compressed bytes.
143
+type Reader struct {
144
+ r io.Reader
145
+ err error
146
+ decoded []byte
147
+ buf []byte
148
+ // decoded[i:j] contains decoded bytes that have not yet been passed on.
149
+ i, j int
150
+ readHeader bool
151
+}
152
+
153
+// Reset discards any buffered data, resets all state, and switches the Snappy
154
+// reader to read from r. This permits reusing a Reader rather than allocating
155
+// a new one.
156
+func (r *Reader) Reset(reader io.Reader) {
157
+ r.r = reader
158
+ r.err = nil
159
+ r.i = 0
160
+ r.j = 0
161
+ r.readHeader = false
162
+}
163
+
164
+func (r *Reader) readFull(p []byte) (ok bool) {
165
+ if _, r.err = io.ReadFull(r.r, p); r.err != nil {
166
+ if r.err == io.ErrUnexpectedEOF {
167
+ r.err = ErrCorrupt
168
+ }
169
+ return false
170
+ }
171
+ return true
172
+}
173
+
174
+// Read satisfies the io.Reader interface.
175
+func (r *Reader) Read(p []byte) (int, error) {
176
+ if r.err != nil {
177
+ return 0, r.err
178
+ }
179
+ for {
180
+ if r.i < r.j {
181
+ n := copy(p, r.decoded[r.i:r.j])
182
+ r.i += n
183
+ return n, nil
184
+ }
185
+ if !r.readFull(r.buf[:4]) {
186
+ return 0, r.err
187
+ }
188
+ chunkType := r.buf[0]
189
+ if !r.readHeader {
190
+ if chunkType != chunkTypeStreamIdentifier {
191
+ r.err = ErrCorrupt
192
+ return 0, r.err
193
+ }
194
+ r.readHeader = true
195
+ }
196
+ chunkLen := int(r.buf[1]) | int(r.buf[2])<<8 | int(r.buf[3])<<16
197
+ if chunkLen > len(r.buf) {
198
+ r.err = ErrUnsupported
199
+ return 0, r.err
200
+ }
201
+
202
+ // The chunk types are specified at
203
+ // https://code.google.com/p/snappy/source/browse/trunk/framing_format.txt
204
+ switch chunkType {
205
+ case chunkTypeCompressedData:
206
+ // Section 4.2. Compressed data (chunk type 0x00).
207
+ if chunkLen < checksumSize {
208
+ r.err = ErrCorrupt
209
+ return 0, r.err
210
+ }
211
+ buf := r.buf[:chunkLen]
212
+ if !r.readFull(buf) {
213
+ return 0, r.err
214
+ }
215
+ checksum := uint32(buf[0]) | uint32(buf[1])<<8 | uint32(buf[2])<<16 | uint32(buf[3])<<24
216
+ buf = buf[checksumSize:]
217
+
218
+ n, err := DecodedLen(buf)
219
+ if err != nil {
220
+ r.err = err
221
+ return 0, r.err
222
+ }
223
+ if n > len(r.decoded) {
224
+ r.err = ErrCorrupt
225
+ return 0, r.err
226
+ }
227
+ if _, err := Decode(r.decoded, buf); err != nil {
228
+ r.err = err
229
+ return 0, r.err
230
+ }
231
+ if crc(r.decoded[:n]) != checksum {
232
+ r.err = ErrCorrupt
233
+ return 0, r.err
234
+ }
235
+ r.i, r.j = 0, n
236
+ continue
237
+
238
+ case chunkTypeUncompressedData:
239
+ // Section 4.3. Uncompressed data (chunk type 0x01).
240
+ if chunkLen < checksumSize {
241
+ r.err = ErrCorrupt
242
+ return 0, r.err
243
+ }
244
+ buf := r.buf[:checksumSize]
245
+ if !r.readFull(buf) {
246
+ return 0, r.err
247
+ }
248
+ checksum := uint32(buf[0]) | uint32(buf[1])<<8 | uint32(buf[2])<<16 | uint32(buf[3])<<24
249
+ // Read directly into r.decoded instead of via r.buf.
250
+ n := chunkLen - checksumSize
251
+ if !r.readFull(r.decoded[:n]) {
252
+ return 0, r.err
253
+ }
254
+ if crc(r.decoded[:n]) != checksum {
255
+ r.err = ErrCorrupt
256
+ return 0, r.err
257
+ }
258
+ r.i, r.j = 0, n
259
+ continue
260
+
261
+ case chunkTypeStreamIdentifier:
262
+ // Section 4.1. Stream identifier (chunk type 0xff).
263
+ if chunkLen != len(magicBody) {
264
+ r.err = ErrCorrupt
265
+ return 0, r.err
266
+ }
267
+ if !r.readFull(r.buf[:len(magicBody)]) {
268
+ return 0, r.err
269
+ }
270
+ for i := 0; i < len(magicBody); i++ {
271
+ if r.buf[i] != magicBody[i] {
272
+ r.err = ErrCorrupt
273
+ return 0, r.err
274
+ }
275
+ }
276
+ continue
277
+ }
278
+
279
+ if chunkType <= 0x7f {
280
+ // Section 4.5. Reserved unskippable chunks (chunk types 0x02-0x7f).
281
+ r.err = ErrUnsupported
282
+ return 0, r.err
283
+
284
+ } else {
285
+ // Section 4.4 Padding (chunk type 0xfe).
286
+ // Section 4.6. Reserved skippable chunks (chunk types 0x80-0xfd).
287
+ if !r.readFull(r.buf[:chunkLen]) {
288
+ return 0, r.err
289
+ }
290
+ }
291
+ }
292
+}
Godeps/_workspace/src/github.com/syndtr/gosnappy/snappy/encode.go
+84
@@ -6,6 +6,7 @@ package snappy
6
7
import (
8
"encoding/binary"
9
+ "io"
10
)
11
12
// We limit how far copy back-references can go, the same as the C++ code.
@@ -172,3 +173,86 @@ func MaxEncodedLen(srcLen int) int {
173
// This last factor dominates the blowup, so the final estimate is:
174
return 32 + srcLen + srcLen/6
175
}
176
+
177
+// NewWriter returns a new Writer that compresses to w, using the framing
178
+// format described at
179
+// https://code.google.com/p/snappy/source/browse/trunk/framing_format.txt
180
+func NewWriter(w io.Writer) *Writer {
181
+ return &Writer{
182
+ w: w,
183
+ enc: make([]byte, MaxEncodedLen(maxUncompressedChunkLen)),
184
+ }
185
+}
186
+
187
+// Writer is an io.Writer than can write Snappy-compressed bytes.
188
+type Writer struct {
189
+ w io.Writer
190
+ err error
191
+ enc []byte
192
+ buf [checksumSize + chunkHeaderSize]byte
193
+ wroteHeader bool
194
+}
195
+
196
+// Reset discards the writer's state and switches the Snappy writer to write to
197
+// w. This permits reusing a Writer rather than allocating a new one.
198
+func (w *Writer) Reset(writer io.Writer) {
199
+ w.w = writer
200
+ w.err = nil
201
+ w.wroteHeader = false
202
+}
203
+
204
+// Write satisfies the io.Writer interface.
205
+func (w *Writer) Write(p []byte) (n int, errRet error) {
206
+ if w.err != nil {
207
+ return 0, w.err
208
+ }
209
+ if !w.wroteHeader {
210
+ copy(w.enc, magicChunk)
211
+ if _, err := w.w.Write(w.enc[:len(magicChunk)]); err != nil {
212
+ w.err = err
213
+ return n, err
214
+ }
215
+ w.wroteHeader = true
216
+ }
217
+ for len(p) > 0 {
218
+ var uncompressed []byte
219
+ if len(p) > maxUncompressedChunkLen {
220
+ uncompressed, p = p[:maxUncompressedChunkLen], p[maxUncompressedChunkLen:]
221
+ } else {
222
+ uncompressed, p = p, nil
223
+ }
224
+ checksum := crc(uncompressed)
225
+
226
+ // Compress the buffer, discarding the result if the improvement
227
+ // isn't at least 12.5%.
228
+ chunkType := uint8(chunkTypeCompressedData)
229
+ chunkBody, err := Encode(w.enc, uncompressed)
230
+ if err != nil {
231
+ w.err = err
232
+ return n, err
233
+ }
234
+ if len(chunkBody) >= len(uncompressed)-len(uncompressed)/8 {
235
+ chunkType, chunkBody = chunkTypeUncompressedData, uncompressed
236
+ }
237
+
238
+ chunkLen := 4 + len(chunkBody)
239
+ w.buf[0] = chunkType
240
+ w.buf[1] = uint8(chunkLen >> 0)
241
+ w.buf[2] = uint8(chunkLen >> 8)
242
+ w.buf[3] = uint8(chunkLen >> 16)
243
+ w.buf[4] = uint8(checksum >> 0)
244
+ w.buf[5] = uint8(checksum >> 8)
245
+ w.buf[6] = uint8(checksum >> 16)
246
+ w.buf[7] = uint8(checksum >> 24)
247
+ if _, err = w.w.Write(w.buf[:]); err != nil {
248
+ w.err = err
249
+ return n, err
250
+ }
251
+ if _, err = w.w.Write(chunkBody); err != nil {
252
+ w.err = err
253
+ return n, err
254
+ }
255
+ n += len(uncompressed)
256
+ }
257
+ return n, nil
258
+}
Godeps/_workspace/src/github.com/syndtr/gosnappy/snappy/snappy.go
+30
@@ -8,6 +8,10 @@
8
// The C++ snappy implementation is at http://code.google.com/p/snappy/
9
package snappy
10
11
+import (
12
+ "hash/crc32"
13
+)
14
+
15
/*
16
Each encoded block begins with the varint-encoded length of the decoded data,
17
followed by a sequence of chunks. Chunks begin and end on byte boundaries. The
@@ -36,3 +40,29 @@ const (
40
tagCopy2 = 0x02
41
tagCopy4 = 0x03
42
)
43
+
44
+const (
45
+ checksumSize = 4
46
+ chunkHeaderSize = 4
47
+ magicChunk = "\xff\x06\x00\x00" + magicBody
48
+ magicBody = "sNaPpY"
49
+ // https://code.google.com/p/snappy/source/browse/trunk/framing_format.txt says
50
+ // that "the uncompressed data in a chunk must be no longer than 65536 bytes".
51
+ maxUncompressedChunkLen = 65536
52
+)
53
+
54
+const (
55
+ chunkTypeCompressedData = 0x00
56
+ chunkTypeUncompressedData = 0x01
57
+ chunkTypePadding = 0xfe
58
+ chunkTypeStreamIdentifier = 0xff
59
+)
60
+
61
+var crcTable = crc32.MakeTable(crc32.Castagnoli)
62
+
63
+// crc implements the checksum specified in section 3 of
64
+// https://code.google.com/p/snappy/source/browse/trunk/framing_format.txt
65
+func crc(b []byte) uint32 {
66
+ c := crc32.Update(0, crcTable, b)
67
+ return uint32(c>>15|c<<17) + 0xa282ead8
68
+}
Godeps/_workspace/src/github.com/syndtr/gosnappy/snappy/snappy_test.go
+152
-49
@@ -18,7 +18,10 @@ import (
18
"testing"
19
)
20
21
-var download = flag.Bool("download", false, "If true, download any missing files before running benchmarks")
21
+var (
22
+ download = flag.Bool("download", false, "If true, download any missing files before running benchmarks")
23
+ testdata = flag.String("testdata", "testdata", "Directory containing the test data")
24
+)
25
26
func roundtrip(b, ebuf, dbuf []byte) error {
27
e, err := Encode(ebuf, b)
@@ -55,11 +58,11 @@ func TestSmallCopy(t *testing.T) {
58
}
59
60
func TestSmallRand(t *testing.T) {
58
- rand.Seed(27354294)
61
+ rng := rand.New(rand.NewSource(27354294))
62
for n := 1; n < 20000; n += 23 {
63
b := make([]byte, n)
61
- for i, _ := range b {
62
- b[i] = uint8(rand.Uint32())
64
+ for i := range b {
65
+ b[i] = uint8(rng.Uint32())
66
}
67
if err := roundtrip(b, nil, nil); err != nil {
68
t.Fatal(err)
@@ -70,7 +73,7 @@ func TestSmallRand(t *testing.T) {
73
func TestSmallRegular(t *testing.T) {
74
for n := 1; n < 20000; n += 23 {
75
b := make([]byte, n)
73
- for i, _ := range b {
76
+ for i := range b {
77
b[i] = uint8(i%10 + 'a')
78
}
79
if err := roundtrip(b, nil, nil); err != nil {
@@ -79,6 +82,120 @@ func TestSmallRegular(t *testing.T) {
82
}
83
}
84
85
+func cmp(a, b []byte) error {
86
+ if len(a) != len(b) {
87
+ return fmt.Errorf("got %d bytes, want %d", len(a), len(b))
88
+ }
89
+ for i := range a {
90
+ if a[i] != b[i] {
91
+ return fmt.Errorf("byte #%d: got 0x%02x, want 0x%02x", i, a[i], b[i])
92
+ }
93
+ }
94
+ return nil
95
+}
96
+
97
+func TestFramingFormat(t *testing.T) {
98
+ // src is comprised of alternating 1e5-sized sequences of random
99
+ // (incompressible) bytes and repeated (compressible) bytes. 1e5 was chosen
100
+ // because it is larger than maxUncompressedChunkLen (64k).
101
+ src := make([]byte, 1e6)
102
+ rng := rand.New(rand.NewSource(1))
103
+ for i := 0; i < 10; i++ {
104
+ if i%2 == 0 {
105
+ for j := 0; j < 1e5; j++ {
106
+ src[1e5*i+j] = uint8(rng.Intn(256))
107
+ }
108
+ } else {
109
+ for j := 0; j < 1e5; j++ {
110
+ src[1e5*i+j] = uint8(i)
111
+ }
112
+ }
113
+ }
114
+
115
+ buf := new(bytes.Buffer)
116
+ if _, err := NewWriter(buf).Write(src); err != nil {
117
+ t.Fatalf("Write: encoding: %v", err)
118
+ }
119
+ dst, err := ioutil.ReadAll(NewReader(buf))
120
+ if err != nil {
121
+ t.Fatalf("ReadAll: decoding: %v", err)
122
+ }
123
+ if err := cmp(dst, src); err != nil {
124
+ t.Fatal(err)
125
+ }
126
+}
127
+
128
+func TestReaderReset(t *testing.T) {
129
+ gold := bytes.Repeat([]byte("All that is gold does not glitter,\n"), 10000)
130
+ buf := new(bytes.Buffer)
131
+ if _, err := NewWriter(buf).Write(gold); err != nil {
132
+ t.Fatalf("Write: %v", err)
133
+ }
134
+ encoded, invalid, partial := buf.String(), "invalid", "partial"
135
+ r := NewReader(nil)
136
+ for i, s := range []string{encoded, invalid, partial, encoded, partial, invalid, encoded, encoded} {
137
+ if s == partial {
138
+ r.Reset(strings.NewReader(encoded))
139
+ if _, err := r.Read(make([]byte, 101)); err != nil {
140
+ t.Errorf("#%d: %v", i, err)
141
+ continue
142
+ }
143
+ continue
144
+ }
145
+ r.Reset(strings.NewReader(s))
146
+ got, err := ioutil.ReadAll(r)
147
+ switch s {
148
+ case encoded:
149
+ if err != nil {
150
+ t.Errorf("#%d: %v", i, err)
151
+ continue
152
+ }
153
+ if err := cmp(got, gold); err != nil {
154
+ t.Errorf("#%d: %v", i, err)
155
+ continue
156
+ }
157
+ case invalid:
158
+ if err == nil {
159
+ t.Errorf("#%d: got nil error, want non-nil", i)
160
+ continue
161
+ }
162
+ }
163
+ }
164
+}
165
+
166
+func TestWriterReset(t *testing.T) {
167
+ gold := bytes.Repeat([]byte("Not all those who wander are lost;\n"), 10000)
168
+ var gots, wants [][]byte
169
+ const n = 20
170
+ w, failed := NewWriter(nil), false
171
+ for i := 0; i <= n; i++ {
172
+ buf := new(bytes.Buffer)
173
+ w.Reset(buf)
174
+ want := gold[:len(gold)*i/n]
175
+ if _, err := w.Write(want); err != nil {
176
+ t.Errorf("#%d: Write: %v", i, err)
177
+ failed = true
178
+ continue
179
+ }
180
+ got, err := ioutil.ReadAll(NewReader(buf))
181
+ if err != nil {
182
+ t.Errorf("#%d: ReadAll: %v", i, err)
183
+ failed = true
184
+ continue
185
+ }
186
+ gots = append(gots, got)
187
+ wants = append(wants, want)
188
+ }
189
+ if failed {
190
+ return
191
+ }
192
+ for i := range gots {
193
+ if err := cmp(gots[i], wants[i]); err != nil {
194
+ t.Errorf("#%d: %v", i, err)
195
+ }
196
+ }
197
+}
198
+
199
func benchDecode(b *testing.B, src []byte) {
200
encoded, err := Encode(nil, src)
201
if err != nil {
@@ -102,7 +219,7 @@ func benchEncode(b *testing.B, src []byte) {
219
}
220
}
221
105
-func readFile(b *testing.B, filename string) []byte {
222
+func readFile(b testing.TB, filename string) []byte {
223
src, err := ioutil.ReadFile(filename)
224
if err != nil {
225
b.Fatalf("failed reading %s: %s", filename, err)
@@ -144,7 +261,7 @@ func BenchmarkWordsEncode1e5(b *testing.B) { benchWords(b, 1e5, false) }
261
func BenchmarkWordsEncode1e6(b *testing.B) { benchWords(b, 1e6, false) }
262
263
// testFiles' values are copied directly from
147
-// https://code.google.com/p/snappy/source/browse/trunk/snappy_unittest.cc.
264
+// https://raw.githubusercontent.com/google/snappy/master/snappy_unittest.cc
265
// The label field is unused in snappy-go.
266
var testFiles = []struct {
267
label string
@@ -152,29 +269,36 @@ var testFiles = []struct {
269
}{
270
{"html", "html"},
271
{"urls", "urls.10K"},
155
- {"jpg", "house.jpg"},
156
- {"pdf", "mapreduce-osdi-1.pdf"},
272
+ {"jpg", "fireworks.jpeg"},
273
+ {"jpg_200", "fireworks.jpeg"},
274
+ {"pdf", "paper-100k.pdf"},
275
{"html4", "html_x_4"},
158
- {"cp", "cp.html"},
159
- {"c", "fields.c"},
160
- {"lsp", "grammar.lsp"},
161
- {"xls", "kennedy.xls"},
276
{"txt1", "alice29.txt"},
277
{"txt2", "asyoulik.txt"},
278
{"txt3", "lcet10.txt"},
279
{"txt4", "plrabn12.txt"},
166
- {"bin", "ptt5"},
167
- {"sum", "sum"},
168
- {"man", "xargs.1"},
280
{"pb", "geo.protodata"},
281
{"gaviota", "kppkn.gtb"},
282
}
283
284
// The test data files are present at this canonical URL.
174
-const baseURL = "https://snappy.googlecode.com/svn/trunk/testdata/"
285
+const baseURL = "https://raw.githubusercontent.com/google/snappy/master/testdata/"
286
287
func downloadTestdata(basename string) (errRet error) {
177
- filename := filepath.Join("testdata", basename)
288
+ filename := filepath.Join(*testdata, basename)
289
+ if stat, err := os.Stat(filename); err == nil && stat.Size() != 0 {
290
+ return nil
291
+ }
292
+
293
+ if !*download {
294
+ return fmt.Errorf("test data not found; skipping benchmark without the -download flag")
295
+ }
296
+ // Download the official snappy C++ implementation reference test data
297
+ // files for benchmarking.
298
+ if err := os.Mkdir(*testdata, 0777); err != nil && !os.IsExist(err) {
299
+ return fmt.Errorf("failed to create testdata: %s", err)
300
+ }
301
+
302
f, err := os.Create(filename)
303
if err != nil {
304
return fmt.Errorf("failed to create %s: %s", filename, err)
@@ -185,36 +309,27 @@ func downloadTestdata(basename string) (errRet error) {
309
os.Remove(filename)
310
}
311
}()
188
- resp, err := http.Get(baseURL + basename)
312
+ url := baseURL + basename
313
+ resp, err := http.Get(url)
314
if err != nil {
190
- return fmt.Errorf("failed to download %s: %s", baseURL+basename, err)
315
+ return fmt.Errorf("failed to download %s: %s", url, err)
316
}
317
defer resp.Body.Close()
318
+ if s := resp.StatusCode; s != http.StatusOK {
319
+ return fmt.Errorf("downloading %s: HTTP status code %d (%s)", url, s, http.StatusText(s))
320
+ }
321
_, err = io.Copy(f, resp.Body)
322
if err != nil {
195
- return fmt.Errorf("failed to write %s: %s", filename, err)
323
+ return fmt.Errorf("failed to download %s to %s: %s", url, filename, err)
324
}
325
return nil
326
}
327
328
func benchFile(b *testing.B, n int, decode bool) {
201
- filename := filepath.Join("testdata", testFiles[n].filename)
202
- if stat, err := os.Stat(filename); err != nil || stat.Size() == 0 {
203
- if !*download {
204
- b.Fatal("test data not found; skipping benchmark without the -download flag")
205
- }
206
- // Download the official snappy C++ implementation reference test data
207
- // files for benchmarking.
208
- if err := os.Mkdir("testdata", 0777); err != nil && !os.IsExist(err) {
209
- b.Fatalf("failed to create testdata: %s", err)
210
- }
211
- for _, tf := range testFiles {
212
- if err := downloadTestdata(tf.filename); err != nil {
213
- b.Fatalf("failed to download testdata: %s", err)
214
- }
215
- }
329
+ if err := downloadTestdata(testFiles[n].filename); err != nil {
330
+ b.Fatalf("failed to download testdata: %s", err)
331
}
217
- data := readFile(b, filename)
332
+ data := readFile(b, filepath.Join(*testdata, testFiles[n].filename))
333
if decode {
334
benchDecode(b, data)
335
} else {
@@ -235,12 +350,6 @@ func Benchmark_UFlat8(b *testing.B) { benchFile(b, 8, true) }
350
func Benchmark_UFlat9(b *testing.B) { benchFile(b, 9, true) }
351
func Benchmark_UFlat10(b *testing.B) { benchFile(b, 10, true) }
352
func Benchmark_UFlat11(b *testing.B) { benchFile(b, 11, true) }
238
-func Benchmark_UFlat12(b *testing.B) { benchFile(b, 12, true) }
239
-func Benchmark_UFlat13(b *testing.B) { benchFile(b, 13, true) }
240
-func Benchmark_UFlat14(b *testing.B) { benchFile(b, 14, true) }
241
-func Benchmark_UFlat15(b *testing.B) { benchFile(b, 15, true) }
242
-func Benchmark_UFlat16(b *testing.B) { benchFile(b, 16, true) }
243
-func Benchmark_UFlat17(b *testing.B) { benchFile(b, 17, true) }
353
func Benchmark_ZFlat0(b *testing.B) { benchFile(b, 0, false) }
354
func Benchmark_ZFlat1(b *testing.B) { benchFile(b, 1, false) }
355
func Benchmark_ZFlat2(b *testing.B) { benchFile(b, 2, false) }
@@ -253,9 +362,3 @@ func Benchmark_ZFlat8(b *testing.B) { benchFile(b, 8, false) }
362
func Benchmark_ZFlat9(b *testing.B) { benchFile(b, 9, false) }
363
func Benchmark_ZFlat10(b *testing.B) { benchFile(b, 10, false) }
364
func Benchmark_ZFlat11(b *testing.B) { benchFile(b, 11, false) }
256
-func Benchmark_ZFlat12(b *testing.B) { benchFile(b, 12, false) }
257
-func Benchmark_ZFlat13(b *testing.B) { benchFile(b, 13, false) }
258
-func Benchmark_ZFlat14(b *testing.B) { benchFile(b, 14, false) }
259
-func Benchmark_ZFlat15(b *testing.B) { benchFile(b, 15, false) }
260
-func Benchmark_ZFlat16(b *testing.B) { benchFile(b, 16, false) }
261
-func Benchmark_ZFlat17(b *testing.B) { benchFile(b, 17, false) }